diff --git a/GlitchyEditor/src/EditWindows/EntityHierarchyWindow.bf b/GlitchyEditor/src/EditWindows/EntityHierarchyWindow.bf index f160afa..537b1c7 100644 --- a/GlitchyEditor/src/EditWindows/EntityHierarchyWindow.bf +++ b/GlitchyEditor/src/EditWindows/EntityHierarchyWindow.bf @@ -30,6 +30,7 @@ namespace GlitchyEditor.EditWindows public void SetContext(Scene scene) { + _selectedEntities.Clear(); _scene = scene; } diff --git a/GlitchyEditor/src/EditWindows/SceneViewportWindow.bf b/GlitchyEditor/src/EditWindows/SceneViewportWindow.bf index 357cbd2..2e6e816 100644 --- a/GlitchyEditor/src/EditWindows/SceneViewportWindow.bf +++ b/GlitchyEditor/src/EditWindows/SceneViewportWindow.bf @@ -10,8 +10,6 @@ namespace GlitchyEditor.EditWindows { class SceneViewportWindow : EditorWindow { - //public OldCamera _camera; - public const String s_WindowTitle = "Scene"; private RenderTarget2D _renderTarget ~ _?.ReleaseRef(); @@ -38,8 +36,6 @@ namespace GlitchyEditor.EditWindows private ImGui.Vec2 oldViewportSize; private bool viewPortChanged; - public Entity CameraEntity { get; set; } - protected override void InternalShow() { ImGui.PushStyleVar(.WindowPadding, ImGui.Vec2(1, 1)); @@ -89,20 +85,20 @@ namespace GlitchyEditor.EditWindows topLeft.y += cntMin.y; ImGuizmo.SetRect(topLeft.x, topLeft.y, viewportSize.x, viewportSize.y); - var cameraTransformCmp = CameraEntity.GetComponent(); + var cameraTransformCmp = _editor.CurrentCamera.GetComponent(); var view = cameraTransformCmp.WorldTransform.Invert(); - var cameraCmp = CameraEntity.GetComponent(); + var cameraCmp = _editor.CurrentCamera.GetComponent(); var projection = cameraCmp.Camera.Projection; Matrix mat = .Identity; ImGuizmo.DrawGrid((.)&view, (.)&projection, (.)&mat, 10); - if(_editor.SelectedEntities.Count > 0) + if(_editor.EntityHierarchyWindow.SelectedEntities.Count > 0) { - var entity = _editor.SelectedEntities.Front; + var entity = _editor.EntityHierarchyWindow.SelectedEntities.Front; - var transformCmp = _editor.World.GetComponent(entity); + var transformCmp = entity.GetComponent(); var transform = transformCmp.LocalTransform; diff --git a/GlitchyEditor/src/Editor.bf b/GlitchyEditor/src/Editor.bf index 27027ca..17e1832 100644 --- a/GlitchyEditor/src/Editor.bf +++ b/GlitchyEditor/src/Editor.bf @@ -4,115 +4,67 @@ using System; using System.Collections; using GlitchyEngine.Collections; using GlitchyEditor.EditWindows; +using GlitchyEngine; namespace GlitchyEditor { class Editor { - private EcsWorld _ecsWorld; private Scene _scene; + private Entity _currentCamera; private EntityHierarchyWindow _entityHierarchyWindow ~ delete _; private ComponentEditWindow _componentEditWindow ~ delete _; private SceneViewportWindow _sceneViewportWindow = new .(this) ~ delete _; - private List _selectedEntities = new .() ~ delete _; - - public EcsWorld World => _ecsWorld; - - public List SelectedEntities => _selectedEntities; - public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow; public ComponentEditWindow ComponentEditWindow => _componentEditWindow; public SceneViewportWindow SceneViewportWindow => _sceneViewportWindow; + public Scene CurrentScene + { + get => _scene; + set + { + _scene = value; + _entityHierarchyWindow.SetContext(_scene); + FindCurrentEditorCamera(); + } + } + + public Entity CurrentCamera => _currentCamera; + /// Creates a new editor for the given world public this(Scene scene) { - _scene = scene; - _ecsWorld = _scene.[Friend]_ecsWorld; - _entityHierarchyWindow = new EntityHierarchyWindow(_scene); + CurrentScene = scene; + _componentEditWindow = new ComponentEditWindow(_entityHierarchyWindow); } public void Update() { + var scriptComponent = CurrentCamera.GetComponent(); + + if (var camController = scriptComponent?.Instance as EditorCameraController) + { + camController.IsEnabled = (SceneViewportWindow.HasFocus && Input.IsMouseButtonPressed(.RightButton)); + } + _entityHierarchyWindow.Show(); _componentEditWindow.Show(); _sceneViewportWindow.Show(); } - /// Creates a new entity with a transform component. - internal EcsEntity CreateEntityWithTransform() + private void FindCurrentEditorCamera() { - var entity = _ecsWorld.NewEntity(); - - var transformComponent = ref *_ecsWorld.AssignComponent(entity); - transformComponent = TransformComponent(); - - var nameComponent = ref *_ecsWorld.AssignComponent(entity); - nameComponent.SetName("Entity"); - - return entity; - } - - - /// Returns whether or not all selected entities have the same parent. - internal bool AllSelectionsOnSameLevel() - { - EcsEntity? parent = .InvalidEntity; - - for(var selectedEntity in _selectedEntities) + _currentCamera = .(.InvalidEntity, _scene); + // TODO: a bit sketchy + for (var (entity, editComp, cam) in _scene.[Friend]_ecsWorld.Enumerate()) { - var parentComponent = _ecsWorld.GetComponent(selectedEntity); - - if(parent == .InvalidEntity) - { - parent = parentComponent?.Entity; - } - else if(parentComponent?.Entity != parent) - { - return false; - } + _currentCamera = .(entity, CurrentScene); } - - return true; - } - - /// Finds all children of the given entity and stores their IDs in the given list. - internal void FindChildren(EcsEntity entity, List entities) - { - for(var (child, childParent) in _ecsWorld.Enumerate()) - { - if(childParent.Entity == entity) - { - if(!entities.Contains(child)) - entities.Add(child); - - FindChildren(child, entities); - } - } - } - - /// Deletes all selected entities and their children. - internal void DeleteSelectedEntities() - { - List entities = scope .(); - - for(var entity in _selectedEntities) - { - entities.Add(entity); - - FindChildren(entity, entities); - } - - for(var entity in entities) - { - _ecsWorld.RemoveEntity(entity); - } - - _selectedEntities.Clear(); } } } diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index 2080f32..d573c01 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -10,6 +10,7 @@ using GlitchyEngine.World; using GlitchyEngine.Content; using System.Collections; using GlitchyEngine.Renderer.Animation; +using System.IO; namespace GlitchyEditor { @@ -25,6 +26,19 @@ namespace GlitchyEditor DepthStencilState _depthStencilState ~ _.ReleaseRef(); Scene _scene = new Scene() ~ delete _; + String _sceneFilePath = new String() ~ delete _; + + public String SceneFilePath + { + get => _sceneFilePath; + set + { + _sceneFilePath.Clear(); + + if (value != null) + _sceneFilePath.Append(value); + } + } Editor _editor ~ delete _; @@ -81,48 +95,112 @@ namespace GlitchyEditor InitGraphics(); - { - _cameraEntity = _scene.CreateEntity("Camera Entity"); - let camera = _cameraEntity.AddComponent(); - camera.Camera.SetPerspective(MathHelper.ToRadians(75), 0.1f, 10000.0f); - camera.Primary = true; - camera.FixedAspectRatio = false; - camera.RenderTarget = _cameraTarget; - let transform = _cameraEntity.GetComponent(); - //transform.Position = .(-1.5f, 1.5f, -2.5f); - transform.Position = .(3.5f, 1.25f, 2.75f); - //transform.RotationEuler = .(MathHelper.ToRadians(25), MathHelper.ToRadians(35), 0); - transform.RotationEuler = .(MathHelper.ToRadians(25), MathHelper.ToRadians(40), 0); + NewScene(); - _cameraEntity.AddComponent().Bind(); - _cameraEntity.AddComponent(); + InitEditor(); + } + + private void InitGraphics() + { + _context = Application.Get().Window.Context..AddRef(); + + RasterizerStateDescription rsDesc = .(.Solid, .Back, true); + _rasterizerState = new RasterizerState(rsDesc); + + rsDesc.FrontCounterClockwise = false; + _rasterizerStateClockWise = new RasterizerState(rsDesc); + + BlendStateDescription blendDesc = .(); + blendDesc.RenderTarget[0] = .(true, .SourceAlpha, .InvertedSourceAlpha, .Add, .SourceAlpha, .InvertedSourceAlpha, .Add, .All); + _alphaBlendState = new BlendState(blendDesc); + _opaqueBlendState = new BlendState(.Default); + + DepthStencilStateDescription dsDesc = .(); + _depthStencilState = new DepthStencilState(dsDesc); + + _cameraTarget = new RenderTarget2D(RenderTarget2DDescription(.R16G16B16A16_Float, 100, 100) {DepthStencilFormat = .D32_Float}); + _cameraTarget.SamplerState = SamplerStateManager.LinearClamp; + + _viewportTarget = new RenderTarget2D(RenderTarget2DDescription(.R8G8B8A8_UNorm, 100, 100)); + _viewportTarget.SamplerState = SamplerStateManager.LinearClamp; + } + + private void InitEditor() + { + _editor = new Editor(_scene); + _editor.SceneViewportWindow.ViewportSizeChangedEvent.Add(new (s, e) => ViewportSizeChanged(s, e)); + } + + public override void Update(GameTime gameTime) + { + // Clear the swapchain-buffer + RenderCommand.Clear(null, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0); + + RenderCommand.Clear(_viewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0); + + RenderCommand.SetRenderTarget(_viewportTarget, 0, true); + RenderCommand.BindRenderTargets(); + + RenderCommand.SetViewport(Viewport(0, 0, _viewportTarget.Width, _viewportTarget.Height)); + + RenderCommand.SetBlendState(_alphaBlendState); + RenderCommand.SetDepthStencilState(_depthStencilState); + + _scene.Update(gameTime, _viewportTarget); + + RenderCommand.UnbindRenderTargets(); + RenderCommand.SetRenderTarget(null, 0, true); + RenderCommand.BindRenderTargets(); + + RenderCommand.SetViewport(_context.SwapChain.BackbufferViewport); + } + + public override void OnEvent(Event event) + { + EventDispatcher dispatcher = EventDispatcher(event); + + dispatcher.Dispatch(scope (e) => OnImGuiRender(e)); + dispatcher.Dispatch(scope (e) => OnWindowResize(e)); + dispatcher.Dispatch(scope (e) => OnKeyPressed(e)); + } + + ImGui.ID _mainDockspaceId; + + TextureViewer viewer = new TextureViewer() ~ delete _; + + private bool OnImGuiRender(ImGuiRenderEvent event) + { + viewer.ViewTexture(_cameraTarget); + + /*ImGui.Begin("Test"); + + static bool cameraA = true; + + if (ImGui.Checkbox("Camera A", &cameraA)) + { + _cameraEntity.GetComponent().Primary = cameraA; + _otherCameraEntity.GetComponent().Primary = !cameraA; } - /*{ - _otherCameraEntity = _scene.CreateEntity("Other Camera Entity"); - let camera = _otherCameraEntity.AddComponent(); - camera.Camera.SetPerspective(MathHelper.ToRadians(45), 0.1f, 1000.0f); - camera.Primary = false; - camera.FixedAspectRatio = false; - camera.RenderTarget = _viewportTarget; - let transform = _otherCameraEntity.GetComponent(); - transform.Position = .(0, 0, -5); + ImGui.End();*/ - _otherCameraEntity.AddComponent().Bind(); - _otherCameraEntity.AddComponent(); - }*/ + ImGui.Viewport* viewport = ImGui.GetMainViewport(); + ImGui.DockSpaceOverViewport(viewport); - { - var lightNtt = _scene.CreateEntity("My Sexy Sun"); - let transform = lightNtt.GetComponent(); - transform.Position = .(0, 0, 0); - transform.RotationEuler = .(MathHelper.ToRadians(45), MathHelper.ToRadians(-100), 0); + DrawMainMenuBar(); - let light = lightNtt.AddComponent(); - light.SceneLight.Illuminance = 10.0f; - light.SceneLight.Color = .(1.0f, 0.95f, 0.8f); - } + _editor.SceneViewportWindow.RenderTarget = _viewportTarget; + _editor.Update(); + + _settingsWindow.Show(); + + return false; + } + + // Just for testing + private void TestEntitiesWithModels() + { { var lightNtt = _scene.CreateEntity("My Sexy Sun 2"); let transform = lightNtt.GetComponent(); @@ -145,20 +223,17 @@ namespace GlitchyEditor light.SceneLight.Color = .(1.0f, 0.95f, 0.8f); } - InitEditor(); - - TestEntitiesWithModels(); - } - - private void TestEntitiesWithModels() - { var fxLib = Application.Get().EffectLibrary; using (Effect myEffect = fxLib.Load("content/Shaders/myEffect.hlsl")) - using (Texture2D albedo = new Texture2D("Textures/TestMat/rustediron2_albedo.png", true)) + using (Texture2D albedo = new Texture2D("Textures/White.png", true)) + using (Texture2D normal = new Texture2D("Textures/White.png")) + using (Texture2D rough = new Texture2D("Textures/White.png")) + using (Texture2D metal = new Texture2D("Textures/White.png")) + /*using (Texture2D albedo = new Texture2D("Textures/TestMat/rustediron2_albedo.png", true)) using (Texture2D normal = new Texture2D("Textures/TestMat/rustediron2_normal.png")) using (Texture2D rough = new Texture2D("Textures/TestMat/rustediron2_roughness.png")) - using (Texture2D metal = new Texture2D("Textures/TestMat/rustediron2_metallic.png")) + using (Texture2D metal = new Texture2D("Textures/TestMat/rustediron2_metallic.png"))*/ { albedo.SamplerState = SamplerStateManager.AnisotropicWrap; normal.SamplerState = SamplerStateManager.AnisotropicWrap; @@ -238,118 +313,100 @@ namespace GlitchyEditor } } - private void InitGraphics() + private void PrepareSceneForEditor() { - _context = Application.Get().Window.Context..AddRef(); - - RasterizerStateDescription rsDesc = .(.Solid, .Back, true); - _rasterizerState = new RasterizerState(rsDesc); - - rsDesc.FrontCounterClockwise = false; - _rasterizerStateClockWise = new RasterizerState(rsDesc); - - BlendStateDescription blendDesc = .(); - blendDesc.RenderTarget[0] = .(true, .SourceAlpha, .InvertedSourceAlpha, .Add, .SourceAlpha, .InvertedSourceAlpha, .Add, .All); - _alphaBlendState = new BlendState(blendDesc); - _opaqueBlendState = new BlendState(.Default); - - DepthStencilStateDescription dsDesc = .(); - _depthStencilState = new DepthStencilState(dsDesc); - - _cameraTarget = new RenderTarget2D(RenderTarget2DDescription(.R16G16B16A16_Float, 100, 100) {DepthStencilFormat = .D32_Float}); - _cameraTarget.SamplerState = SamplerStateManager.LinearClamp; - - _viewportTarget = new RenderTarget2D(RenderTarget2DDescription(.R8G8B8A8_UNorm, 100, 100)); - _viewportTarget.SamplerState = SamplerStateManager.LinearClamp; - } - - private void InitEditor() - { - _editor = new Editor(_scene); - _editor.SceneViewportWindow.ViewportSizeChangedEvent.Add(new (s, e) => ViewportSizeChanged(s, e)); - - //_editor.[Friend]CreateEntityWithTransform(); - - _editor.SceneViewportWindow.CameraEntity = _cameraEntity; - } - - public override void Update(GameTime gameTime) - { - var scriptComponent = _cameraEntity.GetComponent(); - - if (var camController = scriptComponent.Instance as EditorCameraController) + // Create the editor camera { - camController.IsEnabled = (_editor.SceneViewportWindow.HasFocus && Input.IsMouseButtonPressed(.RightButton)); + _cameraEntity = _scene.CreateEntity("Editor Camera"); + // Add EditorComponent so that the engine knows that this is not part of the game. + _cameraEntity.AddComponent(); + // Script for controlling the camera. + _cameraEntity.AddComponent().Bind(); + + let camera = _cameraEntity.AddComponent(); + camera.Camera.SetPerspective(MathHelper.ToRadians(75), 0.1f, 10000.0f); + camera.Primary = true; + camera.RenderTarget = _cameraTarget; + let transform = _cameraEntity.GetComponent(); + //transform.Position = .(-1.5f, 1.5f, -2.5f); + transform.Position = .(3.5f, 1.25f, 2.75f); + //transform.RotationEuler = .(MathHelper.ToRadians(25), MathHelper.ToRadians(35), 0); + transform.RotationEuler = .(MathHelper.ToRadians(25), MathHelper.ToRadians(40), 0); + + } + } + + /// Creates a new scene. + private void NewScene() + { + SceneFilePath = null; + + // Create the default light source + { + var lightNtt = _scene.CreateEntity("Light"); + let transform = lightNtt.GetComponent(); + transform.Position = .(0, 0, 0); + transform.RotationEuler = .(MathHelper.ToRadians(45), MathHelper.ToRadians(-100), 0); + + let light = lightNtt.AddComponent(); + light.SceneLight.Illuminance = 10.0f; + light.SceneLight.Color = .(1.0f, 0.95f, 0.8f); } - //TransformSystem.Update(_world); + TestEntitiesWithModels(); - RenderCommand.Clear(_viewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0); - - RenderCommand.SetRenderTarget(_viewportTarget, 0, true); - RenderCommand.BindRenderTargets(); - - RenderCommand.SetViewport(Viewport(0, 0, _viewportTarget.Width, _viewportTarget.Height)); - - RenderCommand.SetBlendState(_alphaBlendState); - RenderCommand.SetDepthStencilState(_depthStencilState); - - //Renderer.BeginScene(_cameraController.Camera); - - //DebugRenderer.Render(_scene.[Friend]_ecsWorld); - - //Renderer.EndScene(); - - _scene.Update(gameTime, _viewportTarget); - - RenderCommand.Clear(null, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0); - - RenderCommand.SetRenderTarget(null, 0, true); - RenderCommand.BindRenderTargets(); - - RenderCommand.SetViewport(_context.SwapChain.BackbufferViewport); + PrepareSceneForEditor(); } - - public override void OnEvent(Event event) - { - EventDispatcher dispatcher = EventDispatcher(event); - - dispatcher.Dispatch(scope (e) => OnImGuiRender(e)); - dispatcher.Dispatch(scope (e) => OnWindowResize(e)); - } - - ImGui.ID _mainDockspaceId; - TextureViewer viewer = new TextureViewer() ~ delete _; - - private bool OnImGuiRender(ImGuiRenderEvent event) + /// Saves the scene in the file that is was loaded from or saved to last. If there is no such path (i.e. it is a new scene) the save file dialog will open. + private void SaveScene() { - viewer.ViewTexture(_cameraTarget); - - /*ImGui.Begin("Test"); - - static bool cameraA = true; - - if (ImGui.Checkbox("Camera A", &cameraA)) + if (String.IsNullOrWhiteSpace(SceneFilePath)) { - _cameraEntity.GetComponent().Primary = cameraA; - _otherCameraEntity.GetComponent().Primary = !cameraA; + SaveSceneAs(); + return; } + + SceneSerializer serializer = scope .(_scene); + serializer.Serialize(SceneFilePath); + } + + /// Opens a save file dialog and saves the scene at the user specified location. + private void SaveSceneAs() + { + SaveFileDialog sfd = scope .(); + if (sfd.ShowDialog() case .Ok(let val)) + { + if (val == .OK) + { + SceneFilePath = sfd.FileNames[0]; - ImGui.End();*/ + SaveScene(); + } + } + } + + /// Opens a open file dialog and load the scene selected by the user specified. + private void OpenScene() + { + OpenFileDialog ofd = scope .(); + if (ofd.ShowDialog() case .Ok(let val)) + { + if (val == .OK) + { + SceneFilePath = ofd.FileNames[0]; - ImGui.Viewport* viewport = ImGui.GetMainViewport(); - ImGui.DockSpaceOverViewport(viewport); + delete _scene; + _scene = new Scene(); - DrawMainMenuBar(); + SceneSerializer serializer = scope .(_scene); + serializer.Deserialize(SceneFilePath); - _editor.SceneViewportWindow.RenderTarget = _viewportTarget; + PrepareSceneForEditor(); - _editor.Update(); - - _settingsWindow.Show(); - - return false; + _editor.CurrentScene = _scene; + } + } } private void DrawMainMenuBar() @@ -358,8 +415,27 @@ namespace GlitchyEditor if(ImGui.BeginMenu("File", true)) { + if (ImGui.MenuItem("New", "Ctrl+N")) + NewScene(); + + if (ImGui.MenuItem("Save", "Ctrl+S")) + SaveScene(); + + if (ImGui.MenuItem("Save as...", "Ctrl+Shift+N")) + SaveSceneAs(); + + if (ImGui.MenuItem("Open...", "Ctrl+O")) + OpenScene(); + + ImGui.Separator(); + if (ImGui.MenuItem("Settings")) _settingsWindow.Open = true; + + ImGui.Separator(); + + if (ImGui.MenuItem("Exit")) + Application.Get().Close(); ImGui.EndMenu(); } @@ -406,5 +482,34 @@ namespace GlitchyEditor _scene.OnViewportResize(sizeX, sizeY); } + + private bool OnKeyPressed(KeyPressedEvent e) + { + bool control = Input.IsKeyPressed(Key.Control); + bool shift = Input.IsKeyPressed(Key.Shift); + + if (control) + { + switch (e.KeyCode) + { + case .N: + NewScene(); + return true; + case .O: + OpenScene(); + return true; + case .S: + if (shift) + SaveSceneAs(); + else + SaveScene(); + + return true; + default: + } + } + + return false; + } } } diff --git a/GlitchyEngine/src/Application.bf b/GlitchyEngine/src/Application.bf index 577ddbb..7b61e3a 100644 --- a/GlitchyEngine/src/Application.bf +++ b/GlitchyEngine/src/Application.bf @@ -167,6 +167,12 @@ namespace GlitchyEngine } } + /// Closes the applcation. + public void Close() + { + _running = false; + } + public void PushLayer(Layer ownLayer) { Profiler.ProfileFunction!(); diff --git a/GlitchyEngine/src/Extension/Bon/Serialize.bf b/GlitchyEngine/src/Extension/Bon/Serialize.bf new file mode 100644 index 0000000..be89cf6 --- /dev/null +++ b/GlitchyEngine/src/Extension/Bon/Serialize.bf @@ -0,0 +1,37 @@ +using System; + +namespace Bon.Integrated +{ + extension Serialize + { + public static void Value(BonWriter writer, StringView identifier, in T value, BonEnvironment env = gBonEnv) + { + writer.Identifier(identifier); + Serialize.Value(writer, ValueView(typeof(T), &value), env); + } + + public static void Value(BonWriter writer, in T value, BonEnvironment env = gBonEnv) + { + Serialize.Value(writer, ValueView(typeof(T), &value), env); + } + } + + extension Deserialize + { + public static Result Value(BonReader reader, StringView identifier, out T value, BonEnvironment env = gBonEnv) + { + value = ?; + + if (Try!(reader.Identifier()) != identifier) + return .Err; + + return Deserialize.Value(reader, ValueView(typeof(T), &value), env); + } + + public static Result Value(BonReader reader, out T value, BonEnvironment env = gBonEnv) + { + value = ?; + return Deserialize.Value(reader, ValueView(typeof(T), &value), env); + } + } +} diff --git a/GlitchyEngine/src/Math/Quaternion.bf b/GlitchyEngine/src/Math/Quaternion.bf index 5fc3e27..39e3d5e 100644 --- a/GlitchyEngine/src/Math/Quaternion.bf +++ b/GlitchyEngine/src/Math/Quaternion.bf @@ -1,7 +1,9 @@ +using Bon; using System; namespace GlitchyEngine.Math { + [BonTarget] public struct Quaternion { public const Quaternion Zero = .(); diff --git a/GlitchyEngine/src/Math/Vector2.bf b/GlitchyEngine/src/Math/Vector2.bf index 4577296..1dd8b30 100644 --- a/GlitchyEngine/src/Math/Vector2.bf +++ b/GlitchyEngine/src/Math/Vector2.bf @@ -1,7 +1,9 @@ +using Bon; using System; namespace GlitchyEngine.Math { + [BonTarget] [SwizzleVector(2, "Vector")] public struct Vector2 { diff --git a/GlitchyEngine/src/Math/Vector3.bf b/GlitchyEngine/src/Math/Vector3.bf index 20b2a14..033e8de 100644 --- a/GlitchyEngine/src/Math/Vector3.bf +++ b/GlitchyEngine/src/Math/Vector3.bf @@ -1,7 +1,9 @@ +using Bon; using System; namespace GlitchyEngine.Math { + [BonTarget] [SwizzleVector(3, "Vector")] public struct Vector3 { diff --git a/GlitchyEngine/src/Math/Vector4.bf b/GlitchyEngine/src/Math/Vector4.bf index ffb493d..965aa39 100644 --- a/GlitchyEngine/src/Math/Vector4.bf +++ b/GlitchyEngine/src/Math/Vector4.bf @@ -1,7 +1,9 @@ +using Bon; using System; namespace GlitchyEngine.Math { + [BonTarget] [SwizzleVector(4, "Vector")] public struct Vector4 { diff --git a/GlitchyEngine/src/Platform/DX11/Math/Vector.bf b/GlitchyEngine/src/Platform/DX11/Math/Vector.bf index 18cd634..e9ffcd4 100644 --- a/GlitchyEngine/src/Platform/DX11/Math/Vector.bf +++ b/GlitchyEngine/src/Platform/DX11/Math/Vector.bf @@ -4,6 +4,7 @@ #pragma warning disable 4204 using System; +using Bon; namespace GlitchyEngine.Math { @@ -35,4 +36,16 @@ namespace GlitchyEngine.Math } } +namespace DirectX +{ + [BonTarget] + extension Color; + + [BonTarget] + extension ColorRGB; + + [BonTarget] + extension ColorRGBA; +} + #endif diff --git a/GlitchyEngine/src/World/Components.bf b/GlitchyEngine/src/World/Components.bf index fb4afe4..a085e40 100644 --- a/GlitchyEngine/src/World/Components.bf +++ b/GlitchyEngine/src/World/Components.bf @@ -1,6 +1,6 @@ +using System; using GlitchyEngine.Math; using GlitchyEngine.Renderer; -using System; namespace GlitchyEngine.World { @@ -251,9 +251,8 @@ namespace GlitchyEngine.World struct CameraComponent : IDisposableComponent { - public SceneCamera Camera; + public SceneCamera Camera = .(); public bool Primary = true; // Todo: probably move into scene - public bool FixedAspectRatio = false; private RenderTarget2D _renderTarget = null; public RenderTarget2D RenderTarget @@ -265,11 +264,6 @@ namespace GlitchyEngine.World } } - public this() - { - Camera = .(); - } - public void Dispose() { _renderTarget?.ReleaseRef(); diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index 5011c50..eb338a4 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -56,7 +56,7 @@ namespace GlitchyEngine.World for (var (entity, transform, camera) in _ecsWorld.Enumerate()) { - if (camera.Primary) + if (camera.Primary && camera.RenderTarget != null) { primaryCamera = &camera.Camera; primaryCameraTransform = transform.WorldTransform; @@ -134,7 +134,7 @@ namespace GlitchyEngine.World for (var (entity, cameraComponent) in _ecsWorld.Enumerate()) { - if (!cameraComponent.FixedAspectRatio) + if (!cameraComponent.Camera.FixedAspectRatio) { cameraComponent.Camera.SetViewportSize(width, height); } diff --git a/GlitchyEngine/src/World/SceneSerializer.bf b/GlitchyEngine/src/World/SceneSerializer.bf new file mode 100644 index 0000000..36099b9 --- /dev/null +++ b/GlitchyEngine/src/World/SceneSerializer.bf @@ -0,0 +1,445 @@ +using Bon; +using Bon.Integrated; +using System; +using System.Reflection; +using System.IO; +using GlitchyEngine.Math; + +namespace GlitchyEngine.World +{ + using internal GlitchyEngine.World; + + class SceneSerializer + { + private Scene _scene; + + public this(Scene scene) + { + _scene = scene; + } + + public void Serialize(StringView filePath) + { + Debug.Profiler.ProfileResourceFunction!(); + + String buffer = scope String(); + let writer = scope BonWriter(buffer, true); + Serialize.Start(writer); + + gBonEnv.serializeFlags |= .IncludeDefault | .Verbose; + + using (writer.ObjectBlock()) + { + // TODO: Scene name goes here! + Serialize.Value(writer, "Name", "Scene name here pls!!!"); + + writer.Identifier("Entities"); + + using (writer.ArrayBlock()) + { + for (EcsEntity e in _scene._ecsWorld.Enumerate()) + { + Entity entity = .(e, _scene); + + // TODO: also serialize object with EditorComponent (e.g. to save the location of the editor camera) + if (entity.HasComponent()) + continue; + + SerializeEntity(writer, entity); + } + } + + writer.EntryEnd(); + } + + Serialize.End(writer); + + String targetDirectory = Path.GetDirectoryPath(filePath, .. scope String()); + Directory.CreateDirectory(targetDirectory); + + File.WriteAllText(filePath, buffer); + } + + static void SerializeEntity(BonWriter writer, Entity entity) + { + writer.EntryStart(); + + using (writer.ObjectBlock()) + { + // TODO: Entity GUID goes here! + Serialize.Value(writer, "Id", entity.Handle.Index); + + SerializeComponent(writer, entity, "EditorComponent", scope (component) => {}); + + SerializeComponent(writer, entity, "NameComponent", scope (component) => + { + Serialize.Value(writer, "Name", component.DebugName); + }); + + SerializeComponent(writer, entity, "SpriterRendererComponent", scope (component) => + { + // TODO: Texture + + Serialize.Value(writer, "Color", component.Color); + }); + + SerializeComponent(writer, entity, "TransformComponent", scope (component) => + { + // TODO: Use GUIDs + if (component.Parent != .InvalidEntity) + Serialize.Value(writer, "ParentId", component.Parent.Index); + + Serialize.Value(writer, "Position", component.Position); + Serialize.Value(writer, "Rotation", component.Rotation); + Serialize.Value(writer, "Scale", component.Scale); + + Serialize.Value(writer, "EditorEulerRotation", component.EditorRotationEuler); + }); + + SerializeComponent(writer, entity, "CameraComponent", scope (component) => + { + SceneCamera camera = component.Camera; + + Serialize.Value(writer, "Primary", component.Primary); + + // TODO: Render target + + Serialize.Value(writer, "ProjectionType", camera.ProjectionType); + + Serialize.Value(writer, "PerspectiveFovY", camera.PerspectiveFovY); + Serialize.Value(writer, "PerspectiveNearPlane", camera.PerspectiveNearPlane); + Serialize.Value(writer, "PerspectiveFarPlane", camera.PerspectiveFarPlane); + Serialize.Value(writer, "OrthographicHeight", camera.OrthographicHeight); + Serialize.Value(writer, "OrthographicNearPlane", camera.OrthographicNearPlane); + Serialize.Value(writer, "OrthographicFarPlane", camera.OrthographicFarPlane); + Serialize.Value(writer, "AspectRatio", camera.AspectRatio); + Serialize.Value(writer, "FixedAspectRatio", camera.FixedAspectRatio); + }); + + // TODO: native script component + + SerializeComponent(writer, entity, "LightComponent", scope (component) => + { + SceneLight light = component.SceneLight; + + Serialize.Value(writer, "LightType", light.LightType); + + Serialize.Value(writer, "Illuminance", light.Illuminance); + + Serialize.Value(writer, "Color", light.Color); + }); + } + + writer.EntryEnd(); + } + + static void SerializeComponent(BonWriter writer, Entity entity, String identifier, delegate void(T* component) serialize) where T : struct, new + { + if (!entity.HasComponent()) + return; + + var component = entity.GetComponent(); + + writer.Identifier(identifier); + + using (writer.ObjectBlock()) + { + serialize(component); + } + writer.EntryEnd(); + } + + public void SerializeRuntime(StringView filePath) + { + Runtime.NotImplemented(); + } + + public Result Deserialize(StringView filePath) + { + Debug.Profiler.ProfileResourceFunction!(); + + String buffer = scope String(); + File.ReadAllText(filePath, buffer); + + let reader = scope BonReader(); + Try!(reader.Setup(buffer)); + Try!(Deserialize.Start(reader)); + + Try!(reader.ObjectBlock()); + + // TODO: Scene name goes here! + String testName; + Deserialize.Value(reader, "Name", out testName); + delete testName; + + Try!(reader.EntryEnd()); + + if (Try!(reader.Identifier()) != "Entities") + return .Err; + + Try!(reader.ArrayBlock()); + + bool first = true; + while (reader.ArrayHasMore()) + { + if (!first) + { + Try!(reader.EntryEnd()); + } + + Try!(DeserializeEntity(reader)); + + first = false; + } + + Try!(reader.ArrayBlockEnd()); + + Try!(reader.ObjectBlockEnd()); + + Try!(Deserialize.End(reader)); + + return .Ok; + } + + private Result DeserializeEntity(BonReader reader) + { + Try!(reader.ObjectBlock()); + + Entity entity = _scene.CreateEntity(); + + // TODO: GUID? + Deserialize.Value(reader, "Id", let id); + + while(reader.ObjectHasMore()) + { + Try!(reader.EntryEnd()); + + StringView identifier = Try!(reader.Identifier()); + + switch(identifier) + { + case "EditorComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => { return .Ok; })); + case "NameComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + String name; + + Deserialize.Value(reader, "Name", out name); + + component.SetName(name); + + delete name; + + return .Ok; + })); + case "NameComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + String name; + + Deserialize.Value(reader, "Name", out name); + + component.SetName(name); + + delete name; + + return .Ok; + })); + case "SpriterRendererComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + // TODO: Texture + + Try!(Deserialize.Value(reader, "Color", out component.Color)); + + return .Ok; + })); + case "TransformComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + let nextId = Try!(reader.Identifier()); + if (nextId == "ParentId") + { + // TODO: Use GUIDs + uint32 pId; + Deserialize.Value(reader, out pId); + reader.EntryEnd(); + Deserialize.Value(reader, "Position", out component.[Friend]_position); + reader.EntryEnd(); + } + else if (nextId == "Position") + { + Deserialize.Value(reader, out component.[Friend]_position); + reader.EntryEnd(); + } + else + { + return .Err; + } + + Deserialize.Value(reader, "Rotation", out component.[Friend]_rotation); + reader.EntryEnd(); + Deserialize.Value(reader, "Scale", out component.[Friend]_scale); + reader.EntryEnd(); + + Deserialize.Value(reader, "EditorEulerRotation", out component.[Friend]_editorRotationEuler); + + component.IsDirty = true; + + return .Ok; + })); + case "CameraComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + SceneCamera camera = component.Camera; + + Deserialize.Value(reader, "Primary", out component.Primary); + reader.EntryEnd(); + + // TODO: Render target + + Deserialize.Value(reader, "ProjectionType", out camera.[Friend]_projectionType); + reader.EntryEnd(); + + Deserialize.Value(reader, "PerspectiveFovY", out camera.[Friend]_perspectiveFovY); + reader.EntryEnd(); + Deserialize.Value(reader, "PerspectiveNearPlane", out camera.[Friend]_perspectiveNearPlane); + reader.EntryEnd(); + Deserialize.Value(reader, "PerspectiveFarPlane", out camera.[Friend]_perspectiveFarPlane); + reader.EntryEnd(); + Deserialize.Value(reader, "OrthographicHeight", out camera.[Friend]_orthographicHeight); + reader.EntryEnd(); + Deserialize.Value(reader, "OrthographicNearPlane", out camera.[Friend]_orthographicNearPlane); + reader.EntryEnd(); + Deserialize.Value(reader, "OrthographicFarPlane", out camera.[Friend]_orthographicFarPlane); + reader.EntryEnd(); + Deserialize.Value(reader, "AspectRatio", out camera.[Friend]_aspectRatio); + reader.EntryEnd(); + Deserialize.Value(reader, "FixedAspectRatio", out camera.[Friend]_fixedAspectRatio); + + camera.[Friend]CalculateProjection(); + + return .Ok; + })); + // TODO: native script component + case "LightComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + SceneLight light = component.SceneLight; + + Deserialize.Value(reader, "LightType", out light.[Friend]_type); + reader.EntryEnd(); + + Deserialize.Value(reader, "Illuminance", out light.[Friend]_illuminance); + reader.EntryEnd(); + + Deserialize.Value(reader, "Color", out light.[Friend]_color); + return .Ok; + })); + + default: + return .Err; + } + } + + Try!(reader.ObjectBlockEnd()); + + return .Ok; + + /*writer.EntryStart(); + + using (writer.ObjectBlock()) + { + // TODO: Entity GUID goes here! + Serialize.Value(writer, "Id", entity.Handle.Index); + + SerializeComponent(writer, entity, "EditorComponent", scope (component) => {}); + + SerializeComponent(writer, entity, "NameComponent", scope (component) => + { + Serialize.Value(writer, "Name", component.DebugName); + }); + + SerializeComponent(writer, entity, "SpriterRendererComponent", scope (component) => + { + // TODO: Texture + + Serialize.Value(writer, "Color", component.Color); + }); + + SerializeComponent(writer, entity, "TransformComponent", scope (component) => + { + // TODO: Use GUIDs + if (component.Parent != .InvalidEntity) + Serialize.Value(writer, "ParentId", component.Parent.Index); + + Serialize.Value(writer, "Position", component.Position); + Serialize.Value(writer, "Rotation", component.Rotation); + Serialize.Value(writer, "Scale", component.Scale); + + Serialize.Value(writer, "EditorEulerRotation", component.EditorRotationEuler); + }); + + SerializeComponent(writer, entity, "CameraComponent", scope (component) => + { + SceneCamera camera = component.Camera; + + Serialize.Value(writer, "Primary", component.Primary); + + // TODO: Render target + + Serialize.Value(writer, "ProjectionType", camera.ProjectionType); + + Serialize.Value(writer, "PerspectiveFovY", camera.PerspectiveFovY); + Serialize.Value(writer, "PerspectiveNearPlane", camera.PerspectiveNearPlane); + Serialize.Value(writer, "PerspectiveFarPlane", camera.PerspectiveFarPlane); + Serialize.Value(writer, "OrthographicHeight", camera.OrthographicHeight); + Serialize.Value(writer, "OrthographicNearPlane", camera.OrthographicNearPlane); + Serialize.Value(writer, "OrthographicFarPlane", camera.OrthographicFarPlane); + Serialize.Value(writer, "AspectRatio", camera.AspectRatio); + Serialize.Value(writer, "FixedAspectRatio", camera.FixedAspectRatio); + }); + + // TODO: native script component + + + SerializeComponent(writer, entity, "LightComponent", scope (component) => + { + SceneLight light = component.SceneLight; + + Serialize.Value(writer, "LightType", light.LightType); + + Serialize.Value(writer, "Illuminance", light.Illuminance); + + Serialize.Value(writer, "Color", light.Color); + }); + } + + writer.EntryEnd();*/ + } + + static Result DeserializeComponent(BonReader reader, Entity entity, delegate Result(T* component) deserialize) where T : struct, new + { + Try!(reader.ObjectBlock()); + + T* component; + + if (entity.HasComponent()) + component = entity.GetComponent(); + else + component = entity.AddComponent(); + + Try!(deserialize(component)); + + Try!(reader.ObjectBlockEnd()); + + return .Ok; + } + + public bool DeserializeRuntime(StringView filePath) + { + Runtime.NotImplemented(); + } + } +} \ No newline at end of file diff --git a/GlitchyEngine/src/World/WorldEnumerator.bf b/GlitchyEngine/src/World/WorldEnumerator.bf index d46bad2..e328b55 100644 --- a/GlitchyEngine/src/World/WorldEnumerator.bf +++ b/GlitchyEngine/src/World/WorldEnumerator.bf @@ -13,6 +13,8 @@ namespace GlitchyEngine.World internal EcsWorld.BitmaskEntry* _currentEntry; internal EcsWorld.BitmaskEntry* _endEntry; + public bool IsEmpty => _bitMask == null; + public this(EcsWorld world, Type[] componentTypes) { _world = world; @@ -32,7 +34,11 @@ namespace GlitchyEngine.World } else { - Log.EngineLogger.AssertDebug(false, "Queried component is not registered for this world. This is invalid because the query would never return any results."); +#if GE_WORLD_ENUMERATOR_UNREGISTERED_COMPONENT_IS_WARNING + Log.EngineLogger.Warning($"Queried component of type \"{type}\" is not registered for this world. The query will never return any results."); +#endif + DeleteAndNullify!(_bitMask); + _endEntry = _currentEntry; } } } @@ -67,7 +73,14 @@ namespace GlitchyEngine.World public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent))) { - _componentPool = &world.GetComponentPool(); + if (base.IsEmpty) + { + _componentPool = null; + } + else + { + _componentPool = &world.GetComponentPool(); + } } public new Result<(EcsEntity Entity, TComponent* Component)> GetNext() mut @@ -92,8 +105,16 @@ namespace GlitchyEngine.World public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent0), typeof(TComponent1))) { - _componentPool0 = &world.GetComponentPool(); - _componentPool1 = &world.GetComponentPool(); + if (base.IsEmpty) + { + _componentPool0 = null; + _componentPool1 = null; + } + else + { + _componentPool0 = &world.GetComponentPool(); + _componentPool1 = &world.GetComponentPool(); + } } public new Result<(EcsEntity Entity, TComponent0* Component0, TComponent1* Component1)> GetNext() mut @@ -120,9 +141,18 @@ namespace GlitchyEngine.World public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent0), typeof(TComponent1), typeof(TComponent2))) { - _componentPool0 = &world.GetComponentPool(); - _componentPool1 = &world.GetComponentPool(); - _componentPool2 = &world.GetComponentPool(); + if (base.IsEmpty) + { + _componentPool0 = null; + _componentPool1 = null; + _componentPool2 = null; + } + else + { + _componentPool0 = &world.GetComponentPool(); + _componentPool1 = &world.GetComponentPool(); + _componentPool2 = &world.GetComponentPool(); + } } public new Result<(EcsEntity Entity, TComponent0* Component0, TComponent1* Component1, TComponent2* Component2)> GetNext() mut @@ -151,10 +181,20 @@ namespace GlitchyEngine.World public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent0), typeof(TComponent1), typeof(TComponent2), typeof(TComponent3))) { - _componentPool0 = &world.GetComponentPool(); - _componentPool1 = &world.GetComponentPool(); - _componentPool2 = &world.GetComponentPool(); - _componentPool3 = &world.GetComponentPool(); + if (base.IsEmpty) + { + _componentPool0 = null; + _componentPool1 = null; + _componentPool2 = null; + _componentPool3 = null; + } + else + { + _componentPool0 = &world.GetComponentPool(); + _componentPool1 = &world.GetComponentPool(); + _componentPool2 = &world.GetComponentPool(); + _componentPool3 = &world.GetComponentPool(); + } } public new Result<(EcsEntity Entity, TComponent0* Component0, TComponent1* Component1, TComponent2* Component2, TComponent3* Component3)> GetNext() mut