949 lines
35 KiB
C++
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
|