refactor: drive scene view outline from object id

This commit is contained in:
2026-04-01 17:33:07 +08:00
parent 409e08d03c
commit b85571d9d4
8 changed files with 168 additions and 710 deletions

View File

@@ -8,9 +8,7 @@
#include "SceneViewportCameraController.h"
#include "SceneViewportInfiniteGridPass.h"
#include "SceneViewportPostPassPlan.h"
#include "SceneViewportSelectionMaskPass.h"
#include "SceneViewportSelectionOutlinePass.h"
#include "SceneViewportSelectionUtils.h"
#include "UI/ImGuiBackendBridge.h"
#include <XCEngine/Components/CameraComponent.h>
@@ -128,7 +126,6 @@ public:
m_device = nullptr;
m_backend = nullptr;
m_sceneGridPass.Shutdown();
m_sceneSelectionMaskPass.Shutdown();
m_sceneSelectionOutlinePass.Shutdown();
m_sceneRenderer.reset();
}
@@ -309,16 +306,13 @@ private:
RHI::RHIResourceView* colorView = nullptr;
RHI::RHITexture* depthTexture = nullptr;
RHI::RHIResourceView* depthView = nullptr;
RHI::RHITexture* selectionMaskTexture = nullptr;
RHI::RHIResourceView* selectionMaskView = nullptr;
RHI::RHIResourceView* selectionMaskShaderView = nullptr;
RHI::RHITexture* objectIdTexture = nullptr;
RHI::RHIResourceView* objectIdView = nullptr;
RHI::RHIResourceView* objectIdShaderView = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE imguiCpuHandle = {};
D3D12_GPU_DESCRIPTOR_HANDLE imguiGpuHandle = {};
ImTextureID textureId = {};
RHI::ResourceStates colorState = RHI::ResourceStates::Common;
RHI::ResourceStates selectionMaskState = RHI::ResourceStates::Common;
RHI::ResourceStates objectIdState = RHI::ResourceStates::Common;
bool hasValidObjectIdFrame = false;
std::string statusText;
@@ -333,8 +327,7 @@ private:
struct SceneViewportRenderState {
SceneViewportOverlayData overlay = {};
Rendering::RenderPassSequence postPasses;
Rendering::RenderCameraData cameraData = {};
std::vector<Rendering::VisibleRenderItem> selectionRenderables;
std::vector<uint64_t> selectedObjectIds;
};
ViewportEntry& GetEntry(EditorViewportKind kind) {
@@ -465,29 +458,6 @@ private:
return entry.depthView != nullptr;
}
bool CreateSceneViewportSelectionMaskResources(ViewportEntry& entry) {
const RHI::TextureDesc selectionMaskDesc =
BuildViewportTextureDesc(entry.width, entry.height, RHI::Format::R8G8B8A8_UNorm);
entry.selectionMaskTexture = m_device->CreateTexture(selectionMaskDesc);
if (entry.selectionMaskTexture == nullptr) {
return false;
}
const RHI::ResourceViewDesc selectionMaskViewDesc =
BuildViewportTextureViewDesc(RHI::Format::R8G8B8A8_UNorm);
entry.selectionMaskView = m_device->CreateRenderTargetView(
entry.selectionMaskTexture,
selectionMaskViewDesc);
if (entry.selectionMaskView == nullptr) {
return false;
}
entry.selectionMaskShaderView = m_device->CreateShaderResourceView(
entry.selectionMaskTexture,
selectionMaskViewDesc);
return entry.selectionMaskShaderView != nullptr;
}
bool CreateSceneViewportObjectIdResources(ViewportEntry& entry) {
const RHI::TextureDesc objectIdDesc =
BuildViewportTextureDesc(entry.width, entry.height, RHI::Format::R8G8B8A8_UNorm);
@@ -501,7 +471,14 @@ private:
entry.objectIdView = m_device->CreateRenderTargetView(
entry.objectIdTexture,
objectIdViewDesc);
return entry.objectIdView != nullptr;
if (entry.objectIdView == nullptr) {
return false;
}
entry.objectIdShaderView = m_device->CreateShaderResourceView(
entry.objectIdTexture,
objectIdViewDesc);
return entry.objectIdShaderView != nullptr;
}
bool CreateViewportTextureDescriptor(ViewportEntry& entry) {
@@ -537,11 +514,9 @@ private:
entry.depthTexture != nullptr &&
entry.depthView != nullptr &&
(entry.kind != EditorViewportKind::Scene ||
(entry.selectionMaskTexture != nullptr &&
entry.selectionMaskView != nullptr &&
entry.selectionMaskShaderView != nullptr &&
entry.objectIdTexture != nullptr &&
entry.objectIdView != nullptr)) &&
(entry.objectIdTexture != nullptr &&
entry.objectIdView != nullptr &&
entry.objectIdShaderView != nullptr)) &&
entry.textureId != ImTextureID{}) {
return true;
}
@@ -561,12 +536,6 @@ private:
return false;
}
if (entry.kind == EditorViewportKind::Scene &&
!CreateSceneViewportSelectionMaskResources(entry)) {
DestroyViewportResources(entry);
return false;
}
if (entry.kind == EditorViewportKind::Scene &&
!CreateSceneViewportObjectIdResources(entry)) {
DestroyViewportResources(entry);
@@ -578,7 +547,6 @@ private:
return false;
}
entry.colorState = RHI::ResourceStates::Common;
entry.selectionMaskState = RHI::ResourceStates::Common;
entry.objectIdState = RHI::ResourceStates::Common;
entry.hasValidObjectIdFrame = false;
return true;
@@ -593,15 +561,6 @@ private:
return surface;
}
Rendering::RenderSurface BuildSelectionMaskSurface(const ViewportEntry& entry) const {
Rendering::RenderSurface surface(entry.width, entry.height);
surface.SetColorAttachment(entry.selectionMaskView);
surface.SetDepthAttachment(entry.depthView);
surface.SetColorStateBefore(entry.selectionMaskState);
surface.SetColorStateAfter(RHI::ResourceStates::PixelShaderResource);
return surface;
}
Rendering::RenderSurface BuildObjectIdSurface(const ViewportEntry& entry) const {
Rendering::RenderSurface surface(entry.width, entry.height);
surface.SetColorAttachment(entry.objectIdView);
@@ -611,45 +570,6 @@ private:
return surface;
}
void AddSceneSelectionMaskPass(
ViewportEntry& entry,
const Rendering::RenderSurface& selectionMaskSurface,
const Rendering::RenderCameraData& cameraData,
const std::vector<Rendering::VisibleRenderItem>& selectionRenderables,
Rendering::RenderPassSequence& outPostPasses) {
outPostPasses.AddPass(MakeLambdaRenderPass(
"SceneSelectionMask",
[this, &entry, selectionMaskSurface, &cameraData, &selectionRenderables](
const Rendering::RenderPassContext& context) mutable {
context.renderContext.commandList->TransitionBarrier(
entry.selectionMaskView,
entry.selectionMaskState,
RHI::ResourceStates::RenderTarget);
entry.selectionMaskState = RHI::ResourceStates::RenderTarget;
const float maskClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
RHI::RHIResourceView* maskView = entry.selectionMaskView;
context.renderContext.commandList->SetRenderTargets(1, &maskView, entry.depthView);
context.renderContext.commandList->ClearRenderTarget(maskView, maskClearColor);
const bool rendered = m_sceneSelectionMaskPass.Render(
context.renderContext,
selectionMaskSurface,
cameraData,
selectionRenderables);
if (!rendered) {
SetViewportStatusIfEmpty(entry.statusText, "Scene selection mask pass failed");
}
context.renderContext.commandList->TransitionBarrier(
entry.selectionMaskView,
entry.selectionMaskState,
RHI::ResourceStates::PixelShaderResource);
entry.selectionMaskState = RHI::ResourceStates::PixelShaderResource;
return rendered;
}));
}
void AddSceneColorToRenderTargetPass(
ViewportEntry& entry,
Rendering::RenderPassSequence& outPostPasses) {
@@ -685,14 +605,17 @@ private:
void AddSceneSelectionOutlinePass(
ViewportEntry& entry,
const std::vector<uint64_t>& selectedObjectIds,
Rendering::RenderPassSequence& outPostPasses) {
outPostPasses.AddPass(MakeLambdaRenderPass(
"SceneSelectionOutline",
[this, &entry](const Rendering::RenderPassContext& context) {
[this, &entry, selectedObjectIds](const Rendering::RenderPassContext& context) {
const bool rendered = m_sceneSelectionOutlinePass.Render(
context.renderContext,
context.surface,
entry.selectionMaskShaderView);
entry.objectIdShaderView,
selectedObjectIds,
false);
if (!rendered) {
SetViewportStatusIfEmpty(entry.statusText, "Scene selection outline pass failed");
}
@@ -717,23 +640,23 @@ private:
void AddSceneSelectionMaskDebugPass(
ViewportEntry& entry,
const Rendering::RenderCameraData& cameraData,
const std::vector<Rendering::VisibleRenderItem>& selectionRenderables,
const std::vector<uint64_t>& selectedObjectIds,
Rendering::RenderPassSequence& outPostPasses) {
outPostPasses.AddPass(MakeLambdaRenderPass(
"SceneSelectionMaskDebug",
[this, &entry, &cameraData, &selectionRenderables](
[this, &entry, selectedObjectIds](
const Rendering::RenderPassContext& context) {
const float debugClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
RHI::RHIResourceView* colorView = entry.colorView;
context.renderContext.commandList->SetRenderTargets(1, &colorView, entry.depthView);
context.renderContext.commandList->ClearRenderTarget(colorView, debugClearColor);
const bool rendered = m_sceneSelectionMaskPass.Render(
const bool rendered = m_sceneSelectionOutlinePass.Render(
context.renderContext,
context.surface,
cameraData,
selectionRenderables);
entry.objectIdShaderView,
selectedObjectIds,
true);
if (!rendered) {
SetViewportStatusIfEmpty(entry.statusText, "Scene selection mask debug pass failed");
}
@@ -745,19 +668,9 @@ private:
SceneViewportPostPassStep step,
ViewportEntry& entry,
const SceneViewportOverlayData& overlay,
const Rendering::RenderSurface& selectionMaskSurface,
const Rendering::RenderCameraData& cameraData,
const std::vector<Rendering::VisibleRenderItem>& selectionRenderables,
const std::vector<uint64_t>& selectedObjectIds,
Rendering::RenderPassSequence& outPostPasses) {
switch (step) {
case SceneViewportPostPassStep::SelectionMask:
AddSceneSelectionMaskPass(
entry,
selectionMaskSurface,
cameraData,
selectionRenderables,
outPostPasses);
break;
case SceneViewportPostPassStep::ColorToRenderTarget:
AddSceneColorToRenderTargetPass(entry, outPostPasses);
break;
@@ -765,7 +678,7 @@ private:
AddSceneInfiniteGridPass(entry, overlay, outPostPasses);
break;
case SceneViewportPostPassStep::SelectionOutline:
AddSceneSelectionOutlinePass(entry, outPostPasses);
AddSceneSelectionOutlinePass(entry, selectedObjectIds, outPostPasses);
break;
case SceneViewportPostPassStep::ColorToShaderResource:
AddSceneColorToShaderResourcePass(entry, outPostPasses);
@@ -773,8 +686,7 @@ private:
case SceneViewportPostPassStep::SelectionMaskDebug:
AddSceneSelectionMaskDebugPass(
entry,
cameraData,
selectionRenderables,
selectedObjectIds,
outPostPasses);
break;
default:
@@ -785,43 +697,33 @@ private:
bool BuildSceneViewPostPassSequence(
ViewportEntry& entry,
const SceneViewportOverlayData& overlay,
const Rendering::RenderCameraData& cameraData,
const std::vector<Rendering::VisibleRenderItem>& selectionRenderables,
const std::vector<uint64_t>& selectedObjectIds,
Rendering::RenderPassSequence& outPostPasses) {
const bool hasSelection = !selectionRenderables.empty();
const bool hasSelectionMaskRenderTarget = entry.selectionMaskView != nullptr;
const bool hasSelectionMaskShaderView = entry.selectionMaskShaderView != nullptr;
const bool hasSelection = !selectedObjectIds.empty();
const bool hasObjectIdShaderView = entry.objectIdShaderView != nullptr;
if (hasSelection &&
!kDebugSceneSelectionMask &&
(!hasSelectionMaskRenderTarget || !hasSelectionMaskShaderView)) {
SetViewportStatusIfEmpty(entry.statusText, "Scene selection mask target is unavailable");
!hasObjectIdShaderView) {
SetViewportStatusIfEmpty(entry.statusText, "Scene object id shader view is unavailable");
}
const SceneViewportPostPassPlan plan = BuildSceneViewportPostPassPlan({
overlay.valid,
hasSelection,
kDebugSceneSelectionMask,
hasSelectionMaskRenderTarget,
hasSelectionMaskShaderView
hasObjectIdShaderView
});
if (!plan.valid) {
return false;
}
Rendering::RenderSurface selectionMaskSurface = {};
if (plan.usesSelectionMaskSurface) {
selectionMaskSurface = BuildSelectionMaskSurface(entry);
}
for (const SceneViewportPostPassStep step : plan.steps) {
AddSceneViewPostPassStep(
step,
entry,
overlay,
selectionMaskSurface,
cameraData,
selectionRenderables,
selectedObjectIds,
outPostPasses);
}
@@ -833,27 +735,18 @@ private:
IEditorContext& context,
const Components::Scene& scene,
SceneViewportRenderState& outState) {
(void)scene;
outState.overlay = GetSceneViewOverlayData();
if (!outState.overlay.valid) {
return;
}
outState.selectionRenderables = CollectSceneViewportSelectionRenderables(
scene,
context.GetSelectionManager().GetSelectedEntities(),
outState.overlay.cameraPosition);
if (!outState.selectionRenderables.empty()) {
outState.cameraData = BuildSceneViewportCameraData(
*m_sceneViewCamera.camera,
entry.width,
entry.height);
}
outState.selectedObjectIds = context.GetSelectionManager().GetSelectedEntities();
BuildSceneViewPostPassSequence(
entry,
outState.overlay,
outState.cameraData,
outState.selectionRenderables,
outState.selectedObjectIds,
outState.postPasses);
}
@@ -1046,30 +939,18 @@ private:
m_backend->FreeTextureDescriptor(entry.imguiCpuHandle, entry.imguiGpuHandle);
}
if (entry.selectionMaskShaderView != nullptr) {
entry.selectionMaskShaderView->Shutdown();
delete entry.selectionMaskShaderView;
entry.selectionMaskShaderView = nullptr;
}
if (entry.selectionMaskView != nullptr) {
entry.selectionMaskView->Shutdown();
delete entry.selectionMaskView;
entry.selectionMaskView = nullptr;
}
if (entry.selectionMaskTexture != nullptr) {
entry.selectionMaskTexture->Shutdown();
delete entry.selectionMaskTexture;
entry.selectionMaskTexture = nullptr;
}
if (entry.objectIdView != nullptr) {
entry.objectIdView->Shutdown();
delete entry.objectIdView;
entry.objectIdView = nullptr;
}
if (entry.objectIdShaderView != nullptr) {
entry.objectIdShaderView->Shutdown();
delete entry.objectIdShaderView;
entry.objectIdShaderView = nullptr;
}
if (entry.objectIdTexture != nullptr) {
entry.objectIdTexture->Shutdown();
delete entry.objectIdTexture;
@@ -1106,7 +987,6 @@ private:
entry.imguiGpuHandle = {};
entry.textureId = {};
entry.colorState = RHI::ResourceStates::Common;
entry.selectionMaskState = RHI::ResourceStates::Common;
entry.objectIdState = RHI::ResourceStates::Common;
entry.hasValidObjectIdFrame = false;
}
@@ -1118,7 +998,6 @@ private:
std::array<ViewportEntry, 2> m_entries = {};
SceneViewCameraState m_sceneViewCamera;
SceneViewportInfiniteGridPass m_sceneGridPass;
SceneViewportSelectionMaskPass m_sceneSelectionMaskPass;
SceneViewportSelectionOutlinePass m_sceneSelectionOutlinePass;
};