From 36a88b280c4cc659f0bd5d26ad7be4e8469a6c77 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 29 Dec 2025 11:42:02 +0100 Subject: [PATCH] Fixed ECS so now components can actually be added and removed accordingly (still nothing renders though) --- .../EngineSharp.Core/ECS/Component.cs | 2 +- .../EngineSharp.Core/ECS/Entity.cs | 101 ++++++++++++++---- .../EngineSharp.Core/ECS/Scene.cs | 97 +++-------------- .../EngineSharp.Core/OpenGLEngine.cs | 2 +- .../Rendering/MeshRenderer.cs | 1 - src/EngineSharp/IcoSphere.cs | 6 +- src/EngineSharp/Planet.cs | 29 +---- src/EngineSharp/PlanetGenerator.cs | 33 ++++++ src/EngineSharp/Program.cs | 5 +- 9 files changed, 138 insertions(+), 138 deletions(-) create mode 100644 src/EngineSharp/PlanetGenerator.cs diff --git a/src/EngineSharp.Core/EngineSharp.Core/ECS/Component.cs b/src/EngineSharp.Core/EngineSharp.Core/ECS/Component.cs index 34088d2..f63da09 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/Component.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/Component.cs @@ -2,5 +2,5 @@ public abstract class Component { - + public Entity Entity; } \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/ECS/Entity.cs b/src/EngineSharp.Core/EngineSharp.Core/ECS/Entity.cs index f201d87..331a011 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/Entity.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/Entity.cs @@ -1,4 +1,6 @@ using System.ComponentModel; +using Silk.NET.Maths; +using Silk.NET.OpenGL; namespace EngineSharp.Core.ECS; @@ -6,29 +8,88 @@ namespace EngineSharp.Core.ECS; // https://github.com/friflo/Friflo.Engine.ECS/tree/main // OR: https://github.com/genaray/Arch -public struct Entity +public class Entity { - public readonly long Id; - private Scene _scene; - - public required string Name { get; set; } - - internal Entity(long id, Scene scene) - { - Id = id; - _scene = scene; - } + // TODO: Maybe use a Hashset and implement a comparer or something? Should be faster than the current .OfType<>().Any() implementation + private readonly List _logicComponents = []; + private readonly List _dataComponents = []; + private readonly List _renderComponents = []; /// - /// If the component already exists on the entity, it will not be added. + /// Do not set this property manually! Will be set via the ECS. /// - public void AddComponent(Component component) - { - _scene.AddComponent(this, component); - } + public long Id { get; init; } + public string Name { get; init; } + + /// + /// If the component already exists on the entity, it will not be added + /// + public bool AddComponent(T component) + where T : Component + { + component.Entity = this; + return component switch + { + LogicComponent logic => AddComponent(logic, _logicComponents), + DataComponent data => AddComponent(data, _dataComponents), + RenderComponent render => AddComponent(render, _renderComponents), + _ => false + }; + } + + public T? GetComponent(T component) + where T : Component + { + return component switch + { + LogicComponent => _logicComponents.OfType().SingleOrDefault(), + DataComponent => _dataComponents.OfType().SingleOrDefault(), + RenderComponent => _renderComponents.OfType().SingleOrDefault(), + _ => null + }; + } + + internal void InitialiseComponents() + { + foreach (var components in _logicComponents) + { + components.Initialise(); + } + } + + internal void UpdateComponents(double deltaTime) + { + foreach (var components in _logicComponents) + { + components.OnUpdate(deltaTime); + } + } + internal void RenderComponents(GL gl, Matrix4X4 projectionMatrix, Matrix4X4 viewMatrix) + { + // TODO: retrieve the data component "Transform" which every Entity must have (although currently there is no transform component) + var modelMatrix = Matrix4X4.CreateTranslation(0, 0, -10f); + + foreach (var components in _renderComponents) + { + components.Render(gl, projectionMatrix, viewMatrix, modelMatrix); + } + } + + /// + /// If the component already exists on the entity, it will not be added + /// + private static bool AddComponent(TComponent component, List componentStore) + where TArchetype : Component + where TComponent : TArchetype + { + if (componentStore.OfType().Any()) + { + return false; + } + + componentStore.Add(component); + return true; + + } - public Component? GetComponent(Component component) - { - return _scene.GetComponent(this, component); - } } \ 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 d888bdd..0279234 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/Scene.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/Scene.cs @@ -1,113 +1,48 @@ -using System.Collections.Generic; -using System.Linq; -using Silk.NET.Maths; +using Silk.NET.Maths; using Silk.NET.OpenGL; namespace EngineSharp.Core.ECS; public class Scene { - // 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(); - + private readonly Dictionary _entities = []; private long _nextSceneId; // not the prettiest solution but it works - public Entity CreateEntity(string name) + public TEntity CreateEntity(string name) + where TEntity : Entity, new() { - return new Entity(_nextSceneId++, this) + var entity = new TEntity { + Id = _nextSceneId++, Name = name, }; + _entities.Add(entity.Id, entity); + + return entity; } - internal void InitialiseComponents() + internal void InitialiseEntities() { - foreach (var components in _logicComponents.Values) + foreach (var entity in _entities.Values) { - components.ForEach(c => c.Initialise()); + entity.InitialiseComponents(); } } internal void UpdateComponents(double deltaTime) { - foreach (var components in _logicComponents.Values) + foreach (var entity in _entities.Values) { - components.ForEach(c => c.OnUpdate(deltaTime)); + entity.UpdateComponents(deltaTime); } } internal void RenderComponents(GL gl, Matrix4X4 projectionMatrix, Matrix4X4 viewMatrix) { - foreach (var components in _renderComponents.Values) + foreach (var entity in _entities.Values) { - // 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; - var t = Matrix4X4.CreateTranslation(0, 0, -10f); - components.ForEach(c => c.Render(gl, projectionMatrix, viewMatrix, t)); - } - } - - /// - /// If the component already exists on the entity, it will not be added - /// - internal void AddComponent(Entity entity, T component) - where T : Component - { - switch (component) - { - case LogicComponent logic: - AddComponent(entity.Id, logic, _logicComponents); - break; - case DataComponent data: - AddComponent(entity.Id, data, _dataComponents); - break; - case RenderComponent render: - AddComponent(entity.Id, render, _renderComponents); - break; - } - } - - internal T? GetComponent(Entity entity, T component) - where T : Component - { - return component switch - { - LogicComponent => GetComponent(entity.Id, _logicComponents), - DataComponent => GetComponent(entity.Id, _dataComponents), - RenderComponent => GetComponent(entity.Id, _renderComponents), - _ => null - }; - } - - private static TComponent? GetComponent(long id, Dictionary> componentStore) - where TComponent : Component - { - // ReSharper disable once ConvertIfStatementToReturnStatement - if (componentStore.TryGetValue(id, out var components)) - { - return components.OfType().SingleOrDefault(); - } - - return null; - } - - /// - /// If the component already exists on the entity, it will not be added - /// - private static void AddComponent(long id, TComponent component, Dictionary> componentStore) - where TArchetype : Component - where TComponent : TArchetype - { - if (componentStore.TryGetValue(id, out var components) && !components.OfType().Any()) - { - components.Add(component); - } - else - { - componentStore.Add(id, [component]); + entity.RenderComponents(gl, projectionMatrix, viewMatrix); } } } \ 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 3d18d20..5fdea16 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs @@ -75,7 +75,7 @@ internal class OpenGLEngine : Engine if (_scenes.TryGetValue(sceneName, out var scene)) { _currentScene = scene; - _currentScene.InitialiseComponents(); + _currentScene.InitialiseEntities(); } else { diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/MeshRenderer.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/MeshRenderer.cs index 29bdbc4..54f3ce1 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Rendering/MeshRenderer.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/MeshRenderer.cs @@ -14,7 +14,6 @@ public class MeshRenderer : RenderComponent internal override void Render(GL gl, Matrix4X4 projectionMatrix, Matrix4X4 viewMatrix, Matrix4X4 modelMatrix) { GenerateRenderableMesh(gl, projectionMatrix, viewMatrix, modelMatrix); - gl.BindVertexArray(vao); gl.DrawElements(PrimitiveType.Triangles, (uint)Mesh.Indices.Length, DrawElementsType.UnsignedInt, 0); } diff --git a/src/EngineSharp/IcoSphere.cs b/src/EngineSharp/IcoSphere.cs index 82a9ee9..6ed8b05 100644 --- a/src/EngineSharp/IcoSphere.cs +++ b/src/EngineSharp/IcoSphere.cs @@ -1,10 +1,8 @@ -using System; -using System.Collections.Generic; -using EngineSharp.Core.Rendering; +using EngineSharp.Core.Rendering; using EngineSharp.Extensions; using Silk.NET.Maths; -namespace Engine_silk.NET; +namespace EngineSharp; public class IcoSphere { diff --git a/src/EngineSharp/Planet.cs b/src/EngineSharp/Planet.cs index a261251..4cdbdb3 100644 --- a/src/EngineSharp/Planet.cs +++ b/src/EngineSharp/Planet.cs @@ -1,34 +1,11 @@ -using Engine_silk.NET; -using EngineSharp.Core; using EngineSharp.Core.Rendering; namespace EngineSharp; -public class Planet : Core.ECS.LogicComponent +public class Planet : Core.ECS.Entity { - public MeshRenderer PlanetMesh; - - public override void Initialise() + public Planet() { - var shader = Shader.GetShader("./assets/shaders/sphere.vert"); - 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 + AddComponent(new PlanetGenerator()); } } \ No newline at end of file diff --git a/src/EngineSharp/PlanetGenerator.cs b/src/EngineSharp/PlanetGenerator.cs new file mode 100644 index 0000000..cefb998 --- /dev/null +++ b/src/EngineSharp/PlanetGenerator.cs @@ -0,0 +1,33 @@ +using EngineSharp.Core.Rendering; + +namespace EngineSharp; + +public class PlanetGenerator : Core.ECS.LogicComponent +{ + public override void Initialise() + { + var shader = Shader.GetShader("./assets/shaders/sphere.vert"); + var material = new Material + { + Shader = shader, + }; + var sphereGenerator = new IcoSphere + { + Resolution = 10, + Material = material, + }; + + // TODO: MeshRenderer should probably not have the mesh as a required property. + var planetMesh = new MeshRenderer + { + Mesh = sphereGenerator.CreateSphere(), + }; + + Entity.AddComponent(planetMesh); + } + + 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 5a0a927..f149348 100644 --- a/src/EngineSharp/Program.cs +++ b/src/EngineSharp/Program.cs @@ -18,10 +18,7 @@ internal static class Program var engine = EngineFactory.Create(GraphicsAPI.OpenGL, options); var mainScene = SceneManager.CreateDefaultScene("default"); - var planet = mainScene.CreateEntity("planet"); - - // TODO: Find a way to call "AddComponent" from a component. Otherwise the MeshRenderer I created inside the Planet-Component doesn't get registered and can't therefore be rendered - planet.AddComponent(new Planet()); + var planet = mainScene.CreateEntity("planet"); engine.Start(); }