diff --git a/src/EngineSharp.Core/EngineSharp.Core/ECS/IComponent.cs b/src/EngineSharp.Core/EngineSharp.Core/ECS/Component.cs similarity index 54% rename from src/EngineSharp.Core/EngineSharp.Core/ECS/IComponent.cs rename to src/EngineSharp.Core/EngineSharp.Core/ECS/Component.cs index ba64139..34088d2 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/IComponent.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/Component.cs @@ -1,6 +1,6 @@ namespace EngineSharp.Core.ECS; -public interface IComponent +public abstract class Component { - + } \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/ECS/DataComponent.cs b/src/EngineSharp.Core/EngineSharp.Core/ECS/DataComponent.cs index ed9af42..c4ae577 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/DataComponent.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/DataComponent.cs @@ -1,6 +1,6 @@ namespace EngineSharp.Core.ECS; -public abstract class DataComponent : IComponent +public abstract class DataComponent : Component { } \ 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 8850f70..f201d87 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/Entity.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/Entity.cs @@ -22,12 +22,12 @@ public struct Entity /// /// If the component already exists on the entity, it will not be added. /// - public void AddComponent(IComponent component) + public void AddComponent(Component component) { _scene.AddComponent(this, component); } - public IComponent? GetComponent(IComponent component) + public Component? GetComponent(Component component) { return _scene.GetComponent(this, component); } diff --git a/src/EngineSharp.Core/EngineSharp.Core/ECS/LogicComponent.cs b/src/EngineSharp.Core/EngineSharp.Core/ECS/LogicComponent.cs index 637aa23..2eeacab 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/LogicComponent.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/LogicComponent.cs @@ -1,6 +1,6 @@ namespace EngineSharp.Core.ECS; -public abstract class LogicComponent : IComponent // Kinda like Unity's MonoBehaviour +public abstract class LogicComponent : Component // Kinda like Unity's MonoBehaviour { public abstract void Initialise(); public abstract void OnUpdate(double deltaTime); diff --git a/src/EngineSharp.Core/EngineSharp.Core/ECS/RenderComponent.cs b/src/EngineSharp.Core/EngineSharp.Core/ECS/RenderComponent.cs index e9729c9..b92cf3a 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/RenderComponent.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/RenderComponent.cs @@ -3,7 +3,7 @@ using Silk.NET.OpenGL; namespace EngineSharp.Core.ECS; -public abstract class RenderComponent : IComponent +public abstract class RenderComponent : Component { 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 2ef75e3..d888bdd 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/ECS/Scene.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/ECS/Scene.cs @@ -54,7 +54,7 @@ public class Scene /// If the component already exists on the entity, it will not be added /// internal void AddComponent(Entity entity, T component) - where T : class, IComponent + where T : Component { switch (component) { @@ -71,7 +71,7 @@ public class Scene } internal T? GetComponent(Entity entity, T component) - where T : class, IComponent + where T : Component { return component switch { @@ -83,7 +83,7 @@ public class Scene } private static TComponent? GetComponent(long id, Dictionary> componentStore) - where TComponent : class, IComponent + where TComponent : Component { // ReSharper disable once ConvertIfStatementToReturnStatement if (componentStore.TryGetValue(id, out var components)) @@ -98,7 +98,7 @@ public class Scene /// 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 : class, IComponent + where TArchetype : Component where TComponent : TArchetype { if (componentStore.TryGetValue(id, out var components) && !components.OfType().Any()) diff --git a/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs b/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs index 23fb0fe..411bb55 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/IEngine.cs @@ -1,6 +1,4 @@ -using System; -using EngineSharp.Core.ECS; -using EngineSharp.Core.Rendering; +using EngineSharp.Core.Rendering; using Silk.NET.Windowing; namespace EngineSharp.Core; @@ -9,10 +7,7 @@ public interface IEngine { void Start(); void Stop(); - - Scene CreateScene(); - void SetCurrentScene(Scene scene); - + Shader GetShader(string vertexShaderPath); } diff --git a/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs b/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs index f7af9da..3d18d20 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs @@ -12,7 +12,7 @@ namespace EngineSharp.Core; internal class OpenGLEngine : Engine { private readonly IWindow _window; - private readonly List _scenes; + private readonly Dictionary _scenes; 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!; @@ -47,16 +47,6 @@ internal class OpenGLEngine : Engine _window.Close(); } - // TODO: pass in name as parameter - // Name must be unique and is used to switch scenes while the application is running. - // We need exactly one default scene which is the initial scene (call SetCurrentScene during the Load() method - public override Scene CreateScene() - { - var scene = new Scene(); - _scenes.Add(scene); - return scene; - } - private void InitialiseShaders(IEnumerable vertexShaderFiles) { foreach (var vertexShaderFile in vertexShaderFiles) @@ -69,14 +59,37 @@ internal class OpenGLEngine : Engine } } - public override void SetCurrentScene(Scene scene) + internal override Scene CreateScene(string name) { - _currentScene = scene; - _currentScene.InitialiseComponents(); + var scene = new Scene(); + if (!_scenes.TryAdd(name, scene)) + { + throw new ArgumentException($"Scene '{name}' already exists."); + } + + return scene; + } + + internal override void SetCurrentScene(string sceneName) + { + if (_scenes.TryGetValue(sceneName, out var scene)) + { + _currentScene = scene; + _currentScene.InitialiseComponents(); + } + else + { + throw new ArgumentException($"Scene '{sceneName}' does not exist."); + } } private void OnLoad() { + if (string.IsNullOrEmpty(_defaultSceneName)) + { + throw new InvalidOperationException("No default scene defined."); + } + _gl = _window.CreateOpenGL(); _input = new Input(_window.CreateInput()); @@ -86,7 +99,7 @@ internal class OpenGLEngine : Engine _camera = new PerspectiveCamera(Vector3D.Zero, _window.FramebufferSize); InitialiseShaders(Directory.GetFiles("./assets/shaders", "*.vert")); - // TODO: SetCurrentScene -> with the default scene + SetCurrentScene(_defaultSceneName); } private void OnUpdate(double deltaTime) diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs index 87b6061..93ebb9f 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs @@ -13,14 +13,28 @@ internal abstract class Engine : IEngine } protected readonly Dictionary _shaders = []; + protected string? _defaultSceneName; public static Engine Instance => SingletonInstance ?? throw new InvalidOperationException("No engine has been created yet. Cannot return an instance."); public abstract void Start(); public abstract void Stop(); - public abstract Scene CreateScene(); - public abstract void SetCurrentScene(Scene scene); + internal abstract Scene CreateScene(string name); + internal abstract void SetCurrentScene(string sceneName); + internal Scene CreateScene(string sceneName, bool isDefaultScene) + { + + if (isDefaultScene && !string.IsNullOrEmpty(_defaultSceneName)) + { + throw new InvalidOperationException( + $"A default scene already exists, cannot create a new one. Name of the current default scene: '{_defaultSceneName}'."); + } + + _defaultSceneName = sceneName; + return CreateScene(sceneName); + } + public Shader GetShader(string vertexShaderPath) { Instance._shaders.TryGetValue(vertexShaderPath, out var shader); diff --git a/src/EngineSharp.Core/EngineSharp.Core/SceneManager.cs b/src/EngineSharp.Core/EngineSharp.Core/SceneManager.cs new file mode 100644 index 0000000..c452083 --- /dev/null +++ b/src/EngineSharp.Core/EngineSharp.Core/SceneManager.cs @@ -0,0 +1,35 @@ +using EngineSharp.Core.ECS; +using EngineSharp.Core.Rendering; + +namespace EngineSharp.Core; + +public static class SceneManager +{ + private static bool DefaultSceneCreated = false; + + /// + /// Creates the scene that will be loaded once the game is started.
+ /// Can only be called once. + ///
+ /// + public static Scene CreateDefaultScene(string sceneName) + { + if (DefaultSceneCreated) + { + throw new InvalidOperationException("Default scene has already been created."); + } + + var scene = Engine.Instance.CreateScene(sceneName, true); + return scene; + } + + public static Scene CreateScene(string sceneName) + { + return Engine.Instance.CreateScene(sceneName); + } + + public static void SetCurrentScene(string sceneName) + { + Engine.Instance.SetCurrentScene(sceneName); + } +} \ No newline at end of file diff --git a/src/EngineSharp/Planet.cs b/src/EngineSharp/Planet.cs index 8978726..a261251 100644 --- a/src/EngineSharp/Planet.cs +++ b/src/EngineSharp/Planet.cs @@ -1,4 +1,5 @@ using Engine_silk.NET; +using EngineSharp.Core; using EngineSharp.Core.Rendering; namespace EngineSharp; @@ -19,6 +20,7 @@ public class Planet : Core.ECS.LogicComponent Resolution = 10, Material = material, }; + PlanetMesh = new MeshRenderer { Mesh = sphereGenerator.CreateSphere(), diff --git a/src/EngineSharp/Program.cs b/src/EngineSharp/Program.cs index 01e7358..5a0a927 100644 --- a/src/EngineSharp/Program.cs +++ b/src/EngineSharp/Program.cs @@ -17,12 +17,12 @@ internal static class Program }; var engine = EngineFactory.Create(GraphicsAPI.OpenGL, options); - var mainScene = engine.CreateScene(); + 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()); - engine.SetCurrentScene(mainScene); engine.Start(); } } \ No newline at end of file