diff --git a/DoomDeathmatch.sln b/DoomDeathmatch.sln
index 15066e3..3dc2ea0 100644
--- a/DoomDeathmatch.sln
+++ b/DoomDeathmatch.sln
@@ -1,7 +1,5 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoomDeathmatch", "DoomDeathmatch\DoomDeathmatch.csproj", "{7AE4D009-6590-4E44-9B68-FE37B6650F70}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{5EE134DE-2275-40C0-8B9D-4EFF22474F63}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EngineTests", "EngineTests\EngineTests.csproj", "{CC28C26C-0998-4C13-8855-978658B8B0D6}"
@@ -12,16 +10,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresenterConsole", "Present
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresenterNative", "PresenterNative\PresenterNative.csproj", "{3B8B7867-5B38-4013-A366-159083F5C095}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoomDeathmatch", "DoomDeathmatch\DoomDeathmatch.csproj", "{4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {7AE4D009-6590-4E44-9B68-FE37B6650F70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7AE4D009-6590-4E44-9B68-FE37B6650F70}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7AE4D009-6590-4E44-9B68-FE37B6650F70}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7AE4D009-6590-4E44-9B68-FE37B6650F70}.Release|Any CPU.Build.0 = Release|Any CPU
{5EE134DE-2275-40C0-8B9D-4EFF22474F63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EE134DE-2275-40C0-8B9D-4EFF22474F63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EE134DE-2275-40C0-8B9D-4EFF22474F63}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -42,5 +38,9 @@ Global
{3B8B7867-5B38-4013-A366-159083F5C095}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B8B7867-5B38-4013-A366-159083F5C095}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B8B7867-5B38-4013-A366-159083F5C095}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/DoomDeathmatch/DoomDeathmatch.csproj b/DoomDeathmatch/DoomDeathmatch.csproj
index 025d582..e1baa34 100644
--- a/DoomDeathmatch/DoomDeathmatch.csproj
+++ b/DoomDeathmatch/DoomDeathmatch.csproj
@@ -1,21 +1,13 @@
- Exe
net8.0
enable
enable
-
-
-
-
-
-
-
-
+
diff --git a/DoomDeathmatch/src/Component/ControllerComponent.cs b/DoomDeathmatch/src/Component/ControllerComponent.cs
new file mode 100644
index 0000000..fbb0131
--- /dev/null
+++ b/DoomDeathmatch/src/Component/ControllerComponent.cs
@@ -0,0 +1,33 @@
+using Engine.Input;
+using OpenTK.Mathematics;
+
+namespace DoomDeathmatch.Component;
+
+public class ControllerComponent : Engine.Scene.Component.Component
+{
+ public float Speed { get; set; } = 10.0f;
+
+ private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
+
+ public override void Update(double parDeltaTime)
+ {
+ var movement = Vector3.Zero;
+ if (_inputHandler.IsKeyPressed(KeyboardButtonCode.W))
+ movement.Z += 1;
+ if (_inputHandler.IsKeyPressed(KeyboardButtonCode.S))
+ movement.Z -= 1;
+ if (_inputHandler.IsKeyPressed(KeyboardButtonCode.A))
+ movement.X -= 1;
+ if (_inputHandler.IsKeyPressed(KeyboardButtonCode.D))
+ movement.X += 1;
+ if (_inputHandler.IsKeyPressed(KeyboardButtonCode.Space))
+ movement.Y += 1;
+ if (_inputHandler.IsKeyPressed(KeyboardButtonCode.Shift))
+ movement.Y -= 1;
+
+ if (movement.LengthSquared > 0)
+ movement.Normalize();
+
+ GameObject.Transform.Translation += movement * Speed * (float)parDeltaTime;
+ }
+}
\ No newline at end of file
diff --git a/DoomDeathmatch/src/Component/RotateComponent.cs b/DoomDeathmatch/src/Component/RotateComponent.cs
new file mode 100644
index 0000000..d961dd9
--- /dev/null
+++ b/DoomDeathmatch/src/Component/RotateComponent.cs
@@ -0,0 +1,22 @@
+using Engine.Input;
+using OpenTK.Mathematics;
+
+namespace DoomDeathmatch.Component;
+
+public class RotateComponent : Engine.Scene.Component.Component
+{
+ private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
+
+ public override void Update(double parDeltaTime)
+ {
+ if (_inputHandler.IsKeyPressed(KeyboardButtonCode.Q))
+ GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitY, (float)parDeltaTime * 0.5f);
+ else if (_inputHandler.IsKeyPressed(KeyboardButtonCode.E))
+ GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitY, -(float)parDeltaTime * 0.5f);
+
+ if (_inputHandler.IsMouseButtonPressed(MouseButtonCode.Left))
+ GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitZ, (float)parDeltaTime * 0.5f);
+ else if (_inputHandler.IsMouseButtonPressed(MouseButtonCode.Right))
+ GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitZ, -(float)parDeltaTime * 0.5f);
+ }
+}
\ No newline at end of file
diff --git a/DoomDeathmatch/src/DoomDeathmatch.cs b/DoomDeathmatch/src/DoomDeathmatch.cs
new file mode 100644
index 0000000..e4a3b16
--- /dev/null
+++ b/DoomDeathmatch/src/DoomDeathmatch.cs
@@ -0,0 +1,37 @@
+using DoomDeathmatch.Component;
+using Engine.Asset.Mesh.Loader;
+using Engine.Scene;
+using Engine.Scene.Component.BuiltIn;
+using Engine.Scene.Component.BuiltIn.Renderer;
+using OpenTK.Mathematics;
+
+namespace DoomDeathmatch;
+
+public static class DoomDeathmatch
+{
+ public static void Initialize(Engine.Engine parEngine)
+ {
+ parEngine.SceneManager.TransitionTo(MainScene());
+ }
+
+ private static Scene MainScene()
+ {
+ var cameraObject = new GameObject();
+ cameraObject.Transform.Translation.Z = -6;
+ cameraObject.AddComponent();
+ cameraObject.AddComponent();
+
+ using var reader = new StreamReader("../DoomDeathmatch/asset/model/test2.obj");
+
+ var box2dRenderer = new GameObject();
+ box2dRenderer.AddComponent(new MeshRenderer { Mesh = ObjMeshLoader.Load(reader) });
+ box2dRenderer.AddComponent();
+ box2dRenderer.AddComponent();
+
+ var scene = new Scene();
+ scene.Add(cameraObject);
+ scene.Add(box2dRenderer);
+
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/DoomDeathmatch/src/Program.cs b/DoomDeathmatch/src/Program.cs
deleted file mode 100644
index 5f53be4..0000000
--- a/DoomDeathmatch/src/Program.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Diagnostics;
-using System.Drawing;
-using System.Runtime.InteropServices;
-
-namespace DoomDeathmatch;
-
-internal abstract class Program
-{
- public static void Main(string[] args)
- {
- }
-}
\ No newline at end of file
diff --git a/DoomDeathmatch/src/QuadVertex.cs b/DoomDeathmatch/src/QuadVertex.cs
deleted file mode 100644
index f26909f..0000000
--- a/DoomDeathmatch/src/QuadVertex.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Engine.Graphics.Buffer.Vertex;
-using OpenTK.Graphics.OpenGL;
-using OpenTK.Mathematics;
-using Half = System.Half;
-
-namespace DoomDeathmatch;
-
-public struct QuadVertex : IVertex
-{
- [Vertex(VertexAttribType.Float, 2)] public Vector2 Position2;
-
- [Vertex(VertexAttribType.Float, 2)] public Vector2 Position;
-
- [Vertex(VertexAttribType.Float, 2)] public Vector2 Position4;
-
- [Vertex(VertexAttribType.Float, 2)] public Vector2 Position3;
-}
\ No newline at end of file
diff --git a/Engine/assets/shader/mesh.shader b/Engine/assets/shader/mesh.shader
index 3d4ec80..89fccde 100644
--- a/Engine/assets/shader/mesh.shader
+++ b/Engine/assets/shader/mesh.shader
@@ -6,29 +6,40 @@ uniform mat4 uViewMatrix;
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
-layout (location = 2) in vec2 aTexCoords;
-layout (location = 3) in mat4 aModelMatrix;
+layout (location = 2) in vec2 aUV;
+layout (location = 3) in int aTextureId;
+layout (location = 4) in mat4 aModelMatrix;
-layout (location = 0) out vec2 oTexCoords;
+layout (location = 0) out vec2 oUV;
+layout (location = 1) out int oTextureId;
+layout (location = 2) out vec3 oNormal;
void main()
{
+ oUV = aUV;
+ oTextureId = aTextureId;
+ oNormal = aNormal;
gl_Position = uProjectionMatrix * uViewMatrix * aModelMatrix * vec4(aPos, 1.0);
- oTexCoords = aTexCoords;
}
// #type fragment
#version 460 core
-uniform sampler2D uTexture;
+uniform sampler2D uTexture[16];
-layout (location = 0) in vec2 iTexCoords;
+layout (location = 0) in vec2 iUV;
+layout (location = 1) flat in int iTextureId;
+layout (location = 2) in vec3 iNormal;
layout (location = 0) out vec4 FragColor;
void main()
{
- FragColor = texture(uTexture, iTexCoords);
+ vec3 lightColor = vec3(iUV, 0);
+ FragColor = vec4(lightColor, 1.0);
+ if (iTextureId >= 0)
+ FragColor *= texture(uTexture[iTextureId], iUV);
+
if (FragColor.a == 0.0)
- discard;
+ discard;
}
\ No newline at end of file
diff --git a/Engine/src/Asset/Mesh/Loader/IMeshLoader.cs b/Engine/src/Asset/Mesh/Loader/IMeshLoader.cs
index c5871ec..b62a427 100644
--- a/Engine/src/Asset/Mesh/Loader/IMeshLoader.cs
+++ b/Engine/src/Asset/Mesh/Loader/IMeshLoader.cs
@@ -2,9 +2,9 @@
public interface IMeshLoader
{
- public Mesh LoadMesh(string parPath, MeshLoaderParameters parAmeters = MeshLoaderParameters._default);
+ public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default);
- public static Mesh Optimize(Mesh parMesh)
+ internal static Mesh Optimize(Mesh parMesh)
{
var optimizedMesh = new Mesh();
var vertexMap = new Dictionary();
diff --git a/Engine/src/Asset/Mesh/Loader/MeshLoaderParameters.cs b/Engine/src/Asset/Mesh/Loader/MeshLoaderParameters.cs
index 09a8d79..995be3e 100644
--- a/Engine/src/Asset/Mesh/Loader/MeshLoaderParameters.cs
+++ b/Engine/src/Asset/Mesh/Loader/MeshLoaderParameters.cs
@@ -3,10 +3,10 @@
[Flags]
public enum MeshLoaderParameters
{
- _none = 0,
- _loadNormals = 1 << 0,
- _loadUVs = 1 << 1,
- _optimize = 1 << 2,
+ None = 0,
+ LoadNormals = 1 << 0,
+ LoadUVs = 1 << 1,
+ Optimize = 1 << 2,
- _default = _loadNormals | _loadUVs | _optimize
+ Default = LoadNormals | LoadUVs | Optimize
}
\ No newline at end of file
diff --git a/Engine/src/Asset/Mesh/Loader/ObjMeshLoader.cs b/Engine/src/Asset/Mesh/Loader/ObjMeshLoader.cs
index ff3fc6b..655bfad 100644
--- a/Engine/src/Asset/Mesh/Loader/ObjMeshLoader.cs
+++ b/Engine/src/Asset/Mesh/Loader/ObjMeshLoader.cs
@@ -7,12 +7,16 @@ public class ObjMeshLoader : IMeshLoader
{
private static readonly ObjMeshLoader _instance = new();
- public static Mesh Load(string parPath, MeshLoaderParameters parAmeters = MeshLoaderParameters._default)
+ public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
{
- return _instance.LoadMesh(parPath, parAmeters);
+ return _instance.LoadMesh(parReader, parParameters);
}
- public Mesh LoadMesh(string parPath, MeshLoaderParameters parAmeters = MeshLoaderParameters._default)
+ private ObjMeshLoader()
+ {
+ }
+
+ public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
{
var mesh = new Mesh();
@@ -21,13 +25,11 @@ public class ObjMeshLoader : IMeshLoader
var tempUVs = new List();
var index = 0u;
- var loadNormals = parAmeters.HasFlag(MeshLoaderParameters._loadNormals);
- var loadUVs = parAmeters.HasFlag(MeshLoaderParameters._loadUVs);
-
- using var reader = new StreamReader(parPath);
- while (reader.ReadLine() is { } line)
+ var loadNormals = parParameters.HasFlag(MeshLoaderParameters.LoadNormals);
+ var loadUVs = parParameters.HasFlag(MeshLoaderParameters.LoadUVs);
+ while (parReader.ReadLine() is { } line)
{
- string[]? parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
+ var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 0 || parts[0].StartsWith('#'))
{
@@ -62,7 +64,7 @@ public class ObjMeshLoader : IMeshLoader
case "f":
for (var i = 1; i <= 3; i++)
{
- string[]? faceComponents = parts[i].Split('/');
+ var faceComponents = parts[i].Split('/');
var meshVertex = new Mesh.Vertex { _position = tempVertices[int.Parse(faceComponents[0]) - 1] };
if (loadUVs && faceComponents.Length > 1 && faceComponents[1] != "")
@@ -83,7 +85,7 @@ public class ObjMeshLoader : IMeshLoader
}
}
- if (parAmeters.HasFlag(MeshLoaderParameters._optimize))
+ if (parParameters.HasFlag(MeshLoaderParameters.Optimize))
{
mesh = IMeshLoader.Optimize(mesh);
}
diff --git a/Engine/src/Asset/Mesh/Loader/StlMeshLoader.cs b/Engine/src/Asset/Mesh/Loader/StlMeshLoader.cs
index 28078ae..91e05da 100644
--- a/Engine/src/Asset/Mesh/Loader/StlMeshLoader.cs
+++ b/Engine/src/Asset/Mesh/Loader/StlMeshLoader.cs
@@ -7,20 +7,25 @@ public class StlMeshLoader : IMeshLoader
{
private static readonly StlMeshLoader _instance = new();
- public static Mesh Load(string parPath, MeshLoaderParameters parAmeters = MeshLoaderParameters._default)
+ public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
{
- return _instance.LoadMesh(parPath, parAmeters);
+ return _instance.LoadMesh(parReader, parParameters);
}
- public Mesh LoadMesh(string parPath, MeshLoaderParameters parAmeters = MeshLoaderParameters._default)
+ private StlMeshLoader()
+ {
+ }
+
+ public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
{
var mesh = new Mesh();
+ var loadNormals = parParameters.HasFlag(MeshLoaderParameters.LoadNormals);
+
var currentNormal = new Vector3();
var index = 0u;
- using var reader = new StreamReader(parPath);
- while (reader.ReadLine() is { } line)
+ while (parReader.ReadLine() is { } line)
{
line = line.Trim();
@@ -35,11 +40,11 @@ public class StlMeshLoader : IMeshLoader
break;
}
- string[]? parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
+ var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
switch (parts[0])
{
- case "facet" when parts[1] == "normal" && parAmeters.HasFlag(MeshLoaderParameters._loadNormals):
+ case "facet" when parts[1] == "normal" && loadNormals:
currentNormal = new Vector3(
float.Parse(parts[2], CultureInfo.InvariantCulture),
float.Parse(parts[3], CultureInfo.InvariantCulture),
@@ -61,7 +66,7 @@ public class StlMeshLoader : IMeshLoader
}
}
- if (parAmeters.HasFlag(MeshLoaderParameters._optimize))
+ if (parParameters.HasFlag(MeshLoaderParameters.Optimize))
{
mesh = IMeshLoader.Optimize(mesh);
}
diff --git a/Engine/src/Engine.cs b/Engine/src/Engine.cs
index 1ec77b1..8bf215b 100644
--- a/Engine/src/Engine.cs
+++ b/Engine/src/Engine.cs
@@ -12,6 +12,8 @@ namespace Engine;
public sealed class Engine
{
+ public static Engine Instance { get; private set; } = null!;
+
public Renderer Renderer { get; }
public SceneManager SceneManager { get; } = new();
@@ -36,10 +38,10 @@ public sealed class Engine
}
}
- internal IInputHandler? InputHandler
+ public IInputHandler? InputHandler
{
get => _inputHandler;
- set => _inputHandler = value;
+ internal set => _inputHandler = value;
}
private readonly ILogger _logger;
@@ -51,6 +53,13 @@ public sealed class Engine
public Engine(int parWidth, int parHeight, bool parHeadless, string parTitle, ILogger parLogger)
{
+ if (Instance != null)
+ {
+ throw new InvalidOperationException("Engine is already running");
+ }
+
+ Instance = this;
+
var settings = new NativeWindowSettings
{
ClientSize = parHeadless ? new Vector2i(1, 1) : new Vector2i(parWidth, parHeight),
@@ -67,46 +76,69 @@ public sealed class Engine
_logger = Log.ForContext();
}
+ private readonly object _sceneLock = new();
+
public void Run()
{
- _updateThread = new Thread(RunUpdate);
+ _updateThread = new Thread(RunUpdate) { Name = "UpdateThread" };
_updateThread.Start();
- var timer = Stopwatch.StartNew();
- var deltaTime = 0.0;
+ RunRender();
+
+ _updateThread.Join();
+ }
+
+ private void RunRender()
+ {
while (!Presenter?.IsExiting ?? false)
{
Renderer.StartFrame();
- GL.ClearColor(0.6f, 0.0f, 0.6f, 1.0f);
- GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
+ var view = Matrix4.Identity;
+ var projection = Matrix4.Identity;
+ lock (_sceneLock)
+ {
+ var camera = SceneManager.CurrentScene?.MainCamera;
+ if (camera != null)
+ {
+ camera.ScreenSize = new Vector2i(Renderer.ViewportWidth, Renderer.ViewportHeight);
+ view = camera.View;
+ projection = camera.Projection;
+ }
- Renderer.QuadRenderer.Commit(
- Matrix4.CreateRotationZ((float)deltaTime),
- new Vector4((MathF.Sin((float)deltaTime) + 1.0f) / 2, (MathF.Sin((float)deltaTime) + 1.0f) / 2,
- (MathF.Sin((float)deltaTime) + 1.0f) / 2, 1.0f));
- Renderer.QuadRenderer.Render(Matrix4.Identity, Matrix4.Identity);
+ SceneManager.Render();
+ }
+
+ Renderer.QuadRenderer.Render(projection, view);
Renderer.QuadRenderer.Reset();
+ Renderer.GlobalMeshRenderer.Render(projection, view);
+ Renderer.GlobalMeshRenderer.Reset();
+
Renderer.EndFrame();
Presenter!.Present(Renderer.TextureInternal);
- Presenter!.Update(deltaTime);
- deltaTime = timer.Elapsed.TotalSeconds;
+ Presenter!.Render();
}
-
- _updateThread.Join();
}
private void RunUpdate()
{
var timer = Stopwatch.StartNew();
var deltaTime = 0.0;
+
while (!Presenter?.IsExiting ?? false)
{
- SceneManager.Update(deltaTime);
- timer.Restart();
+ Presenter!.Update(deltaTime);
+ InputHandler?.Update(deltaTime);
+
+ lock (_sceneLock)
+ {
+ SceneManager.Update(deltaTime);
+ }
+
deltaTime = timer.Elapsed.TotalSeconds;
+ timer.Restart();
}
}
diff --git a/Engine/src/EngineBuilder.cs b/Engine/src/EngineBuilder.cs
index 264a3b2..21efb22 100644
--- a/Engine/src/EngineBuilder.cs
+++ b/Engine/src/EngineBuilder.cs
@@ -23,8 +23,6 @@ public sealed class EngineBuilder
private string? _logFilePath;
private LogEventLevel _logLevel = LogEventLevel.Information;
- public EngineBuilder() { }
-
public EngineBuilder Title(string parTitle)
{
_title = parTitle;
@@ -53,12 +51,6 @@ public sealed class EngineBuilder
return this;
}
- public EngineBuilder Presenter(IPresenter parPresenter)
- {
- _presenterFunc = _ => parPresenter;
- return this;
- }
-
public EngineBuilder Presenter(Func parPresenterFunc)
{
_presenterFunc = parPresenterFunc;
diff --git a/Engine/src/Graphics/Buffer/VertexArray.cs b/Engine/src/Graphics/Buffer/VertexArray.cs
index 9a5d0e9..1851176 100644
--- a/Engine/src/Graphics/Buffer/VertexArray.cs
+++ b/Engine/src/Graphics/Buffer/VertexArray.cs
@@ -8,14 +8,14 @@ namespace Engine.Graphics.Buffer;
public class VertexArray : OpenGlObject
{
- // private IndexBuffer? _boundIndexBuffer;
- // private readonly Dictionary> _boundVertexBuffers = new();
private int _enabledAttribs = 0;
public VertexArray()
{
GL.CreateVertexArrays(1, out int handle);
Handle = handle;
+
+ Log.Debug("Vertex array {Handle} created", Handle);
}
public void BindIndexBuffer(IndexBuffer parBuffer)
@@ -28,18 +28,11 @@ public class VertexArray : OpenGlObject
public void BindVertexBuffer(VertexBuffer parBuffer, int parBindingIndex = 0, int parDivisor = 0)
where T : struct, IVertex
{
- if (parBindingIndex < 0)
- {
- throw new ArgumentException("Binding index must be greater than 0");
- }
-
- if (parDivisor < 0)
- {
- throw new ArgumentException("Divisor must be greater than 0");
- }
+ ArgumentOutOfRangeException.ThrowIfNegative(parBindingIndex);
+ ArgumentOutOfRangeException.ThrowIfNegative(parDivisor);
var stride = Marshal.SizeOf();
- IOrderedEnumerable? fields = IVertex.GetFields();
+ var fields = IVertex.GetFields();
GL.VertexArrayVertexBuffer(Handle, parBindingIndex, parBuffer.Handle, 0, stride);
diff --git a/Engine/src/Graphics/Framebuffer/Framebuffer.cs b/Engine/src/Graphics/Framebuffer/Framebuffer.cs
index 3fe3a38..c5c5436 100644
--- a/Engine/src/Graphics/Framebuffer/Framebuffer.cs
+++ b/Engine/src/Graphics/Framebuffer/Framebuffer.cs
@@ -10,12 +10,9 @@ public class Framebuffer : OpenGlObject
public int Width
{
get => _width;
- protected set
+ private set
{
- if (value <= 0)
- {
- throw new ArgumentException("Width must be greater than 0");
- }
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
_width = value;
}
@@ -24,12 +21,9 @@ public class Framebuffer : OpenGlObject
public int Height
{
get => _height;
- protected set
+ private set
{
- if (value <= 0)
- {
- throw new ArgumentException("Height must be greater than 0");
- }
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
_height = value;
}
@@ -51,18 +45,9 @@ public class Framebuffer : OpenGlObject
GL.CreateFramebuffers(1, out int handle);
Handle = handle;
- foreach (KeyValuePair attachment in _attachments)
+ foreach (var attachment in _attachments)
{
- switch (attachment.Value.Type)
- {
- case IFramebufferAttachment.AttachmentType.Texture:
- GL.NamedFramebufferTexture(Handle, attachment.Key, attachment.Value.Handle, 0);
- break;
- case IFramebufferAttachment.AttachmentType.Renderbuffer:
- GL.NamedFramebufferRenderbuffer(Handle, attachment.Key, RenderbufferTarget.Renderbuffer,
- attachment.Value.Handle);
- break;
- }
+ attachment.Value.Attach(this, attachment.Key);
}
var status = GL.CheckNamedFramebufferStatus(Handle, FramebufferTarget.Framebuffer);
diff --git a/Engine/src/Graphics/Framebuffer/FramebufferBuilder.cs b/Engine/src/Graphics/Framebuffer/FramebufferBuilder.cs
index a5cf4b6..0f8c0ef 100644
--- a/Engine/src/Graphics/Framebuffer/FramebufferBuilder.cs
+++ b/Engine/src/Graphics/Framebuffer/FramebufferBuilder.cs
@@ -55,7 +55,7 @@ public class FramebufferBuilder(int parWidth, int parHeight)
public Framebuffer Build()
{
- Dictionary? attachments = _attachments
+ var attachments = _attachments
.ToDictionary(
parAttachment => parAttachment.FramebufferAttachment,
parAttachment => parAttachment.Create(parWidth, parHeight)
diff --git a/Engine/src/Graphics/Framebuffer/IFramebufferAttachment.cs b/Engine/src/Graphics/Framebuffer/IFramebufferAttachment.cs
index 5ae762d..bdcb4e6 100644
--- a/Engine/src/Graphics/Framebuffer/IFramebufferAttachment.cs
+++ b/Engine/src/Graphics/Framebuffer/IFramebufferAttachment.cs
@@ -1,15 +1,12 @@
-namespace Engine.Graphics.Framebuffer;
+using OpenTK.Graphics.OpenGL;
+
+namespace Engine.Graphics.Framebuffer;
public interface IFramebufferAttachment
{
- public AttachmentType Type { get; }
internal int Handle { get; }
public void Resize(int parWidth, int parHeight);
- public enum AttachmentType
- {
- Texture,
- Renderbuffer
- }
+ internal void Attach(Framebuffer parFramebuffer, FramebufferAttachment parAttachment);
}
\ No newline at end of file
diff --git a/Engine/src/Graphics/Framebuffer/Renderbuffer.cs b/Engine/src/Graphics/Framebuffer/Renderbuffer.cs
index 301aeda..2075ebe 100644
--- a/Engine/src/Graphics/Framebuffer/Renderbuffer.cs
+++ b/Engine/src/Graphics/Framebuffer/Renderbuffer.cs
@@ -7,8 +7,6 @@ public class Renderbuffer : OpenGlObject, IFramebufferAttachment
public int Width { get; private set; }
public int Height { get; private set; }
- public IFramebufferAttachment.AttachmentType Type => IFramebufferAttachment.AttachmentType.Renderbuffer;
-
private readonly RenderbufferStorage _format;
public Renderbuffer(int parWidth, int parHeight, RenderbufferStorage parFormat)
@@ -36,6 +34,11 @@ public class Renderbuffer : OpenGlObject, IFramebufferAttachment
GL.NamedRenderbufferStorage(Handle, _format, Width, Height);
}
+ public void Attach(Framebuffer parFramebuffer, FramebufferAttachment parAttachment)
+ {
+ GL.NamedFramebufferRenderbuffer(parFramebuffer.Handle, parAttachment, RenderbufferTarget.Renderbuffer, Handle);
+ }
+
public override void Bind()
{
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Handle);
diff --git a/Engine/src/Graphics/IPresenter.cs b/Engine/src/Graphics/IPresenter.cs
index 445301b..90ea680 100644
--- a/Engine/src/Graphics/IPresenter.cs
+++ b/Engine/src/Graphics/IPresenter.cs
@@ -4,7 +4,7 @@ using OpenTK.Windowing.Common;
namespace Engine.Graphics;
-public interface IPresenter : IUpdate
+public interface IPresenter : IUpdate, IRender
{
public bool IsExiting { get; }
public int Width { get; }
diff --git a/Engine/src/Graphics/OpenGLObject.cs b/Engine/src/Graphics/OpenGLObject.cs
index 2d69d00..6d4a1ac 100644
--- a/Engine/src/Graphics/OpenGLObject.cs
+++ b/Engine/src/Graphics/OpenGLObject.cs
@@ -13,10 +13,18 @@ public abstract class OpenGlObject
~OpenGlObject()
{
- Destroy();
+ if (Handle == -1)
+ {
+ return;
+ }
- Log.Debug("OpenGL object {Handle} destroyed", Handle);
+ Engine.Instance.Renderer.Schedule(() =>
+ {
+ Destroy();
- Handle = -1;
+ Log.Debug("OpenGL object {Handle} destroyed", Handle);
+
+ Handle = -1;
+ });
}
}
\ No newline at end of file
diff --git a/Engine/src/Graphics/Render/InstancedRenderer.cs b/Engine/src/Graphics/Render/InstancedRenderer.cs
index e0a0e9e..56b418c 100644
--- a/Engine/src/Graphics/Render/InstancedRenderer.cs
+++ b/Engine/src/Graphics/Render/InstancedRenderer.cs
@@ -12,10 +12,10 @@ public abstract class InstancedRenderer
{
protected readonly Renderer _renderer;
- protected readonly IndexBuffer _indexBuffer;
- protected readonly VertexBuffer _commonVertexBuffer;
- protected readonly VertexBuffer _instanceVertexBuffer;
- protected readonly VertexArray _vertexArray;
+ private readonly IndexBuffer _indexBuffer;
+ private readonly VertexBuffer _commonVertexBuffer;
+ private readonly VertexBuffer _instanceVertexBuffer;
+ private readonly VertexArray _vertexArray;
protected readonly int _instanceCount;
protected int _queuedInstanceCount;
diff --git a/Engine/src/Graphics/Render/Mesh/GlobalMeshRenderer.cs b/Engine/src/Graphics/Render/Mesh/GlobalMeshRenderer.cs
new file mode 100644
index 0000000..0b784d4
--- /dev/null
+++ b/Engine/src/Graphics/Render/Mesh/GlobalMeshRenderer.cs
@@ -0,0 +1,54 @@
+using Engine.Graphics.Shader;
+using OpenTK.Mathematics;
+
+namespace Engine.Graphics.Render.Mesh;
+
+public class GlobalMeshRenderer(Renderer parRenderer, int parMaxInstanceCount)
+{
+ private readonly Dictionary _meshRenderers = new();
+ private readonly HashSet _frameMeshes = [];
+
+ private readonly Program _program = ProgramLoader.LoadFromSource(ShaderResource.Mesh);
+
+ public void Commit(Asset.Mesh.Mesh parMesh, Matrix4 parModelMatrix)
+ {
+ if (_meshRenderers.TryGetValue(parMesh, out var meshRenderer))
+ {
+ meshRenderer.Commit(parModelMatrix);
+ }
+ else
+ {
+ var newMeshRenderer = new MeshRenderer(parRenderer, parMesh, parMaxInstanceCount, _program);
+ newMeshRenderer.Commit(parModelMatrix);
+
+ _meshRenderers.Add(parMesh, newMeshRenderer);
+ }
+
+ _frameMeshes.Add(parMesh);
+ }
+
+ public void Render(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
+ {
+ foreach (var meshRenderer in _meshRenderers.Values)
+ {
+ meshRenderer.Render(parProjectionMatrix, parViewMatrix);
+ }
+ }
+
+ public void Reset()
+ {
+ foreach (var meshRenderer in _meshRenderers.Values)
+ {
+ meshRenderer.Reset();
+ }
+
+ var meshes = _meshRenderers.Keys;
+ var unusedMeshes = meshes.Where(parMesh => !_frameMeshes.Contains(parMesh));
+ foreach (var unusedMesh in unusedMeshes)
+ {
+ _meshRenderers.Remove(unusedMesh);
+ }
+
+ _frameMeshes.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Engine/src/Graphics/Render/Mesh/MeshInstanceVertex.cs b/Engine/src/Graphics/Render/Mesh/MeshInstanceVertex.cs
index 49d8a6e..a521dd2 100644
--- a/Engine/src/Graphics/Render/Mesh/MeshInstanceVertex.cs
+++ b/Engine/src/Graphics/Render/Mesh/MeshInstanceVertex.cs
@@ -6,5 +6,6 @@ namespace Engine.Graphics.Render.Mesh;
public struct MeshInstanceVertex : IVertex
{
+ [Vertex(VertexAttribType.Int)] public int _textureId;
[Vertex(VertexAttribType.Float, 4, 4)] public Matrix4 _modelMatrix;
}
\ No newline at end of file
diff --git a/Engine/src/Graphics/Render/Mesh/MeshRenderer.cs b/Engine/src/Graphics/Render/Mesh/MeshRenderer.cs
index 1b60b0e..49521c0 100644
--- a/Engine/src/Graphics/Render/Mesh/MeshRenderer.cs
+++ b/Engine/src/Graphics/Render/Mesh/MeshRenderer.cs
@@ -1,26 +1,48 @@
using Engine.Graphics.Shader;
+using Engine.Graphics.Texture;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
namespace Engine.Graphics.Render.Mesh;
-public class MeshRenderer : InstancedRenderer
+public class MeshRenderer(Renderer parRenderer, Asset.Mesh.Mesh parMesh, int parInstanceCount, Program parProgram)
+ : InstancedRenderer(
+ parRenderer, PrimitiveType.Triangles,
+ parInstanceCount,
+ parMesh.Indices.ToArray(),
+ parMesh.Vertices.ToArray(),
+ parProgram
+ )
{
- public MeshRenderer(Renderer parRenderer, int parInstanceCount, Asset.Mesh.Mesh parMesh)
- : base(parRenderer, PrimitiveType.Triangles, parInstanceCount, parMesh.Indices.ToArray(),
- parMesh.Vertices.ToArray(),
- ProgramLoader.LoadFromSource(ShaderResource.Mesh))
- {
- }
+ private readonly TextureUnitMap _textureUnitMap = new(16);
+ private readonly int[] _textureUnitIndices = new int[16];
- public void Commit(Matrix4 parModelMatrix)
+ public void Commit(Matrix4 parModelMatrix, Texture.Texture? parTexture = null)
{
if (_queuedInstanceCount >= _instanceCount)
{
throw new InvalidOperationException("Instance count exceeded");
}
+ var textureId = -1;
+ if (parTexture != null)
+ {
+ textureId = _textureUnitMap.GetUnit(parTexture);
+ }
+
+ _instanceVertices[_queuedInstanceCount]._textureId = textureId;
_instanceVertices[_queuedInstanceCount]._modelMatrix = parModelMatrix;
_queuedInstanceCount++;
}
+
+ protected override void SetAdditionalUniforms(Program parProgram)
+ {
+ foreach (var (texture, unit) in _textureUnitMap.Textures)
+ {
+ texture.BindUnit(unit);
+ _textureUnitIndices[unit] = unit;
+ }
+
+ parProgram.SetUniform("uTexture", _textureUnitIndices);
+ }
}
\ No newline at end of file
diff --git a/Engine/src/Graphics/Renderer.cs b/Engine/src/Graphics/Renderer.cs
index 5f91f69..b328b78 100644
--- a/Engine/src/Graphics/Renderer.cs
+++ b/Engine/src/Graphics/Renderer.cs
@@ -1,4 +1,5 @@
using Engine.Graphics.Pixel;
+using Engine.Graphics.Render.Mesh;
using Engine.Graphics.Render.Quad;
using OpenTK.Graphics.OpenGL;
@@ -9,11 +10,15 @@ public class Renderer
internal Texture.Texture TextureInternal => _framebuffer.TextureInternal!;
public QuadRenderer QuadRenderer { get; }
+ public GlobalMeshRenderer GlobalMeshRenderer { get; }
+ public int ViewportWidth => _framebuffer.Width;
+ public int ViewportHeight => _framebuffer.Height;
private readonly Framebuffer.Framebuffer _framebuffer;
-
private readonly Thread _renderThread;
+ private readonly Queue _scheduleActions = new();
+
public Renderer(int parWidth, int parHeight)
{
Thread.CurrentThread.Name = "RendererThread";
@@ -28,6 +33,7 @@ public class Renderer
.Build();
QuadRenderer = new QuadRenderer(this, 1024 * 8);
+ GlobalMeshRenderer = new GlobalMeshRenderer(this, 1024);
}
private void InitializeOpenGl(int parWidth, int parHeight)
@@ -54,15 +60,25 @@ public class Renderer
return;
}
- throw new InvalidOperationException("Renderer is not on render thread");
+ throw new InvalidOperationException("Not on render thread");
#endif
}
+ internal void Schedule(Action parAction)
+ {
+ _scheduleActions.Enqueue(parAction);
+ }
+
internal void StartFrame()
{
EnsureRenderThread();
+ RunScheduledActions();
+
_framebuffer.Bind();
+
+ GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
}
internal void EndFrame()
@@ -77,4 +93,12 @@ public class Renderer
_framebuffer.Resize(parWidth, parHeight);
GL.Viewport(0, 0, parWidth, parHeight);
}
+
+ private void RunScheduledActions()
+ {
+ while (_scheduleActions.TryDequeue(out var action))
+ {
+ action();
+ }
+ }
}
\ No newline at end of file
diff --git a/Engine/src/Graphics/Texture/DynamicTexture.cs b/Engine/src/Graphics/Texture/DynamicTexture.cs
index 7ba4e03..725065e 100644
--- a/Engine/src/Graphics/Texture/DynamicTexture.cs
+++ b/Engine/src/Graphics/Texture/DynamicTexture.cs
@@ -7,8 +7,6 @@ namespace Engine.Graphics.Texture;
public class DynamicTexture : Texture, IFramebufferAttachment
{
- public IFramebufferAttachment.AttachmentType Type => IFramebufferAttachment.AttachmentType.Texture;
-
private readonly PixelFormat _format;
private readonly PixelType _type;
private readonly PixelInternalFormat _internalFormat;
@@ -49,4 +47,9 @@ public class DynamicTexture : Texture, IFramebufferAttachment
Log.Debug("Texture {Handle} resized to {Width}x{Height}", Handle, Width, Height);
}
+
+ public void Attach(Framebuffer.Framebuffer parFramebuffer, FramebufferAttachment parAttachment)
+ {
+ GL.NamedFramebufferTexture(parFramebuffer.Handle, parAttachment, Handle, 0);
+ }
}
\ No newline at end of file
diff --git a/Engine/src/Input/IInputHandler.cs b/Engine/src/Input/IInputHandler.cs
index 3002ec9..c5a065c 100644
--- a/Engine/src/Input/IInputHandler.cs
+++ b/Engine/src/Input/IInputHandler.cs
@@ -4,10 +4,9 @@ namespace Engine.Input;
public interface IInputHandler : IUpdate
{
- bool IsKeyPressed(KeyCode parKeyCode);
- bool IsKeyJustPressed(KeyCode parKeyCode);
- bool IsKeyRepeat(KeyCode parKeyCode);
+ bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode);
+ bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode);
- bool IsMouseButtonPressed(MouseButton parButton);
- bool IsMouseButtonJustPressed(MouseButton parButton);
+ bool IsMouseButtonPressed(MouseButtonCode parButtonCode);
+ bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode);
}
\ No newline at end of file
diff --git a/Engine/src/Input/KeyCode.cs b/Engine/src/Input/KeyboardButtonCode.cs
similarity index 81%
rename from Engine/src/Input/KeyCode.cs
rename to Engine/src/Input/KeyboardButtonCode.cs
index 1b43403..baff356 100644
--- a/Engine/src/Input/KeyCode.cs
+++ b/Engine/src/Input/KeyboardButtonCode.cs
@@ -1,6 +1,6 @@
namespace Engine.Input;
-public enum KeyCode
+public enum KeyboardButtonCode
{
A,
B,
@@ -48,5 +48,7 @@ public enum KeyCode
Home,
End,
PageUp,
- PageDown
+ PageDown,
+
+ TotalCount = PageDown + 1
}
\ No newline at end of file
diff --git a/Engine/src/Input/MouseButton.cs b/Engine/src/Input/MouseButton.cs
deleted file mode 100644
index 41712a4..0000000
--- a/Engine/src/Input/MouseButton.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Engine.Input;
-
-public enum MouseButton
-{
- Left,
- Right,
- Middle,
-
- Button4,
- Button5,
- Button6,
- Button7
-}
\ No newline at end of file
diff --git a/Engine/src/Input/MouseButtonCode.cs b/Engine/src/Input/MouseButtonCode.cs
new file mode 100644
index 0000000..4fcc558
--- /dev/null
+++ b/Engine/src/Input/MouseButtonCode.cs
@@ -0,0 +1,10 @@
+namespace Engine.Input;
+
+public enum MouseButtonCode
+{
+ Left,
+ Right,
+ Middle,
+
+ TotalCount = Middle + 1
+}
\ No newline at end of file
diff --git a/Engine/src/Input/WindowInputHandler.cs b/Engine/src/Input/WindowInputHandler.cs
new file mode 100644
index 0000000..95b8843
--- /dev/null
+++ b/Engine/src/Input/WindowInputHandler.cs
@@ -0,0 +1,99 @@
+using OpenTK.Windowing.GraphicsLibraryFramework;
+
+namespace Engine.Input;
+
+public class WindowInputHandler(Window parWindow) : IInputHandler
+{
+ private KeyboardState _previousKeyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
+ private KeyboardState _keyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
+
+ public void Update(double parDeltaTime)
+ {
+ _previousKeyboardState = _keyboardState;
+ _keyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
+ }
+
+ public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
+ {
+ return _keyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode));
+ }
+
+ public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
+ {
+ return _keyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode)) &&
+ !_previousKeyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode));
+ }
+
+ public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
+ {
+ return parWindow.NativeWindow.MouseState.IsButtonDown(MapMouseButtonCode(parButtonCode));
+ }
+
+ public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
+ {
+ return parWindow.NativeWindow.MouseState.IsButtonPressed(MapMouseButtonCode(parButtonCode));
+ }
+
+ private static MouseButton MapMouseButtonCode(MouseButtonCode parButton)
+ {
+ return parButton switch
+ {
+ MouseButtonCode.Left => MouseButton.Left,
+ MouseButtonCode.Right => MouseButton.Right,
+ MouseButtonCode.Middle => MouseButton.Middle,
+ _ => throw new ArgumentOutOfRangeException(nameof(parButton), parButton, null)
+ };
+ }
+
+ private static Keys MapKeyCode(KeyboardButtonCode parKeyboardButtonCode)
+ {
+ return parKeyboardButtonCode switch
+ {
+ KeyboardButtonCode.A => Keys.A,
+ KeyboardButtonCode.B => Keys.B,
+ KeyboardButtonCode.C => Keys.C,
+ KeyboardButtonCode.D => Keys.D,
+ KeyboardButtonCode.E => Keys.E,
+ KeyboardButtonCode.F => Keys.F,
+ KeyboardButtonCode.G => Keys.G,
+ KeyboardButtonCode.H => Keys.H,
+ KeyboardButtonCode.I => Keys.I,
+ KeyboardButtonCode.J => Keys.J,
+ KeyboardButtonCode.K => Keys.K,
+ KeyboardButtonCode.L => Keys.L,
+ KeyboardButtonCode.M => Keys.M,
+ KeyboardButtonCode.N => Keys.N,
+ KeyboardButtonCode.O => Keys.O,
+ KeyboardButtonCode.P => Keys.P,
+ KeyboardButtonCode.Q => Keys.Q,
+ KeyboardButtonCode.R => Keys.R,
+ KeyboardButtonCode.S => Keys.S,
+ KeyboardButtonCode.T => Keys.T,
+ KeyboardButtonCode.U => Keys.U,
+ KeyboardButtonCode.V => Keys.V,
+ KeyboardButtonCode.W => Keys.W,
+ KeyboardButtonCode.X => Keys.X,
+ KeyboardButtonCode.Y => Keys.Y,
+ KeyboardButtonCode.Z => Keys.Z,
+ KeyboardButtonCode.Ctrl => Keys.LeftControl,
+ KeyboardButtonCode.Alt => Keys.LeftAlt,
+ KeyboardButtonCode.Shift => Keys.LeftShift,
+ KeyboardButtonCode.Up => Keys.Up,
+ KeyboardButtonCode.Down => Keys.Down,
+ KeyboardButtonCode.Left => Keys.Left,
+ KeyboardButtonCode.Right => Keys.Right,
+ KeyboardButtonCode.Escape => Keys.Escape,
+ KeyboardButtonCode.Enter => Keys.Enter,
+ KeyboardButtonCode.Space => Keys.Space,
+ KeyboardButtonCode.Tab => Keys.Tab,
+ KeyboardButtonCode.Backspace => Keys.Backspace,
+ KeyboardButtonCode.Delete => Keys.Delete,
+ KeyboardButtonCode.Insert => Keys.Insert,
+ KeyboardButtonCode.Home => Keys.Home,
+ KeyboardButtonCode.End => Keys.End,
+ KeyboardButtonCode.PageUp => Keys.PageUp,
+ KeyboardButtonCode.PageDown => Keys.PageDown,
+ _ => throw new ArgumentOutOfRangeException(nameof(parKeyboardButtonCode), parKeyboardButtonCode, null)
+ };
+ }
+}
\ No newline at end of file
diff --git a/Engine/src/Scene/Component/BuiltIn/Camera.cs b/Engine/src/Scene/Component/BuiltIn/Camera.cs
index 94072d2..7d012be 100644
--- a/Engine/src/Scene/Component/BuiltIn/Camera.cs
+++ b/Engine/src/Scene/Component/BuiltIn/Camera.cs
@@ -1,5 +1,6 @@
using Engine.Graphics.Camera;
using OpenTK.Mathematics;
+using Serilog;
namespace Engine.Scene.Component.BuiltIn;
diff --git a/Engine/src/Scene/Component/BuiltIn/OrthographicCamera.cs b/Engine/src/Scene/Component/BuiltIn/OrthographicCamera.cs
index 232bb1a..210f931 100644
--- a/Engine/src/Scene/Component/BuiltIn/OrthographicCamera.cs
+++ b/Engine/src/Scene/Component/BuiltIn/OrthographicCamera.cs
@@ -4,8 +4,8 @@ using OpenTK.Mathematics;
namespace Engine.Scene.Component.BuiltIn;
public class OrthographicCamera(
- float parNearPlane = 0.1f,
- float parFarPlane = 1000f,
+ float parNearPlane = -10000f,
+ float parFarPlane = 10000f,
float parSize = 10f,
OrthographicCamera.Axis parAxis = OrthographicCamera.Axis.Y
)
@@ -17,8 +17,8 @@ public class OrthographicCamera(
public override Matrix4 View => GameObject.Transform.TransformMatrix.Inverted();
public override Matrix4 Projection => FixedAxis == Axis.X
- ? Matrix4.CreateOrthographic(Size, Size / AspectRatio, NearPlane, FarPlane)
- : Matrix4.CreateOrthographic(Size * AspectRatio, Size, NearPlane, FarPlane);
+ ? Matrix4.CreateOrthographic(Size, -Size / AspectRatio, NearPlane, FarPlane)
+ : Matrix4.CreateOrthographic(Size * AspectRatio, -Size, NearPlane, FarPlane);
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
{
diff --git a/Engine/src/Scene/Component/BuiltIn/PerspectiveCamera.cs b/Engine/src/Scene/Component/BuiltIn/PerspectiveCamera.cs
index e26e9b4..be3632f 100644
--- a/Engine/src/Scene/Component/BuiltIn/PerspectiveCamera.cs
+++ b/Engine/src/Scene/Component/BuiltIn/PerspectiveCamera.cs
@@ -1,11 +1,12 @@
using Engine.Util;
using OpenTK.Mathematics;
+using Math = System.Math;
namespace Engine.Scene.Component.BuiltIn;
public class PerspectiveCamera(
float parFieldOfView = 90.0f,
- float parNearPlane = 0.1f,
+ float parNearPlane = 0.01f,
float parFarPlane = 1000f
)
: Camera(parNearPlane, parFarPlane)
@@ -19,14 +20,15 @@ public class PerspectiveCamera(
var transformMatrix = GameObject.Transform.TransformMatrix;
var forward = new Vector4(0, 0, 1, 1).MulProject(transformMatrix);
var eye = new Vector4(0, 0, 0, 1).MulProject(transformMatrix);
- var up = (new Vector4(0, 1, 0, 1).MulProject(transformMatrix) - eye).Normalized();
+ var up = (new Vector4(0, -1, 0, 1).MulProject(transformMatrix) - eye).Normalized();
return Matrix4.LookAt(eye.Xyz, forward.Xyz, up.Xyz);
}
}
public override Matrix4 Projection =>
- Matrix4.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlane, FarPlane);
+ Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(FieldOfView), AspectRatio,
+ NearPlane, FarPlane);
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
{
diff --git a/Engine/src/Scene/Component/BuiltIn/Renderer/Box2DRenderer.cs b/Engine/src/Scene/Component/BuiltIn/Renderer/Box2DRenderer.cs
new file mode 100644
index 0000000..e6acdf2
--- /dev/null
+++ b/Engine/src/Scene/Component/BuiltIn/Renderer/Box2DRenderer.cs
@@ -0,0 +1,17 @@
+using Engine.Graphics.Texture;
+using OpenTK.Mathematics;
+
+namespace Engine.Scene.Component.BuiltIn.Renderer;
+
+public class Box2DRenderer : Component
+{
+ public ref Vector4 Color => ref _color;
+ public Texture? Texture { get; set; } = null;
+
+ private Vector4 _color = Vector4.One;
+
+ public override void Render()
+ {
+ Engine.Instance.Renderer.QuadRenderer.Commit(GameObject.Transform.TransformMatrix, Color, Texture);
+ }
+}
\ No newline at end of file
diff --git a/Engine/src/Scene/Component/BuiltIn/Renderer/MeshRenderer.cs b/Engine/src/Scene/Component/BuiltIn/Renderer/MeshRenderer.cs
new file mode 100644
index 0000000..a8e100f
--- /dev/null
+++ b/Engine/src/Scene/Component/BuiltIn/Renderer/MeshRenderer.cs
@@ -0,0 +1,13 @@
+using Engine.Asset.Mesh;
+
+namespace Engine.Scene.Component.BuiltIn.Renderer;
+
+public class MeshRenderer : Component
+{
+ public Mesh Mesh { get; set; } = null!;
+
+ public override void Render()
+ {
+ Engine.Instance.Renderer.GlobalMeshRenderer.Commit(Mesh, GameObject.Transform.TransformMatrix);
+ }
+}
\ No newline at end of file
diff --git a/Engine/src/Scene/Component/BuiltIn/Transform.cs b/Engine/src/Scene/Component/BuiltIn/Transform.cs
index d8f1925..895566a 100644
--- a/Engine/src/Scene/Component/BuiltIn/Transform.cs
+++ b/Engine/src/Scene/Component/BuiltIn/Transform.cs
@@ -4,14 +4,19 @@ namespace Engine.Scene.Component.BuiltIn;
public class Transform : Component
{
- public Vector3 Position { get; set; } = Vector3.Zero;
- public Quaternion Rotation { get; set; } = Quaternion.Identity;
- public Vector3 Scale { get; set; } = Vector3.One;
- public Vector3 LocalScale { get; set; } = Vector3.One;
+ private Vector3 _translation = Vector3.Zero;
+ private Quaternion _rotation = Quaternion.Identity;
+ private Vector3 _scale = Vector3.One;
+ private Vector3 _localScale = Vector3.One;
+
+ public ref Vector3 Translation => ref _translation;
+ public ref Quaternion Rotation => ref _rotation;
+ public ref Vector3 Scale => ref _scale;
+ public ref Vector3 LocalScale => ref _localScale;
public Matrix4 LocalTransformMatrix => Matrix4.CreateScale(Scale) *
Matrix4.CreateFromQuaternion(Rotation) *
- Matrix4.CreateTranslation(Position);
+ Matrix4.CreateTranslation(Translation);
public Matrix4 TransformMatrix => LocalTransformMatrix * ParentTransformMatrix;
@@ -27,7 +32,7 @@ public class Transform : Component
public Transform Clone()
{
var clone =
- new Transform { Position = Position, Rotation = Rotation, Scale = Scale, LocalScale = LocalScale };
+ new Transform { Translation = Translation, Rotation = Rotation, Scale = Scale, LocalScale = LocalScale };
return clone;
}
diff --git a/Engine/src/Scene/GameObject.cs b/Engine/src/Scene/GameObject.cs
index 8801c1d..b19e25b 100644
--- a/Engine/src/Scene/GameObject.cs
+++ b/Engine/src/Scene/GameObject.cs
@@ -13,8 +13,8 @@ public sealed class GameObject : IUpdate, IRender
private readonly Queue _componentActions = new();
- private readonly IList _components = new List();
- private readonly ISet _addedComponentTypes = new HashSet();
+ private readonly List _components = [];
+ private readonly HashSet _addedComponentTypes = [];
public GameObject()
{
diff --git a/Engine/src/Scene/Hierarchy.cs b/Engine/src/Scene/Hierarchy.cs
index 063118c..841f8c8 100644
--- a/Engine/src/Scene/Hierarchy.cs
+++ b/Engine/src/Scene/Hierarchy.cs
@@ -1,12 +1,13 @@
-using System.Collections;
-using System.Collections.Concurrent;
+using System.Collections.Concurrent;
using Engine.Util;
namespace Engine.Scene;
-public class Hierarchy : IEnumerable
+public class Hierarchy
where T : class
{
+ internal Dictionary.KeyCollection Objects => _parentLookup.Keys;
+
private readonly Dictionary, IList> _childrenLookup = new();
private readonly Dictionary _parentLookup = new();
@@ -147,14 +148,4 @@ public class Hierarchy : IEnumerable
}
}
}
-
- public IEnumerator GetEnumerator()
- {
- return _parentLookup.Keys.GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
}
\ No newline at end of file
diff --git a/Engine/src/Scene/Scene.cs b/Engine/src/Scene/Scene.cs
index 1eef787..a32b379 100644
--- a/Engine/src/Scene/Scene.cs
+++ b/Engine/src/Scene/Scene.cs
@@ -8,7 +8,7 @@ public class Scene : IUpdate, IRender
public bool IsPlaying { get; private set; }
public ICamera? MainCamera { get; private set; }
- internal Hierarchy Hierarchy { get; } = [];
+ internal Hierarchy Hierarchy { get; } = new();
private readonly Queue _sceneActions = [];
@@ -28,7 +28,7 @@ public class Scene : IUpdate, IRender
public T? FindFirstComponent() where T : Component.Component
{
- return Hierarchy.Select(parGameObject => parGameObject.GetComponent()).FirstOrDefault();
+ return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent()).FirstOrDefault();
}
public void Update(double parDeltaTime)
@@ -40,7 +40,7 @@ public class Scene : IUpdate, IRender
ProcessChanges();
- foreach (var gameObject in Hierarchy)
+ foreach (var gameObject in Hierarchy.Objects)
{
gameObject.Update(parDeltaTime);
}
@@ -53,7 +53,7 @@ public class Scene : IUpdate, IRender
throw new InvalidOperationException("Scene is not playing");
}
- foreach (var gameObject in Hierarchy)
+ foreach (var gameObject in Hierarchy.Objects)
{
gameObject.Render();
}
@@ -66,7 +66,7 @@ public class Scene : IUpdate, IRender
throw new InvalidOperationException("Scene is not playing");
}
- foreach (var gameObject in Hierarchy)
+ foreach (var gameObject in Hierarchy.Objects)
{
gameObject.Destroy();
}
@@ -114,7 +114,7 @@ public class Scene : IUpdate, IRender
action();
}
- foreach (var gameObject in Hierarchy)
+ foreach (var gameObject in Hierarchy.Objects)
{
gameObject.ProcessChanges();
}
diff --git a/Engine/src/Scene/SceneManager.cs b/Engine/src/Scene/SceneManager.cs
index 62ef2ad..3c34dca 100644
--- a/Engine/src/Scene/SceneManager.cs
+++ b/Engine/src/Scene/SceneManager.cs
@@ -1,6 +1,4 @@
-using Engine.Scene.Component.BuiltIn;
-
-namespace Engine.Scene;
+namespace Engine.Scene;
public class SceneManager : IUpdate, IRender
{
diff --git a/Engine/src/Window.cs b/Engine/src/Window.cs
index d315603..6d2fb70 100644
--- a/Engine/src/Window.cs
+++ b/Engine/src/Window.cs
@@ -1,6 +1,4 @@
-using Engine.Asset;
-using Engine.Graphics;
-using Engine.Graphics.Pixel;
+using Engine.Graphics;
using Engine.Graphics.Texture;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
@@ -17,6 +15,8 @@ public class Window : IPresenter
public int Height { get; private set; }
public event Action? Resize;
+ internal NativeWindow NativeWindow => _window;
+
private readonly Engine _engine;
private readonly NativeWindow _window;
private readonly bool _headless;
@@ -36,21 +36,33 @@ public class Window : IPresenter
Height = parArgs.Height;
Resize?.Invoke(parArgs);
};
+
+ _window.VSync = VSyncMode.On;
}
public void Update(double parDeltaTime)
+ {
+ }
+
+ public void Render()
{
if (_headless)
{
return;
}
+ _window.NewInputFrame();
NativeWindow.ProcessWindowEvents(false);
_window.SwapBuffers();
}
public void Present(IConstTexture parTexture)
{
+ if (_headless)
+ {
+ return;
+ }
+
GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
diff --git a/EngineTests/src/Scene/HierarchyTests.cs b/EngineTests/src/Scene/HierarchyTests.cs
index 508bf98..4bb7257 100644
--- a/EngineTests/src/Scene/HierarchyTests.cs
+++ b/EngineTests/src/Scene/HierarchyTests.cs
@@ -9,7 +9,7 @@ public class HierarchyTests
[SetUp]
public void Setup()
{
- _hierarchy = [];
+ _hierarchy = new Hierarchy