From 54411da2f347488a694178708095fd90afb1e6b0 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 30 Aug 2025 12:05:05 +0200 Subject: [PATCH] added input system and perspective camera --- README.md | 7 ++- src/EngineSharp.Core/EngineSharp.Core.sln | 6 +++ .../Extensions/NumberExtensions.cs | 7 +++ .../Extensions/VectorExtensions.cs | 36 +++++++++++++ .../EngineSharp.Core/IEngine.cs | 28 ++++++++++ .../EngineSharp.Core/Input.cs | 52 ++++++++++++++++++ .../EngineSharp.Core/OpenGLEngine.cs | 34 ++++++++---- .../EngineSharp.Core/PerspectiveCamera.cs | 54 +++++++++++++++++++ src/EngineSharp/EngineSharp.csproj | 18 +++++++ src/EngineSharp/Program.cs | 21 ++++++++ 10 files changed, 253 insertions(+), 10 deletions(-) create mode 100644 src/EngineSharp.Core/EngineSharp.Core/Extensions/NumberExtensions.cs create mode 100644 src/EngineSharp.Core/EngineSharp.Core/Extensions/VectorExtensions.cs create mode 100644 src/EngineSharp.Core/EngineSharp.Core/IEngine.cs create mode 100644 src/EngineSharp.Core/EngineSharp.Core/Input.cs create mode 100644 src/EngineSharp.Core/EngineSharp.Core/PerspectiveCamera.cs create mode 100644 src/EngineSharp/EngineSharp.csproj create mode 100644 src/EngineSharp/Program.cs diff --git a/README.md b/README.md index b08f04d..4b7baa6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ # EngineSharp -A second attempt at creating an engine using Silk.Net \ No newline at end of file +A second attempt at creating an engine using Silk.Net + + + +Using a Left-Handed Y-Up coordinate system + diff --git a/src/EngineSharp.Core/EngineSharp.Core.sln b/src/EngineSharp.Core/EngineSharp.Core.sln index 795a9dd..40d2949 100644 --- a/src/EngineSharp.Core/EngineSharp.Core.sln +++ b/src/EngineSharp.Core/EngineSharp.Core.sln @@ -2,6 +2,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EngineSharp.Core", "EngineSharp.Core\EngineSharp.Core.csproj", "{A617B903-6114-448A-864F-17B157C58C53}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EngineSharp", "..\EngineSharp\EngineSharp.csproj", "{1D984FEE-1A61-4E35-9D00-0264D92A31F4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +14,9 @@ Global {A617B903-6114-448A-864F-17B157C58C53}.Debug|Any CPU.Build.0 = Debug|Any CPU {A617B903-6114-448A-864F-17B157C58C53}.Release|Any CPU.ActiveCfg = Release|Any CPU {A617B903-6114-448A-864F-17B157C58C53}.Release|Any CPU.Build.0 = Release|Any CPU + {1D984FEE-1A61-4E35-9D00-0264D92A31F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D984FEE-1A61-4E35-9D00-0264D92A31F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D984FEE-1A61-4E35-9D00-0264D92A31F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D984FEE-1A61-4E35-9D00-0264D92A31F4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/EngineSharp.Core/EngineSharp.Core/Extensions/NumberExtensions.cs b/src/EngineSharp.Core/EngineSharp.Core/Extensions/NumberExtensions.cs new file mode 100644 index 0000000..86fba70 --- /dev/null +++ b/src/EngineSharp.Core/EngineSharp.Core/Extensions/NumberExtensions.cs @@ -0,0 +1,7 @@ +namespace EngineSharp.Core; + +public static class NumberExtensions +{ + public static float ToRadians(this float degrees) => degrees * MathF.PI / 180.0f; + public static double ToRadians(this double degrees) => degrees * Math.PI / 180.0; +} \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/Extensions/VectorExtensions.cs b/src/EngineSharp.Core/EngineSharp.Core/Extensions/VectorExtensions.cs new file mode 100644 index 0000000..5c64e5a --- /dev/null +++ b/src/EngineSharp.Core/EngineSharp.Core/Extensions/VectorExtensions.cs @@ -0,0 +1,36 @@ +using Silk.NET.Maths; + +namespace EngineSharp.Core; + +// TODO: Pretty useless currently as I am extending instances and not the class sadly +public static class VectorExtensions +{ + public static Vector3D Up(this Vector3D _) where T : unmanaged, IFormattable, IEquatable, IComparable => Vector3D.UnitY; + public static Vector3D Down(this Vector3D _) where T : unmanaged, IFormattable, IEquatable, IComparable => -Vector3D.UnitY; + public static Vector3D Left(this Vector3D _) where T : unmanaged, IFormattable, IEquatable, IComparable => Vector3D.UnitX; + public static Vector3D Right(this Vector3D _) where T : unmanaged, IFormattable, IEquatable, IComparable => -Vector3D.UnitX; + public static Vector3D Front(this Vector3D _) where T : unmanaged, IFormattable, IEquatable, IComparable => -Vector3D.UnitZ; + public static Vector3D Back(this Vector3D _) where T : unmanaged, IFormattable, IEquatable, IComparable => -Vector3D.UnitZ; + + // Thanks to: https://stackoverflow.com/a/67920029 + // public static Vector3D Slerp(this Vector3D start, Vector3D end, float percent) + // where T : unmanaged, IFormattable, IEquatable, IComparable + // { + // // the cosine of the angle between 2 vectors. + // var dot = Vector3D.Dot(start, end); + // + // // Clamp it to be in the range of Acos() + // // This may be unnecessary, but floating point precision can be a fickle mistress. + // Math.Clamp(dot, -1, 1); + // + // // Acos(dot) returns the angle between start and end, + // // And multiplying that by percent returns the angle between start and the final result. + // var theta = Math.Acos(dot) * percent; + // var relativeVec = end - start * dot; + // relativeVec = Vector3D.Normalize(relativeVec); + // + // // Orthonormal basis + // // The final result. + // return ((start * Math.Cos(theta)) + (relativeVec * Math.Sin(theta))); + // } +} \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs b/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs new file mode 100644 index 0000000..b885ca7 --- /dev/null +++ b/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs @@ -0,0 +1,28 @@ +using Silk.NET.Windowing; + +namespace EngineSharp.Core; + +public interface IEngine +{ + void Start(); + void Stop(); +} + +public enum GraphicsAPI +{ + OpenGL, + Vulkan, +} + +public static class EngineFactory +{ + public static IEngine Create(GraphicsAPI api, WindowOptions windowOptions) + { + return api switch + { + GraphicsAPI.OpenGL => new OpenGLEngine(windowOptions), + GraphicsAPI.Vulkan => throw new NotSupportedException($"The graphics API {api} is not currently supported"), + _ => throw new ArgumentException($"The graphics API {api} is unknown.", nameof(api)) + }; + } +} \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/Input.cs b/src/EngineSharp.Core/EngineSharp.Core/Input.cs new file mode 100644 index 0000000..f27433c --- /dev/null +++ b/src/EngineSharp.Core/EngineSharp.Core/Input.cs @@ -0,0 +1,52 @@ +using Silk.NET.Input; + +namespace EngineSharp.Core; + +public class Input +{ + private readonly IInputContext _context; + private readonly IDictionary> _pressedKeys = new Dictionary>(); + + // TODO: Add mouse + internal Input(IInputContext inputContext) + { + _context = inputContext; + } + + /// + /// Returns true if the given key has been pressed on any keyboard. + /// + public bool IsKeyPressed(Key key) => _pressedKeys.Values.Any(k => k.Contains(key)); + /// + /// Returns true if the given key is pressed on the given keyboard. + /// + public bool IsKeyPressed(IKeyboard keyboard, Key key) + { + if (_pressedKeys.TryGetValue(keyboard, out var pressedKeys)) + { + return pressedKeys.Contains(key); + } + + return false; + } + + private void OnKeyDown(IKeyboard keyboard, Key key, int keyCode) + { + if (_pressedKeys.TryGetValue(keyboard, out var existingSet)) + { + existingSet.Add(key); + } + else + { + var set = new HashSet { key }; + _pressedKeys.Add(keyboard, set); + } + } + private void OnKeyUp(IKeyboard keyboard, Key key, int keyCode) + { + if (_pressedKeys.TryGetValue(keyboard, out var existingSet)) + { + existingSet.Remove(key); + } + } +} \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs b/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs index 517e63c..16ddc2f 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs @@ -1,15 +1,16 @@ -using Silk.NET.Windowing; +using System.Drawing; +using Silk.NET.Input; +using Silk.NET.Maths; +using Silk.NET.OpenGL; +using Silk.NET.Windowing; namespace EngineSharp.Core; -public interface IEngine -{ - void Start(); -} - -public class OpenGLEngine : IEngine +internal class OpenGLEngine : IEngine { private readonly IWindow _window; + private GL _gl = null!; // because between constructing the engine and OnLoad being called nothing should be able to happen, we do this to get rid of warnings + private Input _input = null!; public OpenGLEngine(WindowOptions windowOptions) { @@ -17,16 +18,26 @@ public class OpenGLEngine : IEngine _window.Load += OnLoad; _window.Update += OnUpdate; _window.Render += OnRender; + _window.Resize += OnResize; } - + public void Start() { _window.Run(); } + public void Stop() + { + _window.Close(); + } + private void OnLoad() { - // TODO: Create openGL context etc. + _gl = _window.CreateOpenGL(); + _input = new Input(_window.CreateInput()); + + _gl.ClearColor(Color.Black); + _gl.Enable(EnableCap.DepthTest); } private void OnUpdate(double deltaTime) @@ -38,4 +49,9 @@ public class OpenGLEngine : IEngine { // TODO: Here render all meshes etc. } + + private void OnResize(Vector2D obj) + { + // TODO: update aspect of camera; on entity component system the camera should probably read the aspect ratio from some location rather than the engine updating the aspect on the camera + } } \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/PerspectiveCamera.cs b/src/EngineSharp.Core/EngineSharp.Core/PerspectiveCamera.cs new file mode 100644 index 0000000..2463d2d --- /dev/null +++ b/src/EngineSharp.Core/EngineSharp.Core/PerspectiveCamera.cs @@ -0,0 +1,54 @@ +// using System.Numerics; +using Silk.NET.Maths; + +namespace EngineSharp.Core; + +public class PerspectiveCamera +{ + private readonly Vector3D _position; + private readonly Vector3D _worldUp; + private Matrix3X3 _intrinsicCoordinates; // right | up | front + private float _yaw; + private float _pitch; + private float _fov; + private float _nearClippingPlane; + private float _farClippingPlane; + + public Matrix4X4 ViewMatrix => Matrix4X4.CreateLookAt(_position, _position + _intrinsicCoordinates.Column3, _worldUp); // TODO: I hope this is Left-Handed. If not I need to update the readme + public Matrix4X4 ProjectionMatrix => Matrix4X4.CreatePerspectiveFieldOfView(_fov, , _nearClippingPlane, _farClippingPlane); + + public PerspectiveCamera(Vector3D position, float yaw = -90.0f, float pitch = 0, float fov = 45.0f, float nearClippingPlane = 0.1f, float farClippingPlane = 100.0f) + { + _position = position; + _yaw = yaw; + _pitch = pitch; + _fov = fov; + _nearClippingPlane = nearClippingPlane; + _farClippingPlane = farClippingPlane; + _worldUp = Vector3D.UnitY; + + UpdateCameraVectors(); + } + + internal void UpdateAspect() + { + + } + + private void UpdateCameraVectors() + { + var front = Vector3D.Normalize(new Vector3D( + MathF.Cos(_yaw.ToRadians()) * MathF.Cos(_pitch.ToRadians()), + MathF.Sin(_pitch.ToRadians()), + MathF.Sin(_yaw.ToRadians()) * MathF.Cos(_pitch.ToRadians()) + )); + var right = Vector3D.Normalize(Vector3D.Cross(front, _worldUp)); + var up = Vector3D.Normalize(Vector3D.Cross(right, front)); + + _intrinsicCoordinates = new Matrix3X3( + right.X, up.X, front.X, + right.Y, up.Y, front.Y, + right.Z, up.Z, front.Z + ); + } +} \ No newline at end of file diff --git a/src/EngineSharp/EngineSharp.csproj b/src/EngineSharp/EngineSharp.csproj new file mode 100644 index 0000000..440c32e --- /dev/null +++ b/src/EngineSharp/EngineSharp.csproj @@ -0,0 +1,18 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + + + + + diff --git a/src/EngineSharp/Program.cs b/src/EngineSharp/Program.cs new file mode 100644 index 0000000..af71db6 --- /dev/null +++ b/src/EngineSharp/Program.cs @@ -0,0 +1,21 @@ +using EngineSharp.Core; +using Silk.NET.Maths; +using Silk.NET.Windowing; +using GraphicsAPI = EngineSharp.Core.GraphicsAPI; + +namespace EngineSharp; + +static class Program +{ + static void Main(string[] args) + { + var options = WindowOptions.Default with + { + Title = "EngineSharp Engine", + Size = new Vector2D(800, 600), + }; + + var engine = EngineFactory.Create(GraphicsAPI.OpenGL, options); + engine.Start(); + } +} \ No newline at end of file