#include "Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace XCEngine::UI::Editor::App { namespace { using ::XCEngine::Components::CameraComponent; using ::XCEngine::Components::GameObject; using ::XCEngine::Components::LightComponent; using ::XCEngine::Math::RectInt; using ::XCEngine::RHI::RHIResourceView; using ::XCEngine::Rendering::RenderPass; using ::XCEngine::Rendering::RenderPassContext; constexpr std::uint64_t kMinDynamicVertexBufferBytes = 4096u; constexpr ::XCEngine::Math::Color kSelectedCameraHelperColor(1.0f, 1.0f, 1.0f, 1.0f); constexpr ::XCEngine::Math::Color kSelectedLightHelperColor(1.0f, 0.92f, 0.24f, 1.0f); constexpr std::size_t kSelectedLightHelperSegmentCount = 32u; const char kSelectedHelpersLineHlsl[] = R"( cbuffer OverlayConstants : register(b0) { float4x4 gViewProjectionMatrix; }; struct VSInput { float3 position : POSITION; float4 color : COLOR0; }; struct VSOutput { float4 position : SV_POSITION; float4 color : COLOR0; }; VSOutput MainVS(VSInput input) { VSOutput output; output.position = mul(gViewProjectionMatrix, float4(input.position, 1.0)); output.color = input.color; return output; } float4 MainPS(VSOutput input) : SV_TARGET0 { return input.color; } )"; struct SelectedHelpersCameraData { bool valid = false; ::XCEngine::Math::Matrix4x4 viewProjection = ::XCEngine::Math::Matrix4x4::Identity(); ::XCEngine::Math::Vector3 cameraPosition = ::XCEngine::Math::Vector3::Zero(); ::XCEngine::Math::Vector3 cameraForward = ::XCEngine::Math::Vector3::Forward(); float verticalFovRadians = 60.0f * ::XCEngine::Math::DEG_TO_RAD; float nearClipPlane = 0.03f; float farClipPlane = 2000.0f; }; struct HelperLine { ::XCEngine::Math::Vector3 startWorld = ::XCEngine::Math::Vector3::Zero(); ::XCEngine::Math::Vector3 endWorld = ::XCEngine::Math::Vector3::Zero(); ::XCEngine::Math::Color color = ::XCEngine::Math::Color::White(); }; struct OverlayConstants { ::XCEngine::Math::Matrix4x4 viewProjection = ::XCEngine::Math::Matrix4x4::Identity(); }; struct OverlayLineVertex { ::XCEngine::Math::Vector3 position = ::XCEngine::Math::Vector3::Zero(); ::XCEngine::Math::Color color = ::XCEngine::Math::Color::White(); }; bool CanBuildHelpersForGameObject( const ::XCEngine::Components::GameObject* gameObject) { return gameObject != nullptr && gameObject->GetTransform() != nullptr && gameObject->IsActiveInHierarchy(); } SelectedHelpersCameraData BuildCameraData( const ::XCEngine::Rendering::RenderSceneData& sceneData) { SelectedHelpersCameraData data = {}; if (!sceneData.HasCamera()) { return data; } data.valid = true; data.viewProjection = sceneData.cameraData.viewProjection; data.cameraPosition = sceneData.cameraData.worldPosition; data.cameraForward = sceneData.cameraData.worldForward; data.verticalFovRadians = sceneData.cameraData.verticalFovRadians; data.nearClipPlane = sceneData.cameraData.nearClipPlane; data.farClipPlane = sceneData.cameraData.farClipPlane; return data; } float ResolveCameraAspect( const ::XCEngine::Components::CameraComponent& camera, float viewportWidth, float viewportHeight) { const ::XCEngine::Math::Rect viewportRect = camera.GetViewportRect(); const float resolvedWidth = viewportWidth * (viewportRect.width > ::XCEngine::Math::EPSILON ? viewportRect.width : 1.0f); const float resolvedHeight = viewportHeight * (viewportRect.height > ::XCEngine::Math::EPSILON ? viewportRect.height : 1.0f); return resolvedHeight > ::XCEngine::Math::EPSILON ? resolvedWidth / resolvedHeight : 1.0f; } float ComputeWorldUnitsPerPixel( const SelectedHelpersCameraData& overlay, const ::XCEngine::Math::Vector3& worldPoint, float viewportHeight) { if (!overlay.valid || viewportHeight <= 1.0f) { return 0.0f; } const ::XCEngine::Math::Vector3 cameraForward = overlay.cameraForward.Normalized(); const float depth = ::XCEngine::Math::Vector3::Dot( worldPoint - overlay.cameraPosition, cameraForward); if (depth <= ::XCEngine::Math::EPSILON) { return 0.0f; } return 2.0f * depth * std::tan(overlay.verticalFovRadians * 0.5f) / viewportHeight; } void AppendWorldLine( std::vector& outLines, const ::XCEngine::Math::Vector3& startWorld, const ::XCEngine::Math::Vector3& endWorld, const ::XCEngine::Math::Color& color) { HelperLine line = {}; line.startWorld = startWorld; line.endWorld = endWorld; line.color = color; outLines.push_back(std::move(line)); } void AppendWireCircle( std::vector& outLines, const ::XCEngine::Math::Vector3& center, const ::XCEngine::Math::Vector3& basisA, const ::XCEngine::Math::Vector3& basisB, float radius, const ::XCEngine::Math::Color& color) { const ::XCEngine::Math::Vector3 axisA = basisA.Normalized(); const ::XCEngine::Math::Vector3 axisB = basisB.Normalized(); if (radius <= ::XCEngine::Math::EPSILON || axisA.SqrMagnitude() <= ::XCEngine::Math::EPSILON || axisB.SqrMagnitude() <= ::XCEngine::Math::EPSILON) { return; } for (std::size_t segmentIndex = 0; segmentIndex < kSelectedLightHelperSegmentCount; ++segmentIndex) { const float angle0 = static_cast(segmentIndex) / static_cast(kSelectedLightHelperSegmentCount) * ::XCEngine::Math::PI * 2.0f; const float angle1 = static_cast(segmentIndex + 1u) / static_cast(kSelectedLightHelperSegmentCount) * ::XCEngine::Math::PI * 2.0f; const ::XCEngine::Math::Vector3 p0 = center + axisA * std::cos(angle0) * radius + axisB * std::sin(angle0) * radius; const ::XCEngine::Math::Vector3 p1 = center + axisA * std::cos(angle1) * radius + axisB * std::sin(angle1) * radius; AppendWorldLine(outLines, p0, p1, color); } } void AppendSelectedCameraHelper( std::vector& outLines, const ::XCEngine::Components::CameraComponent& camera, const ::XCEngine::Components::GameObject& gameObject, float viewportWidth, float viewportHeight) { const ::XCEngine::Components::TransformComponent* transform = gameObject.GetTransform(); if (transform == nullptr) { return; } const ::XCEngine::Math::Vector3 position = transform->GetPosition(); const ::XCEngine::Math::Vector3 forward = transform->GetForward().Normalized(); const ::XCEngine::Math::Vector3 right = transform->GetRight().Normalized(); const ::XCEngine::Math::Vector3 up = transform->GetUp().Normalized(); if (forward.SqrMagnitude() <= ::XCEngine::Math::EPSILON || right.SqrMagnitude() <= ::XCEngine::Math::EPSILON || up.SqrMagnitude() <= ::XCEngine::Math::EPSILON) { return; } const float nearClip = (std::max)(camera.GetNearClipPlane(), 0.01f); const float farClip = (std::max)(camera.GetFarClipPlane(), nearClip + 0.01f); const float aspect = ResolveCameraAspect(camera, viewportWidth, viewportHeight); float nearHalfHeight = 0.0f; float nearHalfWidth = 0.0f; float farHalfHeight = 0.0f; float farHalfWidth = 0.0f; if (camera.GetProjectionType() == ::XCEngine::Components::CameraProjectionType::Perspective) { const float halfFovRadians = std::clamp(camera.GetFieldOfView(), 1.0f, 179.0f) * ::XCEngine::Math::DEG_TO_RAD * 0.5f; nearHalfHeight = std::tan(halfFovRadians) * nearClip; nearHalfWidth = nearHalfHeight * aspect; farHalfHeight = std::tan(halfFovRadians) * farClip; farHalfWidth = farHalfHeight * aspect; } else { const float halfHeight = (std::max)(camera.GetOrthographicSize(), 0.01f); const float halfWidth = halfHeight * aspect; nearHalfHeight = halfHeight; nearHalfWidth = halfWidth; farHalfHeight = halfHeight; farHalfWidth = halfWidth; } const ::XCEngine::Math::Vector3 nearCenter = position + forward * nearClip; const ::XCEngine::Math::Vector3 farCenter = position + forward * farClip; const std::array<::XCEngine::Math::Vector3, 8> corners = {{ nearCenter + up * nearHalfHeight - right * nearHalfWidth, nearCenter + up * nearHalfHeight + right * nearHalfWidth, nearCenter - up * nearHalfHeight + right * nearHalfWidth, nearCenter - up * nearHalfHeight - right * nearHalfWidth, farCenter + up * farHalfHeight - right * farHalfWidth, farCenter + up * farHalfHeight + right * farHalfWidth, farCenter - up * farHalfHeight + right * farHalfWidth, farCenter - up * farHalfHeight - right * farHalfWidth }}; static constexpr std::array, 12> kFrustumEdges = {{ { 0u, 1u }, { 1u, 2u }, { 2u, 3u }, { 3u, 0u }, { 4u, 5u }, { 5u, 6u }, { 6u, 7u }, { 7u, 4u }, { 0u, 4u }, { 1u, 5u }, { 2u, 6u }, { 3u, 7u } }}; for (const auto& edge : kFrustumEdges) { AppendWorldLine( outLines, corners[edge.first], corners[edge.second], kSelectedCameraHelperColor); } } void AppendSelectedDirectionalLightHelper( std::vector& outLines, const SelectedHelpersCameraData& overlay, const ::XCEngine::Components::GameObject& gameObject, float viewportHeight) { const ::XCEngine::Components::TransformComponent* transform = gameObject.GetTransform(); if (transform == nullptr) { return; } const ::XCEngine::Math::Vector3 position = transform->GetPosition(); const ::XCEngine::Math::Vector3 forward = transform->GetForward().Normalized(); const ::XCEngine::Math::Vector3 right = transform->GetRight().Normalized(); const ::XCEngine::Math::Vector3 up = transform->GetUp().Normalized(); if (forward.SqrMagnitude() <= ::XCEngine::Math::EPSILON || right.SqrMagnitude() <= ::XCEngine::Math::EPSILON || up.SqrMagnitude() <= ::XCEngine::Math::EPSILON) { return; } const float worldUnitsPerPixel = ComputeWorldUnitsPerPixel(overlay, position, viewportHeight); if (worldUnitsPerPixel <= ::XCEngine::Math::EPSILON) { return; } constexpr std::array kRayAngles = {{ 0.0f, ::XCEngine::Math::PI / 3.0f, ::XCEngine::Math::PI * 2.0f / 3.0f, ::XCEngine::Math::PI, ::XCEngine::Math::PI * 4.0f / 3.0f, ::XCEngine::Math::PI * 5.0f / 3.0f }}; const float ringRadius = worldUnitsPerPixel * 26.0f; const float ringOffset = worldUnitsPerPixel * 54.0f; const float innerRayRadius = ringRadius * 0.52f; const float rayLength = worldUnitsPerPixel * 96.0f; const ::XCEngine::Math::Vector3 ringCenter = position + forward * ringOffset; AppendWireCircle( outLines, ringCenter, right, up, ringRadius, kSelectedLightHelperColor); AppendWorldLine(outLines, position, ringCenter, kSelectedLightHelperColor); AppendWorldLine( outLines, ringCenter, ringCenter + forward * rayLength, kSelectedLightHelperColor); for (float angle : kRayAngles) { const ::XCEngine::Math::Vector3 rayStart = ringCenter + right * std::cos(angle) * innerRayRadius + up * std::sin(angle) * innerRayRadius; AppendWorldLine( outLines, rayStart, rayStart + forward * rayLength, kSelectedLightHelperColor); } } void AppendSelectedPointLightHelper( std::vector& outLines, const ::XCEngine::Components::GameObject& gameObject, const ::XCEngine::Components::LightComponent& light) { const ::XCEngine::Components::TransformComponent* transform = gameObject.GetTransform(); if (transform == nullptr) { return; } const ::XCEngine::Math::Vector3 position = transform->GetPosition(); const ::XCEngine::Math::Vector3 right = transform->GetRight().Normalized(); const ::XCEngine::Math::Vector3 up = transform->GetUp().Normalized(); const ::XCEngine::Math::Vector3 forward = transform->GetForward().Normalized(); if (right.SqrMagnitude() <= ::XCEngine::Math::EPSILON || up.SqrMagnitude() <= ::XCEngine::Math::EPSILON || forward.SqrMagnitude() <= ::XCEngine::Math::EPSILON) { return; } const float range = (std::max)(light.GetRange(), 0.001f); AppendWireCircle( outLines, position, right, up, range, kSelectedLightHelperColor); AppendWireCircle( outLines, position, right, forward, range, kSelectedLightHelperColor); AppendWireCircle( outLines, position, up, forward, range, kSelectedLightHelperColor); } void AppendSelectedSpotLightHelper( std::vector& outLines, const ::XCEngine::Components::GameObject& gameObject, const ::XCEngine::Components::LightComponent& light) { const ::XCEngine::Components::TransformComponent* transform = gameObject.GetTransform(); if (transform == nullptr) { return; } const ::XCEngine::Math::Vector3 position = transform->GetPosition(); const ::XCEngine::Math::Vector3 forward = transform->GetForward().Normalized(); const ::XCEngine::Math::Vector3 right = transform->GetRight().Normalized(); const ::XCEngine::Math::Vector3 up = transform->GetUp().Normalized(); if (forward.SqrMagnitude() <= ::XCEngine::Math::EPSILON || right.SqrMagnitude() <= ::XCEngine::Math::EPSILON || up.SqrMagnitude() <= ::XCEngine::Math::EPSILON) { return; } const float range = (std::max)(light.GetRange(), 0.001f); const float halfAngleRadians = std::clamp(light.GetSpotAngle(), 1.0f, 179.0f) * ::XCEngine::Math::DEG_TO_RAD * 0.5f; const float coneRadius = std::tan(halfAngleRadians) * range; const ::XCEngine::Math::Vector3 coneBaseCenter = position + forward * range; AppendWorldLine( outLines, position, coneBaseCenter, kSelectedLightHelperColor); AppendWireCircle( outLines, coneBaseCenter, right, up, coneRadius, kSelectedLightHelperColor); static constexpr std::array kConeEdgeAngles = {{ 0.0f, ::XCEngine::Math::PI * 0.5f, ::XCEngine::Math::PI, ::XCEngine::Math::PI * 1.5f }}; for (float angle : kConeEdgeAngles) { const ::XCEngine::Math::Vector3 coneEdgePoint = coneBaseCenter + right * std::cos(angle) * coneRadius + up * std::sin(angle) * coneRadius; AppendWorldLine( outLines, position, coneEdgePoint, kSelectedLightHelperColor); } } std::vector BuildHelperLines( const SceneViewportRenderRequest& request, const SelectedHelpersCameraData& overlay, float viewportWidth, float viewportHeight) { std::vector lines = {}; if (!request.IsValid() || request.scene == nullptr || request.selectedObjectIds.empty()) { return lines; } if (!overlay.valid) { return lines; } for (const std::uint64_t entityId : request.selectedObjectIds) { if (entityId == 0) { continue; } GameObject* gameObject = request.scene->FindByID(entityId); if (!CanBuildHelpersForGameObject(gameObject)) { continue; } if (CameraComponent* camera = gameObject->GetComponent(); camera != nullptr && camera->IsEnabled()) { AppendSelectedCameraHelper( lines, *camera, *gameObject, viewportWidth, viewportHeight); } if (LightComponent* light = gameObject->GetComponent(); light != nullptr && light->IsEnabled()) { switch (light->GetLightType()) { case ::XCEngine::Components::LightType::Directional: AppendSelectedDirectionalLightHelper( lines, overlay, *gameObject, viewportHeight); break; case ::XCEngine::Components::LightType::Point: AppendSelectedPointLightHelper(lines, *gameObject, *light); break; case ::XCEngine::Components::LightType::Spot: AppendSelectedSpotLightHelper(lines, *gameObject, *light); break; default: break; } } } return lines; } ::XCEngine::RHI::GraphicsPipelineDesc BuildLinePipelineDesc( ::XCEngine::RHI::RHIPipelineLayout* pipelineLayout, const ::XCEngine::Rendering::RenderSurface& surface) { ::XCEngine::RHI::GraphicsPipelineDesc pipelineDesc = {}; pipelineDesc.pipelineLayout = pipelineLayout; pipelineDesc.topologyType = static_cast(::XCEngine::RHI::PrimitiveTopologyType::Line); ::XCEngine::Rendering::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc( surface, pipelineDesc); pipelineDesc.depthStencilFormat = static_cast(::XCEngine::Rendering::ResolveSurfaceDepthFormat(surface)); pipelineDesc.inputLayout.elements = { { "POSITION", 0, static_cast(::XCEngine::RHI::Format::R32G32B32_Float), 0, 0, 0, 0 }, { "COLOR", 0, static_cast(::XCEngine::RHI::Format::R32G32B32A32_Float), 0, 12, 0, 0 } }; pipelineDesc.rasterizerState.fillMode = static_cast(::XCEngine::RHI::FillMode::Solid); pipelineDesc.rasterizerState.cullMode = static_cast(::XCEngine::RHI::CullMode::None); pipelineDesc.rasterizerState.frontFace = static_cast(::XCEngine::RHI::FrontFace::CounterClockwise); pipelineDesc.rasterizerState.depthClipEnable = true; pipelineDesc.blendState.blendEnable = true; pipelineDesc.blendState.srcBlend = static_cast(::XCEngine::RHI::BlendFactor::SrcAlpha); pipelineDesc.blendState.dstBlend = static_cast(::XCEngine::RHI::BlendFactor::InvSrcAlpha); pipelineDesc.blendState.srcBlendAlpha = static_cast(::XCEngine::RHI::BlendFactor::One); pipelineDesc.blendState.dstBlendAlpha = static_cast(::XCEngine::RHI::BlendFactor::InvSrcAlpha); pipelineDesc.blendState.blendOp = static_cast(::XCEngine::RHI::BlendOp::Add); pipelineDesc.blendState.blendOpAlpha = static_cast(::XCEngine::RHI::BlendOp::Add); pipelineDesc.blendState.colorWriteMask = static_cast(::XCEngine::RHI::ColorWriteMask::All); pipelineDesc.depthStencilState.depthTestEnable = false; pipelineDesc.depthStencilState.depthWriteEnable = false; pipelineDesc.depthStencilState.depthFunc = static_cast(::XCEngine::RHI::ComparisonFunc::LessEqual); pipelineDesc.vertexShader.source.assign( kSelectedHelpersLineHlsl, kSelectedHelpersLineHlsl + std::strlen(kSelectedHelpersLineHlsl)); pipelineDesc.vertexShader.sourceLanguage = ::XCEngine::RHI::ShaderLanguage::HLSL; pipelineDesc.vertexShader.entryPoint = L"MainVS"; pipelineDesc.vertexShader.profile = L"vs_5_0"; pipelineDesc.fragmentShader.source.assign( kSelectedHelpersLineHlsl, kSelectedHelpersLineHlsl + std::strlen(kSelectedHelpersLineHlsl)); pipelineDesc.fragmentShader.sourceLanguage = ::XCEngine::RHI::ShaderLanguage::HLSL; pipelineDesc.fragmentShader.entryPoint = L"MainPS"; pipelineDesc.fragmentShader.profile = L"ps_5_0"; return pipelineDesc; } class SceneViewportSelectedHelpersPass final : public RenderPass { public: SceneViewportSelectedHelpersPass( SceneViewportSelectedHelpersPassRenderer& renderer, SceneViewportRenderRequest request) : m_renderer(renderer) , m_request(std::move(request)) { } const char* GetName() const override { return "SceneViewportSelectedHelpers"; } bool Execute(const RenderPassContext& context) override { return m_renderer.Render(context, m_request); } private: SceneViewportSelectedHelpersPassRenderer& m_renderer; SceneViewportRenderRequest m_request = {}; }; } // namespace class SceneViewportSelectedHelpersPassRenderer::Impl { public: void Shutdown() { DestroyResources(); } bool Render( const ::XCEngine::Rendering::RenderPassContext& context, const SceneViewportRenderRequest& request) { const ::XCEngine::Rendering::RenderContext& renderContext = context.renderContext; const ::XCEngine::Rendering::RenderSurface& surface = context.surface; if (!request.IsValid() || !renderContext.IsValid() || renderContext.commandList == nullptr) { return true; } const std::vector& colorAttachments = surface.GetColorAttachments(); if (!::XCEngine::Rendering::HasSingleColorAttachment(surface) || colorAttachments.empty() || colorAttachments[0] == nullptr || surface.GetDepthAttachment() == nullptr) { return false; } const RectInt renderArea = surface.GetRenderArea(); if (renderArea.width <= 0 || renderArea.height <= 0) { return false; } const SelectedHelpersCameraData cameraData = BuildCameraData(context.sceneData); const std::vector lines = BuildHelperLines( request, cameraData, static_cast(renderArea.width), static_cast(renderArea.height)); if (lines.empty()) { return true; } if (!EnsureInitialized(renderContext, surface) || !EnsureLineBufferCapacity(lines.size() * 2u)) { return false; } std::vector vertices = {}; vertices.reserve(lines.size() * 2u); for (const HelperLine& line : lines) { vertices.push_back({ line.startWorld, line.color }); vertices.push_back({ line.endWorld, line.color }); } m_lineVertexBuffer->SetData( vertices.data(), vertices.size() * sizeof(OverlayLineVertex)); OverlayConstants constants = {}; constants.viewProjection = cameraData.viewProjection; m_constantSet->WriteConstant(0, &constants, sizeof(constants)); ::XCEngine::RHI::RHICommandList* commandList = renderContext.commandList; ::XCEngine::RHI::RHIResourceView* renderTarget = colorAttachments[0]; ::XCEngine::RHI::RHIResourceView* depthAttachment = surface.GetDepthAttachment(); if (surface.IsAutoTransitionEnabled()) { commandList->TransitionBarrier( renderTarget, surface.GetColorStateAfter(), ::XCEngine::RHI::ResourceStates::RenderTarget); commandList->TransitionBarrier( depthAttachment, surface.GetDepthStateAfter(), ::XCEngine::RHI::ResourceStates::DepthWrite); } commandList->SetRenderTargets(1, &renderTarget, depthAttachment); const ::XCEngine::RHI::Viewport viewport = { static_cast(renderArea.x), static_cast(renderArea.y), static_cast(renderArea.width), static_cast(renderArea.height), 0.0f, 1.0f }; const ::XCEngine::RHI::Rect scissorRect = { renderArea.x, renderArea.y, renderArea.x + renderArea.width, renderArea.y + renderArea.height }; commandList->SetViewport(viewport); commandList->SetScissorRect(scissorRect); commandList->SetPrimitiveTopology( ::XCEngine::RHI::PrimitiveTopology::LineList); ::XCEngine::RHI::RHIResourceView* vertexBuffers[] = { m_lineVertexBufferView }; const std::uint64_t offsets[] = { 0u }; const std::uint32_t strides[] = { static_cast(sizeof(OverlayLineVertex)) }; commandList->SetVertexBuffers(0, 1, vertexBuffers, offsets, strides); commandList->SetPipelineState(m_pipelineState); ::XCEngine::RHI::RHIDescriptorSet* descriptorSets[] = { m_constantSet }; commandList->SetGraphicsDescriptorSets( 0, 1, descriptorSets, m_pipelineLayout); commandList->Draw(static_cast(vertices.size()), 1, 0, 0); commandList->EndRenderPass(); if (surface.IsAutoTransitionEnabled()) { commandList->TransitionBarrier( renderTarget, ::XCEngine::RHI::ResourceStates::RenderTarget, surface.GetColorStateAfter()); commandList->TransitionBarrier( depthAttachment, ::XCEngine::RHI::ResourceStates::DepthWrite, surface.GetDepthStateAfter()); } return true; } private: bool EnsureInitialized( const ::XCEngine::Rendering::RenderContext& renderContext, const ::XCEngine::Rendering::RenderSurface& surface) { const ::XCEngine::RHI::Format renderTargetFormat = ::XCEngine::Rendering::ResolveSurfaceColorFormat(surface, 0u); const ::XCEngine::RHI::Format depthStencilFormat = ::XCEngine::Rendering::ResolveSurfaceDepthFormat(surface); const std::uint32_t renderTargetSampleCount = ::XCEngine::Rendering::ResolveSurfaceSampleCount(surface); if (m_device == renderContext.device && m_backendType == renderContext.backendType && m_pipelineLayout != nullptr && m_pipelineState != nullptr && m_constantPool != nullptr && m_constantSet != nullptr && m_renderTargetFormat == renderTargetFormat && m_depthStencilFormat == depthStencilFormat && m_renderTargetSampleCount == renderTargetSampleCount) { return true; } DestroyResources(); return CreateResources(renderContext, surface); } bool CreateResources( const ::XCEngine::Rendering::RenderContext& renderContext, const ::XCEngine::Rendering::RenderSurface& surface) { if (!renderContext.IsValid()) { return false; } if (!::XCEngine::Rendering::HasSingleColorAttachment(surface) || ::XCEngine::Rendering::ResolveSurfaceColorFormat(surface, 0u) == ::XCEngine::RHI::Format::Unknown || ::XCEngine::Rendering::ResolveSurfaceDepthFormat(surface) == ::XCEngine::RHI::Format::Unknown) { return false; } m_device = renderContext.device; m_backendType = renderContext.backendType; m_renderTargetFormat = ::XCEngine::Rendering::ResolveSurfaceColorFormat(surface, 0u); m_depthStencilFormat = ::XCEngine::Rendering::ResolveSurfaceDepthFormat(surface); m_renderTargetSampleCount = ::XCEngine::Rendering::ResolveSurfaceSampleCount(surface); ::XCEngine::RHI::DescriptorSetLayoutBinding constantBinding = {}; constantBinding.binding = 0; constantBinding.type = static_cast(::XCEngine::RHI::DescriptorType::CBV); constantBinding.count = 1; ::XCEngine::RHI::DescriptorSetLayoutDesc constantLayout = {}; constantLayout.bindings = &constantBinding; constantLayout.bindingCount = 1; ::XCEngine::RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; pipelineLayoutDesc.setLayouts = &constantLayout; pipelineLayoutDesc.setLayoutCount = 1; m_pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); if (m_pipelineLayout == nullptr) { DestroyResources(); return false; } ::XCEngine::RHI::DescriptorPoolDesc constantPoolDesc = {}; constantPoolDesc.type = ::XCEngine::RHI::DescriptorHeapType::CBV_SRV_UAV; constantPoolDesc.descriptorCount = 1; constantPoolDesc.shaderVisible = false; m_constantPool = m_device->CreateDescriptorPool(constantPoolDesc); if (m_constantPool == nullptr) { DestroyResources(); return false; } m_constantSet = m_constantPool->AllocateSet(constantLayout); if (m_constantSet == nullptr) { DestroyResources(); return false; } m_pipelineState = m_device->CreatePipelineState( BuildLinePipelineDesc(m_pipelineLayout, surface)); if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) { DestroyResources(); return false; } return true; } bool EnsureLineBufferCapacity(std::size_t requiredVertexCount) { const std::uint64_t requiredBytes = static_cast(requiredVertexCount * sizeof(OverlayLineVertex)); if (requiredBytes == 0u) { return true; } if (m_lineVertexBuffer != nullptr && m_lineVertexBufferCapacity >= requiredBytes) { return true; } if (m_lineVertexBufferView != nullptr) { m_lineVertexBufferView->Shutdown(); delete m_lineVertexBufferView; m_lineVertexBufferView = nullptr; } if (m_lineVertexBuffer != nullptr) { m_lineVertexBuffer->Shutdown(); delete m_lineVertexBuffer; m_lineVertexBuffer = nullptr; } m_lineVertexBufferCapacity = (std::max)(requiredBytes, kMinDynamicVertexBufferBytes); ::XCEngine::RHI::BufferDesc bufferDesc = {}; bufferDesc.size = m_lineVertexBufferCapacity; bufferDesc.stride = static_cast(sizeof(OverlayLineVertex)); bufferDesc.bufferType = static_cast(::XCEngine::RHI::BufferType::Vertex); m_lineVertexBuffer = m_device->CreateBuffer(bufferDesc); if (m_lineVertexBuffer == nullptr) { m_lineVertexBufferCapacity = 0u; return false; } m_lineVertexBuffer->SetStride(bufferDesc.stride); m_lineVertexBuffer->SetBufferType(::XCEngine::RHI::BufferType::Vertex); ::XCEngine::RHI::ResourceViewDesc viewDesc = {}; viewDesc.dimension = ::XCEngine::RHI::ResourceViewDimension::Buffer; viewDesc.structureByteStride = bufferDesc.stride; m_lineVertexBufferView = m_device->CreateVertexBufferView(m_lineVertexBuffer, viewDesc); return m_lineVertexBufferView != nullptr; } void DestroyResources() { if (m_lineVertexBufferView != nullptr) { m_lineVertexBufferView->Shutdown(); delete m_lineVertexBufferView; m_lineVertexBufferView = nullptr; } if (m_lineVertexBuffer != nullptr) { m_lineVertexBuffer->Shutdown(); delete m_lineVertexBuffer; m_lineVertexBuffer = nullptr; } if (m_pipelineState != nullptr) { m_pipelineState->Shutdown(); delete m_pipelineState; m_pipelineState = nullptr; } if (m_constantSet != nullptr) { m_constantSet->Shutdown(); delete m_constantSet; m_constantSet = nullptr; } if (m_constantPool != nullptr) { m_constantPool->Shutdown(); delete m_constantPool; m_constantPool = nullptr; } if (m_pipelineLayout != nullptr) { m_pipelineLayout->Shutdown(); delete m_pipelineLayout; m_pipelineLayout = nullptr; } m_lineVertexBufferCapacity = 0u; m_renderTargetFormat = ::XCEngine::RHI::Format::Unknown; m_depthStencilFormat = ::XCEngine::RHI::Format::Unknown; m_renderTargetSampleCount = 1u; m_device = nullptr; m_backendType = ::XCEngine::RHI::RHIType::D3D12; } ::XCEngine::RHI::RHIDevice* m_device = nullptr; ::XCEngine::RHI::RHIType m_backendType = ::XCEngine::RHI::RHIType::D3D12; ::XCEngine::RHI::RHIPipelineLayout* m_pipelineLayout = nullptr; ::XCEngine::RHI::RHIPipelineState* m_pipelineState = nullptr; ::XCEngine::RHI::RHIDescriptorPool* m_constantPool = nullptr; ::XCEngine::RHI::RHIDescriptorSet* m_constantSet = nullptr; ::XCEngine::RHI::RHIBuffer* m_lineVertexBuffer = nullptr; ::XCEngine::RHI::RHIResourceView* m_lineVertexBufferView = nullptr; std::uint64_t m_lineVertexBufferCapacity = 0u; ::XCEngine::RHI::Format m_renderTargetFormat = ::XCEngine::RHI::Format::Unknown; ::XCEngine::RHI::Format m_depthStencilFormat = ::XCEngine::RHI::Format::Unknown; std::uint32_t m_renderTargetSampleCount = 1u; }; SceneViewportSelectedHelpersPassRenderer::SceneViewportSelectedHelpersPassRenderer() : m_impl(std::make_unique()) { } SceneViewportSelectedHelpersPassRenderer::~SceneViewportSelectedHelpersPassRenderer() = default; void SceneViewportSelectedHelpersPassRenderer::Shutdown() { m_impl->Shutdown(); } bool SceneViewportSelectedHelpersPassRenderer::Render( const ::XCEngine::Rendering::RenderPassContext& context, const SceneViewportRenderRequest& request) { return m_impl->Render(context, request); } std::unique_ptr<::XCEngine::Rendering::RenderPass> CreateSceneViewportSelectedHelpersPass( SceneViewportSelectedHelpersPassRenderer& renderer, const SceneViewportRenderRequest& request) { return std::make_unique(renderer, request); } } // namespace XCEngine::UI::Editor::App