diff --git a/editor/src/UI/ConsoleFilterState.h b/editor/src/UI/ConsoleFilterState.h index 7b084316..2f33861e 100644 --- a/editor/src/UI/ConsoleFilterState.h +++ b/editor/src/UI/ConsoleFilterState.h @@ -8,24 +8,68 @@ namespace UI { class ConsoleFilterState { public: - bool& ShowInfo() { - return m_showInfo; + bool& ShowLog() { + return m_showLog; + } + + bool ShowLog() const { + return m_showLog; } bool& ShowWarning() { return m_showWarning; } + bool ShowWarning() const { + return m_showWarning; + } + bool& ShowError() { return m_showError; } + bool ShowError() const { + return m_showError; + } + + bool& Collapse() { + return m_collapse; + } + + bool Collapse() const { + return m_collapse; + } + + bool& ClearOnPlay() { + return m_clearOnPlay; + } + + bool ClearOnPlay() const { + return m_clearOnPlay; + } + + bool& ErrorPause() { + return m_errorPause; + } + + bool ErrorPause() const { + return m_errorPause; + } + + bool& ShowInfo() { + return ShowLog(); + } + + bool ShowInfo() const { + return ShowLog(); + } + bool Allows(::XCEngine::Debug::LogLevel level) const { switch (level) { case ::XCEngine::Debug::LogLevel::Verbose: case ::XCEngine::Debug::LogLevel::Debug: case ::XCEngine::Debug::LogLevel::Info: - return m_showInfo; + return m_showLog; case ::XCEngine::Debug::LogLevel::Warning: return m_showWarning; case ::XCEngine::Debug::LogLevel::Error: @@ -37,9 +81,12 @@ public: } private: - bool m_showInfo = true; + bool m_showLog = true; bool m_showWarning = true; bool m_showError = true; + bool m_collapse = false; + bool m_clearOnPlay = false; + bool m_errorPause = false; }; } // namespace UI diff --git a/editor/src/Viewport/ViewportHostService.h b/editor/src/Viewport/ViewportHostService.h index 690ac2b1..e131aa65 100644 --- a/editor/src/Viewport/ViewportHostService.h +++ b/editor/src/Viewport/ViewportHostService.h @@ -26,8 +26,10 @@ #include #include +#include #include #include +#include namespace XCEngine { namespace Editor { @@ -36,6 +38,60 @@ namespace { constexpr bool kDebugSceneSelectionMask = false; +class LambdaRenderPass final : public Rendering::RenderPass { +public: + using ExecuteCallback = std::function; + + LambdaRenderPass(std::string name, ExecuteCallback executeCallback) + : m_name(std::move(name)) + , m_executeCallback(std::move(executeCallback)) { + } + + const char* GetName() const override { + return m_name.c_str(); + } + + bool Execute(const Rendering::RenderPassContext& context) override { + return m_executeCallback != nullptr && m_executeCallback(context); + } + +private: + std::string m_name; + ExecuteCallback m_executeCallback; +}; + +template +std::unique_ptr MakeLambdaRenderPass(const char* name, Callback&& callback) { + return std::make_unique( + name, + LambdaRenderPass::ExecuteCallback(std::forward(callback))); +} + +inline void SetViewportStatusIfEmpty(std::string& statusText, const char* message) { + if (statusText.empty()) { + statusText = message; + } +} + +Math::Vector3 GetSceneViewportOrientationAxisVector(SceneViewportOrientationAxis axis) { + switch (axis) { + case SceneViewportOrientationAxis::PositiveX: + return Math::Vector3::Right(); + case SceneViewportOrientationAxis::NegativeX: + return Math::Vector3::Left(); + case SceneViewportOrientationAxis::PositiveY: + return Math::Vector3::Up(); + case SceneViewportOrientationAxis::NegativeY: + return Math::Vector3::Down(); + case SceneViewportOrientationAxis::PositiveZ: + return Math::Vector3::Forward(); + case SceneViewportOrientationAxis::NegativeZ: + return Math::Vector3::Back(); + default: + return Math::Vector3::Zero(); + } +} + } // namespace class ViewportHostService : public IViewportHostService { @@ -152,6 +208,21 @@ public: return PickSceneViewportEntity(request).entityId; } + void AlignSceneViewToOrientationAxis(SceneViewportOrientationAxis axis) override { + if (!EnsureSceneViewCamera()) { + return; + } + + const Math::Vector3 axisDirection = GetSceneViewportOrientationAxisVector(axis); + if (axisDirection.SqrMagnitude() <= Math::EPSILON) { + return; + } + + // To make the clicked cone face the screen, the camera must look back along that axis. + m_sceneViewCamera.controller.AnimateToForward(axisDirection * -1.0f); + ApplySceneViewCameraController(); + } + SceneViewportOverlayData GetSceneViewOverlayData() const override { SceneViewportOverlayData data = {}; if (m_sceneViewCamera.gameObject == nullptr || m_sceneViewCamera.camera == nullptr) { @@ -457,6 +528,150 @@ private: return surface; } + bool BuildSceneViewPostPassSequence( + ViewportEntry& entry, + const SceneViewportOverlayData& overlay, + const Rendering::RenderCameraData& cameraData, + const std::vector& selectionRenderables, + Rendering::RenderPassSequence& outPostPasses) { + if (!overlay.valid) { + return false; + } + + const bool hasSelection = !selectionRenderables.empty(); + if (hasSelection && kDebugSceneSelectionMask) { + outPostPasses.AddPass(MakeLambdaRenderPass( + "SceneColorToRenderTarget", + [&entry](const Rendering::RenderPassContext& context) { + context.renderContext.commandList->TransitionBarrier( + entry.colorView, + context.surface.GetColorStateAfter(), + RHI::ResourceStates::RenderTarget); + entry.colorState = RHI::ResourceStates::RenderTarget; + return true; + })); + outPostPasses.AddPass(MakeLambdaRenderPass( + "SceneSelectionMaskDebug", + [this, &entry, &cameraData, &selectionRenderables]( + const Rendering::RenderPassContext& context) { + const float debugClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + RHI::RHIResourceView* colorView = entry.colorView; + context.renderContext.commandList->SetRenderTargets(1, &colorView, entry.depthView); + context.renderContext.commandList->ClearRenderTarget(colorView, debugClearColor); + + const bool rendered = m_sceneSelectionMaskPass.Render( + context.renderContext, + context.surface, + cameraData, + selectionRenderables); + if (!rendered) { + SetViewportStatusIfEmpty(entry.statusText, "Scene selection mask debug pass failed"); + } + return rendered; + })); + outPostPasses.AddPass(MakeLambdaRenderPass( + "SceneColorToShaderResource", + [&entry](const Rendering::RenderPassContext& context) { + context.renderContext.commandList->TransitionBarrier( + entry.colorView, + RHI::ResourceStates::RenderTarget, + context.surface.GetColorStateAfter()); + entry.colorState = context.surface.GetColorStateAfter(); + return true; + })); + return true; + } + + if (hasSelection) { + if (entry.selectionMaskView == nullptr || entry.selectionMaskShaderView == nullptr) { + SetViewportStatusIfEmpty(entry.statusText, "Scene selection mask target is unavailable"); + } else { + Rendering::RenderSurface selectionMaskSurface = BuildSelectionMaskSurface(entry); + outPostPasses.AddPass(MakeLambdaRenderPass( + "SceneSelectionMask", + [this, &entry, selectionMaskSurface, &cameraData, &selectionRenderables]( + const Rendering::RenderPassContext& context) mutable { + context.renderContext.commandList->TransitionBarrier( + entry.selectionMaskView, + entry.selectionMaskState, + RHI::ResourceStates::RenderTarget); + entry.selectionMaskState = RHI::ResourceStates::RenderTarget; + + const float maskClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + RHI::RHIResourceView* maskView = entry.selectionMaskView; + context.renderContext.commandList->SetRenderTargets(1, &maskView, entry.depthView); + context.renderContext.commandList->ClearRenderTarget(maskView, maskClearColor); + + const bool rendered = m_sceneSelectionMaskPass.Render( + context.renderContext, + selectionMaskSurface, + cameraData, + selectionRenderables); + if (!rendered) { + SetViewportStatusIfEmpty(entry.statusText, "Scene selection mask pass failed"); + } + + context.renderContext.commandList->TransitionBarrier( + entry.selectionMaskView, + entry.selectionMaskState, + RHI::ResourceStates::PixelShaderResource); + entry.selectionMaskState = RHI::ResourceStates::PixelShaderResource; + return rendered; + })); + } + } + + outPostPasses.AddPass(MakeLambdaRenderPass( + "SceneColorToRenderTarget", + [&entry](const Rendering::RenderPassContext& context) { + context.renderContext.commandList->TransitionBarrier( + entry.colorView, + context.surface.GetColorStateAfter(), + RHI::ResourceStates::RenderTarget); + entry.colorState = RHI::ResourceStates::RenderTarget; + return true; + })); + outPostPasses.AddPass(MakeLambdaRenderPass( + "SceneInfiniteGrid", + [this, overlay, &entry](const Rendering::RenderPassContext& context) { + const bool rendered = m_sceneGridPass.Render( + context.renderContext, + context.surface, + overlay); + if (!rendered) { + SetViewportStatusIfEmpty(entry.statusText, "Scene grid pass failed"); + } + return rendered; + })); + + if (hasSelection && entry.selectionMaskShaderView != nullptr) { + outPostPasses.AddPass(MakeLambdaRenderPass( + "SceneSelectionOutline", + [this, &entry](const Rendering::RenderPassContext& context) { + const bool rendered = m_sceneSelectionOutlinePass.Render( + context.renderContext, + context.surface, + entry.selectionMaskShaderView); + if (!rendered) { + SetViewportStatusIfEmpty(entry.statusText, "Scene selection outline pass failed"); + } + return rendered; + })); + } + + outPostPasses.AddPass(MakeLambdaRenderPass( + "SceneColorToShaderResource", + [&entry](const Rendering::RenderPassContext& context) { + context.renderContext.commandList->TransitionBarrier( + entry.colorView, + RHI::ResourceStates::RenderTarget, + context.surface.GetColorStateAfter()); + entry.colorState = context.surface.GetColorStateAfter(); + return true; + })); + return true; + } + void RenderViewportEntry( ViewportEntry& entry, IEditorContext& context, @@ -470,6 +685,7 @@ private: Rendering::RenderSurface surface = BuildSurface(entry); if (entry.kind == EditorViewportKind::Scene) { + surface.SetClearColorOverride(Math::Color(0.27f, 0.27f, 0.27f, 1.0f)); if (!EnsureSceneViewCamera()) { entry.statusText = "Scene view camera is unavailable"; ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f); @@ -477,124 +693,54 @@ private: } ApplySceneViewCameraController(); + entry.statusText.clear(); if (scene == nullptr) { entry.statusText = "No active scene"; ClearViewport(entry, renderContext, 0.07f, 0.08f, 0.10f, 1.0f); - } else if (!m_sceneRenderer->Render(*scene, m_sceneViewCamera.camera, renderContext, surface)) { - entry.statusText = "Scene renderer failed"; - ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f); return; - } else { - entry.colorState = RHI::ResourceStates::PixelShaderResource; - entry.statusText.clear(); } const SceneViewportOverlayData overlay = GetSceneViewOverlayData(); + Rendering::RenderPassSequence sceneViewPostPasses; + Rendering::RenderCameraData cameraData = {}; + std::vector selectionRenderables; if (overlay.valid) { - RHI::RHICommandList* commandList = renderContext.commandList; - bool hasSelection = false; - Rendering::RenderCameraData cameraData = {}; - std::vector selectionRenderables; - - if (scene != nullptr) { - selectionRenderables = CollectSceneViewportSelectionRenderables( - *scene, - context.GetSelectionManager().GetSelectedEntities(), - overlay.cameraPosition); - if (!selectionRenderables.empty()) { - hasSelection = true; - cameraData = BuildSceneViewportCameraData( - *m_sceneViewCamera.camera, - entry.width, - entry.height); - } + selectionRenderables = CollectSceneViewportSelectionRenderables( + *scene, + context.GetSelectionManager().GetSelectedEntities(), + overlay.cameraPosition); + if (!selectionRenderables.empty()) { + cameraData = BuildSceneViewportCameraData( + *m_sceneViewCamera.camera, + entry.width, + entry.height); } - - commandList->TransitionBarrier( - entry.colorView, - entry.colorState, - RHI::ResourceStates::RenderTarget); - entry.colorState = RHI::ResourceStates::RenderTarget; - - if (kDebugSceneSelectionMask && hasSelection) { - const float debugClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; - RHI::RHIResourceView* colorView = entry.colorView; - commandList->SetRenderTargets(1, &colorView, entry.depthView); - commandList->ClearRenderTarget(colorView, debugClearColor); - - if (!m_sceneSelectionMaskPass.Render( - renderContext, - surface, - cameraData, - selectionRenderables) && - entry.statusText.empty()) { - entry.statusText = "Scene selection mask debug pass failed"; - } - - commandList->TransitionBarrier( - entry.colorView, - entry.colorState, - RHI::ResourceStates::PixelShaderResource); - entry.colorState = RHI::ResourceStates::PixelShaderResource; - return; - } - - if (hasSelection) { - if (entry.selectionMaskView == nullptr || entry.selectionMaskShaderView == nullptr) { - entry.statusText = entry.statusText.empty() - ? "Scene selection mask target is unavailable" - : entry.statusText; - } else { - Rendering::RenderSurface selectionMaskSurface = BuildSelectionMaskSurface(entry); - commandList->TransitionBarrier( - entry.selectionMaskView, - entry.selectionMaskState, - RHI::ResourceStates::RenderTarget); - entry.selectionMaskState = RHI::ResourceStates::RenderTarget; - - const float maskClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; - RHI::RHIResourceView* maskView = entry.selectionMaskView; - commandList->SetRenderTargets(1, &maskView, entry.depthView); - commandList->ClearRenderTarget(maskView, maskClearColor); - - if (!m_sceneSelectionMaskPass.Render( - renderContext, - selectionMaskSurface, - cameraData, - selectionRenderables) && - entry.statusText.empty()) { - entry.statusText = "Scene selection mask pass failed"; - } - - commandList->TransitionBarrier( - entry.selectionMaskView, - entry.selectionMaskState, - RHI::ResourceStates::PixelShaderResource); - entry.selectionMaskState = RHI::ResourceStates::PixelShaderResource; - } - } - - if (!m_sceneGridPass.Render(renderContext, surface, overlay)) { - entry.statusText = entry.statusText.empty() - ? "Scene grid pass failed" - : entry.statusText; - } - - if (hasSelection && - !m_sceneSelectionOutlinePass.Render( - renderContext, - surface, - entry.selectionMaskShaderView) && - entry.statusText.empty()) { - entry.statusText = "Scene selection outline pass failed"; - } - - commandList->TransitionBarrier( - entry.colorView, - entry.colorState, - RHI::ResourceStates::PixelShaderResource); - entry.colorState = RHI::ResourceStates::PixelShaderResource; + BuildSceneViewPostPassSequence( + entry, + overlay, + cameraData, + selectionRenderables, + sceneViewPostPasses); } + + std::vector requests = + m_sceneRenderer->BuildRenderRequests(*scene, m_sceneViewCamera.camera, renderContext, surface); + if (requests.empty()) { + SetViewportStatusIfEmpty(entry.statusText, "Scene renderer failed"); + ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f); + return; + } + + if (sceneViewPostPasses.GetPassCount() > 0) { + requests[0].postScenePasses = &sceneViewPostPasses; + } + + if (!m_sceneRenderer->Render(requests)) { + SetViewportStatusIfEmpty(entry.statusText, "Scene renderer failed"); + ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f); + return; + } + entry.colorState = RHI::ResourceStates::PixelShaderResource; return; }