diff --git a/Examples/Tutorial 4.1 - Model Loading/Program.cs b/Examples/Tutorial 4.1 - Model Loading/Program.cs index a3e0cad..8f974e5 100644 --- a/Examples/Tutorial 4.1 - Model Loading/Program.cs +++ b/Examples/Tutorial 4.1 - Model Loading/Program.cs @@ -85,10 +85,11 @@ private static void OnLoad() Shader = new Shader(Gl, PathExtensions.GetAssemblyPath("shader2.vert"), Default.LightShader, "test"); Texture = new Texture(Gl, "silk.png"); - var model = ObjLoaderFactory.Load(Gl, "Untitled2.obj"); - - _models.Add(model); - _models.Add(model); + if (ObjLoaderFactory.Load(Gl, "D:\\Untitled2.obj", out var model)) + { + _models.Add(model!); + _models.Add(model!); + } } private static void OnUpdate(double deltaTime) diff --git a/ObjLoader/DataStore.cs b/ObjLoader/DataStore.cs index ec82bc1..1840908 100644 --- a/ObjLoader/DataStore.cs +++ b/ObjLoader/DataStore.cs @@ -9,14 +9,23 @@ namespace SharpEngine.Core.ObjLoader { /// - /// Stores parsed OBJ file data such as vertices, normals, texture coordinates, materials and groups. - /// This type is used by the OBJ loader type parsers to collect geometry and material information. + /// Represents the smoothing group information for a face group. /// + /// Determines whether smoothing is enabled for the face group. + /// The ID of the smoothing group, if applicable. + public readonly record struct SmoothingGroup(bool Enabled, int? GroupId); + + /// /// Stores parsed OBJ file data such as vertices, normals, texture coordinates, materials and groups. /// This type is used by the OBJ loader type parsers to collect geometry and material information. /// - public class DataStore : IGroupDataStore, IFaceGroup, ITextureDataStore, INormalDataStore, IVertexDataStore, IMaterialDataStore + /// + /// TODO: This is probably not implemented corretly. + /// We are not handling things on a per-object basis and we are not handling smoothing groups correctly. + /// We should probably have a more complex data structure that can handle multiple objects, each with their own groups, materials, and smoothing groups. + /// + public class DataStore : IGroupDataStore, IFaceGroup, ITextureDataStore, INormalDataStore, IVertexDataStore, IMaterialDataStore, ISmoothingGroupDataStore, IObjectNameDataStore { private Group _currentGroup = null!; @@ -35,6 +44,12 @@ public class DataStore : IGroupDataStore, IFaceGroup, ITextureDataStore, INormal /// Gets the list of groups present in the file. public List Groups { get; } = []; + /// Gets the list of smoothing groups defined in the file. + public List SmoothingGroups { get; } = []; + + /// Gets the name of the object defined in the file, if any. + public string ObjectName { get; private set; } = string.Empty; + /// /// Initializes a new instance of and creates a default group. /// @@ -83,5 +98,17 @@ public void AddVertex(Vertex vertex) /// public void AddMaterial(Material currentMaterial) => Materials.Add(currentMaterial); + + /// + public void SetSmoothingGroup(int groupNumber) + => SmoothingGroups.Add(new SmoothingGroup(true, groupNumber)); + + /// + public void SetSmoothingGroupOff() + => SmoothingGroups.Add(new SmoothingGroup(false, null)); + + /// + public void SetObjectName(string name) + => ObjectName = name; } } \ No newline at end of file diff --git a/ObjLoader/IObjectNameDataStore.cs b/ObjLoader/IObjectNameDataStore.cs new file mode 100644 index 0000000..44806a0 --- /dev/null +++ b/ObjLoader/IObjectNameDataStore.cs @@ -0,0 +1,13 @@ +namespace SharpEngine.Core.ObjLoader; + +/// +/// Holds the name of the object being parsed, allowing it to be set during parsing and retrieved later. +/// +public interface IObjectNameDataStore +{ + /// + /// Sets the name of the object being parsed. + /// + /// The name of the object. + void SetObjectName(string name); +} \ No newline at end of file diff --git a/ObjLoader/ISmoothingGroupDataStore.cs b/ObjLoader/ISmoothingGroupDataStore.cs new file mode 100644 index 0000000..8afc13c --- /dev/null +++ b/ObjLoader/ISmoothingGroupDataStore.cs @@ -0,0 +1,18 @@ +namespace SharpEngine.Core.ObjLoader; + +/// +/// Represents a data store for smoothing group information in an OBJ file, allowing the storage of smoothing group lines for later processing. +/// +public interface ISmoothingGroupDataStore +{ + /// + /// Sets the current smoothing group to the specified group number. + /// + /// The smoothing group number. + void SetSmoothingGroup(int groupNumber); + + /// + /// Turns off the current smoothing group. + /// + void SetSmoothingGroupOff(); +} \ No newline at end of file diff --git a/ObjLoader/Loaders/LoaderBase.cs b/ObjLoader/Loaders/LoaderBase.cs index 5253b8f..206c6d5 100644 --- a/ObjLoader/Loaders/LoaderBase.cs +++ b/ObjLoader/Loaders/LoaderBase.cs @@ -1,3 +1,5 @@ +using Microsoft.Extensions.Logging; +using SharpEngine.Telemetry; using System.IO; using System.IO.Abstractions; @@ -11,6 +13,8 @@ public abstract class LoaderBase private readonly IFileStreamFactory _fileStreamFactory; private readonly IFileManager _fileManager; + private readonly ILogger _logger = LoggingExtensions.CreateLogger(); + /// /// Initializes a new instance of the . /// @@ -27,8 +31,15 @@ protected LoaderBase(IFileStreamFactory fileStreamFactory, IFileManager fileMana /// I/O exceptions (for example FileNotFoundException, UnauthorizedAccessException, IOException) are propagated to the caller. /// /// The path of the file to open and parse. - public void ParseFile(string path) + /// if the file was parsed successfully; otherwise, . + public bool ParseFile(string path) { + if (!_fileStreamFactory.FileSystem.File.Exists(path)) + { + _logger.LogError("File not found: {Path}", path); + return false; + } + using var fileStream = _fileStreamFactory.FileSystem.FileStream.New(path, FileMode.Open, FileAccess.Read); using var lineStreamReader = _fileManager.StreamReader(path); @@ -37,20 +48,28 @@ public void ParseFile(string path) var currentLine = lineStreamReader.ReadLine(); if (!string.IsNullOrWhiteSpace(currentLine)) - ParseLine(currentLine); + { + if (!ParseLine(currentLine)) + { + _logger.LogError("Failed to parse line: {Line}", currentLine); + // return false; + } + } } + + return true; } - private void ParseLine(string currentLine) + private bool ParseLine(string currentLine) { if (currentLine[0] == '#') - return; + return true; var fields = currentLine.Trim().Split(null, 2); var keyword = fields[0].Trim(); var data = fields[1].Trim(); - ParseLine(keyword, data); + return ParseLine(keyword, data); } /// @@ -58,6 +77,7 @@ private void ParseLine(string currentLine) /// /// Keyword that identifies the line type or command. /// Data associated with the keyword; may be empty or contain parameters or raw text to interpret. - protected abstract void ParseLine(string keyword, string data); + /// if the line was parsed successfully; otherwise, . + protected abstract bool ParseLine(string keyword, string data); } } \ No newline at end of file diff --git a/ObjLoader/Loaders/MaterialLoader/MaterialLibraryLoader.cs b/ObjLoader/Loaders/MaterialLoader/MaterialLibraryLoader.cs index 9861031..b45b5a0 100644 --- a/ObjLoader/Loaders/MaterialLoader/MaterialLibraryLoader.cs +++ b/ObjLoader/Loaders/MaterialLoader/MaterialLibraryLoader.cs @@ -74,17 +74,18 @@ private void AddParseAction(string key, Action action) => _parseActionDictionary.Add(key.ToLowerInvariant(), action); /// - protected override void ParseLine(string keyword, string data) + protected override bool ParseLine(string keyword, string data) { var parseAction = GetKeywordAction(keyword, out bool found); if (!found) { _unrecognizedLines.Add(keyword + " " + data); - return; + return false; } parseAction!(data); + return true; } private Action? GetKeywordAction(string keyword, out bool found) diff --git a/ObjLoader/Loaders/ObjLoader/FileManager.cs b/ObjLoader/Loaders/ObjLoader/FileManager.cs new file mode 100644 index 0000000..0e44ce3 --- /dev/null +++ b/ObjLoader/Loaders/ObjLoader/FileManager.cs @@ -0,0 +1,15 @@ +using SharpEngine.Core.ObjLoader.Loader.Loaders; +using System.IO; + +namespace SharpEngine.Core.ObjLoader.Loaders.ObjLoader +{ + /// + /// Represents a testable wrapper for creating stream file related objects. + /// + public class FileManager : IFileManager + { + /// + public StreamReader StreamReader(string path) + => new(path); + } +} \ No newline at end of file diff --git a/ObjLoader/Loaders/ObjLoader/ObjLoader.cs b/ObjLoader/Loaders/ObjLoader/ObjLoader.cs index a79ac5b..fbb9b0f 100644 --- a/ObjLoader/Loaders/ObjLoader/ObjLoader.cs +++ b/ObjLoader/Loaders/ObjLoader/ObjLoader.cs @@ -1,11 +1,13 @@ -using SharpEngine.Core.Entities.Properties.Meshes; +using Microsoft.Extensions.Logging; +using SharpEngine.Core.Entities.Properties.Meshes; using SharpEngine.Core.ObjLoader.Loader.Loaders; using SharpEngine.Core.ObjLoader.TypeParsers; - +using SharpEngine.Telemetry; using Silk.NET.OpenGL; using System.Collections.Generic; using System.IO.Abstractions; +using System.Linq; namespace SharpEngine.Core.ObjLoader.Loaders.ObjLoader { @@ -19,6 +21,8 @@ public class ObjLoader : LoaderBase private readonly List _typeParsers = []; private readonly List _unrecognizedLines = []; + private readonly ILogger _logger = LoggingExtensions.CreateLogger(); + /// /// Initializes a new instance of with the specified file path and data store. /// @@ -46,16 +50,25 @@ public ObjLoader SetupTypeParsers(params ITypeParser[] parsers) } /// - protected override void ParseLine(string keyword, string data) + protected override bool ParseLine(string keyword, string data) { foreach (var typeParser in _typeParsers) + { if (typeParser.CanParse(keyword)) { typeParser.Parse(data); - return; + return true; } + else + { + _logger.LogDebug("Type parser {Parser} cannot parse keyword: {Keyword}", typeParser.GetType().Name, keyword); + } + } _unrecognizedLines.Add(keyword + " " + data); + _logger.LogError("Unrecognized line in OBJ file: {Line}", keyword + " " + data); + + return false; } /// @@ -64,12 +77,18 @@ protected override void ParseLine(string keyword, string data) /// The OpenGL context used to construct GPU resources. public List Load(GL gl) { - ParseFile(_path); + if (!ParseFile(_path)) + { + _logger.LogError("Failed to parse OBJ file: {Path}", _path); + return []; + } + // TODO: Handle multiple meshes and objects in the OBJ file. Currently, this implementation assumes a single mesh. return [ new(gl) { + Name = _dataStore.ObjectName, Vertices2 = _dataStore.Vertices, TextureCoordinates2 = _dataStore.Textures, Normals2 = _dataStore.Normals, diff --git a/ObjLoader/Loaders/ObjLoader/ObjLoaderFactory.cs b/ObjLoader/Loaders/ObjLoader/ObjLoaderFactory.cs index b99400c..20b7d20 100644 --- a/ObjLoader/Loaders/ObjLoader/ObjLoaderFactory.cs +++ b/ObjLoader/Loaders/ObjLoader/ObjLoaderFactory.cs @@ -1,9 +1,10 @@ +using Microsoft.Extensions.Logging; using SharpEngine.Core.Components.Properties.Meshes; using SharpEngine.Core.Entities.Properties.Meshes; -using SharpEngine.Core.ObjLoader.Loader.Loaders; using SharpEngine.Core.ObjLoader.Loader.TypeParsers; using SharpEngine.Core.ObjLoader.Loaders.MaterialLoader; - +using SharpEngine.Core.ObjLoader.TypeParsers; +using SharpEngine.Telemetry; using Silk.NET.OpenGL; using System; @@ -13,16 +14,6 @@ namespace SharpEngine.Core.ObjLoader.Loaders.ObjLoader { - /// - /// Represents a testable wrapper for creating stream file related objects. - /// - public class FileManager : IFileManager - { - /// - public StreamReader StreamReader(string path) - => new(path); - } - /// /// Handles loading 3D models. /// @@ -31,6 +22,8 @@ public static class ObjLoaderFactory private static readonly FileSystem _fileSystem; private static readonly FileManager _fileManager; + private readonly static ILogger _logger = LoggingExtensions.CreateLogger(typeof(ObjLoaderFactory)); + static ObjLoaderFactory() { _fileSystem = new FileSystem(); @@ -45,15 +38,33 @@ static ObjLoaderFactory() /// /// The OpenGL context where the model should be bound. /// Specifies the file path of the mesh to be loaded, which determines the loading method based on its extension. + /// The model loaded from the file. /// The model loaded from the file. /// Thrown when the file extension of the provided path is not recognized as a supported mesh format. - public static Model Load(GL gl, string path) - => Path.GetExtension(path) switch + /// Thrown when no meshes are loaded. + public static bool Load(GL gl, string path, out Model? model) + { + try + { + string fileExtension = Path.GetExtension(path); + model = fileExtension switch + { + FbxExtension => LoadFbx("", path), + ObjExtension => LoadObj(gl, path), + _ => throw new NotSupportedException($"The file extension {fileExtension} is not a supported mesh file.") + }; + + _logger.LogDebug("Successfully loaded model '{name}' from path: {path}.", model.Name, path); + return true; + } + catch (Exception ex) { - FbxExtension => LoadFbx("", path), - ObjExtension => LoadObj(gl, path), - _ => throw new NotSupportedException($"The file extension {Path.GetExtension(path)} is not a supported mesh file.") - }; + _logger.LogWarning(ex, "Failed to load model from path: {path}.", path); + + model = null; + return false; + } + } // TODO: #3 Load fbx mesh from file private static Model LoadFbx(string identifier, string meshFilePath) @@ -64,6 +75,10 @@ private static Model LoadFbx(string identifier, string meshFilePath) private static Model LoadObj(GL gl, string path) { var meshes = LoadObjMeshes(gl, path); + + if (meshes.Count == 0) + throw new InvalidOperationException("The .obj file contains no meshes."); + return new Model(gl, path, meshes); } @@ -76,6 +91,8 @@ private static List LoadObjMeshes(GL gl, string path) var normalParser = new NormalParser(dataStore); var textureParser = new TextureParser(dataStore); var vertexParser = new VertexParser(dataStore); + var smoothingGroupParser = new SmoothingGroupParser(dataStore); + var objectNameParser = new ObjectNameParser(dataStore); var materialLibraryLoader = new MaterialLibraryLoader(dataStore, _fileSystem.FileStream, _fileManager); @@ -84,7 +101,7 @@ private static List LoadObjMeshes(GL gl, string path) var useMaterialParser = new UseMaterialParser(dataStore); var loader = new ObjLoader(path, dataStore, _fileSystem.FileStream, _fileManager) - .SetupTypeParsers(faceParser, groupParser, normalParser, textureParser, vertexParser, materialLibraryParser, useMaterialParser); + .SetupTypeParsers(faceParser, groupParser, normalParser, textureParser, vertexParser, materialLibraryParser, useMaterialParser, smoothingGroupParser, objectNameParser); return loader.Load(gl); } diff --git a/ObjLoader/TypeParsers/FaceParser.cs b/ObjLoader/TypeParsers/FaceParser.cs index 0df5e68..83581d5 100644 --- a/ObjLoader/TypeParsers/FaceParser.cs +++ b/ObjLoader/TypeParsers/FaceParser.cs @@ -27,7 +27,7 @@ public FaceParser(IFaceGroup faceGroup) protected override string Keyword => "f"; /// - public override void Parse(string line) + public override bool Parse(string line) { string[] vertices = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); @@ -35,19 +35,22 @@ public override void Parse(string line) foreach (string vertexString in vertices) { - var faceVertex = ParseFaceVertex(vertexString); - face.AddVertex(faceVertex); + if (TryParseFaceVertex(vertexString, out FaceVertex faceVertex)) + { + face.AddVertex(faceVertex); + } } _faceGroup.AddFace(face); + return true; } - private static FaceVertex ParseFaceVertex(string vertexString) + private static bool TryParseFaceVertex(string vertexString, out FaceVertex faceVertex) { string[] fields = vertexString.Split('/', StringSplitOptions.None); int vertexIndex = fields[0].ParseInvariantInt(); - var faceVertex = new FaceVertex(vertexIndex, 0, 0); + faceVertex = new FaceVertex(vertexIndex, 0, 0); if (fields.Length > 1) { @@ -61,7 +64,7 @@ private static FaceVertex ParseFaceVertex(string vertexString) faceVertex.NormalIndex = normalIndex; } - return faceVertex; + return true; } } } \ No newline at end of file diff --git a/ObjLoader/TypeParsers/GroupParser.cs b/ObjLoader/TypeParsers/GroupParser.cs index e5dae69..0934906 100644 --- a/ObjLoader/TypeParsers/GroupParser.cs +++ b/ObjLoader/TypeParsers/GroupParser.cs @@ -23,7 +23,10 @@ public GroupParser(IGroupDataStore dataStore) protected override string Keyword => "g"; /// - public override void Parse(string line) - => _dataStore.PushGroup(line); + public override bool Parse(string line) + { + _dataStore.PushGroup(line); + return true; + } } } \ No newline at end of file diff --git a/ObjLoader/TypeParsers/ITypeParser.cs b/ObjLoader/TypeParsers/ITypeParser.cs index 1b041ba..3fd685b 100644 --- a/ObjLoader/TypeParsers/ITypeParser.cs +++ b/ObjLoader/TypeParsers/ITypeParser.cs @@ -9,13 +9,14 @@ public interface ITypeParser /// Determines whether the specified keyword can be parsed. /// /// The keyword to check. - /// True if the keyword can be parsed; otherwise, false. + /// if the keyword can be parsed; otherwise, . bool CanParse(string keyword); /// /// Parses a single input line and updates the object's state accordingly. /// /// The input line to parse. - void Parse(string line); + /// if the line was successfully parsed; otherwise, . + bool Parse(string line); } } \ No newline at end of file diff --git a/ObjLoader/TypeParsers/MaterialLibraryParser.cs b/ObjLoader/TypeParsers/MaterialLibraryParser.cs index f622cc8..7be0889 100644 --- a/ObjLoader/TypeParsers/MaterialLibraryParser.cs +++ b/ObjLoader/TypeParsers/MaterialLibraryParser.cs @@ -23,6 +23,10 @@ public MaterialLibraryParser(IMaterialLibraryLoaderFacade facade) protected override string Keyword => "mtllib"; /// - public override void Parse(string line) => _libraryLoaderFacade.Load(line); + public override bool Parse(string line) + { + _libraryLoaderFacade.Load(line); + return true; + } } } \ No newline at end of file diff --git a/ObjLoader/TypeParsers/NormalParser.cs b/ObjLoader/TypeParsers/NormalParser.cs index af7974b..111800b 100644 --- a/ObjLoader/TypeParsers/NormalParser.cs +++ b/ObjLoader/TypeParsers/NormalParser.cs @@ -25,7 +25,7 @@ public NormalParser(INormalDataStore dataStore) protected override string Keyword => "vn"; /// - public override void Parse(string line) + public override bool Parse(string line) { string[] parts = line.Split(' '); @@ -35,6 +35,8 @@ public override void Parse(string line) var normal = new Normal(x, y, z); _dataStore.AddNormal(normal); + + return true; } } } \ No newline at end of file diff --git a/ObjLoader/TypeParsers/ObjectNameParser.cs b/ObjLoader/TypeParsers/ObjectNameParser.cs new file mode 100644 index 0000000..13d0d9f --- /dev/null +++ b/ObjLoader/TypeParsers/ObjectNameParser.cs @@ -0,0 +1,33 @@ +using SharpEngine.Core.ObjLoader.Loader.TypeParsers; +using System; + +namespace SharpEngine.Core.ObjLoader.TypeParsers; + +/// +/// Parses object name lines `o object_name` and handles the object name accordingly. +/// +public class ObjectNameParser : TypeParserBase, ITypeParser +{ + private IObjectNameDataStore _dataStore; + + /// + /// Initializes a new instance of . + /// + /// The data store to store the parsed object name. + public ObjectNameParser(IObjectNameDataStore dataStore) + { + _dataStore = dataStore; + } + + /// + protected override string Keyword => "o"; + + /// + /// Thrown when the line format is invalid. + public override bool Parse(string data) + { + _dataStore.SetObjectName(data); + + return true; + } +} diff --git a/ObjLoader/TypeParsers/SmoothingGroupParser.cs b/ObjLoader/TypeParsers/SmoothingGroupParser.cs new file mode 100644 index 0000000..693bcea --- /dev/null +++ b/ObjLoader/TypeParsers/SmoothingGroupParser.cs @@ -0,0 +1,45 @@ +using SharpEngine.Core.ObjLoader.Loader.TypeParsers; +using System; + +namespace SharpEngine.Core.ObjLoader.TypeParsers; + +/// +/// Parses smoothing group definitions ("s") and populates the current group's smoothing group information. +/// +public class SmoothingGroupParser : TypeParserBase, ITypeParser +{ + private ISmoothingGroupDataStore _dataStore; + + /// + /// Initializes a new instance of . + /// + /// The data store to store the parsed smoothing group information. + public SmoothingGroupParser(ISmoothingGroupDataStore dataStore) + { + _dataStore = dataStore; + } + + /// + protected override string Keyword => "s"; + + /// + /// + /// The line is expected to be in the format "group_number" or "off" so the s at the beginning is not included in the data string passed to this method. + /// + /// Thrown when the line format is invalid or the smoothing group number is not a valid integer. + public override bool Parse(string data) + { + if (data.Equals("off", StringComparison.OrdinalIgnoreCase)) + { + _dataStore.SetSmoothingGroupOff(); + return true; + } + + // Parse the smoothing group number + if (!int.TryParse(data, out int groupNumber)) + throw new FormatException($"Invalid smoothing group identifier: {data}"); + + _dataStore.SetSmoothingGroup(groupNumber); + return true; + } +} diff --git a/ObjLoader/TypeParsers/TextureParser.cs b/ObjLoader/TypeParsers/TextureParser.cs index 43a2e8e..df5a0e3 100644 --- a/ObjLoader/TypeParsers/TextureParser.cs +++ b/ObjLoader/TypeParsers/TextureParser.cs @@ -25,7 +25,7 @@ public TextureParser(ITextureDataStore dataStore) protected override string Keyword => "vt"; /// - public override void Parse(string line) + public override bool Parse(string line) { string[] parts = line.Split(' '); @@ -34,6 +34,8 @@ public override void Parse(string line) var texture = new TextureCoordinate(x, y); _dataStore.AddTexture(texture); + + return true; } } } \ No newline at end of file diff --git a/ObjLoader/TypeParsers/TypeParserBase.cs b/ObjLoader/TypeParsers/TypeParserBase.cs index 7410790..30ac334 100644 --- a/ObjLoader/TypeParsers/TypeParserBase.cs +++ b/ObjLoader/TypeParsers/TypeParserBase.cs @@ -17,6 +17,6 @@ public abstract class TypeParserBase : ITypeParser public bool CanParse(string keyword) => keyword.EqualsOrdinalIgnoreCase(Keyword); /// - public abstract void Parse(string line); + public abstract bool Parse(string line); } } \ No newline at end of file diff --git a/ObjLoader/TypeParsers/UseMaterialParser.cs b/ObjLoader/TypeParsers/UseMaterialParser.cs index aa4d6eb..76d6f5d 100644 --- a/ObjLoader/TypeParsers/UseMaterialParser.cs +++ b/ObjLoader/TypeParsers/UseMaterialParser.cs @@ -1,5 +1,4 @@ -using SharpEngine.Core.Components.ObjLoader.DataStore; -using SharpEngine.Core.ObjLoader.TypeParsers; +using SharpEngine.Core.ObjLoader.TypeParsers; namespace SharpEngine.Core.ObjLoader.Loader.TypeParsers { @@ -23,6 +22,10 @@ public UseMaterialParser(IMaterialDataStore dataStore) protected override string Keyword => "usemtl"; /// - public override void Parse(string line) => _dataStore.SetMaterial(line); + public override bool Parse(string line) + { + _dataStore.SetMaterial(line); + return true; + } } } \ No newline at end of file diff --git a/ObjLoader/TypeParsers/VertexParser.cs b/ObjLoader/TypeParsers/VertexParser.cs index 94159b3..fadb733 100644 --- a/ObjLoader/TypeParsers/VertexParser.cs +++ b/ObjLoader/TypeParsers/VertexParser.cs @@ -27,7 +27,7 @@ public VertexParser(IVertexDataStore dataStore) protected override string Keyword => "v"; /// - public override void Parse(string line) + public override bool Parse(string line) { string[] parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); @@ -41,6 +41,8 @@ public override void Parse(string line) }; _dataStore.AddVertex(vertex); + + return true; } } } \ No newline at end of file diff --git a/SharpEngine.Core.Components/Properties/Meshes/Model.cs b/SharpEngine.Core.Components/Properties/Meshes/Model.cs index 360b321..2947ec0 100644 --- a/SharpEngine.Core.Components/Properties/Meshes/Model.cs +++ b/SharpEngine.Core.Components/Properties/Meshes/Model.cs @@ -23,12 +23,14 @@ public class Model : IDisposable /// Gets the list of meshes contained within the model. public List Meshes { get; set; } = []; + public string Name { get; } + /// /// Initializes a new instance of the Model class with the specified OpenGL context and model file path. /// /// The GL context used for rendering and resource management. /// The file path of the model asset. - public Model(GL gl, string path) + public Model(GL gl, string path) : this(gl, path, Enumerable.Empty()) { _gl = gl; Path = path; @@ -47,7 +49,11 @@ public Model(GL gl, string path, IEnumerable meshes) { _gl = gl; Path = path; - Meshes = [.. meshes.Select(ProcessMesh)]; + + if (meshes.Any()) + Meshes = meshes.Select(ProcessMesh).ToList(); + + Name = meshes.FirstOrDefault()?.Name ?? System.IO.Path.GetFileNameWithoutExtension(path) ?? "UnnamedModel"; } /// diff --git a/SharpEngine.Core/Abstractions/ISettings.cs b/SharpEngine.Core/Abstractions/ISettings.cs index 6800463..d0984b2 100644 --- a/SharpEngine.Core/Abstractions/ISettings.cs +++ b/SharpEngine.Core/Abstractions/ISettings.cs @@ -1,4 +1,5 @@ -using SharpEngine.Core.Renderers; +using Microsoft.Extensions.Logging; +using SharpEngine.Core.Renderers; using Silk.NET.Input; namespace SharpEngine.Core.Interfaces; @@ -26,6 +27,11 @@ public interface ISettings /// Gets or sets the secondary mouse button. public MouseButton SecondaryButton { get; set; } + + /// + /// Gets or sets the log level for the game engine. + /// + public LogLevel LogLevel { get; set; } } /// @@ -45,4 +51,7 @@ public class DefaultSettings : ISettings /// public MouseButton SecondaryButton { get; set; } = MouseButton.Right; + + /// + public LogLevel LogLevel { get; set; } = LogLevel.Information; } diff --git a/SharpEngine.Core/Entities/Views/Settings/DefaultViewSettings.cs b/SharpEngine.Core/Entities/Views/Settings/DefaultViewSettings.cs index 03d03f7..dbeef01 100644 --- a/SharpEngine.Core/Entities/Views/Settings/DefaultViewSettings.cs +++ b/SharpEngine.Core/Entities/Views/Settings/DefaultViewSettings.cs @@ -1,4 +1,5 @@ -using SharpEngine.Core.Renderers; +using Microsoft.Extensions.Logging; +using SharpEngine.Core.Renderers; using Silk.NET.Input; using Silk.NET.Maths; @@ -48,4 +49,7 @@ public DefaultViewSettings() /// public MouseButton SecondaryButton { get; set; } = MouseButton.Right; + + /// + public LogLevel LogLevel { get; set; } = LogLevel.Information; } diff --git a/SharpEngine.Core/Entities/Views/Settings/ViewSettings.cs b/SharpEngine.Core/Entities/Views/Settings/ViewSettings.cs index 09c65cb..013f31b 100644 --- a/SharpEngine.Core/Entities/Views/Settings/ViewSettings.cs +++ b/SharpEngine.Core/Entities/Views/Settings/ViewSettings.cs @@ -1,4 +1,5 @@ -using SharpEngine.Core.Renderers; +using Microsoft.Extensions.Logging; +using SharpEngine.Core.Renderers; using Silk.NET.Input; using Silk.NET.Windowing; @@ -52,4 +53,7 @@ public ViewSettings() /// public MouseButton SecondaryButton { get; set; } = MouseButton.Right; + + /// + public LogLevel LogLevel { get; set; } = LogLevel.Information; }