Files
XCEngine/editor/app/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.cpp
2026-04-25 16:46:01 +08:00

949 lines
35 KiB
C++

#include "Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h"
#include <XCEngine/Components/CameraComponent.h>
#include <XCEngine/Components/GameObject.h>
#include <XCEngine/Components/LightComponent.h>
#include <XCEngine/Components/TransformComponent.h>
#include <XCEngine/Core/Math/Rect.h>
#include <XCEngine/Core/Math/Matrix4.h>
#include <XCEngine/Core/Math/Vector3.h>
#include <XCEngine/RHI/RHIBuffer.h>
#include <XCEngine/RHI/RHICommandList.h>
#include <XCEngine/RHI/RHIDescriptorPool.h>
#include <XCEngine/RHI/RHIDescriptorSet.h>
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIPipelineLayout.h>
#include <XCEngine/RHI/RHIPipelineState.h>
#include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/Rendering/FrameData/RenderSceneData.h>
#include <XCEngine/Rendering/RenderSurfacePipelineUtils.h>
#include <XCEngine/Scene/Scene.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <cstring>
#include <utility>
#include <vector>
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<HelperLine>& 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<HelperLine>& 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<float>(segmentIndex) / static_cast<float>(kSelectedLightHelperSegmentCount) *
::XCEngine::Math::PI * 2.0f;
const float angle1 =
static_cast<float>(segmentIndex + 1u) / static_cast<float>(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<HelperLine>& 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<std::pair<std::size_t, std::size_t>, 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<HelperLine>& 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<float, 6> 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<HelperLine>& 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<HelperLine>& 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<float, 4> 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<HelperLine> BuildHelperLines(
const SceneViewportRenderRequest& request,
const SelectedHelpersCameraData& overlay,
float viewportWidth,
float viewportHeight) {
std::vector<HelperLine> 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<CameraComponent>();
camera != nullptr && camera->IsEnabled()) {
AppendSelectedCameraHelper(
lines,
*camera,
*gameObject,
viewportWidth,
viewportHeight);
}
if (LightComponent* light = gameObject->GetComponent<LightComponent>();
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<std::uint32_t>(::XCEngine::RHI::PrimitiveTopologyType::Line);
::XCEngine::Rendering::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc(
surface,
pipelineDesc);
pipelineDesc.depthStencilFormat =
static_cast<std::uint32_t>(::XCEngine::Rendering::ResolveSurfaceDepthFormat(surface));
pipelineDesc.inputLayout.elements = {
{ "POSITION", 0, static_cast<std::uint32_t>(::XCEngine::RHI::Format::R32G32B32_Float), 0, 0, 0, 0 },
{ "COLOR", 0, static_cast<std::uint32_t>(::XCEngine::RHI::Format::R32G32B32A32_Float), 0, 12, 0, 0 }
};
pipelineDesc.rasterizerState.fillMode =
static_cast<std::uint32_t>(::XCEngine::RHI::FillMode::Solid);
pipelineDesc.rasterizerState.cullMode =
static_cast<std::uint32_t>(::XCEngine::RHI::CullMode::None);
pipelineDesc.rasterizerState.frontFace =
static_cast<std::uint32_t>(::XCEngine::RHI::FrontFace::CounterClockwise);
pipelineDesc.rasterizerState.depthClipEnable = true;
pipelineDesc.blendState.blendEnable = true;
pipelineDesc.blendState.srcBlend =
static_cast<std::uint32_t>(::XCEngine::RHI::BlendFactor::SrcAlpha);
pipelineDesc.blendState.dstBlend =
static_cast<std::uint32_t>(::XCEngine::RHI::BlendFactor::InvSrcAlpha);
pipelineDesc.blendState.srcBlendAlpha =
static_cast<std::uint32_t>(::XCEngine::RHI::BlendFactor::One);
pipelineDesc.blendState.dstBlendAlpha =
static_cast<std::uint32_t>(::XCEngine::RHI::BlendFactor::InvSrcAlpha);
pipelineDesc.blendState.blendOp =
static_cast<std::uint32_t>(::XCEngine::RHI::BlendOp::Add);
pipelineDesc.blendState.blendOpAlpha =
static_cast<std::uint32_t>(::XCEngine::RHI::BlendOp::Add);
pipelineDesc.blendState.colorWriteMask =
static_cast<std::uint8_t>(::XCEngine::RHI::ColorWriteMask::All);
pipelineDesc.depthStencilState.depthTestEnable = false;
pipelineDesc.depthStencilState.depthWriteEnable = false;
pipelineDesc.depthStencilState.depthFunc =
static_cast<std::uint32_t>(::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<RHIResourceView*>& 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<HelperLine> lines =
BuildHelperLines(
request,
cameraData,
static_cast<float>(renderArea.width),
static_cast<float>(renderArea.height));
if (lines.empty()) {
return true;
}
if (!EnsureInitialized(renderContext, surface) ||
!EnsureLineBufferCapacity(lines.size() * 2u)) {
return false;
}
std::vector<OverlayLineVertex> 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<float>(renderArea.x),
static_cast<float>(renderArea.y),
static_cast<float>(renderArea.width),
static_cast<float>(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<std::uint32_t>(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<std::uint32_t>(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<std::uint32_t>(::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<std::uint64_t>(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<std::uint64_t>)(requiredBytes, kMinDynamicVertexBufferBytes);
::XCEngine::RHI::BufferDesc bufferDesc = {};
bufferDesc.size = m_lineVertexBufferCapacity;
bufferDesc.stride = static_cast<std::uint32_t>(sizeof(OverlayLineVertex));
bufferDesc.bufferType =
static_cast<std::uint32_t>(::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<Impl>()) {
}
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<SceneViewportSelectedHelpersPass>(renderer, request);
}
} // namespace XCEngine::UI::Editor::App