From bde20fbf011c39af3c5172ca0933cb48b83026a5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 14 Dec 2025 12:28:45 +0100 Subject: [PATCH] improved architecture; made first steps to compiling shaders on engine startup and not JIT --- .../EngineSharp.Core/ECS/LogicComponent.cs | 6 +- .../EngineSharp.Core/ECS/RenderComponent.cs | 5 +- .../EngineSharp.Core/ECS/Scene.cs | 17 ++- .../Extensions/NumberExtensions.cs | 4 +- .../Extensions/VectorExtensions.cs | 3 +- .../EngineSharp.Core/IEngine.cs | 6 +- .../EngineSharp.Core/Input.cs | 4 +- .../EngineSharp.Core/OpenGLEngine.cs | 51 ++++++--- .../EngineSharp.Core/PerspectiveCamera.cs | 3 +- .../EngineSharp.Core/Rendering/Engine.cs | 28 +++++ .../Exceptions/ShaderCompileException.cs | 3 +- .../Exceptions/ShaderLinkException.cs | 4 +- .../EngineSharp.Core/Rendering/Material.cs | 15 +-- .../EngineSharp.Core/Rendering/Mesh.cs | 11 +- .../Rendering/MeshRenderer.cs | 12 +- .../EngineSharp.Core/Rendering/Shader.cs | 108 +++++++++++++----- .../VectorExtensions.cs | 3 +- src/EngineSharp/EngineSharp.csproj | 4 + src/EngineSharp/IcoSphere.cs | 4 +- src/EngineSharp/Planet.cs | 31 +++++ src/EngineSharp/Program.cs | 25 +--- src/EngineSharp/assets/shaders/sphere.frag | 13 +++ src/EngineSharp/assets/shaders/sphere.vert | 20 ++++ 23 files changed, 280 insertions(+), 100 deletions(-) create mode 100644 src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs create mode 100644 src/EngineSharp/Planet.cs create mode 100644 src/EngineSharp/assets/shaders/sphere.frag create mode 100644 src/EngineSharp/assets/shaders/sphere.vert diff --git a/src/EngineSharp.Core/EngineSharp.Core/ECS/LogicComponent.cs b/src/EngineSharp.Core/EngineSharp.Core/ECS/LogicComponent.cs index 12b1532..637aa23 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/LogicComponent.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/LogicComponent.cs @@ -1,9 +1,7 @@ namespace EngineSharp.Core.ECS; -// TODO: A LogicComponent would have a list of Components. A LogicComponent is basically what will be stored in the scene graph and all components of all LogicComponents will have their Update methods etc. be called - -public abstract class LogicComponent : IComponent +public abstract class LogicComponent : IComponent // Kinda like Unity's MonoBehaviour { - public abstract void Start(); + public abstract void Initialise(); public abstract void OnUpdate(double deltaTime); } \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/ECS/RenderComponent.cs b/src/EngineSharp.Core/EngineSharp.Core/ECS/RenderComponent.cs index 929fe79..e9729c9 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/RenderComponent.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/RenderComponent.cs @@ -1,8 +1,9 @@ -using Silk.NET.OpenGL; +using Silk.NET.Maths; +using Silk.NET.OpenGL; namespace EngineSharp.Core.ECS; public abstract class RenderComponent : IComponent { - internal abstract void Render(GL gl); + internal abstract void Render(GL gl, Matrix4X4 projectionMatrix, Matrix4X4 viewMatrix, Matrix4X4 modelMatrix); } \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/ECS/Scene.cs b/src/EngineSharp.Core/EngineSharp.Core/ECS/Scene.cs index c3e4c2f..c01ecab 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/Scene.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/Scene.cs @@ -1,11 +1,13 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; +using Silk.NET.Maths; using Silk.NET.OpenGL; namespace EngineSharp.Core.ECS; public class Scene { - // TODO: Maybe instead of a List, use a HashSet instead. Maybe implement a equality comparer to ensure, only one element per type can be present + // TODO: Maybe instead of a List, use a HashSet. Implement a equality comparer to ensure, only one element per type can be present private readonly Dictionary> _logicComponents = new(); private readonly Dictionary> _dataComponents = new(); private readonly Dictionary> _renderComponents = new(); @@ -20,11 +22,11 @@ public class Scene }; } - internal void StartComponents() + internal void InitialiseComponents() { foreach (var components in _logicComponents.Values) { - components.ForEach(c => c.Start()); + components.ForEach(c => c.Initialise()); } } @@ -36,11 +38,14 @@ public class Scene } } - internal void RenderComponents(GL gl) + internal void RenderComponents(GL gl, Matrix4X4 projectionMatrix, Matrix4X4 viewMatrix) { foreach (var components in _renderComponents.Values) { - components.ForEach(c => c.Render(gl)); + // TODO: retrieve the transform component of the entity to generate the model matrix + // TODO: make a record which holds the matrices and maybe more, so the parameter list doesn't explode + var modelMatrix = Matrix4X4.Identity; + components.ForEach(c => c.Render(gl, projectionMatrix, viewMatrix, modelMatrix)); } } diff --git a/src/EngineSharp.Core/EngineSharp.Core/Extensions/NumberExtensions.cs b/src/EngineSharp.Core/EngineSharp.Core/Extensions/NumberExtensions.cs index 86fba70..e3d880b 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Extensions/NumberExtensions.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Extensions/NumberExtensions.cs @@ -1,4 +1,6 @@ -namespace EngineSharp.Core; +using System; + +namespace EngineSharp.Core; public static class NumberExtensions { diff --git a/src/EngineSharp.Core/EngineSharp.Core/Extensions/VectorExtensions.cs b/src/EngineSharp.Core/EngineSharp.Core/Extensions/VectorExtensions.cs index 5c64e5a..91765f3 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Extensions/VectorExtensions.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Extensions/VectorExtensions.cs @@ -1,4 +1,5 @@ -using Silk.NET.Maths; +using System; +using Silk.NET.Maths; namespace EngineSharp.Core; diff --git a/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs b/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs index d716868..cc37de0 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs @@ -1,4 +1,6 @@ -using EngineSharp.Core.ECS; +using System; +using EngineSharp.Core.ECS; +using EngineSharp.Core.Rendering; using Silk.NET.Windowing; namespace EngineSharp.Core; @@ -9,6 +11,8 @@ public interface IEngine void Stop(); Scene CreateScene(); + + Shader CreateShader(string vertexShaderPath, string fragmentShaderPath); } public enum GraphicsAPI diff --git a/src/EngineSharp.Core/EngineSharp.Core/Input.cs b/src/EngineSharp.Core/EngineSharp.Core/Input.cs index db315f1..445ecac 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Input.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Input.cs @@ -1,4 +1,6 @@ -using Silk.NET.Input; +using System.Collections.Generic; +using System.Linq; +using Silk.NET.Input; namespace EngineSharp.Core; diff --git a/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs b/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs index 17eb80c..3d55189 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs @@ -1,17 +1,19 @@ using System.Drawing; using EngineSharp.Core.ECS; +using EngineSharp.Core.Rendering; using Silk.NET.Input; using Silk.NET.Maths; using Silk.NET.OpenGL; using Silk.NET.Windowing; +using Shader = EngineSharp.Core.Rendering.Shader; namespace EngineSharp.Core; -internal class OpenGLEngine : IEngine +internal class OpenGLEngine : Engine { private readonly IWindow _window; private readonly List _scenes; - private GL _gl = null!; // because between constructing the engine and OnLoad being called nothing should be able to call these fields. Therefore, we do this to get rid of warnings + private GL _gl = null!; // because between constructing the engine and OnLoad being called nothing should call these fields. Therefore, we do "!" to get rid of warnings private Input _input = null!; private PerspectiveCamera _camera = null!; private Scene? _currentScene; @@ -24,30 +26,52 @@ internal class OpenGLEngine : IEngine _window.Render += OnRender; _window.Resize += OnResize; - _scenes = new(); + _scenes = []; } - public void Start() + public override void Start() { _window.Run(); } - public void Stop() + public override void Stop() { _window.Close(); } - public Scene CreateScene() + public override Scene CreateScene() { var scene = new Scene(); _scenes.Add(scene); return scene; } + public override Shader CreateShader(string vertexShaderPath, string fragmentShaderPath) + { + if (_currentScene is null) + { + throw new InvalidOperationException("Cannot create a shader if there is no scene set as the current scene."); + } + + var shader = new Shader(vertexShaderPath, fragmentShaderPath); + RegisterShader(_currentScene, shader); + + return shader; + } + + protected override void InitialiseShaders(Scene scene) + { + if (_shaders.TryGetValue(scene, out var shaders)) + { + shaders.ForEach(s => s.Initialize(_gl)); + } + } + public void SetCurrentScene(Scene scene) { _currentScene = scene; - _currentScene.StartComponents(); + _currentScene.InitialiseComponents(); + InitialiseShaders(scene); } private void OnLoad() @@ -68,10 +92,7 @@ internal class OpenGLEngine : IEngine _window.Close(); } - if (_currentScene is not null) - { - _currentScene.UpdateComponents(deltaTime); - } + _currentScene?.UpdateComponents(deltaTime); } private void OnRender(double deltaTime) @@ -79,15 +100,13 @@ internal class OpenGLEngine : IEngine var projectionMatrix = _camera.ProjectionMatrix; var viewMatrix = _camera.ViewMatrix; - // TODO: Here render all meshes etc. - // MeshRenderer contains a mesh; Mesh contains a material; Material contains a shader and the values for all uniforms of the shader (apart from the matrices; I am not sure how to best handle the model matrix with this approach) - - _currentScene?.RenderComponents(_gl); + _currentScene?.RenderComponents(_gl, projectionMatrix, viewMatrix); } private void OnResize(Vector2D newDimensions) { - // when using an entity component system the camera should probably read the aspect ratio from a central location or should listen to the "Resize" event rather than the engine updating the aspect on the camera + // when using an entity component system the camera should probably read the aspect ratio from a central location or should listen to the "Resize" event rather than the engine updating the aspect on the camera. + // Especially if more than one camera should be able to exist simultaneously _camera.UpdateAspect(newDimensions); } } \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/PerspectiveCamera.cs b/src/EngineSharp.Core/EngineSharp.Core/PerspectiveCamera.cs index bf64a86..1a43a51 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/PerspectiveCamera.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/PerspectiveCamera.cs @@ -1,4 +1,5 @@ -using Silk.NET.Maths; +using System; +using Silk.NET.Maths; namespace EngineSharp.Core; diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs new file mode 100644 index 0000000..ae0d598 --- /dev/null +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs @@ -0,0 +1,28 @@ +using EngineSharp.Core.ECS; + +namespace EngineSharp.Core.Rendering; + +internal abstract class Engine : IEngine +{ + protected readonly Dictionary> _shaders = []; + + public abstract void Start(); + public abstract void Stop(); + public abstract Scene CreateScene(); + public abstract Shader CreateShader(string vertexShaderPath, string fragmentShaderPath); + + protected void RegisterShader(Scene scene, Shader shader) + { + if (_shaders.TryGetValue(scene, out var shaders)) + { + shaders.Add(shader); + } + else + { + _shaders.Add(scene, [shader]); + } + } + + protected abstract void InitialiseShaders(Scene scene); + +} \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Exceptions/ShaderCompileException.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Exceptions/ShaderCompileException.cs index d80047f..54b8177 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Exceptions/ShaderCompileException.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Exceptions/ShaderCompileException.cs @@ -1,4 +1,5 @@ -using Silk.NET.OpenGL; +using System; +using Silk.NET.OpenGL; namespace EngineSharp.Core.Rendering.Exceptions; diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Exceptions/ShaderLinkException.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Exceptions/ShaderLinkException.cs index 18b7e6f..e86e5fd 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Exceptions/ShaderLinkException.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Exceptions/ShaderLinkException.cs @@ -1,4 +1,6 @@ -namespace EngineSharp.Core.Rendering.Exceptions; +using System; + +namespace EngineSharp.Core.Rendering.Exceptions; public class ShaderLinkException : Exception { diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Material.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Material.cs index 9dcf173..73f34d1 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Material.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Material.cs @@ -1,4 +1,5 @@ using Silk.NET.Maths; +using Silk.NET.OpenGL; namespace EngineSharp.Core.Rendering; @@ -6,44 +7,44 @@ public class Material { public required Shader Shader { get; init; } - internal void UseShader() => Shader.Use(); + internal void UseShader() => Shader.Use(); #region Set uniforms // All of this just relays to the shader. While it is a lot of "unnecessary" code it ensures that everything unsafe of OpenGL/Vulkan specific is abstracted away public void SetInt(string name, int value) { - UseShader(); + Shader.Use(); Shader.SetInt(name, value); } public void SetFloat(string name, float value) { - UseShader(); + Shader.Use(); Shader.SetFloat(name, value); } public void SetVector(string name, Vector3D value) { - UseShader(); + Shader.Use(); Shader.SetVector(name, value); } public void SetVector(string name, Vector3D value) { - UseShader(); + Shader.Use(); Shader.SetVector(name, value); } public void SetMatrix(string name, Matrix4X4 matrix) { - UseShader(); + Shader.Use(); Shader.SetMatrix(name, matrix); } public void SetMatrix(string name, Matrix4X4 matrix) { - UseShader(); + Shader.Use(); Shader.SetMatrix(name, matrix); } diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Mesh.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Mesh.cs index 5c49671..80950a8 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Mesh.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Mesh.cs @@ -1,4 +1,5 @@ using Silk.NET.Maths; +using Silk.NET.OpenGL; namespace EngineSharp.Core.Rendering; @@ -44,5 +45,13 @@ public class Mesh } // This is probably the location where the model, view, projection matrices will be set! - internal void PrepareForRendering() => Material.UseShader(); + internal void PrepareForRendering(Matrix4X4 projectionMatrix, Matrix4X4 viewMatrix, Matrix4X4 modelMatrix) + { + Material.UseShader(); + + // We would also need the inverse (see comment in any of the shaders) and probably other stuff. This other stuff might be automatically added before compiling the shader. kinda like an include for ALL shaders + Material.SetMatrix("projectionMatrix", projectionMatrix); + Material.SetMatrix("viewMatrix", viewMatrix); + Material.SetMatrix("modelMatrix", modelMatrix); + } } \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/MeshRenderer.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/MeshRenderer.cs index 558a680..29bdbc4 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Rendering/MeshRenderer.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/MeshRenderer.cs @@ -1,4 +1,6 @@ -using EngineSharp.Core.ECS; +using System; +using EngineSharp.Core.ECS; +using Silk.NET.Maths; using Silk.NET.OpenGL; namespace EngineSharp.Core.Rendering; @@ -9,14 +11,14 @@ public class MeshRenderer : RenderComponent private uint vao, vbo, ebo; - internal override void Render(GL gl) + internal override void Render(GL gl, Matrix4X4 projectionMatrix, Matrix4X4 viewMatrix, Matrix4X4 modelMatrix) { - GenerateRenderableMesh(gl); + GenerateRenderableMesh(gl, projectionMatrix, viewMatrix, modelMatrix); gl.BindVertexArray(vao); gl.DrawElements(PrimitiveType.Triangles, (uint)Mesh.Indices.Length, DrawElementsType.UnsignedInt, 0); } - private void GenerateRenderableMesh(GL gl) + private void GenerateRenderableMesh(GL gl, Matrix4X4 projectionMatrix, Matrix4X4 viewMatrix, Matrix4X4 modelMatrix) { if(vao == 0) { vao = gl.CreateVertexArray(); } gl.BindVertexArray(vao); @@ -24,7 +26,7 @@ public class MeshRenderer : RenderComponent if(vbo == 0) { vbo = gl.GenBuffer(); } if(ebo == 0) { ebo = gl.GenBuffer(); } - Mesh.PrepareForRendering(); + Mesh.PrepareForRendering(projectionMatrix, viewMatrix, modelMatrix); var meshData = new float[Mesh.Vertices.Length * 3 + Mesh.Indices.Length * 3]; for (int i = 0, insert = 0; i < Mesh.Vertices.Length; i++, insert += 6) { diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Shader.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Shader.cs index 45ec3b7..34e715b 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Shader.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Shader.cs @@ -1,4 +1,5 @@ -using EngineSharp.Core.Rendering.Exceptions; +using System.IO; +using EngineSharp.Core.Rendering.Exceptions; using Silk.NET.Maths; using Silk.NET.OpenGL; @@ -9,55 +10,102 @@ namespace EngineSharp.Core.Rendering; // I don't know if Vulkan also uses an ID as a uint but I think so. If so, this change would decouple the shader from OpenGL (but the question is, do I want to decouple the shader? Maybe not, as this could then couple the material to OpenGL) public class Shader { - private readonly GL _gl; // not as a variable. Add it as a parameter to the methods probably (in the future it should be possible to compile the shaders beforehand. if context is passed as parameter, all shaders are JIT compiled which isn't great - private readonly uint _shaderProgramId; + private readonly string _vertexCode, _fragmentCode; + + private GL? _gl; // not as a variable. In the future it should be possible to compile the shaders beforehand. Right now, all shaders are JIT compiled which isn't great + private uint? _shaderProgramId; - public Shader(GL openGLContext, string pathToVertexShader, string pathToFragmentShader) + // TODO: Make this public. The paths are then used as an index into a dictionary. This dictionary stores the shaderProgramId (maybe only use one path as an index, as the other shader should have the same name, just with a different file ending) + // With this, the shaders can be compiled once the engine is started (shaders must be in folder "assets/shaders"). With this I don't have to worry how I can create the shader class and make it compile the shader etc. + // Maybe the dict needs to also store the GL context so the shader can be used ans values can be set on it, but we will see + internal Shader(string pathToVertexShader, string pathToFragmentShader) { - _gl = openGLContext; - var vertexCode = File.ReadAllText(pathToVertexShader); - var fragmentCode = File.ReadAllText(pathToFragmentShader); - - var vertexShader = CompileShader(vertexCode, ShaderType.VertexShader); - var fragmentShader = CompileShader(fragmentCode, ShaderType.FragmentShader); - - _shaderProgramId = CreateProgram(vertexShader, fragmentShader); + _vertexCode = File.ReadAllText(pathToVertexShader); + _fragmentCode = File.ReadAllText(pathToFragmentShader); } - internal void Use() => _gl.UseProgram(_shaderProgramId); - - #region Set uniforms - internal void SetInt(string name, int value) + /// + /// Initialises the shader, by compiling it and making it ready to be used in the application.
+ /// Only initialises the first time it is called. Is a NoOp every subsequent call. + ///
+ /// + internal void Initialize(GL glContext) { - _gl.Uniform1(_gl.GetUniformLocation(_shaderProgramId, name), value); + if (_gl is not null || _shaderProgramId is not null) + { + return; + } + + _gl = glContext; + var vertexShader = CompileShader(_vertexCode, ShaderType.VertexShader); + var fragmentShader = CompileShader(_fragmentCode, ShaderType.FragmentShader); + + _shaderProgramId = CreateProgram(vertexShader, fragmentShader); } - internal void SetFloat(string name, float value) + /// + /// Can only be called after has been called. + /// + internal void Use() { - _gl.Uniform1(_gl.GetUniformLocation(_shaderProgramId, name), value); - } - - internal void SetVector(string name, Vector3D value) - { - _gl.Uniform3(_gl.GetUniformLocation(_shaderProgramId, name), value.X, value.Y, value.Z); - } - internal void SetVector(string name, Vector3D value) - { - _gl.Uniform3(_gl.GetUniformLocation(_shaderProgramId, name), value.X, value.Y, value.Z); + if (_shaderProgramId is null || _gl is null) + { + throw new InvalidOperationException("This shader has not been initialized and can therefore not be used."); + } + + _gl.UseProgram(_shaderProgramId.Value); } + #region Set uniforms + /// + /// Call before calling this method, otherwise the value might be set for the wrong shader. + /// + internal void SetInt(string name, int value) + { + _gl!.Uniform1(_gl.GetUniformLocation(_shaderProgramId!.Value, name), value); + } + + /// + /// Call before calling this method, otherwise the value might be set for the wrong shader. + /// + internal void SetFloat(string name, float value) + { + _gl!.Uniform1(_gl.GetUniformLocation(_shaderProgramId!.Value, name), value); + } + + /// + /// Call before calling this method, otherwise the value might be set for the wrong shader. + /// + internal void SetVector(string name, Vector3D value) + { + _gl!.Uniform3(_gl.GetUniformLocation(_shaderProgramId!.Value, name), value.X, value.Y, value.Z); + } + /// + /// Call before calling this method, otherwise the value might be set for the wrong shader. + /// + internal void SetVector(string name, Vector3D value) + { + _gl!.Uniform3(_gl.GetUniformLocation(_shaderProgramId!.Value, name), value.X, value.Y, value.Z); + } + + /// + /// Call before calling this method, otherwise the value might be set for the wrong shader. + /// internal void SetMatrix(string name, Matrix4X4 matrix) { unsafe { - _gl.UniformMatrix4(_gl.GetUniformLocation(_shaderProgramId, name), 1, false, (double*) &matrix); + _gl!.UniformMatrix4(_gl.GetUniformLocation(_shaderProgramId!.Value, name), 1, false, (double*) &matrix); } } + /// + /// Call before calling this method, otherwise the value might be set for the wrong shader. + /// internal void SetMatrix(string name, Matrix4X4 matrix) { unsafe { - _gl.UniformMatrix4(_gl.GetUniformLocation(_shaderProgramId, name), 1, false, (float*) &matrix); + _gl!.UniformMatrix4(_gl.GetUniformLocation(_shaderProgramId!.Value, name), 1, false, (float*) &matrix); } } #endregion diff --git a/src/EngineSharp.Extensions/VectorExtensions.cs b/src/EngineSharp.Extensions/VectorExtensions.cs index 46021a4..2792f13 100644 --- a/src/EngineSharp.Extensions/VectorExtensions.cs +++ b/src/EngineSharp.Extensions/VectorExtensions.cs @@ -1,4 +1,5 @@ -using Silk.NET.Maths; +using System; +using Silk.NET.Maths; namespace EngineSharp.Extensions; diff --git a/src/EngineSharp/EngineSharp.csproj b/src/EngineSharp/EngineSharp.csproj index edb13ba..3644c70 100644 --- a/src/EngineSharp/EngineSharp.csproj +++ b/src/EngineSharp/EngineSharp.csproj @@ -17,4 +17,8 @@ + + + + diff --git a/src/EngineSharp/IcoSphere.cs b/src/EngineSharp/IcoSphere.cs index 6bb230c..82a9ee9 100644 --- a/src/EngineSharp/IcoSphere.cs +++ b/src/EngineSharp/IcoSphere.cs @@ -1,4 +1,6 @@ -using EngineSharp.Core.Rendering; +using System; +using System.Collections.Generic; +using EngineSharp.Core.Rendering; using EngineSharp.Extensions; using Silk.NET.Maths; diff --git a/src/EngineSharp/Planet.cs b/src/EngineSharp/Planet.cs new file mode 100644 index 0000000..b63975e --- /dev/null +++ b/src/EngineSharp/Planet.cs @@ -0,0 +1,31 @@ +using Engine_silk.NET; +using EngineSharp.Core.Rendering; + +namespace EngineSharp; + +public class Planet : Core.ECS.LogicComponent +{ + public MeshRenderer PlanetMesh; + + public override void Initialise() + { + // var material = new Material + // { + // Shader = shader, + // }; + // var sphereGenerator = new IcoSphere + // { + // Resolution = 10, + // Material = material, + // }; + // PlanetMesh = new MeshRenderer + // { + // Mesh = sphereGenerator.CreateSphere(), + // }; + } + + public override void OnUpdate(double deltaTime) + { + // Nothing to do at the moment + } +} \ No newline at end of file diff --git a/src/EngineSharp/Program.cs b/src/EngineSharp/Program.cs index 1c6a563..5c4a218 100644 --- a/src/EngineSharp/Program.cs +++ b/src/EngineSharp/Program.cs @@ -7,9 +7,9 @@ using GraphicsAPI = EngineSharp.Core.GraphicsAPI; namespace EngineSharp; -static class Program +internal static class Program { - static void Main(string[] args) + private static void Main(string[] args) { var options = WindowOptions.Default with { @@ -19,26 +19,11 @@ static class Program var engine = EngineFactory.Create(GraphicsAPI.OpenGL, options); var mainScene = engine.CreateScene(); - var cube = mainScene.CreateEntity("cube"); + var planet = mainScene.CreateEntity("planet"); - var shader = new Shader(null, "", ""); // make a factory in IEngine so I don't have to pass graphics library specific values - var material = new Material - { - Shader = shader, - }; - var sphereGenerator = new IcoSphere - { - Resolution = 10, - Material = material, - }; - var cubeMeshRenderer = new MeshRenderer - { - Mesh = sphereGenerator.CreateSphere(), - }; + var shader = engine.CreateShader("./assets/shaders/sphere.vert", "./assets/shaders/sphere.frag"); - cube.AddComponent(cubeMeshRenderer); - - // TODO: ensure that model matrix etc. will be set correctly on rendering, so that the icosphere can actually be rendered + planet.AddComponent(new Planet()); engine.Start(); } diff --git a/src/EngineSharp/assets/shaders/sphere.frag b/src/EngineSharp/assets/shaders/sphere.frag new file mode 100644 index 0000000..444e11b --- /dev/null +++ b/src/EngineSharp/assets/shaders/sphere.frag @@ -0,0 +1,13 @@ +#version 330 core + +out vec4 FragColour; + +in vec3 FragPos; +in vec3 Normal; + + +void main() +{ + vec3 col = vec3(1.0, 0.5, 0.2) * FragPos; + FragColour = vec4(col, 1); +} \ No newline at end of file diff --git a/src/EngineSharp/assets/shaders/sphere.vert b/src/EngineSharp/assets/shaders/sphere.vert new file mode 100644 index 0000000..47b10bf --- /dev/null +++ b/src/EngineSharp/assets/shaders/sphere.vert @@ -0,0 +1,20 @@ +#version 330 core + +layout (location = 0) in vec3 aPosition; +layout (location = 1) in vec3 normalVector; + +uniform mat4 modelMatrix; +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; + +out vec3 FragPos; +out vec3 Normal; + +void main() +{ + vec4 pos = vec4(aPosition, 1.0); + // TODO: calculate the inverse of the model matrix beforehand since "inverse()" is very costly to calculate for every vertex + Normal = mat3(transpose(inverse(modelMatrix))) * normalVector; + FragPos = vec3(modelMatrix * pos); + gl_Position = projectionMatrix * viewMatrix * modelMatrix * pos; +} \ No newline at end of file