#pragma once #include "Core/IEditorContext.h" #include "Core/ISceneManager.h" #include "Core/ISelectionManager.h" #include "IViewportHostService.h" #include "Passes/SceneViewportEditorOverlayPass.h" #include "Passes/SceneViewportGridPass.h" #include "Passes/SceneViewportSelectionOutlinePass.h" #include "SceneViewportCameraController.h" #include "SceneViewportEditorOverlayData.h" #include "SceneViewportOverlayHandleBuilder.h" #include "SceneViewportOverlayBuilder.h" #include "SceneViewportRenderPlan.h" #include "ViewportHostRenderFlowUtils.h" #include "ViewportHostRenderTargets.h" #include "ViewportObjectIdPicker.h" #include "UI/ImGuiBackendBridge.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace XCEngine { namespace Editor { namespace { constexpr bool kDebugSceneSelectionMask = false; constexpr uint64_t kSceneViewportOverlaySignatureOffsetBasis = 14695981039346656037ull; constexpr uint64_t kSceneViewportOverlaySignaturePrime = 1099511628211ull; void HashSceneViewportOverlayBytes(uint64_t& hash, const void* data, size_t size) { const auto* bytes = static_cast(data); for (size_t index = 0; index < size; ++index) { hash ^= static_cast(bytes[index]); hash *= kSceneViewportOverlaySignaturePrime; } } template void HashSceneViewportOverlayValue(uint64_t& hash, const TValue& value) { HashSceneViewportOverlayBytes(hash, &value, sizeof(TValue)); } void HashSceneViewportOverlayFloat(uint64_t& hash, float value) { uint32_t bits = 0u; std::memcpy(&bits, &value, sizeof(bits)); HashSceneViewportOverlayValue(hash, bits); } void HashSceneViewportOverlayVector3(uint64_t& hash, const Math::Vector3& value) { HashSceneViewportOverlayFloat(hash, value.x); HashSceneViewportOverlayFloat(hash, value.y); HashSceneViewportOverlayFloat(hash, value.z); } void HashSceneViewportOverlayQuaternion(uint64_t& hash, const Math::Quaternion& value) { HashSceneViewportOverlayFloat(hash, value.x); HashSceneViewportOverlayFloat(hash, value.y); HashSceneViewportOverlayFloat(hash, value.z); HashSceneViewportOverlayFloat(hash, value.w); } void HashSceneViewportOverlayRect(uint64_t& hash, const Math::Rect& value) { HashSceneViewportOverlayFloat(hash, value.x); HashSceneViewportOverlayFloat(hash, value.y); HashSceneViewportOverlayFloat(hash, value.width); HashSceneViewportOverlayFloat(hash, value.height); } void HashSceneViewportOverlayTransform(uint64_t& hash, const Components::TransformComponent& transform) { HashSceneViewportOverlayVector3(hash, transform.GetPosition()); HashSceneViewportOverlayQuaternion(hash, transform.GetRotation()); HashSceneViewportOverlayVector3(hash, transform.GetScale()); } uint64_t BuildSceneViewEditorOverlayContentSignature( const Components::Scene* scene, const std::vector& selectedObjectIds) { uint64_t hash = kSceneViewportOverlaySignatureOffsetBasis; HashSceneViewportOverlayValue(hash, static_cast(selectedObjectIds.size())); for (uint64_t entityId : selectedObjectIds) { HashSceneViewportOverlayValue(hash, entityId); } if (scene == nullptr) { return hash; } for (Components::CameraComponent* camera : scene->FindObjectsOfType()) { Components::GameObject* gameObject = camera != nullptr ? camera->GetGameObject() : nullptr; HashSceneViewportOverlayValue(hash, static_cast(1u)); HashSceneViewportOverlayValue(hash, gameObject != nullptr ? gameObject->GetID() : 0ull); HashSceneViewportOverlayValue(hash, camera != nullptr && camera->IsEnabled()); HashSceneViewportOverlayValue(hash, gameObject != nullptr && gameObject->IsActiveInHierarchy()); if (camera == nullptr || gameObject == nullptr || !camera->IsEnabled() || !gameObject->IsActiveInHierarchy() || gameObject->GetTransform() == nullptr) { continue; } HashSceneViewportOverlayTransform(hash, *gameObject->GetTransform()); HashSceneViewportOverlayValue(hash, static_cast(camera->GetProjectionType())); HashSceneViewportOverlayFloat(hash, camera->GetFieldOfView()); HashSceneViewportOverlayFloat(hash, camera->GetOrthographicSize()); HashSceneViewportOverlayFloat(hash, camera->GetNearClipPlane()); HashSceneViewportOverlayFloat(hash, camera->GetFarClipPlane()); HashSceneViewportOverlayRect(hash, camera->GetViewportRect()); } for (Components::LightComponent* light : scene->FindObjectsOfType()) { Components::GameObject* gameObject = light != nullptr ? light->GetGameObject() : nullptr; HashSceneViewportOverlayValue(hash, static_cast(2u)); HashSceneViewportOverlayValue(hash, gameObject != nullptr ? gameObject->GetID() : 0ull); HashSceneViewportOverlayValue(hash, light != nullptr && light->IsEnabled()); HashSceneViewportOverlayValue(hash, gameObject != nullptr && gameObject->IsActiveInHierarchy()); if (light == nullptr || gameObject == nullptr || !light->IsEnabled() || !gameObject->IsActiveInHierarchy() || gameObject->GetTransform() == nullptr) { continue; } HashSceneViewportOverlayTransform(hash, *gameObject->GetTransform()); HashSceneViewportOverlayValue(hash, static_cast(light->GetLightType())); } return hash; } bool AreEqualSceneViewportVector3(const Math::Vector3& lhs, const Math::Vector3& rhs) { constexpr float kEpsilon = 1e-4f; return std::abs(lhs.x - rhs.x) <= kEpsilon && std::abs(lhs.y - rhs.y) <= kEpsilon && std::abs(lhs.z - rhs.z) <= kEpsilon; } bool AreEqualSceneViewportOverlayData( const SceneViewportOverlayData& lhs, const SceneViewportOverlayData& rhs) { constexpr float kEpsilon = 1e-4f; return lhs.valid == rhs.valid && AreEqualSceneViewportVector3(lhs.cameraPosition, rhs.cameraPosition) && AreEqualSceneViewportVector3(lhs.cameraForward, rhs.cameraForward) && AreEqualSceneViewportVector3(lhs.cameraRight, rhs.cameraRight) && AreEqualSceneViewportVector3(lhs.cameraUp, rhs.cameraUp) && std::abs(lhs.verticalFovDegrees - rhs.verticalFovDegrees) <= kEpsilon && std::abs(lhs.nearClipPlane - rhs.nearClipPlane) <= kEpsilon && std::abs(lhs.farClipPlane - rhs.farClipPlane) <= kEpsilon && std::abs(lhs.orbitDistance - rhs.orbitDistance) <= kEpsilon; } 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 { public: void Initialize(UI::ImGuiBackendBridge& backend, RHI::RHIDevice* device) { Shutdown(); m_backend = &backend; m_device = device; } void Shutdown() { for (ViewportEntry& entry : m_entries) { DestroyViewportRenderTargets(m_backend, entry.renderTargets); entry = {}; } m_sceneViewportSelectionOutlineRenderer.Shutdown(); m_sceneViewportGridRenderer.Shutdown(); m_sceneViewportEditorOverlayRenderer.Shutdown(); m_sceneViewCamera = {}; ResetSceneViewEditorOverlayFrameData(); m_sceneViewTransformGizmoOverlayState = {}; m_sceneViewTransformGizmoOverlayDirty = false; m_sceneViewLastRenderContext = {}; m_device = nullptr; m_backend = nullptr; m_sceneRenderer.reset(); } void BeginFrame() override { for (ViewportEntry& entry : m_entries) { entry.requestedThisFrame = false; entry.requestedWidth = 0; entry.requestedHeight = 0; } m_sceneViewTransformGizmoOverlayState = {}; m_sceneViewTransformGizmoOverlayDirty = true; } EditorViewportFrame RequestViewport(EditorViewportKind kind, const ImVec2& requestedSize) override { ViewportEntry& entry = GetEntry(kind); entry.requestedThisFrame = requestedSize.x > 1.0f && requestedSize.y > 1.0f; entry.requestedWidth = entry.requestedThisFrame ? static_cast(requestedSize.x) : 0u; entry.requestedHeight = entry.requestedThisFrame ? static_cast(requestedSize.y) : 0u; if (entry.requestedThisFrame && m_backend != nullptr && m_device != nullptr) { EnsureViewportResources(entry); } EditorViewportFrame frame = {}; frame.textureId = entry.renderTargets.textureId; frame.requestedSize = requestedSize; frame.renderSize = ImVec2( static_cast(entry.renderTargets.width), static_cast(entry.renderTargets.height)); frame.hasTexture = entry.renderTargets.textureId != ImTextureID{}; frame.wasRequested = entry.requestedThisFrame; frame.statusText = entry.statusText; return frame; } void UpdateSceneViewInput(IEditorContext& context, const SceneViewportInput& input) override { if (!EnsureSceneViewCamera()) { return; } if (input.focusSelectionRequested) { FocusSceneView(context); } SceneViewportCameraInputState controllerInput = {}; controllerInput.viewportHeight = input.viewportSize.y; controllerInput.zoomDelta = input.hovered ? input.mouseWheel : 0.0f; controllerInput.flySpeedDelta = input.hovered ? input.flySpeedDelta : 0.0f; controllerInput.deltaTime = input.deltaTime; controllerInput.moveForward = input.moveForward; controllerInput.moveRight = input.moveRight; controllerInput.moveUp = input.moveUp; controllerInput.fastMove = input.fastMove; if (input.looking) { controllerInput.lookDeltaX = input.mouseDelta.x; controllerInput.lookDeltaY = input.mouseDelta.y; } if (input.orbiting) { controllerInput.orbitDeltaX = input.mouseDelta.x; controllerInput.orbitDeltaY = input.mouseDelta.y; } if (input.panning) { controllerInput.panDeltaX = input.mouseDelta.x; controllerInput.panDeltaY = input.mouseDelta.y; } m_sceneViewCamera.controller.ApplyInput(controllerInput); ApplySceneViewCameraController(); } uint64_t PickSceneViewEntity( IEditorContext& context, const ImVec2& viewportSize, const ImVec2& viewportMousePosition) override { if (!EnsureSceneViewCamera()) { return 0; } if (context.GetSceneManager().GetScene() == nullptr) { return 0; } ViewportEntry& entry = GetEntry(EditorViewportKind::Scene); const ViewportObjectIdPickResult objectIdPick = PickSceneViewEntityWithObjectId( entry, viewportSize, viewportMousePosition); if (objectIdPick.status == ViewportObjectIdPickStatus::ReadbackFailed) { SetViewportStatusIfEmpty(entry.statusText, "Scene object id readback failed"); } return objectIdPick.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) { return data; } const Components::TransformComponent* transform = m_sceneViewCamera.gameObject->GetTransform(); if (transform == nullptr) { return data; } data.valid = true; data.cameraPosition = transform->GetPosition(); data.cameraForward = transform->GetForward(); data.cameraRight = transform->GetRight(); data.cameraUp = transform->GetUp(); data.verticalFovDegrees = m_sceneViewCamera.camera->GetFieldOfView(); data.nearClipPlane = m_sceneViewCamera.camera->GetNearClipPlane(); data.farClipPlane = m_sceneViewCamera.camera->GetFarClipPlane(); data.orbitDistance = m_sceneViewCamera.controller.GetDistance(); return data; } const SceneViewportOverlayFrameData& GetSceneViewEditorOverlayFrameData(IEditorContext& context) override { EnsureSceneViewEditorOverlayFrameData(context); return m_sceneViewEditorOverlayFrameData; } void SetSceneViewTransformGizmoOverlayState( const SceneViewportTransformGizmoOverlayState& state) override { m_sceneViewTransformGizmoOverlayState = state; m_sceneViewTransformGizmoOverlayDirty = true; } void RenderRequestedViewports( IEditorContext& context, const Rendering::RenderContext& renderContext) override { if (m_backend == nullptr || m_device == nullptr || !renderContext.IsValid()) { return; } EnsureSceneRenderer(); m_sceneViewLastRenderContext = renderContext; const auto* scene = context.GetSceneManager().GetScene(); for (ViewportEntry& entry : m_entries) { if (!entry.requestedThisFrame) { continue; } if (!EnsureViewportResources(entry)) { entry.statusText = "Failed to create viewport render targets"; continue; } RenderViewportEntry(entry, context, scene, renderContext); } } private: struct ViewportEntry { EditorViewportKind kind = EditorViewportKind::Scene; uint32_t requestedWidth = 0; uint32_t requestedHeight = 0; bool requestedThisFrame = false; ViewportRenderTargets renderTargets = {}; std::string statusText; }; struct SceneViewCameraState { std::unique_ptr gameObject; Components::CameraComponent* camera = nullptr; SceneViewportCameraController controller; }; struct SceneViewportRenderState { SceneViewportOverlayData overlay = {}; SceneViewportRenderPlan renderPlan = {}; }; ViewportEntry& GetEntry(EditorViewportKind kind) { const size_t index = kind == EditorViewportKind::Scene ? 0u : 1u; m_entries[index].kind = kind; return m_entries[index]; } void EnsureSceneRenderer() { if (!m_sceneRenderer) { m_sceneRenderer = std::make_unique(); } } bool EnsureSceneViewCamera() { if (m_sceneViewCamera.gameObject != nullptr && m_sceneViewCamera.camera != nullptr) { return true; } m_sceneViewCamera.gameObject = std::make_unique("EditorSceneCamera"); m_sceneViewCamera.camera = m_sceneViewCamera.gameObject->AddComponent(); if (m_sceneViewCamera.camera == nullptr) { m_sceneViewCamera.gameObject.reset(); return false; } m_sceneViewCamera.camera->SetPrimary(false); m_sceneViewCamera.camera->SetProjectionType(Components::CameraProjectionType::Perspective); m_sceneViewCamera.camera->SetFieldOfView(60.0f); m_sceneViewCamera.camera->SetNearClipPlane(0.03f); m_sceneViewCamera.camera->SetFarClipPlane(2000.0f); m_sceneViewCamera.controller.Reset(); ApplySceneViewCameraController(); return true; } void ApplySceneViewCameraController() { if (m_sceneViewCamera.gameObject == nullptr) { return; } m_sceneViewCamera.controller.ApplyTo(*m_sceneViewCamera.gameObject->GetTransform()); } void FocusSceneView(IEditorContext& context) { Components::GameObject* target = nullptr; const uint64_t selectedEntity = context.GetSelectionManager().GetSelectedEntity(); if (selectedEntity != 0) { target = context.GetSceneManager().GetEntity(selectedEntity); } if (target != nullptr) { m_sceneViewCamera.controller.Focus(target->GetTransform()->GetPosition()); return; } const auto& roots = context.GetSceneManager().GetRootEntities(); if (roots.empty()) { m_sceneViewCamera.controller.Focus(Math::Vector3::Zero()); return; } Math::Vector3 center = Math::Vector3::Zero(); uint32_t count = 0; for (const Components::GameObject* root : roots) { if (root == nullptr) { continue; } center += root->GetTransform()->GetPosition(); ++count; } if (count > 0) { center /= static_cast(count); } m_sceneViewCamera.controller.Focus(center); } bool EnsureViewportResources(ViewportEntry& entry) { const ViewportHostResourceReuseQuery reuseQuery = BuildViewportRenderTargetsReuseQuery( entry.kind, entry.renderTargets, entry.requestedWidth, entry.requestedHeight); if (CanReuseViewportResources(reuseQuery)) { return true; } if (entry.requestedWidth == 0 || entry.requestedHeight == 0) { return false; } return CreateViewportRenderTargets( entry.kind, entry.requestedWidth, entry.requestedHeight, m_device, m_backend, entry.renderTargets); } Rendering::RenderSurface BuildSurface(const ViewportEntry& entry) const { return BuildViewportColorSurface(entry.renderTargets); } void ResetSceneViewEditorOverlayFrameData() { m_sceneViewEditorOverlayFrameData = {}; m_sceneViewEditorOverlayScene = nullptr; m_sceneViewEditorOverlaySelectedObjectIds.clear(); m_sceneViewEditorOverlayViewportWidth = 0u; m_sceneViewEditorOverlayViewportHeight = 0u; m_sceneViewEditorOverlayContentSignature = 0u; m_sceneViewEditorOverlayCached = false; } void ResolveSceneViewEditorOverlayViewportSize( const ViewportEntry& entry, uint32_t& outWidth, uint32_t& outHeight) const { outWidth = entry.requestedWidth > 0u ? entry.requestedWidth : entry.renderTargets.width; outHeight = entry.requestedHeight > 0u ? entry.requestedHeight : entry.renderTargets.height; } bool ShouldRebuildSceneViewEditorOverlayFrameData( const Components::Scene* scene, const SceneViewportOverlayData& overlay, uint32_t viewportWidth, uint32_t viewportHeight, const std::vector& selectedObjectIds, uint64_t contentSignature) const { return m_sceneViewTransformGizmoOverlayDirty || !m_sceneViewEditorOverlayCached || m_sceneViewEditorOverlayScene != scene || m_sceneViewEditorOverlayViewportWidth != viewportWidth || m_sceneViewEditorOverlayViewportHeight != viewportHeight || m_sceneViewEditorOverlaySelectedObjectIds != selectedObjectIds || m_sceneViewEditorOverlayContentSignature != contentSignature || !AreEqualSceneViewportOverlayData(m_sceneViewEditorOverlayFrameData.overlay, overlay); } void EnsureSceneViewEditorOverlayFrameData(IEditorContext& context) { if (!EnsureSceneViewCamera()) { ResetSceneViewEditorOverlayFrameData(); return; } const ViewportEntry& entry = GetEntry(EditorViewportKind::Scene); uint32_t viewportWidth = 0u; uint32_t viewportHeight = 0u; ResolveSceneViewEditorOverlayViewportSize(entry, viewportWidth, viewportHeight); const Components::Scene* scene = context.GetSceneManager().GetScene(); const SceneViewportOverlayData overlay = GetSceneViewOverlayData(); const std::vector selectedObjectIds = context.GetSelectionManager().GetSelectedEntities(); const uint64_t contentSignature = BuildSceneViewEditorOverlayContentSignature(scene, selectedObjectIds); if (!ShouldRebuildSceneViewEditorOverlayFrameData( scene, overlay, viewportWidth, viewportHeight, selectedObjectIds, contentSignature)) { return; } m_sceneViewEditorOverlayFrameData = {}; m_sceneViewEditorOverlayFrameData.overlay = overlay; if (scene != nullptr && overlay.valid && viewportWidth > 0u && viewportHeight > 0u) { m_sceneViewEditorOverlayFrameData = m_sceneViewportOverlayBuilder.Build( context, overlay, viewportWidth, viewportHeight, selectedObjectIds, &m_sceneViewTransformGizmoOverlayState); } m_sceneViewEditorOverlayScene = scene; m_sceneViewEditorOverlaySelectedObjectIds = selectedObjectIds; m_sceneViewEditorOverlayViewportWidth = viewportWidth; m_sceneViewEditorOverlayViewportHeight = viewportHeight; m_sceneViewEditorOverlayContentSignature = contentSignature; m_sceneViewEditorOverlayCached = true; m_sceneViewTransformGizmoOverlayDirty = false; } void ApplyViewportRenderFailure( ViewportEntry& entry, const Rendering::RenderContext& renderContext, const ViewportRenderFallbackPolicy& policy) { ApplyViewportFailureStatus(entry.statusText, policy); if (policy.invalidateObjectIdFrame) { InvalidateViewportObjectIdFrame(entry.renderTargets); } if (!policy.shouldClear) { return; } ClearViewport( entry, renderContext, policy.clearColor.r, policy.clearColor.g, policy.clearColor.b, policy.clearColor.a); } void BuildSceneViewportRenderState( ViewportEntry& entry, IEditorContext& context, const Components::Scene& scene, SceneViewportRenderState& outState) { (void)scene; outState.overlay = GetSceneViewOverlayData(); if (!outState.overlay.valid) { return; } const std::vector selectedObjectIds = context.GetSelectionManager().GetSelectedEntities(); const SceneViewportOverlayFrameData& editorOverlayFrameData = GetSceneViewEditorOverlayFrameData(context); SceneViewportRenderPlanBuildResult renderPlan = BuildSceneViewportRenderPlan( entry.renderTargets, outState.overlay, selectedObjectIds, editorOverlayFrameData, [this](const Rendering::Passes::InfiniteGridPassData& data) { return CreateSceneViewportGridPass( m_sceneViewportGridRenderer, data); }, [this]( RHI::RHIResourceView* objectIdTextureView, const std::vector& selectedObjectIds, const Rendering::Passes::ObjectIdOutlineStyle& style) { return CreateSceneViewportSelectionOutlinePass( m_sceneViewportSelectionOutlineRenderer, objectIdTextureView, selectedObjectIds, style); }, [this](const SceneViewportOverlayFrameData& frameData) { return CreateSceneViewportEditorOverlayPass( m_sceneViewportEditorOverlayRenderer, frameData); }, kDebugSceneSelectionMask); outState.renderPlan = std::move(renderPlan.plan); if (renderPlan.warningStatusText != nullptr) { SetViewportStatusIfEmpty(entry.statusText, renderPlan.warningStatusText); } } bool RenderSceneViewportEntry( ViewportEntry& entry, IEditorContext& context, const Components::Scene* scene, const Rendering::RenderContext& renderContext, Rendering::RenderSurface& surface) { if (!EnsureSceneViewCamera()) { ApplyViewportRenderFailure( entry, renderContext, BuildSceneViewportRenderFailurePolicy( SceneViewportRenderFailure::MissingSceneViewCamera)); return false; } ApplySceneViewCameraController(); entry.statusText.clear(); if (scene == nullptr) { ApplyViewportRenderFailure( entry, renderContext, BuildSceneViewportRenderFailurePolicy( SceneViewportRenderFailure::NoActiveScene)); return false; } SceneViewportRenderState sceneState = {}; BuildSceneViewportRenderState(entry, context, *scene, sceneState); std::vector requests = m_sceneRenderer->BuildRenderRequests(*scene, m_sceneViewCamera.camera, renderContext, surface); if (requests.empty()) { ApplyViewportRenderFailure( entry, renderContext, BuildSceneViewportRenderFailurePolicy( SceneViewportRenderFailure::SceneRendererFailed)); return false; } ApplySceneViewportRenderPlan(entry.renderTargets, sceneState.renderPlan, requests[0]); if (!m_sceneRenderer->Render(requests)) { ApplyViewportRenderFailure( entry, renderContext, BuildSceneViewportRenderFailurePolicy( SceneViewportRenderFailure::SceneRendererFailed)); return false; } MarkSceneViewportRenderSuccess(entry.renderTargets, requests[0]); const Core::uint32 pendingAsyncLoads = Resources::ResourceManager::Get().GetAsyncPendingCount(); if (pendingAsyncLoads > 0) { entry.statusText = "Loading scene assets... (" + std::to_string(pendingAsyncLoads) + ")"; } return true; } bool RenderGameViewportEntry( ViewportEntry& entry, const Components::Scene* scene, const Rendering::RenderContext& renderContext, const Rendering::RenderSurface& surface) { if (scene == nullptr) { ApplyViewportRenderFailure( entry, renderContext, BuildGameViewportRenderFailurePolicy( GameViewportRenderFailure::NoActiveScene)); return false; } const auto cameras = scene->FindObjectsOfType(); if (cameras.empty()) { ApplyViewportRenderFailure( entry, renderContext, BuildGameViewportRenderFailurePolicy( GameViewportRenderFailure::NoCameraInScene)); return false; } if (!m_sceneRenderer->Render(*scene, nullptr, renderContext, surface)) { ApplyViewportRenderFailure( entry, renderContext, BuildGameViewportRenderFailurePolicy( GameViewportRenderFailure::SceneRendererFailed)); return false; } MarkGameViewportRenderSuccess(entry.renderTargets); entry.statusText.clear(); return true; } void RenderViewportEntry( ViewportEntry& entry, IEditorContext& context, const Components::Scene* scene, const Rendering::RenderContext& renderContext) { if (entry.renderTargets.colorView == nullptr || entry.renderTargets.depthView == nullptr) { ApplyViewportFailureStatus( entry.statusText, BuildViewportRenderTargetUnavailablePolicy()); InvalidateViewportObjectIdFrame(entry.renderTargets); return; } Rendering::RenderSurface surface = BuildSurface(entry); if (entry.kind == EditorViewportKind::Scene) { RenderSceneViewportEntry(entry, context, scene, renderContext, surface); return; } RenderGameViewportEntry(entry, scene, renderContext, surface); } void ClearViewport( ViewportEntry& entry, const Rendering::RenderContext& renderContext, float r, float g, float b, float a) { RHI::RHICommandList* commandList = renderContext.commandList; if (commandList == nullptr) { entry.statusText = "Viewport command list is unavailable"; return; } ViewportRenderTargets& targets = entry.renderTargets; const float clearColor[4] = { r, g, b, a }; RHI::RHIResourceView* colorView = targets.colorView; commandList->TransitionBarrier( colorView, targets.colorState, RHI::ResourceStates::RenderTarget); commandList->SetRenderTargets(1, &colorView, targets.depthView); commandList->ClearRenderTarget(colorView, clearColor); commandList->ClearDepthStencil(targets.depthView, 1.0f, 0); commandList->TransitionBarrier( colorView, RHI::ResourceStates::RenderTarget, RHI::ResourceStates::PixelShaderResource); targets.colorState = RHI::ResourceStates::PixelShaderResource; targets.hasValidObjectIdFrame = false; } ViewportObjectIdPickResult PickSceneViewEntityWithObjectId( ViewportEntry& entry, const ImVec2& viewportSize, const ImVec2& viewportMousePosition) { if (m_device == nullptr) { return {}; } ViewportObjectIdPickContext pickContext = {}; pickContext.commandQueue = m_sceneViewLastRenderContext.commandQueue; pickContext.texture = entry.renderTargets.objectIdTexture; pickContext.textureState = entry.renderTargets.objectIdState; pickContext.textureWidth = entry.renderTargets.width; pickContext.textureHeight = entry.renderTargets.height; pickContext.hasValidFrame = entry.renderTargets.hasValidObjectIdFrame; pickContext.viewportSize = viewportSize; pickContext.viewportMousePosition = viewportMousePosition; return PickViewportObjectIdEntity( pickContext, [this](const ViewportObjectIdReadbackRequest& request, std::array& outRgba) { return m_device != nullptr && m_device->ReadTexturePixelRGBA8( request.commandQueue, request.texture, request.textureState, request.pixelX, request.pixelY, outRgba); }); } UI::ImGuiBackendBridge* m_backend = nullptr; RHI::RHIDevice* m_device = nullptr; std::unique_ptr m_sceneRenderer; SceneViewportOverlayBuilder m_sceneViewportOverlayBuilder = {}; Rendering::RenderContext m_sceneViewLastRenderContext = {}; std::array m_entries = {}; SceneViewCameraState m_sceneViewCamera; SceneViewportOverlayFrameData m_sceneViewEditorOverlayFrameData = {}; SceneViewportTransformGizmoOverlayState m_sceneViewTransformGizmoOverlayState = {}; const Components::Scene* m_sceneViewEditorOverlayScene = nullptr; std::vector m_sceneViewEditorOverlaySelectedObjectIds = {}; uint32_t m_sceneViewEditorOverlayViewportWidth = 0u; uint32_t m_sceneViewEditorOverlayViewportHeight = 0u; uint64_t m_sceneViewEditorOverlayContentSignature = 0u; bool m_sceneViewEditorOverlayCached = false; bool m_sceneViewTransformGizmoOverlayDirty = false; SceneViewportGridPassRenderer m_sceneViewportGridRenderer; SceneViewportSelectionOutlinePassRenderer m_sceneViewportSelectionOutlineRenderer; SceneViewportEditorOverlayPassRenderer m_sceneViewportEditorOverlayRenderer; }; } // namespace Editor } // namespace XCEngine