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