diff --git a/Examples/Minimal/Program.cs b/Examples/Minimal/Program.cs index 26310ec..36a530e 100644 --- a/Examples/Minimal/Program.cs +++ b/Examples/Minimal/Program.cs @@ -16,6 +16,7 @@ public static void Main(string[] _) var game = new Minimal(); using var window = new Window(game); + window.Initialize(); window.Run(); } } diff --git a/Examples/MultipleWindows/Program.cs b/Examples/MultipleWindows/Program.cs index 67751b3..b1dfc16 100644 --- a/Examples/MultipleWindows/Program.cs +++ b/Examples/MultipleWindows/Program.cs @@ -117,10 +117,13 @@ private static Window CreateWindow() private static void EnqueueWindow() { var window = CreateWindow(); - foreach (var mouse in window!.Input!.Mice) - mouse.Click += Mouse_Click; - _inputContexts.Add(window.Input); + // Prefer the higher-level Window event to avoid wiring low-level input handlers. + window.OnButtonMouseDown += (mouse, button) => Mouse_Click(mouse, (MouseButton)button, mouse.Position); + + if (window.Input is not null) + _inputContexts.Add(window.Input); + _windows.Add(window); } diff --git a/ObjLoader/DataStore.cs b/ObjLoader/DataStore.cs index 5833552..ec82bc1 100644 --- a/ObjLoader/DataStore.cs +++ b/ObjLoader/DataStore.cs @@ -79,5 +79,9 @@ public void AddNormal(Normal normal) /// public void AddVertex(Vertex vertex) => Vertices.Add(vertex); + + /// + public void AddMaterial(Material currentMaterial) + => Materials.Add(currentMaterial); } } \ No newline at end of file diff --git a/ObjLoader/IMaterialDataStore.cs b/ObjLoader/IMaterialDataStore.cs index 38f3168..9424ed5 100644 --- a/ObjLoader/IMaterialDataStore.cs +++ b/ObjLoader/IMaterialDataStore.cs @@ -1,14 +1,21 @@ -namespace SharpEngine.Core.ObjLoader; +using SharpEngine.Core.Components.Properties; + +namespace SharpEngine.Core.ObjLoader; /// /// Contains definitions for the material data store, which provides methods to manage material information during the OBJ file loading process. /// public interface IMaterialDataStore { + /// + /// Adds a material to the material data store. + /// + /// The material to add. + void AddMaterial(Material currentMaterial); + /// /// Sets the material for the current group. /// /// The name of the material to set. - public void SetMaterial(string materialName); - + void SetMaterial(string materialName); } \ No newline at end of file diff --git a/ObjLoader/Loaders/IFileManager.cs b/ObjLoader/Loaders/IFileManager.cs new file mode 100644 index 0000000..21e33eb --- /dev/null +++ b/ObjLoader/Loaders/IFileManager.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace SharpEngine.Core.ObjLoader.Loader.Loaders +{ + /// + /// Represents an abstraction of a stream reader for testability. + /// + public interface IFileManager + { + /// + StreamReader StreamReader(string path); + } +} \ No newline at end of file diff --git a/ObjLoader/Loaders/LoaderBase.cs b/ObjLoader/Loaders/LoaderBase.cs index 4ecbabc..5253b8f 100644 --- a/ObjLoader/Loaders/LoaderBase.cs +++ b/ObjLoader/Loaders/LoaderBase.cs @@ -1,4 +1,5 @@ using System.IO; +using System.IO.Abstractions; namespace SharpEngine.Core.ObjLoader.Loader.Loaders { @@ -7,6 +8,18 @@ namespace SharpEngine.Core.ObjLoader.Loader.Loaders /// public abstract class LoaderBase { + private readonly IFileStreamFactory _fileStreamFactory; + private readonly IFileManager _fileManager; + + /// + /// Initializes a new instance of the . + /// + protected LoaderBase(IFileStreamFactory fileStreamFactory, IFileManager fileManager) + { + _fileStreamFactory = fileStreamFactory; + _fileManager = fileManager; + } + /// /// Parses the file at the specified path and processes each line via . /// @@ -16,8 +29,8 @@ public abstract class LoaderBase /// The path of the file to open and parse. public void ParseFile(string path) { - using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); - using var lineStreamReader = new StreamReader(fileStream); + using var fileStream = _fileStreamFactory.FileSystem.FileStream.New(path, FileMode.Open, FileAccess.Read); + using var lineStreamReader = _fileManager.StreamReader(path); while (!lineStreamReader.EndOfStream) { diff --git a/ObjLoader/Loaders/MaterialLoader/MaterialLibraryLoader.cs b/ObjLoader/Loaders/MaterialLoader/MaterialLibraryLoader.cs index 39aa7d3..e2dcb91 100644 --- a/ObjLoader/Loaders/MaterialLoader/MaterialLibraryLoader.cs +++ b/ObjLoader/Loaders/MaterialLoader/MaterialLibraryLoader.cs @@ -1,9 +1,11 @@ -using SharpEngine.Core.Components.Properties; +using SharpEngine.Core.Components.ObjLoader.DataStore; +using SharpEngine.Core.Components.Properties; using SharpEngine.Core.ObjLoader.Loader.Loaders; using SharpEngine.Shared.Extensions; using System; using System.Collections.Generic; +using System.IO.Abstractions; using System.Numerics; namespace SharpEngine.Core.ObjLoader.Loaders.MaterialLoader @@ -19,7 +21,7 @@ namespace SharpEngine.Core.ObjLoader.Loaders.MaterialLoader /// public class MaterialLibraryLoader : LoaderBase { - private readonly DataStore _dataStore; + private readonly IMaterialDataStore _dataStore; private readonly Dictionary> _parseActionDictionary = []; private readonly List _unrecognizedLines = []; @@ -35,7 +37,9 @@ public class MaterialLibraryLoader : LoaderBase /// Ns, d, Tr, illum, map_*, bump, disp, decal) to populate Material instances. /// /// DataStore used to register and store parsed materials and texture references. - public MaterialLibraryLoader(DataStore dataStore) + /// Represents a factory for creating file streams. + /// Represents a manager for handling file operations. + public MaterialLibraryLoader(IMaterialDataStore dataStore, IFileStreamFactory fileStreamFactory, IFileManager fileManager) : base(fileStreamFactory, fileManager) { _dataStore = dataStore; @@ -92,7 +96,7 @@ protected override void ParseLine(string keyword, string data) private void PushMaterial(string materialName) { _currentMaterial = new Material(materialName); - _dataStore.Materials.Add(_currentMaterial); + _dataStore.AddMaterial(_currentMaterial); } private static Vector3 ParseVec3(string data) diff --git a/ObjLoader/Loaders/ObjLoader/ObjLoader.cs b/ObjLoader/Loaders/ObjLoader/ObjLoader.cs index cfcd5f0..a79ac5b 100644 --- a/ObjLoader/Loaders/ObjLoader/ObjLoader.cs +++ b/ObjLoader/Loaders/ObjLoader/ObjLoader.cs @@ -1,8 +1,11 @@ using SharpEngine.Core.Entities.Properties.Meshes; using SharpEngine.Core.ObjLoader.Loader.Loaders; using SharpEngine.Core.ObjLoader.TypeParsers; + using Silk.NET.OpenGL; + using System.Collections.Generic; +using System.IO.Abstractions; namespace SharpEngine.Core.ObjLoader.Loaders.ObjLoader { @@ -21,7 +24,9 @@ public class ObjLoader : LoaderBase /// /// The path to the OBJ file. /// The data store to populate during parsing. - public ObjLoader(string path, DataStore dataStore) + /// Represents a factory for creating file streams. + /// Represents a manager for handling file operations. + public ObjLoader(string path, DataStore dataStore, IFileStreamFactory fileStreamFactory, IFileManager fileManager) : base(fileStreamFactory, fileManager) { _path = path; _dataStore = dataStore; diff --git a/ObjLoader/Loaders/ObjLoader/ObjLoaderFactory.cs b/ObjLoader/Loaders/ObjLoader/ObjLoaderFactory.cs index 5a7d90c..b99400c 100644 --- a/ObjLoader/Loaders/ObjLoader/ObjLoaderFactory.cs +++ b/ObjLoader/Loaders/ObjLoader/ObjLoaderFactory.cs @@ -1,21 +1,42 @@ +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 Silk.NET.OpenGL; using System; using System.Collections.Generic; using System.IO; - -using SharpEngine.Core.Entities.Properties.Meshes; -using SharpEngine.Core.Components.Properties.Meshes; -using SharpEngine.Core.ObjLoader.Loaders.MaterialLoader; -using SharpEngine.Core.ObjLoader.Loader.TypeParsers; +using System.IO.Abstractions; 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. /// public static class ObjLoaderFactory { + private static readonly FileSystem _fileSystem; + private static readonly FileManager _fileManager; + + static ObjLoaderFactory() + { + _fileSystem = new FileSystem(); + _fileManager = new FileManager(); + } + const string FbxExtension = ".fbx"; const string ObjExtension = ".obj"; @@ -56,13 +77,13 @@ private static List LoadObjMeshes(GL gl, string path) var textureParser = new TextureParser(dataStore); var vertexParser = new VertexParser(dataStore); - var materialLibraryLoader = new MaterialLibraryLoader(dataStore); + var materialLibraryLoader = new MaterialLibraryLoader(dataStore, _fileSystem.FileStream, _fileManager); var materialLoader = new MaterialLibraryLoaderFacade(materialLibraryLoader, path); var materialLibraryParser = new MaterialLibraryParser(materialLoader); var useMaterialParser = new UseMaterialParser(dataStore); - var loader = new ObjLoader(path, dataStore) + var loader = new ObjLoader(path, dataStore, _fileSystem.FileStream, _fileManager) .SetupTypeParsers(faceParser, groupParser, normalParser, textureParser, vertexParser, materialLibraryParser, useMaterialParser); return loader.Load(gl); diff --git a/ObjLoader/SharpEngine.Core.ObjLoader.csproj b/ObjLoader/SharpEngine.Core.ObjLoader.csproj index 7a52e96..04ffde4 100644 --- a/ObjLoader/SharpEngine.Core.ObjLoader.csproj +++ b/ObjLoader/SharpEngine.Core.ObjLoader.csproj @@ -7,6 +7,7 @@ + diff --git a/SharpEngine.Core.ArchitectureTests/Class1.cs b/SharpEngine.Core.ArchitectureTests/Class1.cs new file mode 100644 index 0000000..77198f6 --- /dev/null +++ b/SharpEngine.Core.ArchitectureTests/Class1.cs @@ -0,0 +1,6 @@ +namespace SharpEngine.Core.ArchitectureTests; + +public class Class1 +{ + +} diff --git a/SharpEngine.Core.ArchitectureTests/SharpEngine.Core.ArchitectureTests.csproj b/SharpEngine.Core.ArchitectureTests/SharpEngine.Core.ArchitectureTests.csproj new file mode 100644 index 0000000..b760144 --- /dev/null +++ b/SharpEngine.Core.ArchitectureTests/SharpEngine.Core.ArchitectureTests.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/SharpEngine.Core/Audio/Audio.cs b/SharpEngine.Core/Audio/Audio.cs index 7d9d171..7d38c4d 100644 --- a/SharpEngine.Core/Audio/Audio.cs +++ b/SharpEngine.Core/Audio/Audio.cs @@ -8,6 +8,8 @@ namespace SharpEngine.Core.Audio; /// public static class Audio { + private static AudioPlayerBase? audioPlayer = null; + /// /// Plays an audio file if it is in the WAV format. /// @@ -18,11 +20,34 @@ public static void Play(string filePath) switch (Path.GetExtension(filePath)) { case ".wav": - new WavPlayer().Play(filePath); + audioPlayer = new WavPlayer(); + audioPlayer.Play(filePath); + break; + + case ".mp3": + audioPlayer = new Mp3Player(); + audioPlayer.Play(filePath); break; default: throw new NotSupportedException("Unsupported file format."); } } + + /// + public static void Stop() + { + audioPlayer?.Stop(); + audioPlayer = null; + } + + /// + /// Pauses the currently playing audio, if any. + /// + public static void Pause() => audioPlayer?.Pause(); + + /// + /// Resumes the currently paused audio, if any. + /// + public static void Resume() => audioPlayer?.Resume(); } diff --git a/SharpEngine.Core/Audio/AudioBuffer.cs b/SharpEngine.Core/Audio/AudioBuffer.cs index 506f426..6041946 100644 --- a/SharpEngine.Core/Audio/AudioBuffer.cs +++ b/SharpEngine.Core/Audio/AudioBuffer.cs @@ -29,7 +29,7 @@ public AudioBuffer(AL al) /// /// The data to be loaded into the buffer. /// Details about the data. - public void LoadData(ReadOnlySpan data, WavData wavData) + public void LoadData(ReadOnlySpan data, AudioData wavData) { unsafe { diff --git a/SharpEngine.Core/Audio/WavData.cs b/SharpEngine.Core/Audio/AudioData.cs similarity index 90% rename from SharpEngine.Core/Audio/WavData.cs rename to SharpEngine.Core/Audio/AudioData.cs index 80ac937..fa81d42 100644 --- a/SharpEngine.Core/Audio/WavData.cs +++ b/SharpEngine.Core/Audio/AudioData.cs @@ -3,9 +3,9 @@ namespace SharpEngine.Core.Audio; /// -/// Represents audio data in WAV format. +/// Represents the data in a audio file. /// -public class WavData +public class AudioData { /// Gets or sets the number of channels used in the data. public short NumChannels { get; set; } diff --git a/SharpEngine.Core/Audio/AudioDevice.cs b/SharpEngine.Core/Audio/AudioDevice.cs index aa0560d..d5429bc 100644 --- a/SharpEngine.Core/Audio/AudioDevice.cs +++ b/SharpEngine.Core/Audio/AudioDevice.cs @@ -8,6 +8,14 @@ namespace SharpEngine.Core.Audio; /// public unsafe class AudioDevice : IDisposable { + // Shared single AudioDevice instance to ensure a single OpenAL context is used + private static readonly AudioDevice _shared = new AudioDevice(); + + /// + /// Gets the shared audio device instance used by the application. + /// + public static AudioDevice Shared => _shared; + /// Gets the OpenAL context. public AL Al { get; } diff --git a/SharpEngine.Core/Audio/AudioPlayerBase.cs b/SharpEngine.Core/Audio/AudioPlayerBase.cs index 5778672..8df590a 100644 --- a/SharpEngine.Core/Audio/AudioPlayerBase.cs +++ b/SharpEngine.Core/Audio/AudioPlayerBase.cs @@ -1,4 +1,5 @@ using SharpEngine.Core.Scenes; +using Silk.NET.OpenAL; using System; using System.IO; @@ -17,7 +18,7 @@ public abstract class AudioPlayerBase : SceneNode public AudioProperties AudioProperties { get; set; } /// Contains the audio data of the media file. - protected readonly WavData Data; + protected readonly AudioData Data; /// Represents a container for the . protected readonly AudioBuffer AudioBuffer; @@ -30,9 +31,10 @@ public abstract class AudioPlayerBase : SceneNode /// protected AudioPlayerBase() { - Data = new WavData(); + Data = new AudioData(); - var audioDevice = new AudioDevice(); + // Use the shared audio device so multiple players share the same OpenAL context + var audioDevice = AudioDevice.Shared; AudioBuffer = new AudioBuffer(audioDevice.Al); AudioSource = new AudioSource(audioDevice.Al); @@ -61,7 +63,27 @@ protected virtual void ValidateFile(string filePath) } /// - /// Stops the audio playback of the associated AudioSource. + /// Stops the audio playback of the associated . /// - public virtual void Stop() => AudioSource.Stop(); + public virtual void Stop() + { + // Stop playback but do not delete the source here. Deleting the source + // in AudioSource.Stop caused subsequent players to generate new sources + // while buffers still referenced old sources, leading to overlapping audio. + AudioSource.Stop(); + + // Detach buffer from source so it can be reused safely + var al = AudioDevice.Shared.Al; + al.SetSourceProperty(AudioSource.Get(), SourceInteger.Buffer, 0); + } + + /// + /// Pauses the audio playback of the associated . + /// + public virtual void Pause() => AudioSource.Pause(); + + /// + /// Resumes the audio playback of the associated . + /// + public virtual void Resume() => AudioSource.Resume(); } diff --git a/SharpEngine.Core/Audio/AudioSource.cs b/SharpEngine.Core/Audio/AudioSource.cs index d0a3669..6c11cd4 100644 --- a/SharpEngine.Core/Audio/AudioSource.cs +++ b/SharpEngine.Core/Audio/AudioSource.cs @@ -42,6 +42,29 @@ public void Play(AudioBuffer buffer) public void Stop() { _al.SourceStop(_source); + } + + /// + /// Pauses playback of the source. + /// + public void Pause() + { + _al.SourcePause(_source); + } + + /// + /// Resumes playback of the source if it was paused. + /// + public void Resume() + { + _al.SourcePlay(_source); + } + + /// + /// Deletes the underlying OpenAL source. Call when the source will no longer be used. + /// + public void Dispose() + { _al.DeleteSource(_source); } } \ No newline at end of file diff --git a/SharpEngine.Core/Audio/Mp3Player.cs b/SharpEngine.Core/Audio/Mp3Player.cs new file mode 100644 index 0000000..1fe5ff3 --- /dev/null +++ b/SharpEngine.Core/Audio/Mp3Player.cs @@ -0,0 +1,74 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Buffers.Binary; +using System.IO; +using NLayer; +using Silk.NET.OpenAL; + +namespace SharpEngine.Core.Audio; + +internal class Mp3Player : AudioPlayerBase +{ + private static readonly ILogger Logger = LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger(); + + /// + protected override string FileExtension => AudioFileExtensions.Mp3Extension; + + public Mp3Player() + { + } + + /// + /// Thrown if the audio data contains more than 2 channels. + /// Thrown if the file path is invalid. + /// Thrown if the file is not found. + public override void Play(string filePath) + { + ValidateFile(filePath); + + using var fs = File.OpenRead(filePath); + using var mpeg = new MpegFile(fs); + + int sampleRate = mpeg.SampleRate; + int channels = mpeg.Channels; + + // Decode to float samples and convert to 16-bit PCM + using var pcmStream = new MemoryStream(); + var floatBuffer = new float[4096]; + int samplesRead; + while ((samplesRead = mpeg.ReadSamples(floatBuffer, 0, floatBuffer.Length)) > 0) + { + // samplesRead is number of float samples (interleaved channels) + var bytes = new byte[samplesRead * 2]; + for (int i = 0; i < samplesRead; i++) + { + float f = floatBuffer[i]; + // clamp and convert + int intVal = (int)MathF.Round(f * 32767f); + if (intVal > short.MaxValue) intVal = short.MaxValue; + else if (intVal < short.MinValue) intVal = short.MinValue; + short s = (short)intVal; + bytes[i * 2] = (byte)(s & 0xFF); + bytes[i * 2 + 1] = (byte)((s >> 8) & 0xFF); + } + + pcmStream.Write(bytes, 0, bytes.Length); + } + + var pcmData = pcmStream.ToArray(); + + // Fill WavData + Data.NumChannels = (short)channels; + Data.SampleRate = sampleRate; + Data.BitsPerSample = 16; + Data.Format = channels switch + { + 1 => BufferFormat.Mono16, + 2 => BufferFormat.Stereo16, + _ => throw new InvalidOperationException($"Can't play audio with {channels} channels.") + }; + + AudioBuffer.LoadData(pcmData, Data); + AudioSource.Play(AudioBuffer); + } +} \ No newline at end of file diff --git a/SharpEngine.Core/Audio/WavPlayer.cs b/SharpEngine.Core/Audio/WavPlayer.cs index 7869f28..33401aa 100644 --- a/SharpEngine.Core/Audio/WavPlayer.cs +++ b/SharpEngine.Core/Audio/WavPlayer.cs @@ -15,7 +15,7 @@ public class WavPlayer : AudioPlayerBase private static readonly ILogger Logger = LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger(); /// - protected override string FileExtension => ".wav"; + protected override string FileExtension => AudioFileExtensions.WavExtension; private static class WavConstants { @@ -90,7 +90,7 @@ private static bool CheckHeader(ReadOnlySpan file, ref int index, ReadOnly return true; } - private static void ParseFormat(ReadOnlySpan formatChunk, WavData wavData) + private static void ParseFormat(ReadOnlySpan formatChunk, AudioData wavData) { int index = 0; var audioFormat = BinaryPrimitives.ReadInt16LittleEndian(formatChunk.Slice(index, WavConstants.AudioFormatLength)); diff --git a/SharpEngine.Core/Components/ObjLoader/DataStore/IVertexDataStore.cs b/SharpEngine.Core/Components/ObjLoader/DataStore/IVertexDataStore.cs deleted file mode 100644 index e69de29..0000000 diff --git a/SharpEngine.Core/Extensions/AssemblyExtensions.cs b/SharpEngine.Core/Extensions/AssemblyExtensions.cs index 39a3315..2501343 100644 --- a/SharpEngine.Core/Extensions/AssemblyExtensions.cs +++ b/SharpEngine.Core/Extensions/AssemblyExtensions.cs @@ -4,7 +4,7 @@ namespace SharpEngine.Core.Extensions; /// -/// Contains extension methods for the class, providing additional functionality related to assembly metadata and versioning. +/// Contains extension methods for the . /// public static class AssemblyExtensions { @@ -13,6 +13,7 @@ public static class AssemblyExtensions /// /// The assembly to get the version for. /// The version of the assembly. + /// Thrown when the assembly version is null. public static Version GetVersion(this Assembly assembly) => assembly.GetName().Version!; } diff --git a/SharpEngine.Core/Handlers/WindowHandler.cs b/SharpEngine.Core/Handlers/WindowHandler.cs index ef2d1ec..d82c773 100644 --- a/SharpEngine.Core/Handlers/WindowHandler.cs +++ b/SharpEngine.Core/Handlers/WindowHandler.cs @@ -200,8 +200,10 @@ private static Windowing.Window CreateWindow(WindowOptions options) private static void EnqueueWindow(WindowOptions options) { var window = CreateWindow(options); - foreach (var mouse in window!.Input!.Mice) - mouse.Click += Mouse_Click; + // Subscribe to the window's high-level mouse button event instead of + // attaching to the low-level Input.Mice click handlers. This centralizes + // input handling inside Window and avoids duplicate wiring. + window.OnButtonMouseDown += (mouse, button) => Mouse_Click(mouse, (Silk.NET.Input.MouseButton)button, mouse.Position); _inputContexts.Add(window.Input); _windows.Add(window); @@ -217,13 +219,13 @@ private void RegisterWindow(SilkWindow window, bool isMain = false) if (window is null) return; - if (window.Input is not null) - { - foreach (var mouse in window.Input.Mice) - mouse.Click += Mouse_Click; + // Prefer the higher-level event on the Window class for mouse button + // notifications rather than wiring Input.Mice click events directly. + if (window is Windowing.Window w) + w.OnButtonMouseDown += (mouse, button) => Mouse_Click(mouse, (Silk.NET.Input.MouseButton)button, mouse.Position); + if (window.Input is not null) _inputContexts.Add(window.Input); - } _windows.Add(window); diff --git a/SharpEngine.Core/Renderers/ParticleRenderer.cs b/SharpEngine.Core/Renderers/ParticleRenderer.cs index 53f6d93..e34e2e3 100644 --- a/SharpEngine.Core/Renderers/ParticleRenderer.cs +++ b/SharpEngine.Core/Renderers/ParticleRenderer.cs @@ -1,4 +1,7 @@ -using SharpEngine.Core.Interfaces; +using Microsoft.Extensions.Logging; +using SharpEngine.Core.Entities.Views; +using SharpEngine.Core.Interfaces; +using SharpEngine.Core.Scenes; using System; using System.Threading.Tasks; @@ -17,9 +20,27 @@ public ParticleRenderer(ISettings settings) : base(settings) { } + /// + /// Initializes a new instance of . + /// + public ParticleRenderer(CameraView camera, ISettings settings, Scene scene) + : this(camera, settings, scene, LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger()) + { + } + + /// + /// Initializes a new instance of . + /// + public ParticleRenderer(CameraView camera, ISettings settings, Scene scene, ILogger logger) : base(settings) + { + // _scene = scene; + // _camera = camera; + // _logger = logger; + } + /// public override RenderFlags RenderFlag => RenderFlags.All; /// - public override Task Render() => throw new NotImplementedException(); + public override Task Render() => Task.CompletedTask; // => throw new NotImplementedException(); } diff --git a/SharpEngine.Core/Scenes/Scene.cs b/SharpEngine.Core/Scenes/Scene.cs index ff4db26..6ab4c91 100644 --- a/SharpEngine.Core/Scenes/Scene.cs +++ b/SharpEngine.Core/Scenes/Scene.cs @@ -1,10 +1,12 @@ using Microsoft.Extensions.Logging; using SharpEngine.Core.Entities.Properties; +using SharpEngine.Telemetry; using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; namespace SharpEngine.Core.Scenes; @@ -14,7 +16,7 @@ namespace SharpEngine.Core.Scenes; /// public class Scene { - private static readonly ILogger Logger = LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger(); + private static readonly ILogger Logger = LoggingExtensions.CreateLogger(); /// The file extension by which saved scenes are associated with. public const string SceneFileExtension = "sharpscene"; @@ -22,6 +24,7 @@ public class Scene private string? _fileFullPath; /// Gets or sets whether the scene has unsaved changes. + [JsonIgnore] public bool HasUnsavedChanges { get; set; } /// Gets or sets the name of the scene file. @@ -214,21 +217,29 @@ public void SetFileFullPath(string sceneFile) public bool HasSaveFile() => _fileFullPath is not null; + /// + /// Saves the scene. + /// + public void SaveScene() + => SaveSceneAsync().GetAwaiter().GetResult(); + /// /// Saves the scene. /// /// A representing an asynchronous operation. - public async Task SaveScene() - => await SaveScene(_fileFullPath!); + public async Task SaveSceneAsync() + => await SaveSceneAsync(_fileFullPath!); /// /// Saves the scene to the given . /// /// The name of the file to be saved. /// A representing an asynchronous operation. - public async Task SaveScene(string fileName) + public async Task SaveSceneAsync(string fileName) { var json = JsonSerializer.Serialize(this); await System.IO.File.WriteAllTextAsync(fileName, json); + + HasUnsavedChanges = false; } } \ No newline at end of file diff --git a/SharpEngine.Core/SharpEngine.Core.csproj b/SharpEngine.Core/SharpEngine.Core.csproj index 04fb6cb..8760f7b 100644 --- a/SharpEngine.Core/SharpEngine.Core.csproj +++ b/SharpEngine.Core/SharpEngine.Core.csproj @@ -18,6 +18,7 @@ true README.md + @@ -26,6 +27,7 @@ + diff --git a/SharpEngine.Core/Windowing/SilkWindow.cs b/SharpEngine.Core/Windowing/SilkWindow.cs index 1f5e1dd..c7c9fd3 100644 --- a/SharpEngine.Core/Windowing/SilkWindow.cs +++ b/SharpEngine.Core/Windowing/SilkWindow.cs @@ -75,6 +75,14 @@ public Vector2D Size set => CurrentWindow.Size = value; } + /// Gets the width of the window in pixels. + /// Use to change the window size. + public int Width => Size.X; + + /// Gets the height of the window in pixels. + /// Use to change the window size. + public int Height => Size.Y; + /// public string Title { diff --git a/SharpEngine.Core/Windowing/Window.cs b/SharpEngine.Core/Windowing/Window.cs index 903f3df..15afa84 100644 --- a/SharpEngine.Core/Windowing/Window.cs +++ b/SharpEngine.Core/Windowing/Window.cs @@ -92,14 +92,29 @@ public static GL SharedGL public GL GetGL() => _gl; private void SetGL(GL gl) => _gl = gl; + /// + /// Initializes a new instance of . + /// + /// The game instance. + public Window(Game game) + : this(game.Scene, game.Camera.Settings, logger: null, renderers: null) { } + + /// + /// Initializes a new instance of . + /// + /// The scene to be rendered. + /// The settings for the window. + public Window(Scene scene, IViewSettings settings) + : this(scene, settings, logger: null, renderers: null) { } + /// /// Initializes a new instance of . /// /// The camera the window should render from. /// Contains the game scene. /// The settings for the window. - public Window(CameraView camera, Scene scene, IViewSettings settings) : - this(camera, scene, settings, LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger()) { } + public Window(CameraView camera, Scene scene, IViewSettings settings) + : this(camera, scene, settings, logger: null, renderers: null) { } /// /// Initializes a new instance of . @@ -107,9 +122,9 @@ public Window(CameraView camera, Scene scene, IViewSettings settings) : /// The camera the window should render from. /// Contains the game scene. /// The settings for the window. - /// The logger for the window. + /// The logger for the window. When , a default logger will be used. /// The renderers for the window. - public Window(CameraView camera, Scene scene, IViewSettings settings, ILogger logger, IEnumerable? renderers = null) + public Window(CameraView camera, Scene scene, IViewSettings settings, ILogger? logger = null, IEnumerable? renderers = null) { // TODO: // Should the developer need to call for a window initialization? @@ -119,31 +134,14 @@ public Window(CameraView camera, Scene scene, IViewSettings settings, ILogger builder.AddConsole()).CreateLogger(); CheckEngineVersion(); - // NOTE: Window initialization is intentionally not performed automatically - // here. Call InitializeWindow() explicitly when ready to create the underlying - // native window and load resources. See the TODO in the project for reasoning. - } - - private void CheckEngineVersion() - { - var project = new Project(); - var currentAssemlyVersion = typeof(Window).Assembly.GetVersion(); - - if (currentAssemlyVersion != project.EngineVersion) - _logger.LogWarning("The current engine version ({CurrentVersion}) does not match the project engine version ({ProjectVersion}). This may lead to unexpected behavior.", currentAssemlyVersion, project.EngineVersion); + // NOTE: Window initialization is intentionally not performed automatically here. + // Call InitializeWindow() explicitly when ready to create the underlying native window and load resources. } - /// - /// Initializes a new instance of . - /// - /// The game instance. - public Window(Game game) - : this(game.Scene, game.Camera.Settings, LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger()) { } - /// /// Initializes a new instance of . /// @@ -152,18 +150,27 @@ public Window(Game game) /// /// Contains the game scene. /// The settings for the window. - /// The logger for the window. + /// The logger for the window. When , a default logger will be used. /// The renderers for the window. - public Window(Scene scene, IViewSettings settings, ILogger logger, IEnumerable? renderers = null) + public Window(Scene scene, IViewSettings settings, ILogger? logger = null, IEnumerable? renderers = null) { Scene = scene; Settings = settings; Camera = new(Vector3.One, settings); _registeredRenderers = renderers?.ToArray() ?? []; - _logger = logger; + _logger = logger ?? LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger(); + + // NOTE: Window initialization is intentionally not performed automatically here. + // Call InitializeWindow() explicitly when ready to create the underlying native window and load resources. + } - // Initialization is deferred. Call Initialize() when you want the - // native window to be created and resources to be loaded. + private void CheckEngineVersion() + { + var project = new ProjectDto(); + var currentAssemlyVersion = typeof(Window).Assembly.GetVersion(); + + if (currentAssemlyVersion != project.EngineVersion) + _logger.LogWarning("The current engine version ({CurrentVersion}) does not match the project engine version ({ProjectVersion}). This may lead to unexpected behavior.", currentAssemlyVersion, project.EngineVersion); } /// @@ -180,6 +187,11 @@ public override void Initialize() CurrentWindow.Closing += OnClosing; _windowInitialized = true; + + // Ensure the underlying native window is created and initialized. + // SilkWindow.Initialize calls CurrentWindow.Initialize(); call it here to + // complete the initialization sequence (create GL context, input, etc.). + base.Initialize(); } /// @@ -262,7 +274,14 @@ protected void RenderFrame(Frame frame) _gl.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); ToggleWireFrame(Settings.UseWireFrame); + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred during pre-rendering."); + } + try + { UseShaders(); var activeRenderers = _renderers.Where(renderer => Settings.RendererFlags.HasFlag(renderer.RenderFlag)) @@ -271,14 +290,28 @@ protected void RenderFrame(Frame frame) foreach (var renderer in activeRenderers) renderer.Render().GetAwaiter().GetResult(); + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred during rendering."); + } + try + { AfterRender(frame); + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred during post render."); + } + try + { _imGuiController?.Render(); } catch (Exception ex) { - _logger.LogInformation("{Message}", ex.Message); + _logger.LogError(ex, "An error occurred while rendering ImGui interface."); } } diff --git a/SharpEngine.slnx b/SharpEngine.slnx index 4cfa5e7..39b30b0 100644 --- a/SharpEngine.slnx +++ b/SharpEngine.slnx @@ -34,9 +34,23 @@ + + + + + + + + + + + + + + @@ -47,9 +61,6 @@ - - - diff --git a/Tests/Directory.Build.props b/Tests/Directory.Build.props index 3ad3602..3b85263 100644 --- a/Tests/Directory.Build.props +++ b/Tests/Directory.Build.props @@ -4,7 +4,6 @@ False - - + \ No newline at end of file diff --git a/Tests/ObjLoader.Test/Loaders/MaterialLibraryLoaderTests.cs b/Tests/ObjLoader.Test/Loaders/MaterialLibraryLoaderTests.cs index be3028c..987b111 100644 --- a/Tests/ObjLoader.Test/Loaders/MaterialLibraryLoaderTests.cs +++ b/Tests/ObjLoader.Test/Loaders/MaterialLibraryLoaderTests.cs @@ -1,16 +1,19 @@ using System.Collections.Generic; using System.Linq; -using NUnit.Framework; using FluentAssertions; +using Xunit; + using SharpEngine.Core.Components.ObjLoader.DataStore; using SharpEngine.Core.Components.Properties; using SharpEngine.Core.ObjLoader.Loaders.MaterialLoader; using System.Diagnostics.CodeAnalysis; +using Moq; +using SharpEngine.Core.ObjLoader.Loader.Loaders; +using System.IO.Abstractions; namespace SharpEngine.Core.ObjLoader.Tests.Loaders { - [TestFixture] public class MaterialLibraryLoaderTests { private readonly MaterialLibrarySpy _materialLibrarySpy; @@ -24,10 +27,10 @@ public class MaterialLibraryLoaderTests public MaterialLibraryLoaderTests() { _materialLibrarySpy = new MaterialLibrarySpy(); - _materialLibraryLoader = new MaterialLibraryLoader(null!); + _materialLibraryLoader = new MaterialLibraryLoader(_materialLibrarySpy, Mock.Of(), Mock.Of()); } - [Test] + [Fact] public void Adds_correct_materials() { LoadMaterial(); @@ -37,7 +40,7 @@ public void Adds_correct_materials() _secondMaterial.Name.Should().BeEquivalentTo("second_material"); } - [Test] + [Fact] public void Sets_correct_ambient_color() { LoadMaterial(); @@ -47,7 +50,7 @@ public void Sets_correct_ambient_color() _firstMaterial.AmbientColor.Z.Should().BeApproximately(3, Epsilon); } - [Test] + [Fact] public void Sets_correct_diffuse_color() { LoadMaterial(); @@ -57,7 +60,7 @@ public void Sets_correct_diffuse_color() _firstMaterial.DiffuseColor.Z.Should().BeApproximately(0.0314f, Epsilon); } - [Test] + [Fact] public void Sets_correct_specular() { LoadMaterial(); @@ -69,7 +72,7 @@ public void Sets_correct_specular() _firstMaterial.SpecularCoefficient.Should().BeApproximately(32, Epsilon); } - [Test] + [Fact] public void Sets_correct_transparency() { LoadMaterial(); @@ -77,7 +80,7 @@ public void Sets_correct_transparency() _firstMaterial.Transparency.Should().BeApproximately(0.5f, Epsilon); } - [Test] + [Fact] public void Sets_correct_illumination_model() { LoadMaterial(); @@ -85,7 +88,7 @@ public void Sets_correct_illumination_model() _firstMaterial.IlluminationModel.Should().Be(2); } - [Test] + [Fact] public void Sets_correct_texure_maps() { LoadMaterial(); @@ -126,7 +129,7 @@ disp lenna_disp.tga decal lenna_stencil.tga newmtl second_material"; - private class MaterialLibrarySpy : IMaterialLibrary + private class MaterialLibrarySpy : IMaterialDataStore { public List Materials { get; private set; } @@ -135,10 +138,12 @@ public MaterialLibrarySpy() Materials = new List(); } - public void Push(Material material) + public void AddMaterial(Material material) { Materials.Add(material); } + + public void SetMaterial(string materialName) => throw new System.NotImplementedException(); } } } \ No newline at end of file diff --git a/Tests/ObjLoader.Test/Loaders/ObjLoaderTests.cs b/Tests/ObjLoader.Test/Loaders/ObjLoaderTests.cs index 56e9765..9cba209 100644 --- a/Tests/ObjLoader.Test/Loaders/ObjLoaderTests.cs +++ b/Tests/ObjLoader.Test/Loaders/ObjLoaderTests.cs @@ -1,69 +1,59 @@ using System.IO; using System.Text; using System.Linq; -using NUnit.Framework; + using FluentAssertions; +using Xunit; using SharpEngine.Core.Entities.Properties.Meshes; using SharpEngine.Core.ObjLoader.Loader.TypeParsers; using SharpEngine.Core.ObjLoader.Loaders.MaterialLoader; using CoreObjLoader = SharpEngine.Core.ObjLoader.Loaders.ObjLoader.ObjLoader; +using SharpEngine.Core.ObjLoader.Loader.Loaders; +using Moq; +using System.IO.Abstractions; namespace SharpEngine.Core.ObjLoader.Tests.Loaders { - [TestFixture] public class ObjLoaderTests { - private class MaterialStreamProviderSpy : IMaterialStreamProvider - { - public string RequestedMaterialFilePath { get; private set; } - public Stream StreamToReturn { get; set; } - - public Stream Open(string materialFilePath) - { - RequestedMaterialFilePath = materialFilePath; - return StreamToReturn; - } - } - - private CoreObjLoader _loader; + private readonly CoreObjLoader _loader; private Mesh _loadResult; - private DataStore _textureDataStore; - private FaceParser _faceParser; - private GroupParser _groupParser; - private NormalParser _normalParser; - private TextureParser _textureParser; - private VertexParser _vertexParser; - private MaterialLibraryLoader _materialLibraryLoader; - private MaterialLibraryParser _materialLibraryParser; - private UseMaterialParser _useMaterialParser; - private MaterialLibraryLoaderFacade _materialLibraryLoaderFacade; - private MaterialStreamProviderSpy _materialStreamProviderSpy; - - [SetUp] - public void SetUp() + private readonly DataStore _textureDataStore; + private readonly FaceParser _faceParser; + private readonly GroupParser _groupParser; + private readonly NormalParser _normalParser; + private readonly TextureParser _textureParser; + private readonly VertexParser _vertexParser; + private readonly MaterialLibraryLoader _materialLibraryLoader; + private readonly MaterialLibraryParser _materialLibraryParser; + private readonly UseMaterialParser _useMaterialParser; + private readonly MaterialLibraryLoaderFacade _materialLibraryLoaderFacade; + + public ObjLoaderTests() { _textureDataStore = new DataStore(); + var fileStreamFactory = Mock.Of(); + var fileManager = Mock.Of(); + _faceParser = new FaceParser(_textureDataStore); _groupParser = new GroupParser(_textureDataStore); _normalParser = new NormalParser(_textureDataStore); _textureParser = new TextureParser(_textureDataStore); _vertexParser = new VertexParser(_textureDataStore); - _materialStreamProviderSpy = new MaterialStreamProviderSpy(); - _materialStreamProviderSpy.StreamToReturn = CreateMemoryStreamFromString(MaterialLibraryString); - _materialLibraryLoader = new MaterialLibraryLoader(_textureDataStore); + _materialLibraryLoader = new MaterialLibraryLoader(_textureDataStore, fileStreamFactory, fileManager); _materialLibraryLoaderFacade = new MaterialLibraryLoaderFacade(_materialLibraryLoader, ""); _materialLibraryParser = new MaterialLibraryParser(_materialLibraryLoaderFacade); _useMaterialParser = new UseMaterialParser(_textureDataStore); - _loader = new CoreObjLoader("", _textureDataStore); + _loader = new CoreObjLoader("", _textureDataStore, fileStreamFactory, fileManager); } - [Test] + [Fact] public void Loads_object_and_material_correctly() { Load(); @@ -73,8 +63,6 @@ public void Loads_object_and_material_correctly() _loadResult.Normals.Should().HaveCount(8); _loadResult.Materials.Should().HaveCount(1); - _materialStreamProviderSpy.RequestedMaterialFilePath.Should().BeEquivalentTo("cube.mtl"); - _loadResult.Groups.Should().HaveCount(1); var group = _loadResult.Groups.First(); @@ -82,15 +70,9 @@ public void Loads_object_and_material_correctly() group.Material!.Name.Should().BeEquivalentTo("cube_material"); } - [Test] + [Fact] public void Loads_object_correctly_when_material_is_not_found() { - _materialStreamProviderSpy.StreamToReturn = null!; - var materialLibraryLoaderFacade = new MaterialLibraryLoaderFacade(_materialLibraryLoader, ""); - var materialLibraryParser = new MaterialLibraryParser(_materialLibraryLoaderFacade); - - _loader = new CoreObjLoader("", _textureDataStore); - Load(); _loadResult.Vertices.Should().HaveCount(8); @@ -107,17 +89,9 @@ public void Loads_object_correctly_when_material_is_not_found() private void Load() { - var objectStream = CreateMemoryStreamFromString(ObjectFileString); - _loadResult = _loader.Load(null!).First(); } - private Stream CreateMemoryStreamFromString(string str) - { - var data = Encoding.ASCII.GetBytes(str); - return new MemoryStream(data); - } - private const string ObjectFileString = "#Comment\r\n" + "mtllib\tcube.mtl\r\n" + @@ -185,8 +159,4 @@ map_bump lenna_bump.tga disp lenna_disp.tga decal lenna_stencil.tga"; } - - internal interface IMaterialStreamProvider - { - } } \ No newline at end of file diff --git a/Tests/ObjLoader.Test/SharpEngine.Core.ObjLoader.Tests.csproj b/Tests/ObjLoader.Test/SharpEngine.Core.ObjLoader.Tests.csproj index 6a619d7..3a8519e 100644 --- a/Tests/ObjLoader.Test/SharpEngine.Core.ObjLoader.Tests.csproj +++ b/Tests/ObjLoader.Test/SharpEngine.Core.ObjLoader.Tests.csproj @@ -6,10 +6,11 @@ - - - - + + + + + diff --git a/Tests/ObjLoader.Test/TypeParsers/FaceParserTests.cs b/Tests/ObjLoader.Test/TypeParsers/FaceParserTests.cs index df5b03a..05636a0 100644 --- a/Tests/ObjLoader.Test/TypeParsers/FaceParserTests.cs +++ b/Tests/ObjLoader.Test/TypeParsers/FaceParserTests.cs @@ -1,5 +1,5 @@ using FluentAssertions; -using NUnit.Framework; +using Xunit; using SharpEngine.Core.Components.ObjLoader.DataStore; using SharpEngine.Core.Components.Properties.Meshes.MeshData; @@ -7,21 +7,18 @@ namespace SharpEngine.Core.ObjLoader.Tests.TypeParsers { - [TestFixture] public class FaceParserTests { - private FaceGroupSpy _faceGroupSpy; - private FaceParser _faceParser; + private readonly FaceGroupSpy _faceGroupSpy; + private readonly FaceParser _faceParser; - [SetUp] - public void SetUp() + public FaceParserTests() { _faceGroupSpy = new FaceGroupSpy(); - _faceParser = new FaceParser(_faceGroupSpy); } - [Test] + [Fact] public void CanParse_returns_true_on_face_line() { const string groupKeyword = "f"; @@ -30,7 +27,7 @@ public void CanParse_returns_true_on_face_line() canParse.Should().BeTrue(); } - [Test] + [Fact] public void CanParse_returns_false_on_non_normal_line() { const string invalidKeyword = "vt"; @@ -39,7 +36,7 @@ public void CanParse_returns_false_on_non_normal_line() canParse.Should().BeFalse(); } - [Test] + [Fact] public void Parses_normal_line_correctly_1() { const string faceLine = "1 2 3"; @@ -60,7 +57,7 @@ public void Parses_normal_line_correctly_1() parsedFace[2].NormalIndex.Should().Be(0); } - [Test] + [Fact] public void Parses_normal_line_correctly_2() { const string faceLine = "3/1 4/2 5/3"; @@ -83,7 +80,7 @@ public void Parses_normal_line_correctly_2() parsedFace[2].NormalIndex.Should().Be(0); } - [Test] + [Fact] public void Parses_normal_line_correctly_3() { const string faceLine = "6/4/1 3/5/3 7/6/5"; @@ -106,7 +103,7 @@ public void Parses_normal_line_correctly_3() parsedFace[2].NormalIndex.Should().Be(5); } - [Test] + [Fact] public void Parses_normal_line_correctly_4() { const string faceLine = "6//1 3//3 7//5"; diff --git a/Tests/ObjLoader.Test/TypeParsers/GroupParserTests.cs b/Tests/ObjLoader.Test/TypeParsers/GroupParserTests.cs index 061dd2d..1e38c77 100644 --- a/Tests/ObjLoader.Test/TypeParsers/GroupParserTests.cs +++ b/Tests/ObjLoader.Test/TypeParsers/GroupParserTests.cs @@ -1,12 +1,11 @@ using FluentAssertions; -using NUnit.Framework; +using Xunit; using SharpEngine.Core.Components.ObjLoader.DataStore; using SharpEngine.Core.ObjLoader.Loader.TypeParsers; namespace SharpEngine.Core.ObjLoader.Tests.TypeParsers { - [TestFixture] public class GroupParserTests { private readonly GroupDataStoreMock _groupDataStoreMock; @@ -18,7 +17,7 @@ public GroupParserTests() _groupParser = new GroupParser(_groupDataStoreMock); } - [Test] + [Fact] public void CanParse_returns_true_on_normal_line() { const string groupKeyword = "g"; @@ -27,7 +26,7 @@ public void CanParse_returns_true_on_normal_line() canParse.Should().BeTrue(); } - [Test] + [Fact] public void CanParse_returns_false_on_non_normal_line() { const string invalidKeyword = "vt"; @@ -36,7 +35,7 @@ public void CanParse_returns_false_on_non_normal_line() canParse.Should().BeFalse(); } - [Test] + [Fact] public void Parses_normal_line_correctly() { const string normalLine = "test group"; diff --git a/Tests/ObjLoader.Test/TypeParsers/MaterialLibraryParserTests.cs b/Tests/ObjLoader.Test/TypeParsers/MaterialLibraryParserTests.cs index 3c974a6..e1771b6 100644 --- a/Tests/ObjLoader.Test/TypeParsers/MaterialLibraryParserTests.cs +++ b/Tests/ObjLoader.Test/TypeParsers/MaterialLibraryParserTests.cs @@ -1,11 +1,11 @@ using FluentAssertions; -using NUnit.Framework; +using Xunit; + using SharpEngine.Core.ObjLoader.Loader.TypeParsers; using SharpEngine.Core.ObjLoader.Loaders.MaterialLoader; namespace SharpEngine.Core.ObjLoader.Tests.TypeParsers { - [TestFixture] public class MaterialLibraryParserTests { private readonly MaterialLibraryLoaderFacadeSpy _materialLibraryLoaderFacadeSpy; @@ -17,7 +17,7 @@ public MaterialLibraryParserTests() _parser = new MaterialLibraryParser(_materialLibraryLoaderFacadeSpy); } - [Test] + [Fact] public void CanParse_returns_true_on_mtlib_line() { const string groupKeyword = "mtllib"; @@ -26,7 +26,7 @@ public void CanParse_returns_true_on_mtlib_line() canParse.Should().BeTrue(); } - [Test] + [Fact] public void CanParse_returns_false_on_non_mtlib_line() { const string invalidKeyword = "vt"; @@ -35,7 +35,7 @@ public void CanParse_returns_false_on_non_mtlib_line() canParse.Should().BeFalse(); } - [Test] + [Fact] public void Parses_mtlib_line_correctly() { const string faceLine = "cube.mtl"; diff --git a/Tests/ObjLoader.Test/TypeParsers/NormalParserTests.cs b/Tests/ObjLoader.Test/TypeParsers/NormalParserTests.cs index f047eb5..15bce84 100644 --- a/Tests/ObjLoader.Test/TypeParsers/NormalParserTests.cs +++ b/Tests/ObjLoader.Test/TypeParsers/NormalParserTests.cs @@ -1,5 +1,5 @@ using FluentAssertions; -using NUnit.Framework; +using Xunit; using SharpEngine.Core.Components.ObjLoader.DataStore; using SharpEngine.Core.Components.Properties.Meshes.MeshData; @@ -7,7 +7,6 @@ namespace SharpEngine.Core.ObjLoader.Tests.TypeParsers { - [TestFixture] public class NormalParserTests { private readonly NormalParser _normalParser; @@ -19,7 +18,7 @@ public NormalParserTests() _normalParser = new NormalParser(_normalDataStoreMock); } - [Test] + [Fact] public void CanParse_returns_true_on_normal_line() { const string normalLine = "vn"; @@ -28,7 +27,7 @@ public void CanParse_returns_true_on_normal_line() canParse.Should().BeTrue(); } - [Test] + [Fact] public void CanParse_returns_false_on_non_normal_line() { const string invalidNormal = "vt"; @@ -37,7 +36,7 @@ public void CanParse_returns_false_on_non_normal_line() canParse.Should().BeFalse(); } - [Test] + [Fact] public void Parses_normal_line_correctly() { const string normalLine = "1.000000 2.000000 -1.000000"; diff --git a/Tests/ObjLoader.Test/TypeParsers/TextureParserTests.cs b/Tests/ObjLoader.Test/TypeParsers/TextureParserTests.cs index 4b9ffd9..36402df 100644 --- a/Tests/ObjLoader.Test/TypeParsers/TextureParserTests.cs +++ b/Tests/ObjLoader.Test/TypeParsers/TextureParserTests.cs @@ -1,5 +1,5 @@ using FluentAssertions; -using NUnit.Framework; +using Xunit; using SharpEngine.Core.Components.ObjLoader.DataStore; using SharpEngine.Core.Components.Properties.Meshes.MeshData; @@ -7,7 +7,6 @@ namespace SharpEngine.Core.ObjLoader.Tests.TypeParsers { - [TestFixture] public class TextureParserTests { private readonly TextureDataStoreMock _textureDataStoreMock; @@ -20,7 +19,7 @@ public TextureParserTests() _textureParser = new TextureParser(_textureDataStoreMock); } - [Test] + [Fact] public void CanParse_returns_true_on_normal_line() { const string textureKeyword = "vt"; @@ -29,7 +28,7 @@ public void CanParse_returns_true_on_normal_line() canParse.Should().BeTrue(); } - [Test] + [Fact] public void CanParse_returns_false_on_non_normal_line() { const string invalidKeyword = "vn"; @@ -38,7 +37,7 @@ public void CanParse_returns_false_on_non_normal_line() canParse.Should().BeFalse(); } - [Test] + [Fact] public void Parses_normal_line_correctly() { const string textureLine = "0.500 -1.352"; diff --git a/Tests/ObjLoader.Test/TypeParsers/UseMaterialParserTests.cs b/Tests/ObjLoader.Test/TypeParsers/UseMaterialParserTests.cs index 29cbaa8..5b72637 100644 --- a/Tests/ObjLoader.Test/TypeParsers/UseMaterialParserTests.cs +++ b/Tests/ObjLoader.Test/TypeParsers/UseMaterialParserTests.cs @@ -1,10 +1,13 @@ using FluentAssertions; -using NUnit.Framework; +using Xunit; + using SharpEngine.Core.ObjLoader.Loader.TypeParsers; +using SharpEngine.Core.Components.Properties; + +using System; namespace SharpEngine.Core.ObjLoader.Tests.TypeParsers { - [TestFixture] public class UseMaterialParserTests { private readonly ElementGroupSpy _elementGroupSpy; @@ -16,7 +19,7 @@ public UseMaterialParserTests() _parser = new UseMaterialParser(_elementGroupSpy); } - [Test] + [Fact] public void CanParse_returns_true_on_usemtl_line() { const string groupKeyword = "usemtl"; @@ -25,7 +28,7 @@ public void CanParse_returns_true_on_usemtl_line() canParse.Should().BeTrue(); } - [Test] + [Fact] public void CanParse_returns_false_on_non_usemtl_line() { const string invalidKeyword = "vt"; @@ -34,7 +37,7 @@ public void CanParse_returns_false_on_non_usemtl_line() canParse.Should().BeFalse(); } - [Test] + [Fact] public void Parses_usemtl_line_correctly_1() { const string useMtlLine = "materialName"; @@ -47,6 +50,9 @@ private class ElementGroupSpy : IMaterialDataStore { public string MaterialName { get; private set; } + public void AddMaterial(Material currentMaterial) + => throw new NotImplementedException(); + public void SetMaterial(string materialName) { MaterialName = materialName; diff --git a/Tests/ObjLoader.Test/TypeParsers/VertexParserTests.cs b/Tests/ObjLoader.Test/TypeParsers/VertexParserTests.cs index 8009a66..dfa5af7 100644 --- a/Tests/ObjLoader.Test/TypeParsers/VertexParserTests.cs +++ b/Tests/ObjLoader.Test/TypeParsers/VertexParserTests.cs @@ -1,5 +1,5 @@ using FluentAssertions; -using NUnit.Framework; +using Xunit; using SharpEngine.Core.Components.ObjLoader.DataStore; using SharpEngine.Core.Components.Properties.Meshes.MeshData; @@ -7,7 +7,6 @@ namespace SharpEngine.Core.ObjLoader.Tests.TypeParsers { - [TestFixture] public class VertexParserTests { private readonly VertexDataStoreMock _vertexDataStoreMock; @@ -19,7 +18,7 @@ public VertexParserTests() _vertexParser = new VertexParser(_vertexDataStoreMock); } - [Test] + [Fact] public void CanParse_returns_false_on_non_vertex_line() { const string invalidVertex = "vt"; @@ -28,7 +27,7 @@ public void CanParse_returns_false_on_non_vertex_line() canParse.Should().BeFalse(); } - [Test] + [Fact] public void CanParse_returns_true_on_vertex_line() { const string vertexLine = "v"; @@ -37,7 +36,7 @@ public void CanParse_returns_true_on_vertex_line() canParse.Should().BeTrue(); } - [Test] + [Fact] public void Parses_vertex_line_correctly() { const string vertexLine = "0.123 0.234 0.345"; diff --git a/Tests/SharpEngine.Core.Components.Tests/Class1.cs b/Tests/SharpEngine.Core.Components.Tests/Class1.cs new file mode 100644 index 0000000..cab4a8c --- /dev/null +++ b/Tests/SharpEngine.Core.Components.Tests/Class1.cs @@ -0,0 +1,6 @@ +namespace SharpEngine.Core.Components.Tests; + +public class Class1 +{ + +} diff --git a/Tests/SharpEngine.Core.Components.Tests/SharpEngine.Core.Components.Tests.csproj b/Tests/SharpEngine.Core.Components.Tests/SharpEngine.Core.Components.Tests.csproj new file mode 100644 index 0000000..a15a71c --- /dev/null +++ b/Tests/SharpEngine.Core.Components.Tests/SharpEngine.Core.Components.Tests.csproj @@ -0,0 +1,13 @@ + + + + net10.0 + enable + enable + + + + + + + diff --git a/Tests/SharpEngine.Core.Numerics.Tests/Class1.cs b/Tests/SharpEngine.Core.Numerics.Tests/Class1.cs new file mode 100644 index 0000000..580dc2a --- /dev/null +++ b/Tests/SharpEngine.Core.Numerics.Tests/Class1.cs @@ -0,0 +1,6 @@ +namespace SharpEngine.Core.Numerics.Tests; + +public class Class1 +{ + +} diff --git a/Tests/SharpEngine.Core.Numerics.Tests/SharpEngine.Core.Numerics.Tests.csproj b/Tests/SharpEngine.Core.Numerics.Tests/SharpEngine.Core.Numerics.Tests.csproj new file mode 100644 index 0000000..547dedb --- /dev/null +++ b/Tests/SharpEngine.Core.Numerics.Tests/SharpEngine.Core.Numerics.Tests.csproj @@ -0,0 +1,13 @@ + + + + net10.0 + enable + enable + + + + + + + diff --git a/Tests/SharpEngine.Core.Tests/Class1.cs b/Tests/SharpEngine.Core.Tests/Class1.cs deleted file mode 100644 index ff34b98..0000000 --- a/Tests/SharpEngine.Core.Tests/Class1.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace SharpEngine.Core.Tests; - -public class Class1 -{ - -} diff --git a/Tests/SharpEngine.Core.Tests/Extensions/AssemblyExtensionsTests.cs b/Tests/SharpEngine.Core.Tests/Extensions/AssemblyExtensionsTests.cs new file mode 100644 index 0000000..a58a131 --- /dev/null +++ b/Tests/SharpEngine.Core.Tests/Extensions/AssemblyExtensionsTests.cs @@ -0,0 +1,40 @@ +using SharpEngine.Core.Extensions; +using System.Reflection; +using Xunit; + +namespace SharpEngine.Core.Tests.Extensions; + +public class AssemblyExtensionsTests +{ + private class AssemblyStub : Assembly + { + public Version Version { get; } + public AssemblyStub(Version version) + { + Version = version; + } + + public override AssemblyName GetName() + { + return new AssemblyName("TestAssembly") + { + Version = Version + }; + } + } + + [Fact] + public void GetVersion_Returns_Correct_Version() + { + var assemblyVersion = new Version(1, 2, 3, 4); + + var assembly = new AssemblyStub(assemblyVersion); + var version = assembly.GetVersion(); + + Assert.NotNull(version); + Assert.Equal(1, version.Major); + Assert.Equal(2, version.Minor); + Assert.Equal(3, version.Build); + Assert.Equal(4, version.Revision); + } +} diff --git a/Tests/SharpEngine.Core.Tests/SharpEngine.Core.Tests.csproj b/Tests/SharpEngine.Core.Tests/SharpEngine.Core.Tests.csproj index bbcf245..a3a2555 100644 --- a/Tests/SharpEngine.Core.Tests/SharpEngine.Core.Tests.csproj +++ b/Tests/SharpEngine.Core.Tests/SharpEngine.Core.Tests.csproj @@ -6,4 +6,29 @@ enable + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/SharpEngine.Shared.Tests/SharpEngine.Shared.Tests.csproj b/Tests/SharpEngine.Shared.Tests/SharpEngine.Shared.Tests.csproj index fa71b7a..bbcf245 100644 --- a/Tests/SharpEngine.Shared.Tests/SharpEngine.Shared.Tests.csproj +++ b/Tests/SharpEngine.Shared.Tests/SharpEngine.Shared.Tests.csproj @@ -1,7 +1,7 @@  - net8.0 + $(DotNetTargetFramework) enable enable