关键节点
This commit is contained in:
@@ -0,0 +1,948 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user