diff --git a/GlitchyEditor/content/Textures/EditorIcons.dds b/GlitchyEditor/content/Textures/EditorIcons.dds index c2de425..70df89a 100644 Binary files a/GlitchyEditor/content/Textures/EditorIcons.dds and b/GlitchyEditor/content/Textures/EditorIcons.dds differ diff --git a/GlitchyEditor/content/Textures/EditorIcons.psd b/GlitchyEditor/content/Textures/EditorIcons.psd index af27432..c6e8d27 100644 Binary files a/GlitchyEditor/content/Textures/EditorIcons.psd and b/GlitchyEditor/content/Textures/EditorIcons.psd differ diff --git a/GlitchyEditor/src/EditWindows/EditorWindow.bf b/GlitchyEditor/src/EditWindows/EditorWindow.bf index 35c724d..b79ca1c 100644 --- a/GlitchyEditor/src/EditWindows/EditorWindow.bf +++ b/GlitchyEditor/src/EditWindows/EditorWindow.bf @@ -1,3 +1,4 @@ +using System; namespace GlitchyEditor.EditWindows { abstract class EditorWindow diff --git a/GlitchyEditor/src/EditWindows/SceneViewportWindow.bf b/GlitchyEditor/src/EditWindows/SceneViewportWindow.bf index e420dc8..f163d05 100644 --- a/GlitchyEditor/src/EditWindows/SceneViewportWindow.bf +++ b/GlitchyEditor/src/EditWindows/SceneViewportWindow.bf @@ -13,11 +13,30 @@ namespace GlitchyEditor.EditWindows { public const String s_WindowTitle = "Scene"; + private ImGui.Vec2 _oldViewportSize = .(100, 100); + private bool _viewPortChanged; + + /// True if the cursor wrapped from one side of the viewport to the other last frame. + private bool _wrappedCursor; + private RenderTargetGroup _renderTarget ~ _?.ReleaseRef(); - public Event> ViewportSizeChangedEvent ~ _.Dispose(); - public Event> EntityClickedEvent ~ _.Dispose(); + private ImGuizmo.OPERATION _gizmoType = .TRANSLATE; + private ImGuizmo.MODE _gizmoMode = .LOCAL; + private float _snap = 0.5f; + private float _angleSnap = 45.0f; + private bool _doSnap = false; + public uint32 SelectedEntityId {get; private set; } + public bool SelectionChanged { get; private set; } + + public Vector2 ViewportSize => (Vector2)_oldViewportSize; + // Occurs when the viewport is resized. + public Event> ViewportSizeChanged ~ _.Dispose(); + // Occurs when an entity was clicked. + public Event> EntityClicked ~ _.Dispose(); + + /// The render target that is shown in the viewport window. public RenderTargetGroup RenderTarget { get => _renderTarget; @@ -30,21 +49,14 @@ namespace GlitchyEditor.EditWindows } } + /// Gets or sets whether the editor functionality (gizmo, picking etc.) is enabled. + public bool EditorMode { get; set; } = true + public this(Editor editor) { _editor = editor; } - private ImGui.Vec2 oldViewportSize = .(100, 100); - private bool viewPortChanged; - - public uint32 SelectedEntityId; - public bool SelectionChanged; - - public Vector2 ViewportSize => (Vector2)oldViewportSize; - - private bool _wrappedCursor; - protected override void InternalShow() { ImGui.PushStyleVar(.WindowPadding, ImGui.Vec2(1, 1)); @@ -56,8 +68,6 @@ namespace GlitchyEditor.EditWindows return; } - ShowMenuBar(); - let viewportSize = ImGui.GetContentRegionAvail(); if(ImGui.IsWindowHovered() && Input.IsMouseButtonPressing(.RightButton)) @@ -67,62 +77,74 @@ namespace GlitchyEditor.EditWindows } _hasFocus = ImGui.IsWindowFocused(); - - if (_editor.CurrentCamera.[Friend]BindMouse && _hasFocus) - WrapMouseInViewport(); - - // If we wrapped this frame we weren't hovering because the cursor has to be out of bounds to wrap - _editor.CurrentCamera.AllowMove = _hasFocus && (ImGui.IsWindowHovered() || _wrappedCursor); - - if (_hasFocus && !_editor.CurrentCamera.InUse) + + if (EditorMode) { - if (Input.IsKeyPressing(.Q)) - _gizmoType = .TRANSLATE; - if (Input.IsKeyPressing(.W)) - _gizmoType = .ROTATE; - if (Input.IsKeyPressing(.E)) - _gizmoType = .SCALE; + ShowMenuBar(); + + if (_editor.CurrentCamera.[Friend]BindMouse && _hasFocus) + WrapMouseInViewport(); - if (Input.IsKeyPressing(.G)) - _gizmoMode = .WORLD; - if (Input.IsKeyPressing(.L)) - _gizmoMode = .LOCAL; + // If we wrapped this frame we weren't hovering because the cursor has to be be out of bounds to wrap + _editor.CurrentCamera.AllowMove = _hasFocus && (ImGui.IsWindowHovered() || _wrappedCursor); + + if (_hasFocus && !_editor.CurrentCamera.InUse) + { + if (Input.IsKeyPressing(.Q)) + _gizmoType = .TRANSLATE; + if (Input.IsKeyPressing(.W)) + _gizmoType = .ROTATE; + if (Input.IsKeyPressing(.E)) + _gizmoType = .SCALE; + + if (Input.IsKeyPressing(.G)) + _gizmoMode = .WORLD; + if (Input.IsKeyPressing(.L)) + _gizmoMode = .LOCAL; + } } - + if(_renderTarget != null) { ImGui.Image(_renderTarget.GetViewBinding(0), viewportSize); //ImGui.Image(_editor.CurrentCamera.RenderTarget.GetViewBinding(0), viewportSize); //ImGui.Image(_editor.CurrentScene.[Friend]_compositeTarget.GetViewBinding(0), viewportSize); } - + + if (EditorMode) + { + HandleDropTarget(); + + bool gizmoUsed = DrawImGuizmo(viewportSize); + + MousePicking(viewportSize, gizmoUsed); + } + + ImGui.End(); + + if(_oldViewportSize != viewportSize) + { + ViewportSizeChanged.Invoke(this, (Vector2)viewportSize); + _viewPortChanged = true; + _oldViewportSize = viewportSize; + } + } + + /// Provides the ImGui Drop target and handles dropped payload. + private void HandleDropTarget() + { if (ImGui.BeginDragDropTarget()) { ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); if (payload != null) { - Log.EngineLogger.Warning(""); - StringView path = .((char8*)payload.Data, (int)payload.DataSize); _editor.RequestOpenScene(this, path); } ImGui.EndDragDropTarget(); } - - bool gizmoUsed = DrawImGuizmo(viewportSize); - - MousePicking(viewportSize, gizmoUsed); - - ImGui.End(); - - if(oldViewportSize != viewportSize) - { - ViewportSizeChangedEvent.Invoke(this, (Vector2)viewportSize); - viewPortChanged = true; - oldViewportSize = viewportSize; - } } /// Wraps the mouse, so that it always stays in the viewport. @@ -158,11 +180,15 @@ namespace GlitchyEditor.EditWindows if (newMousePos != mousePos) { Input.SetMousePosition((Int2)newMousePos); + // After wrapping the cursor the the other side, the camera controller must not compare the positions, + // because the delta doesn't represent the correct movement of the cursor. + // TODO: can be solved by using direct mouse movement instead of comparing positions _editor.CurrentCamera.[Friend]MouseCooldown = 2; _wrappedCursor = true; } } + /// If the user clicks, the entity beneath the cursor will be selected. private void MousePicking(ImGui.Vec2 viewportSize, bool gizmoUsed) { Vector2 relativeMouse = (Vector2)ImGui.GetMousePos() - (Vector2)ImGui.GetItemRectMin(); @@ -183,7 +209,7 @@ namespace GlitchyEditor.EditWindows SelectionChanged = true; SelectedEntityId = id; - EntityClickedEvent(this, id); + EntityClicked(this, id); } else { @@ -191,12 +217,6 @@ namespace GlitchyEditor.EditWindows } } - private ImGuizmo.OPERATION _gizmoType = .TRANSLATE; - private ImGuizmo.MODE _gizmoMode = .LOCAL; - private float _snap = 0.5f; - private float _angleSnap = 45.0f; - private bool _doSnap = false; - private void ShowMenuBar() { if(ImGui.BeginMenuBar()) @@ -262,6 +282,7 @@ namespace GlitchyEditor.EditWindows private bool DrawImGuizmo(ImGui.Vec2 viewportSize) { + // TODO: Needed if we have a orthographic editor-camera (or support gizmos in the Play-Window, where we can also have ortho projections) ImGuizmo.SetOrthographic(false); ImGuizmo.SetDrawlist(); @@ -299,7 +320,7 @@ namespace GlitchyEditor.EditWindows { // TODO: Fix when parent is scaled // Seems to work fine for parent rotation and translation but scaled parent ruins everything - // (probably because scaling a rotated matrix results in a skewed matrix, but unity can do it, so should we) + // (probably because scaling a rotated matrix results in a skewed matrix, but unity can do it and so should we) transformCmp.LocalTransform = parentView * worldTransform; } diff --git a/GlitchyEditor/src/EditorIcons.bf b/GlitchyEditor/src/EditorIcons.bf index 9580983..686737b 100644 --- a/GlitchyEditor/src/EditorIcons.bf +++ b/GlitchyEditor/src/EditorIcons.bf @@ -13,6 +13,8 @@ namespace GlitchyEditor public SubTexture2D Camera ~ _.ReleaseRef(); public SubTexture2D Folder ~ _.ReleaseRef(); public SubTexture2D File ~ _.ReleaseRef(); + public SubTexture2D Play ~ _.ReleaseRef(); + public SubTexture2D Stop ~ _.ReleaseRef(); public SamplerState SamplerState { @@ -30,6 +32,8 @@ namespace GlitchyEditor Camera = GetNextGridTexture(ref pen, iconSize); Folder = GetNextGridTexture(ref pen, iconSize); File = GetNextGridTexture(ref pen, iconSize); + Play = GetNextGridTexture(ref pen, iconSize); + Stop = GetNextGridTexture(ref pen, iconSize); } private SubTexture2D GetNextGridTexture(ref Vector2 pen, Vector2 iconSize) diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index e7aad13..9064ea1 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -56,6 +56,16 @@ namespace GlitchyEditor EditorIcons _editorIcons ~ _.ReleaseRef(); + enum SceneState + { + Edit, + Play, + // Pause, + // Simulate + } + + SceneState _sceneState = .Edit; + public this() : base("Example") { Application.Get().Window.IsVSync = false; @@ -121,7 +131,7 @@ namespace GlitchyEditor private void InitEditor() { _editor = new Editor(_scene); - _editor.SceneViewportWindow.ViewportSizeChangedEvent.Add(new (s, e) => ViewportSizeChanged(s, e)); + _editor.SceneViewportWindow.ViewportSizeChanged.Add(new (s, e) => ViewportSizeChanged(s, e)); _editor.CurrentCamera = &_camera; _editor.RequestOpenScene.Add(new (s, fileName) => { @@ -141,7 +151,10 @@ namespace GlitchyEditor RenderCommand.SetBlendState(_alphaBlendState); RenderCommand.SetDepthStencilState(_depthStencilState); - _scene.UpdateEditor(gameTime, _camera, _viewportTarget, scope => DebugDraw3D, scope => DebugDraw2D); + if (_sceneState == .Edit) + _scene.UpdateEditor(gameTime, _camera, _viewportTarget, scope => DebugDraw3D, scope => DebugDraw2D); + else if (_sceneState == .Play) + _scene.UpdateRuntime(gameTime, _viewportTarget); RenderCommand.UnbindRenderTargets(); RenderCommand.SetRenderTarget(null, 0, true); @@ -232,7 +245,7 @@ namespace GlitchyEditor private bool OnImGuiRender(ImGuiRenderEvent event) { - //viewer.ViewTexture(Renderer.[Friend]_gBuffer.Albedo); + //viewer.ViewTexture(Renderer.[Friend]_gBuffer.Target); ImGui.Viewport* viewport = ImGui.GetMainViewport(); ImGui.DockSpaceOverViewport(viewport); @@ -245,9 +258,66 @@ namespace GlitchyEditor _settingsWindow.Show(); + UI_Toolbar(); + return false; } + private void UI_Toolbar() + { + ImGui.PushStyleVar(.WindowPadding, ImGui.Vec2(0, 2)); + ImGui.PushStyleVar(.ItemInnerSpacing, ImGui.Vec2(0, 0)); + ImGui.PushStyleColor(.Button, ImGui.Vec4(0, 0, 0, 0)); + + let colors = ImGui.GetStyle().Colors; + + ImGui.Vec4 hoveredColor = colors[(int)ImGui.Col.ButtonHovered]; + hoveredColor.w = 0.5f; + + ImGui.Vec4 activeColor = colors[(int)ImGui.Col.ButtonActive]; + activeColor.w = 0.5f; + + ImGui.PushStyleColor(.ButtonHovered, hoveredColor); + ImGui.PushStyleColor(.ButtonActive, activeColor); + + ImGui.Begin("##toolbar", null, .NoDecoration | .NoScrollbar | .NoScrollWithMouse); + + SubTexture2D icon = _sceneState == .Edit ? _editorIcons.Play : _editorIcons.Stop; + + float size = ImGui.GetWindowHeight() - 4.0f; + + ImGui.SameLine((ImGui.GetContentRegionMax().x / 2 - size / 2)); + + if (ImGui.ImageButton(icon, .(size, size), .Zero, .Ones, 0)) + { + if (_sceneState == .Edit) + { + OnScenePlay(); + } + else if (_sceneState == .Play) + { + OnSceneStop(); + } + } + + ImGui.End(); + + ImGui.PopStyleColor(3); + ImGui.PopStyleVar(2); + } + + private void OnScenePlay() + { + _sceneState = .Play; + _editor.SceneViewportWindow.EditorMode = false; + } + + private void OnSceneStop() + { + _sceneState = .Edit; + _editor.SceneViewportWindow.EditorMode = true; + } + // Just for testing private void TestEntitiesWithModels() { @@ -282,6 +352,7 @@ namespace GlitchyEditor let camera = cameraNtt.AddComponent(); camera.Primary = true; camera.Camera.SetPerspective(MathHelper.ToRadians(45), 0.1f, 10.0f); + camera.RenderTarget = _cameraTarget; } var fxLib = Application.Get().EffectLibrary; diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Buffer.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Buffer.bf index 8a9ad71..26f4914 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Buffer.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Buffer.bf @@ -84,7 +84,7 @@ namespace GlitchyEngine.Renderer return .Ok; } - protected override Result PlatformSetData(void* data, uint32 byteLength, uint32 dstByteOffset, GlitchyEngine.Renderer.MapType mapType) + protected override Result PlatformSetData(void* data, uint32 byteLength, uint32 dstByteOffset, Renderer.MapType mapType) { Debug.Profiler.ProfileResourceFunction!(); @@ -102,7 +102,7 @@ namespace GlitchyEngine.Renderer Box dataBox = .(dstByteOffset, 0, 0, dstByteOffset + byteLength, 1, 1); NativeContext.UpdateSubresource(nativeBuffer, 0, &dataBox, data, byteLength, byteLength); case .Dynamic: - Debug.Assert(mapType.CanWrite, "The map type has to have write access."); + Log.EngineLogger.Assert(mapType == .WriteDiscard || mapType == .WriteNoOverwrite, scope $"When writing to dynamic resources the map type must be {nameof(Renderer.MapType.WriteDiscard)} or {nameof(Renderer.MapType.WriteNoOverwrite)}."); // Todo: DoNotWaitFlag MappedSubresource map = ?; NativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map); diff --git a/GlitchyEngine/src/Renderer/Renderer.bf b/GlitchyEngine/src/Renderer/Renderer.bf index 7bc0da6..4088cbb 100644 --- a/GlitchyEngine/src/Renderer/Renderer.bf +++ b/GlitchyEngine/src/Renderer/Renderer.bf @@ -232,7 +232,7 @@ namespace GlitchyEngine.Renderer _sceneConstants.CameraTarget = renderTarget; _sceneConstants.CompositionTarget = finalTarget; - _sceneBuffer.SetData(viewProjection); + _sceneBuffer.SetData(viewProjection , 0, .WriteDiscard); } public static void BeginScene(EditorCamera camera, RenderTargetGroup finalTarget) diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index b9336d0..11beb4a 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -63,8 +63,163 @@ namespace GlitchyEngine.World public void UpdateRuntime(GameTime gameTime, RenderTargetGroup finalTarget) { - // TODO: adapt changes from UpdateEditor! - Runtime.NotImplemented(); + Debug.Profiler.ProfileRendererFunction!(); + + finalTarget.AddRef(); + + TransformSystem.Update(_ecsWorld); + + // Find camera + Camera* primaryCamera = null; + Matrix primaryCameraTransform = default; + RenderTargetGroup renderTarget = null; + + for (var (entity, transform, camera) in _ecsWorld.Enumerate()) + { + if (camera.Primary)// && camera.RenderTarget != null) + { + primaryCamera = &camera.Camera; + primaryCameraTransform = transform.WorldTransform; + renderTarget = camera.RenderTarget..AddRef(); + } + } + + // 3D render + Renderer.BeginScene(*primaryCamera, primaryCameraTransform, renderTarget, _compositeTarget); + + for (var (entity, transform, mesh, meshRenderer) in _ecsWorld.Enumerate()) + { + Renderer.Submit(mesh.Mesh, meshRenderer.Material, entity, transform.WorldTransform); + } + + for (var (entity, transform, light) in _ecsWorld.Enumerate()) + { + Renderer.Submit(light.SceneLight, transform.WorldTransform); + } + + Renderer.EndScene(); + + renderTarget.ReleaseRef(); + + // TODO: alphablending (handle in Renderer2D) + // TODO: 2D-Postprocessing requires rendering into separate target instead of directly into compositeTarget + + RenderCommand.SetRenderTargetGroup(_compositeTarget); + RenderCommand.BindRenderTargets(); + + // Sprite renderer + Renderer2D.BeginScene(*primaryCamera, primaryCameraTransform, .BackToFront); + + for (var (entity, transform, sprite) in _ecsWorld.Enumerate()) + { + Renderer2D.DrawSprite(transform.WorldTransform, sprite, entity.Index); + } + + Renderer2D.EndScene(); + + // Gamma correct composit target and draw it into viewport + { + RenderCommand.UnbindRenderTargets(); + RenderCommand.SetRenderTargetGroup(finalTarget, false); + RenderCommand.BindRenderTargets(); + + _gammaCorrectEffect.SetTexture("Texture", _compositeTarget, 0); + // TODO: iiihhh + _gammaCorrectEffect.Bind(Application.Get().Window.Context); + + FullscreenQuad.Draw(); + } + + finalTarget.ReleaseRef(); + + + /*Debug.Profiler.ProfileRendererFunction!(); + + finalTarget.AddRef(); + + TransformSystem.Update(_ecsWorld); + + // Run scripts + for (var (entity, script) in _ecsWorld.Enumerate()) + { + if (script.Instance == null) + { + script.Instance = script.InstantiateFunction(); + script.Instance._entity = Entity(entity, this); + script.Instance.[Friend]OnCreate(); + } + + script.Instance.[Friend]OnUpdate(gameTime); + } + + // Find camera + Camera* primaryCamera = null; + Matrix primaryCameraTransform = default; + RenderTargetGroup renderTarget = null; + + for (var (entity, transform, camera) in _ecsWorld.Enumerate()) + { + if (camera.Primary)// && camera.RenderTarget != null) + { + primaryCamera = &camera.Camera; + primaryCameraTransform = transform.WorldTransform; + //renderTarget = camera.RenderTarget..AddRef(); + } + } + + if (primaryCamera != null) + { + // 3D render + Renderer.BeginScene(*primaryCamera, primaryCameraTransform, _compositeTarget, finalTarget); + + for (var (entity, transform, mesh, meshRenderer) in _ecsWorld.Enumerate()) + { + Renderer.Submit(mesh.Mesh, meshRenderer.Material, entity, transform.WorldTransform); + } + + for (var (entity, transform, light) in _ecsWorld.Enumerate()) + { + Renderer.Submit(light.SceneLight, transform.WorldTransform); + } + + Renderer.EndScene(); + + // TODO: alphablending (handle in Renderer2D) + // TODO: 2D-Postprocessing requires rendering into separate target instead of directly into compositeTarget + + RenderCommand.SetRenderTargetGroup(_compositeTarget); + RenderCommand.BindRenderTargets(); + + // Sprite renderer + Renderer2D.BeginScene(*primaryCamera, primaryCameraTransform, .BackToFront); + + for (var (entity, transform, sprite) in _ecsWorld.Enumerate()) + { + Renderer2D.DrawSprite(transform.WorldTransform, sprite, entity.Index); + } + + Renderer2D.EndScene(); + + // Gamma correct composit target and draw it into viewport + { + RenderCommand.UnbindRenderTargets(); + RenderCommand.SetRenderTargetGroup(finalTarget, false); + RenderCommand.BindRenderTargets(); + + _gammaCorrectEffect.SetTexture("Texture", _compositeTarget, 0); + // TODO: iiihhh + _gammaCorrectEffect.Bind(Application.Get().Window.Context); + + FullscreenQuad.Draw(); + } + } + + finalTarget.ReleaseRef();*/ + + + + + /* Debug.Profiler.ProfileRendererFunction!();