Start of serializaation

- Allow enumerating over unregistered components
- Refactored Editor a bit
This commit is contained in:
Simon Lübeß
2022-05-14 23:01:08 +02:00
parent 1d3d30f88d
commit 3956080520
15 changed files with 845 additions and 248 deletions
@@ -30,6 +30,7 @@ namespace GlitchyEditor.EditWindows
public void SetContext(Scene scene) public void SetContext(Scene scene)
{ {
_selectedEntities.Clear();
_scene = scene; _scene = scene;
} }
@@ -10,8 +10,6 @@ namespace GlitchyEditor.EditWindows
{ {
class SceneViewportWindow : EditorWindow class SceneViewportWindow : EditorWindow
{ {
//public OldCamera _camera;
public const String s_WindowTitle = "Scene"; public const String s_WindowTitle = "Scene";
private RenderTarget2D _renderTarget ~ _?.ReleaseRef(); private RenderTarget2D _renderTarget ~ _?.ReleaseRef();
@@ -38,8 +36,6 @@ namespace GlitchyEditor.EditWindows
private ImGui.Vec2 oldViewportSize; private ImGui.Vec2 oldViewportSize;
private bool viewPortChanged; private bool viewPortChanged;
public Entity CameraEntity { get; set; }
protected override void InternalShow() protected override void InternalShow()
{ {
ImGui.PushStyleVar(.WindowPadding, ImGui.Vec2(1, 1)); ImGui.PushStyleVar(.WindowPadding, ImGui.Vec2(1, 1));
@@ -89,20 +85,20 @@ namespace GlitchyEditor.EditWindows
topLeft.y += cntMin.y; topLeft.y += cntMin.y;
ImGuizmo.SetRect(topLeft.x, topLeft.y, viewportSize.x, viewportSize.y); ImGuizmo.SetRect(topLeft.x, topLeft.y, viewportSize.x, viewportSize.y);
var cameraTransformCmp = CameraEntity.GetComponent<TransformComponent>(); var cameraTransformCmp = _editor.CurrentCamera.GetComponent<TransformComponent>();
var view = cameraTransformCmp.WorldTransform.Invert(); var view = cameraTransformCmp.WorldTransform.Invert();
var cameraCmp = CameraEntity.GetComponent<CameraComponent>(); var cameraCmp = _editor.CurrentCamera.GetComponent<CameraComponent>();
var projection = cameraCmp.Camera.Projection; var projection = cameraCmp.Camera.Projection;
Matrix mat = .Identity; Matrix mat = .Identity;
ImGuizmo.DrawGrid((.)&view, (.)&projection, (.)&mat, 10); 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<TransformComponent>(entity); var transformCmp = entity.GetComponent<TransformComponent>();
var transform = transformCmp.LocalTransform; var transform = transformCmp.LocalTransform;
+29 -77
View File
@@ -4,115 +4,67 @@ using System;
using System.Collections; using System.Collections;
using GlitchyEngine.Collections; using GlitchyEngine.Collections;
using GlitchyEditor.EditWindows; using GlitchyEditor.EditWindows;
using GlitchyEngine;
namespace GlitchyEditor namespace GlitchyEditor
{ {
class Editor class Editor
{ {
private EcsWorld _ecsWorld;
private Scene _scene; private Scene _scene;
private Entity _currentCamera;
private EntityHierarchyWindow _entityHierarchyWindow ~ delete _; private EntityHierarchyWindow _entityHierarchyWindow ~ delete _;
private ComponentEditWindow _componentEditWindow ~ delete _; private ComponentEditWindow _componentEditWindow ~ delete _;
private SceneViewportWindow _sceneViewportWindow = new .(this) ~ delete _; private SceneViewportWindow _sceneViewportWindow = new .(this) ~ delete _;
private List<EcsEntity> _selectedEntities = new .() ~ delete _;
public EcsWorld World => _ecsWorld;
public List<EcsEntity> SelectedEntities => _selectedEntities;
public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow; public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow;
public ComponentEditWindow ComponentEditWindow => _componentEditWindow; public ComponentEditWindow ComponentEditWindow => _componentEditWindow;
public SceneViewportWindow SceneViewportWindow => _sceneViewportWindow; 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 /// Creates a new editor for the given world
public this(Scene scene) public this(Scene scene)
{ {
_scene = scene;
_ecsWorld = _scene.[Friend]_ecsWorld;
_entityHierarchyWindow = new EntityHierarchyWindow(_scene); _entityHierarchyWindow = new EntityHierarchyWindow(_scene);
CurrentScene = scene;
_componentEditWindow = new ComponentEditWindow(_entityHierarchyWindow); _componentEditWindow = new ComponentEditWindow(_entityHierarchyWindow);
} }
public void Update() public void Update()
{ {
var scriptComponent = CurrentCamera.GetComponent<NativeScriptComponent>();
if (var camController = scriptComponent?.Instance as EditorCameraController)
{
camController.IsEnabled = (SceneViewportWindow.HasFocus && Input.IsMouseButtonPressed(.RightButton));
}
_entityHierarchyWindow.Show(); _entityHierarchyWindow.Show();
_componentEditWindow.Show(); _componentEditWindow.Show();
_sceneViewportWindow.Show(); _sceneViewportWindow.Show();
} }
/// Creates a new entity with a transform component. private void FindCurrentEditorCamera()
internal EcsEntity CreateEntityWithTransform()
{ {
var entity = _ecsWorld.NewEntity(); _currentCamera = .(.InvalidEntity, _scene);
// TODO: a bit sketchy
var transformComponent = ref *_ecsWorld.AssignComponent<TransformComponent>(entity); for (var (entity, editComp, cam) in _scene.[Friend]_ecsWorld.Enumerate<EditorComponent, CameraComponent>())
transformComponent = TransformComponent();
var nameComponent = ref *_ecsWorld.AssignComponent<DebugNameComponent>(entity);
nameComponent.SetName("Entity");
return entity;
}
/// Returns whether or not all selected entities have the same parent.
internal bool AllSelectionsOnSameLevel()
{ {
EcsEntity? parent = .InvalidEntity; _currentCamera = .(entity, CurrentScene);
for(var selectedEntity in _selectedEntities)
{
var parentComponent = _ecsWorld.GetComponent<ParentComponent>(selectedEntity);
if(parent == .InvalidEntity)
{
parent = parentComponent?.Entity;
}
else if(parentComponent?.Entity != parent)
{
return false;
}
}
return true;
}
/// Finds all children of the given entity and stores their IDs in the given list.
internal void FindChildren(EcsEntity entity, List<EcsEntity> entities)
{
for(var (child, childParent) in _ecsWorld.Enumerate<ParentComponent>())
{
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<EcsEntity> entities = scope .();
for(var entity in _selectedEntities)
{
entities.Add(entity);
FindChildren(entity, entities);
}
for(var entity in entities)
{
_ecsWorld.RemoveEntity(entity);
}
_selectedEntities.Clear();
}
}
} }
+236 -131
View File
@@ -10,6 +10,7 @@ using GlitchyEngine.World;
using GlitchyEngine.Content; using GlitchyEngine.Content;
using System.Collections; using System.Collections;
using GlitchyEngine.Renderer.Animation; using GlitchyEngine.Renderer.Animation;
using System.IO;
namespace GlitchyEditor namespace GlitchyEditor
{ {
@@ -25,6 +26,19 @@ namespace GlitchyEditor
DepthStencilState _depthStencilState ~ _.ReleaseRef(); DepthStencilState _depthStencilState ~ _.ReleaseRef();
Scene _scene = new Scene() ~ delete _; 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 _; Editor _editor ~ delete _;
@@ -81,48 +95,112 @@ namespace GlitchyEditor
InitGraphics(); InitGraphics();
{ NewScene();
_cameraEntity = _scene.CreateEntity("Camera Entity");
let camera = _cameraEntity.AddComponent<CameraComponent>();
camera.Camera.SetPerspective(MathHelper.ToRadians(75), 0.1f, 10000.0f);
camera.Primary = true;
camera.FixedAspectRatio = false;
camera.RenderTarget = _cameraTarget;
let transform = _cameraEntity.GetComponent<TransformComponent>();
//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);
_cameraEntity.AddComponent<NativeScriptComponent>().Bind<EditorCameraController>(); InitEditor();
_cameraEntity.AddComponent<EditorComponent>();
} }
/*{ private void InitGraphics()
_otherCameraEntity = _scene.CreateEntity("Other Camera Entity");
let camera = _otherCameraEntity.AddComponent<CameraComponent>();
camera.Camera.SetPerspective(MathHelper.ToRadians(45), 0.1f, 1000.0f);
camera.Primary = false;
camera.FixedAspectRatio = false;
camera.RenderTarget = _viewportTarget;
let transform = _otherCameraEntity.GetComponent<TransformComponent>();
transform.Position = .(0, 0, -5);
_otherCameraEntity.AddComponent<NativeScriptComponent>().Bind<EditorCameraController>();
_otherCameraEntity.AddComponent<EditorComponent>();
}*/
{ {
var lightNtt = _scene.CreateEntity("My Sexy Sun"); _context = Application.Get().Window.Context..AddRef();
let transform = lightNtt.GetComponent<TransformComponent>();
transform.Position = .(0, 0, 0);
transform.RotationEuler = .(MathHelper.ToRadians(45), MathHelper.ToRadians(-100), 0);
let light = lightNtt.AddComponent<LightComponent>(); RasterizerStateDescription rsDesc = .(.Solid, .Back, true);
light.SceneLight.Illuminance = 10.0f; _rasterizerState = new RasterizerState(rsDesc);
light.SceneLight.Color = .(1.0f, 0.95f, 0.8f);
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<ImGuiRenderEvent>(scope (e) => OnImGuiRender(e));
dispatcher.Dispatch<WindowResizeEvent>(scope (e) => OnWindowResize(e));
dispatcher.Dispatch<KeyPressedEvent>(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<CameraComponent>().Primary = cameraA;
_otherCameraEntity.GetComponent<CameraComponent>().Primary = !cameraA;
}
ImGui.End();*/
ImGui.Viewport* viewport = ImGui.GetMainViewport();
ImGui.DockSpaceOverViewport(viewport);
DrawMainMenuBar();
_editor.SceneViewportWindow.RenderTarget = _viewportTarget;
_editor.Update();
_settingsWindow.Show();
return false;
}
// Just for testing
private void TestEntitiesWithModels()
{
{ {
var lightNtt = _scene.CreateEntity("My Sexy Sun 2"); var lightNtt = _scene.CreateEntity("My Sexy Sun 2");
let transform = lightNtt.GetComponent<TransformComponent>(); let transform = lightNtt.GetComponent<TransformComponent>();
@@ -145,20 +223,17 @@ namespace GlitchyEditor
light.SceneLight.Color = .(1.0f, 0.95f, 0.8f); light.SceneLight.Color = .(1.0f, 0.95f, 0.8f);
} }
InitEditor();
TestEntitiesWithModels();
}
private void TestEntitiesWithModels()
{
var fxLib = Application.Get().EffectLibrary; var fxLib = Application.Get().EffectLibrary;
using (Effect myEffect = fxLib.Load("content/Shaders/myEffect.hlsl")) 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 normal = new Texture2D("Textures/TestMat/rustediron2_normal.png"))
using (Texture2D rough = new Texture2D("Textures/TestMat/rustediron2_roughness.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; albedo.SamplerState = SamplerStateManager.AnisotropicWrap;
normal.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(); // Create the editor camera
{
_cameraEntity = _scene.CreateEntity("Editor Camera");
// Add EditorComponent so that the engine knows that this is not part of the game.
_cameraEntity.AddComponent<EditorComponent>();
// Script for controlling the camera.
_cameraEntity.AddComponent<NativeScriptComponent>().Bind<EditorCameraController>();
RasterizerStateDescription rsDesc = .(.Solid, .Back, true); let camera = _cameraEntity.AddComponent<CameraComponent>();
_rasterizerState = new RasterizerState(rsDesc); camera.Camera.SetPerspective(MathHelper.ToRadians(75), 0.1f, 10000.0f);
camera.Primary = true;
camera.RenderTarget = _cameraTarget;
let transform = _cameraEntity.GetComponent<TransformComponent>();
//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);
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() /// Creates a new scene.
private void NewScene()
{ {
_editor = new Editor(_scene); SceneFilePath = null;
_editor.SceneViewportWindow.ViewportSizeChangedEvent.Add(new (s, e) => ViewportSizeChanged(s, e));
//_editor.[Friend]CreateEntityWithTransform(); // Create the default light source
{
var lightNtt = _scene.CreateEntity("Light");
let transform = lightNtt.GetComponent<TransformComponent>();
transform.Position = .(0, 0, 0);
transform.RotationEuler = .(MathHelper.ToRadians(45), MathHelper.ToRadians(-100), 0);
_editor.SceneViewportWindow.CameraEntity = _cameraEntity; let light = lightNtt.AddComponent<LightComponent>();
light.SceneLight.Illuminance = 10.0f;
light.SceneLight.Color = .(1.0f, 0.95f, 0.8f);
} }
public override void Update(GameTime gameTime) TestEntitiesWithModels();
{
var scriptComponent = _cameraEntity.GetComponent<NativeScriptComponent>();
if (var camController = scriptComponent.Instance as EditorCameraController) PrepareSceneForEditor();
{
camController.IsEnabled = (_editor.SceneViewportWindow.HasFocus && Input.IsMouseButtonPressed(.RightButton));
} }
//TransformSystem.Update(_world); /// 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()
RenderCommand.Clear(_viewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0); {
if (String.IsNullOrWhiteSpace(SceneFilePath))
RenderCommand.SetRenderTarget(_viewportTarget, 0, true); {
RenderCommand.BindRenderTargets(); SaveSceneAs();
return;
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);
} }
public override void OnEvent(Event event) SceneSerializer serializer = scope .(_scene);
{ serializer.Serialize(SceneFilePath);
EventDispatcher dispatcher = EventDispatcher(event);
dispatcher.Dispatch<ImGuiRenderEvent>(scope (e) => OnImGuiRender(e));
dispatcher.Dispatch<WindowResizeEvent>(scope (e) => OnWindowResize(e));
} }
ImGui.ID _mainDockspaceId; /// Opens a save file dialog and saves the scene at the user specified location.
private void SaveSceneAs()
TextureViewer viewer = new TextureViewer() ~ delete _;
private bool OnImGuiRender(ImGuiRenderEvent event)
{ {
viewer.ViewTexture(_cameraTarget); SaveFileDialog sfd = scope .();
if (sfd.ShowDialog() case .Ok(let val))
/*ImGui.Begin("Test");
static bool cameraA = true;
if (ImGui.Checkbox("Camera A", &cameraA))
{ {
_cameraEntity.GetComponent<CameraComponent>().Primary = cameraA; if (val == .OK)
_otherCameraEntity.GetComponent<CameraComponent>().Primary = !cameraA; {
SceneFilePath = sfd.FileNames[0];
SaveScene();
}
}
} }
ImGui.End();*/ /// 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(); delete _scene;
ImGui.DockSpaceOverViewport(viewport); _scene = new Scene();
DrawMainMenuBar(); SceneSerializer serializer = scope .(_scene);
serializer.Deserialize(SceneFilePath);
_editor.SceneViewportWindow.RenderTarget = _viewportTarget; PrepareSceneForEditor();
_editor.Update(); _editor.CurrentScene = _scene;
}
_settingsWindow.Show(); }
return false;
} }
private void DrawMainMenuBar() private void DrawMainMenuBar()
@@ -358,9 +415,28 @@ namespace GlitchyEditor
if(ImGui.BeginMenu("File", true)) 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")) if (ImGui.MenuItem("Settings"))
_settingsWindow.Open = true; _settingsWindow.Open = true;
ImGui.Separator();
if (ImGui.MenuItem("Exit"))
Application.Get().Close();
ImGui.EndMenu(); ImGui.EndMenu();
} }
@@ -406,5 +482,34 @@ namespace GlitchyEditor
_scene.OnViewportResize(sizeX, sizeY); _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;
}
} }
} }
+6
View File
@@ -167,6 +167,12 @@ namespace GlitchyEngine
} }
} }
/// Closes the applcation.
public void Close()
{
_running = false;
}
public void PushLayer(Layer ownLayer) public void PushLayer(Layer ownLayer)
{ {
Profiler.ProfileFunction!(); Profiler.ProfileFunction!();
@@ -0,0 +1,37 @@
using System;
namespace Bon.Integrated
{
extension Serialize
{
public static void Value<T>(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<T>(BonWriter writer, in T value, BonEnvironment env = gBonEnv)
{
Serialize.Value(writer, ValueView(typeof(T), &value), env);
}
}
extension Deserialize
{
public static Result<void> Value<T>(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<void> Value<T>(BonReader reader, out T value, BonEnvironment env = gBonEnv)
{
value = ?;
return Deserialize.Value(reader, ValueView(typeof(T), &value), env);
}
}
}
+2
View File
@@ -1,7 +1,9 @@
using Bon;
using System; using System;
namespace GlitchyEngine.Math namespace GlitchyEngine.Math
{ {
[BonTarget]
public struct Quaternion public struct Quaternion
{ {
public const Quaternion Zero = .(); public const Quaternion Zero = .();
+2
View File
@@ -1,7 +1,9 @@
using Bon;
using System; using System;
namespace GlitchyEngine.Math namespace GlitchyEngine.Math
{ {
[BonTarget]
[SwizzleVector(2, "Vector")] [SwizzleVector(2, "Vector")]
public struct Vector2 public struct Vector2
{ {
+2
View File
@@ -1,7 +1,9 @@
using Bon;
using System; using System;
namespace GlitchyEngine.Math namespace GlitchyEngine.Math
{ {
[BonTarget]
[SwizzleVector(3, "Vector")] [SwizzleVector(3, "Vector")]
public struct Vector3 public struct Vector3
{ {
+2
View File
@@ -1,7 +1,9 @@
using Bon;
using System; using System;
namespace GlitchyEngine.Math namespace GlitchyEngine.Math
{ {
[BonTarget]
[SwizzleVector(4, "Vector")] [SwizzleVector(4, "Vector")]
public struct Vector4 public struct Vector4
{ {
@@ -4,6 +4,7 @@
#pragma warning disable 4204 #pragma warning disable 4204
using System; using System;
using Bon;
namespace GlitchyEngine.Math namespace GlitchyEngine.Math
{ {
@@ -35,4 +36,16 @@ namespace GlitchyEngine.Math
} }
} }
namespace DirectX
{
[BonTarget]
extension Color;
[BonTarget]
extension ColorRGB;
[BonTarget]
extension ColorRGBA;
}
#endif #endif
+2 -8
View File
@@ -1,6 +1,6 @@
using System;
using GlitchyEngine.Math; using GlitchyEngine.Math;
using GlitchyEngine.Renderer; using GlitchyEngine.Renderer;
using System;
namespace GlitchyEngine.World namespace GlitchyEngine.World
{ {
@@ -251,9 +251,8 @@ namespace GlitchyEngine.World
struct CameraComponent : IDisposableComponent struct CameraComponent : IDisposableComponent
{ {
public SceneCamera Camera; public SceneCamera Camera = .();
public bool Primary = true; // Todo: probably move into scene public bool Primary = true; // Todo: probably move into scene
public bool FixedAspectRatio = false;
private RenderTarget2D _renderTarget = null; private RenderTarget2D _renderTarget = null;
public RenderTarget2D RenderTarget public RenderTarget2D RenderTarget
@@ -265,11 +264,6 @@ namespace GlitchyEngine.World
} }
} }
public this()
{
Camera = .();
}
public void Dispose() public void Dispose()
{ {
_renderTarget?.ReleaseRef(); _renderTarget?.ReleaseRef();
+2 -2
View File
@@ -56,7 +56,7 @@ namespace GlitchyEngine.World
for (var (entity, transform, camera) in _ecsWorld.Enumerate<TransformComponent, CameraComponent>()) for (var (entity, transform, camera) in _ecsWorld.Enumerate<TransformComponent, CameraComponent>())
{ {
if (camera.Primary) if (camera.Primary && camera.RenderTarget != null)
{ {
primaryCamera = &camera.Camera; primaryCamera = &camera.Camera;
primaryCameraTransform = transform.WorldTransform; primaryCameraTransform = transform.WorldTransform;
@@ -134,7 +134,7 @@ namespace GlitchyEngine.World
for (var (entity, cameraComponent) in _ecsWorld.Enumerate<CameraComponent>()) for (var (entity, cameraComponent) in _ecsWorld.Enumerate<CameraComponent>())
{ {
if (!cameraComponent.FixedAspectRatio) if (!cameraComponent.Camera.FixedAspectRatio)
{ {
cameraComponent.Camera.SetViewportSize(width, height); cameraComponent.Camera.SetViewportSize(width, height);
} }
+445
View File
@@ -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<EditorComponent>())
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<EditorComponent>(writer, entity, "EditorComponent", scope (component) => {});
SerializeComponent<DebugNameComponent>(writer, entity, "NameComponent", scope (component) =>
{
Serialize.Value(writer, "Name", component.DebugName);
});
SerializeComponent<SpriterRendererComponent>(writer, entity, "SpriterRendererComponent", scope (component) =>
{
// TODO: Texture
Serialize.Value(writer, "Color", component.Color);
});
SerializeComponent<TransformComponent>(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<CameraComponent>(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<LightComponent>(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<T>(BonWriter writer, Entity entity, String identifier, delegate void(T* component) serialize) where T : struct, new
{
if (!entity.HasComponent<T>())
return;
var component = entity.GetComponent<T>();
writer.Identifier(identifier);
using (writer.ObjectBlock())
{
serialize(component);
}
writer.EntryEnd();
}
public void SerializeRuntime(StringView filePath)
{
Runtime.NotImplemented();
}
public Result<void> 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<void> DeserializeEntity(BonReader reader)
{
Try!(reader.ObjectBlock());
Entity entity = _scene.CreateEntity();
// TODO: GUID?
Deserialize.Value<uint32>(reader, "Id", let id);
while(reader.ObjectHasMore())
{
Try!(reader.EntryEnd());
StringView identifier = Try!(reader.Identifier());
switch(identifier)
{
case "EditorComponent":
Try!(DeserializeComponent<EditorComponent>(reader, entity, scope (component) => { return .Ok; }));
case "NameComponent":
Try!(DeserializeComponent<DebugNameComponent>(reader, entity, scope (component) =>
{
String name;
Deserialize.Value(reader, "Name", out name);
component.SetName(name);
delete name;
return .Ok;
}));
case "NameComponent":
Try!(DeserializeComponent<DebugNameComponent>(reader, entity, scope (component) =>
{
String name;
Deserialize.Value(reader, "Name", out name);
component.SetName(name);
delete name;
return .Ok;
}));
case "SpriterRendererComponent":
Try!(DeserializeComponent<SpriterRendererComponent>(reader, entity, scope (component) =>
{
// TODO: Texture
Try!(Deserialize.Value(reader, "Color", out component.Color));
return .Ok;
}));
case "TransformComponent":
Try!(DeserializeComponent<TransformComponent>(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<CameraComponent>(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<LightComponent>(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<EditorComponent>(writer, entity, "EditorComponent", scope (component) => {});
SerializeComponent<DebugNameComponent>(writer, entity, "NameComponent", scope (component) =>
{
Serialize.Value(writer, "Name", component.DebugName);
});
SerializeComponent<SpriterRendererComponent>(writer, entity, "SpriterRendererComponent", scope (component) =>
{
// TODO: Texture
Serialize.Value(writer, "Color", component.Color);
});
SerializeComponent<TransformComponent>(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<CameraComponent>(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<LightComponent>(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<void> DeserializeComponent<T>(BonReader reader, Entity entity, delegate Result<void>(T* component) deserialize) where T : struct, new
{
Try!(reader.ObjectBlock());
T* component;
if (entity.HasComponent<T>())
component = entity.GetComponent<T>();
else
component = entity.AddComponent<T>();
Try!(deserialize(component));
Try!(reader.ObjectBlockEnd());
return .Ok;
}
public bool DeserializeRuntime(StringView filePath)
{
Runtime.NotImplemented();
}
}
}
+41 -1
View File
@@ -13,6 +13,8 @@ namespace GlitchyEngine.World
internal EcsWorld.BitmaskEntry* _currentEntry; internal EcsWorld.BitmaskEntry* _currentEntry;
internal EcsWorld.BitmaskEntry* _endEntry; internal EcsWorld.BitmaskEntry* _endEntry;
public bool IsEmpty => _bitMask == null;
public this(EcsWorld world, Type[] componentTypes) public this(EcsWorld world, Type[] componentTypes)
{ {
_world = world; _world = world;
@@ -32,7 +34,11 @@ namespace GlitchyEngine.World
} }
else 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;
} }
} }
} }
@@ -66,9 +72,16 @@ namespace GlitchyEngine.World
internal EcsWorld.ComponentPoolEntry* _componentPool; internal EcsWorld.ComponentPoolEntry* _componentPool;
public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent))) public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent)))
{
if (base.IsEmpty)
{
_componentPool = null;
}
else
{ {
_componentPool = &world.GetComponentPool<TComponent>(); _componentPool = &world.GetComponentPool<TComponent>();
} }
}
public new Result<(EcsEntity Entity, TComponent* Component)> GetNext() mut public new Result<(EcsEntity Entity, TComponent* Component)> GetNext() mut
{ {
@@ -91,10 +104,18 @@ namespace GlitchyEngine.World
internal EcsWorld.ComponentPoolEntry* _componentPool1; internal EcsWorld.ComponentPoolEntry* _componentPool1;
public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent0), typeof(TComponent1))) public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent0), typeof(TComponent1)))
{
if (base.IsEmpty)
{
_componentPool0 = null;
_componentPool1 = null;
}
else
{ {
_componentPool0 = &world.GetComponentPool<TComponent0>(); _componentPool0 = &world.GetComponentPool<TComponent0>();
_componentPool1 = &world.GetComponentPool<TComponent1>(); _componentPool1 = &world.GetComponentPool<TComponent1>();
} }
}
public new Result<(EcsEntity Entity, TComponent0* Component0, TComponent1* Component1)> GetNext() mut public new Result<(EcsEntity Entity, TComponent0* Component0, TComponent1* Component1)> GetNext() mut
{ {
@@ -119,11 +140,20 @@ namespace GlitchyEngine.World
internal EcsWorld.ComponentPoolEntry* _componentPool2; internal EcsWorld.ComponentPoolEntry* _componentPool2;
public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent0), typeof(TComponent1), typeof(TComponent2))) public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent0), typeof(TComponent1), typeof(TComponent2)))
{
if (base.IsEmpty)
{
_componentPool0 = null;
_componentPool1 = null;
_componentPool2 = null;
}
else
{ {
_componentPool0 = &world.GetComponentPool<TComponent0>(); _componentPool0 = &world.GetComponentPool<TComponent0>();
_componentPool1 = &world.GetComponentPool<TComponent1>(); _componentPool1 = &world.GetComponentPool<TComponent1>();
_componentPool2 = &world.GetComponentPool<TComponent2>(); _componentPool2 = &world.GetComponentPool<TComponent2>();
} }
}
public new Result<(EcsEntity Entity, TComponent0* Component0, TComponent1* Component1, TComponent2* Component2)> GetNext() mut public new Result<(EcsEntity Entity, TComponent0* Component0, TComponent1* Component1, TComponent2* Component2)> GetNext() mut
{ {
@@ -150,12 +180,22 @@ namespace GlitchyEngine.World
internal EcsWorld.ComponentPoolEntry* _componentPool3; internal EcsWorld.ComponentPoolEntry* _componentPool3;
public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent0), typeof(TComponent1), typeof(TComponent2), typeof(TComponent3))) public this(EcsWorld world) : base(world, scope Type[](typeof(TComponent0), typeof(TComponent1), typeof(TComponent2), typeof(TComponent3)))
{
if (base.IsEmpty)
{
_componentPool0 = null;
_componentPool1 = null;
_componentPool2 = null;
_componentPool3 = null;
}
else
{ {
_componentPool0 = &world.GetComponentPool<TComponent0>(); _componentPool0 = &world.GetComponentPool<TComponent0>();
_componentPool1 = &world.GetComponentPool<TComponent1>(); _componentPool1 = &world.GetComponentPool<TComponent1>();
_componentPool2 = &world.GetComponentPool<TComponent2>(); _componentPool2 = &world.GetComponentPool<TComponent2>();
_componentPool3 = &world.GetComponentPool<TComponent3>(); _componentPool3 = &world.GetComponentPool<TComponent3>();
} }
}
public new Result<(EcsEntity Entity, TComponent0* Component0, TComponent1* Component1, TComponent2* Component2, TComponent3* Component3)> GetNext() mut public new Result<(EcsEntity Entity, TComponent0* Component0, TComponent1* Component1, TComponent2* Component2, TComponent3* Component3)> GetNext() mut
{ {