Formalize scene viewport transform gizmo overlay provider

This commit is contained in:
2026-04-03 16:50:46 +08:00
parent 03bd755e0a
commit 73415915e6
11 changed files with 198 additions and 141 deletions

View File

@@ -16,7 +16,7 @@ namespace Editor {
class IEditorContext;
struct SceneViewportOverlayFrameData;
struct SceneViewportTransformGizmoHandleBuildInputs;
struct SceneViewportTransformGizmoOverlayState;
enum class EditorViewportKind {
Scene,
@@ -86,13 +86,8 @@ public:
virtual void AlignSceneViewToOrientationAxis(SceneViewportOrientationAxis axis) = 0;
virtual SceneViewportOverlayData GetSceneViewOverlayData() const = 0;
virtual const SceneViewportOverlayFrameData& GetSceneViewEditorOverlayFrameData(IEditorContext& context) = 0;
virtual const SceneViewportOverlayFrameData& GetSceneViewInteractionOverlayFrameData(
IEditorContext& context,
const SceneViewportOverlayData& overlay,
const SceneViewportTransformGizmoHandleBuildInputs& inputs) = 0;
virtual void SetSceneViewTransientTransformGizmoOverlayData(
const SceneViewportOverlayData& overlay,
const SceneViewportTransformGizmoHandleBuildInputs& inputs) = 0;
virtual void SetSceneViewTransformGizmoOverlayState(
const SceneViewportTransformGizmoOverlayState& state) = 0;
virtual void RenderRequestedViewports(
IEditorContext& context,
const Rendering::RenderContext& renderContext) = 0;

View File

@@ -24,7 +24,8 @@ SceneViewportOverlayFrameData SceneViewportOverlayBuilder::Build(
const SceneViewportOverlayData& overlay,
uint32_t viewportWidth,
uint32_t viewportHeight,
const std::vector<uint64_t>& selectedObjectIds) const {
const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportTransformGizmoOverlayState* transformGizmoOverlayState) const {
SceneViewportOverlayFrameData frameData = {};
frameData.overlay = overlay;
if (!overlay.valid || viewportWidth == 0u || viewportHeight == 0u) {
@@ -42,7 +43,8 @@ SceneViewportOverlayFrameData SceneViewportOverlayBuilder::Build(
&overlay,
viewportWidth,
viewportHeight,
&selectedObjectIds
&selectedObjectIds,
transformGizmoOverlayState
};
m_providerRegistry.AppendOverlay(buildContext, frameData);
return frameData;

View File

@@ -21,7 +21,8 @@ public:
const SceneViewportOverlayData& overlay,
uint32_t viewportWidth,
uint32_t viewportHeight,
const std::vector<uint64_t>& selectedObjectIds) const;
const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportTransformGizmoOverlayState* transformGizmoOverlayState = nullptr) const;
const SceneViewportOverlayProviderRegistry& GetProviderRegistry() const {
return m_providerRegistry;

View File

@@ -20,6 +20,24 @@ struct SceneViewportTransformGizmoHandleBuildInputs {
uint64_t scaleEntityId = 0;
};
struct SceneViewportTransformGizmoOverlayState {
bool hasMoveGizmo = false;
SceneViewportMoveGizmoDrawData moveGizmo = {};
uint64_t moveEntityId = 0;
bool hasRotateGizmo = false;
SceneViewportRotateGizmoDrawData rotateGizmo = {};
uint64_t rotateEntityId = 0;
bool hasScaleGizmo = false;
SceneViewportScaleGizmoDrawData scaleGizmo = {};
uint64_t scaleEntityId = 0;
bool HasAnyVisibleGizmo() const {
return (hasMoveGizmo && moveGizmo.visible) ||
(hasRotateGizmo && rotateGizmo.visible) ||
(hasScaleGizmo && scaleGizmo.visible);
}
};
inline SceneViewportTransformGizmoHandleBuildInputs BuildSceneViewportTransformGizmoHandleBuildInputs(
bool showingMoveGizmo,
const SceneViewportMoveGizmo& moveGizmo,
@@ -47,6 +65,47 @@ inline SceneViewportTransformGizmoHandleBuildInputs BuildSceneViewportTransformG
return inputs;
}
inline SceneViewportTransformGizmoOverlayState BuildSceneViewportTransformGizmoOverlayState(
const SceneViewportTransformGizmoHandleBuildInputs& inputs) {
SceneViewportTransformGizmoOverlayState state = {};
if (inputs.moveGizmo != nullptr) {
state.hasMoveGizmo = true;
state.moveGizmo = *inputs.moveGizmo;
state.moveEntityId = inputs.moveEntityId;
}
if (inputs.rotateGizmo != nullptr) {
state.hasRotateGizmo = true;
state.rotateGizmo = *inputs.rotateGizmo;
state.rotateEntityId = inputs.rotateEntityId;
}
if (inputs.scaleGizmo != nullptr) {
state.hasScaleGizmo = true;
state.scaleGizmo = *inputs.scaleGizmo;
state.scaleEntityId = inputs.scaleEntityId;
}
return state;
}
inline SceneViewportTransformGizmoHandleBuildInputs BuildSceneViewportTransformGizmoHandleBuildInputs(
const SceneViewportTransformGizmoOverlayState& state) {
SceneViewportTransformGizmoHandleBuildInputs inputs = {};
if (state.hasMoveGizmo) {
inputs.moveGizmo = &state.moveGizmo;
inputs.moveEntityId = state.moveEntityId;
}
if (state.hasRotateGizmo) {
inputs.rotateGizmo = &state.rotateGizmo;
inputs.rotateEntityId = state.rotateEntityId;
}
if (state.hasScaleGizmo) {
inputs.scaleGizmo = &state.scaleGizmo;
inputs.scaleEntityId = state.scaleEntityId;
}
return inputs;
}
namespace Detail {
inline constexpr int kSceneViewportHandlePrioritySceneIcon = 100;

View File

@@ -450,6 +450,28 @@ public:
}
};
class SceneViewportTransformGizmoOverlayProvider final : public ISceneViewportOverlayProvider {
public:
const char* GetName() const override {
return "SceneViewportTransformGizmoOverlayProvider";
}
void AppendOverlay(
const SceneViewportOverlayBuildContext& context,
SceneViewportOverlayFrameData& frameData) const override {
if (!context.IsValid() ||
context.transformGizmoOverlayState == nullptr ||
!context.transformGizmoOverlayState->HasAnyVisibleGizmo()) {
return;
}
const SceneViewportTransformGizmoHandleBuildInputs inputs =
BuildSceneViewportTransformGizmoHandleBuildInputs(*context.transformGizmoOverlayState);
AppendTransformGizmoScreenTriangles(frameData, inputs);
AppendTransformGizmoHandleRecords(frameData, inputs);
}
};
} // namespace
void SceneViewportOverlayProviderRegistry::AddProvider(
@@ -491,10 +513,15 @@ std::unique_ptr<ISceneViewportOverlayProvider> CreateSceneViewportLightOverlayPr
return std::make_unique<SceneViewportLightOverlayProvider>();
}
std::unique_ptr<ISceneViewportOverlayProvider> CreateSceneViewportTransformGizmoOverlayProvider() {
return std::make_unique<SceneViewportTransformGizmoOverlayProvider>();
}
SceneViewportOverlayProviderRegistry BuildDefaultSceneViewportOverlayProviderRegistry() {
SceneViewportOverlayProviderRegistry registry;
registry.AddProvider(CreateSceneViewportCameraOverlayProvider());
registry.AddProvider(CreateSceneViewportLightOverlayProvider());
registry.AddProvider(CreateSceneViewportTransformGizmoOverlayProvider());
return registry;
}

View File

@@ -23,6 +23,7 @@ struct SceneViewportOverlayBuildContext {
uint32_t viewportWidth = 0u;
uint32_t viewportHeight = 0u;
const std::vector<uint64_t>* selectedObjectIds = nullptr;
const SceneViewportTransformGizmoOverlayState* transformGizmoOverlayState = nullptr;
bool IsValid() const {
return editorContext != nullptr &&
@@ -60,6 +61,7 @@ private:
std::unique_ptr<ISceneViewportOverlayProvider> CreateSceneViewportCameraOverlayProvider();
std::unique_ptr<ISceneViewportOverlayProvider> CreateSceneViewportLightOverlayProvider();
std::unique_ptr<ISceneViewportOverlayProvider> CreateSceneViewportTransformGizmoOverlayProvider();
SceneViewportOverlayProviderRegistry BuildDefaultSceneViewportOverlayProviderRegistry();
} // namespace Editor

View File

@@ -51,7 +51,6 @@ inline SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan(
const SceneViewportOverlayData& overlay,
const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportOverlayFrameData& editorOverlayFrameData,
const SceneViewportOverlayFrameData& transientOverlayFrameData,
const SceneViewportGridPassFactory& gridPassFactory,
const SceneViewportSelectionOutlinePassFactory& selectionOutlinePassFactory,
const SceneViewportOverlayPassFactory& overlayPassFactory,
@@ -85,11 +84,9 @@ inline SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan(
}
}
SceneViewportOverlayFrameData renderOverlayFrameData = editorOverlayFrameData;
AppendSceneViewportOverlayFrameData(renderOverlayFrameData, transientOverlayFrameData);
if (renderOverlayFrameData.HasOverlayPrimitives() &&
if (editorOverlayFrameData.HasOverlayPrimitives() &&
overlayPassFactory != nullptr) {
std::unique_ptr<Rendering::RenderPass> overlayPass = overlayPassFactory(renderOverlayFrameData);
std::unique_ptr<Rendering::RenderPass> overlayPass = overlayPassFactory(editorOverlayFrameData);
if (overlayPass != nullptr) {
result.plan.overlayPasses.AddPass(std::move(overlayPass));
}

View File

@@ -213,6 +213,8 @@ public:
m_sceneViewportEditorOverlayRenderer.Shutdown();
m_sceneViewCamera = {};
ResetSceneViewEditorOverlayFrameData();
m_sceneViewTransformGizmoOverlayState = {};
m_sceneViewTransformGizmoOverlayDirty = false;
m_sceneViewLastRenderContext = {};
m_device = nullptr;
m_backend = nullptr;
@@ -225,8 +227,8 @@ public:
entry.requestedWidth = 0;
entry.requestedHeight = 0;
}
m_sceneViewTransientTransformGizmoOverlay = {};
m_sceneViewTransientTransformGizmoInputs = {};
m_sceneViewTransformGizmoOverlayState = {};
m_sceneViewTransformGizmoOverlayDirty = true;
}
EditorViewportFrame RequestViewport(EditorViewportKind kind, const ImVec2& requestedSize) override {
@@ -360,23 +362,10 @@ public:
return m_sceneViewEditorOverlayFrameData;
}
const SceneViewportOverlayFrameData& GetSceneViewInteractionOverlayFrameData(
IEditorContext& context,
const SceneViewportOverlayData& overlay,
const SceneViewportTransformGizmoHandleBuildInputs& inputs) override {
EnsureSceneViewEditorOverlayFrameData(context);
m_sceneViewInteractionOverlayFrameData = m_sceneViewEditorOverlayFrameData;
AppendSceneViewportOverlayFrameData(
m_sceneViewInteractionOverlayFrameData,
BuildSceneViewportTransformGizmoOverlayFrameData(overlay, inputs));
return m_sceneViewInteractionOverlayFrameData;
}
void SetSceneViewTransientTransformGizmoOverlayData(
const SceneViewportOverlayData& overlay,
const SceneViewportTransformGizmoHandleBuildInputs& inputs) override {
m_sceneViewTransientTransformGizmoOverlay = overlay;
m_sceneViewTransientTransformGizmoInputs = inputs;
void SetSceneViewTransformGizmoOverlayState(
const SceneViewportTransformGizmoOverlayState& state) override {
m_sceneViewTransformGizmoOverlayState = state;
m_sceneViewTransformGizmoOverlayDirty = true;
}
void RenderRequestedViewports(
@@ -533,7 +522,6 @@ private:
void ResetSceneViewEditorOverlayFrameData() {
m_sceneViewEditorOverlayFrameData = {};
m_sceneViewInteractionOverlayFrameData = {};
m_sceneViewEditorOverlayScene = nullptr;
m_sceneViewEditorOverlaySelectedObjectIds.clear();
m_sceneViewEditorOverlayViewportWidth = 0u;
@@ -557,7 +545,8 @@ private:
uint32_t viewportHeight,
const std::vector<uint64_t>& selectedObjectIds,
uint64_t contentSignature) const {
return !m_sceneViewEditorOverlayCached ||
return m_sceneViewTransformGizmoOverlayDirty ||
!m_sceneViewEditorOverlayCached ||
m_sceneViewEditorOverlayScene != scene ||
m_sceneViewEditorOverlayViewportWidth != viewportWidth ||
m_sceneViewEditorOverlayViewportHeight != viewportHeight ||
@@ -600,7 +589,8 @@ private:
overlay,
viewportWidth,
viewportHeight,
selectedObjectIds);
selectedObjectIds,
&m_sceneViewTransformGizmoOverlayState);
}
m_sceneViewEditorOverlayScene = scene;
@@ -609,6 +599,7 @@ private:
m_sceneViewEditorOverlayViewportHeight = viewportHeight;
m_sceneViewEditorOverlayContentSignature = contentSignature;
m_sceneViewEditorOverlayCached = true;
m_sceneViewTransformGizmoOverlayDirty = false;
}
void ApplyViewportRenderFailure(
@@ -653,7 +644,6 @@ private:
outState.overlay,
selectedObjectIds,
editorOverlayFrameData,
BuildSceneViewTransientTransformGizmoOverlayFrameData(),
[this](const Rendering::Passes::InfiniteGridPassData& data) {
return CreateSceneViewportGridPass(
m_sceneViewportGridRenderer,
@@ -864,12 +854,6 @@ private:
});
}
SceneViewportOverlayFrameData BuildSceneViewTransientTransformGizmoOverlayFrameData() const {
return BuildSceneViewportTransformGizmoOverlayFrameData(
m_sceneViewTransientTransformGizmoOverlay,
m_sceneViewTransientTransformGizmoInputs);
}
UI::ImGuiBackendBridge* m_backend = nullptr;
RHI::RHIDevice* m_device = nullptr;
std::unique_ptr<Rendering::SceneRenderer> m_sceneRenderer;
@@ -878,15 +862,14 @@ private:
std::array<ViewportEntry, 2> m_entries = {};
SceneViewCameraState m_sceneViewCamera;
SceneViewportOverlayFrameData m_sceneViewEditorOverlayFrameData = {};
SceneViewportOverlayFrameData m_sceneViewInteractionOverlayFrameData = {};
SceneViewportOverlayData m_sceneViewTransientTransformGizmoOverlay = {};
SceneViewportTransformGizmoHandleBuildInputs m_sceneViewTransientTransformGizmoInputs = {};
SceneViewportTransformGizmoOverlayState m_sceneViewTransformGizmoOverlayState = {};
const Components::Scene* m_sceneViewEditorOverlayScene = nullptr;
std::vector<uint64_t> m_sceneViewEditorOverlaySelectedObjectIds = {};
uint32_t m_sceneViewEditorOverlayViewportWidth = 0u;
uint32_t m_sceneViewEditorOverlayViewportHeight = 0u;
uint64_t m_sceneViewEditorOverlayContentSignature = 0u;
bool m_sceneViewEditorOverlayCached = false;
bool m_sceneViewTransformGizmoOverlayDirty = false;
SceneViewportGridPassRenderer m_sceneViewportGridRenderer;
SceneViewportSelectionOutlinePassRenderer m_sceneViewportSelectionOutlineRenderer;
SceneViewportEditorOverlayPassRenderer m_sceneViewportEditorOverlayRenderer;

View File

@@ -14,13 +14,9 @@
#include "Platform/Win32Utf8.h"
#include "UI/UI.h"
#include <XCEngine/Debug/Logger.h>
#include <imgui.h>
#include <algorithm>
#include <cstdarg>
#include <cstdio>
#include <filesystem>
#include <vector>
@@ -388,15 +384,6 @@ SceneViewportToolOverlayResult RenderSceneViewportToolOverlay(
return result;
}
void LogSceneViewNavigation(Debug::Logger& logger, const char* format, ...) {
char buffer[512] = {};
va_list args;
va_start(args, format);
std::vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
logger.Info(Debug::LogCategory::General, buffer);
}
bool ShouldBeginSceneViewportNavigationDrag(
bool hasInteractiveViewport,
bool hovered,
@@ -428,7 +415,6 @@ void SceneViewPanel::Render() {
const ViewportPanelContentResult content = RenderViewportPanelContent(*m_context, EditorViewportKind::Scene);
if (IViewportHostService* viewportHostService = m_context->GetViewportHostService()) {
const ImGuiIO& io = ImGui::GetIO();
auto& logger = Debug::Logger::Get();
const bool hasInteractiveViewport = content.hasViewportArea && content.frame.hasTexture;
const SceneViewportToolOverlayResult toolOverlay = RenderSceneViewportToolOverlay(content, m_toolMode);
const bool viewportContentHovered = content.hovered && !toolOverlay.hovered;
@@ -536,12 +522,12 @@ void SceneViewPanel::Render() {
m_scaleGizmo,
gizmoFrameState.scaleContext)
: SceneViewportTransformGizmoHandleBuildInputs{};
const SceneViewportTransformGizmoOverlayState interactionGizmoOverlayState =
BuildSceneViewportTransformGizmoOverlayState(interactionGizmoInputs);
viewportHostService->SetSceneViewTransformGizmoOverlayState(interactionGizmoOverlayState);
const SceneViewportOverlayFrameData& interactionOverlayFrameData =
hasInteractiveViewport
? viewportHostService->GetSceneViewInteractionOverlayFrameData(
*m_context,
overlay,
interactionGizmoInputs)
? viewportHostService->GetSceneViewEditorOverlayFrameData(*m_context)
: emptySceneOverlayFrameData;
const SceneViewportOverlayHandleHitResult overlayHandleHit =
hasInteractiveViewport
@@ -664,25 +650,6 @@ void SceneViewPanel::Render() {
ImGuiMouseButton_Middle);
const bool beginPanDrag = beginLeftPanDrag || beginMiddlePanDrag;
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right) || ImGui::IsMouseClicked(ImGuiMouseButton_Middle)) {
LogSceneViewNavigation(
logger,
"SceneView nav click hovered=%d focused=%d hasViewport=%d clickedR=%d clickedM=%d downR=%d downM=%d "
"look=%d pan=%d gizmoActive=%d ioDelta=(%.2f, %.2f)",
content.hovered ? 1 : 0,
content.focused ? 1 : 0,
hasInteractiveViewport ? 1 : 0,
ImGui::IsMouseClicked(ImGuiMouseButton_Right) ? 1 : 0,
ImGui::IsMouseClicked(ImGuiMouseButton_Middle) ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Right) ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Middle) ? 1 : 0,
m_lookDragging ? 1 : 0,
m_panDragging ? 1 : 0,
gizmoActive ? 1 : 0,
io.MouseDelta.x,
io.MouseDelta.y);
}
if (toolOverlay.clicked || beginTransformGizmo || orientationGizmoClick || sceneIconClick || selectClick || beginLookDrag ||
beginPanDrag) {
ImGui::SetWindowFocus();
@@ -741,14 +708,6 @@ void SceneViewPanel::Render() {
m_panDragging = false;
m_loggedLookDelta = false;
m_loggedPanDelta = false;
LogSceneViewNavigation(
logger,
"SceneView begin look drag hovered=%d focused=%d downR=%d downM=%d gizmoActive=%d",
content.hovered ? 1 : 0,
content.focused ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Right) ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Middle) ? 1 : 0,
gizmoActive ? 1 : 0);
}
if (beginPanDrag) {
m_panDragging = true;
@@ -756,25 +715,13 @@ void SceneViewPanel::Render() {
m_panDragButton = beginLeftPanDrag ? ImGuiMouseButton_Left : ImGuiMouseButton_Middle;
m_loggedPanDelta = false;
m_loggedLookDelta = false;
LogSceneViewNavigation(
logger,
"SceneView begin pan drag hovered=%d focused=%d button=%d downR=%d downM=%d downL=%d gizmoActive=%d",
content.hovered ? 1 : 0,
content.focused ? 1 : 0,
m_panDragButton,
ImGui::IsMouseDown(ImGuiMouseButton_Right) ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Middle) ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Left) ? 1 : 0,
gizmoActive ? 1 : 0);
}
if (m_lookDragging && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
LogSceneViewNavigation(logger, "SceneView end look drag");
m_lookDragging = false;
m_loggedLookDelta = false;
}
if (m_panDragging && !ImGui::IsMouseDown(m_panDragButton)) {
LogSceneViewNavigation(logger, "SceneView end pan drag");
m_panDragging = false;
m_panDragButton = ImGuiMouseButton_Middle;
m_loggedPanDelta = false;
@@ -819,13 +766,6 @@ void SceneViewPanel::Render() {
input.mouseDelta = io.MouseDelta;
if (!m_loggedLookDelta &&
(input.mouseDelta.x != 0.0f || input.mouseDelta.y != 0.0f)) {
LogSceneViewNavigation(
logger,
"SceneView look delta=(%.2f, %.2f) hovered=%d downR=%d",
input.mouseDelta.x,
input.mouseDelta.y,
content.hovered ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Right) ? 1 : 0);
m_loggedLookDelta = true;
}
}
@@ -834,13 +774,6 @@ void SceneViewPanel::Render() {
input.mouseDelta = io.MouseDelta;
if (!m_loggedPanDelta &&
(input.mouseDelta.x != 0.0f || input.mouseDelta.y != 0.0f)) {
LogSceneViewNavigation(
logger,
"SceneView pan delta=(%.2f, %.2f) hovered=%d downM=%d",
input.mouseDelta.x,
input.mouseDelta.y,
content.hovered ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Middle) ? 1 : 0);
m_loggedPanDelta = true;
}
}
@@ -866,18 +799,18 @@ void SceneViewPanel::Render() {
showingScaleGizmo,
m_scaleGizmo);
viewportHostService->SetSceneViewTransientTransformGizmoOverlayData(
overlay,
BuildSceneViewportTransformGizmoHandleBuildInputs(
showingMoveGizmo,
m_moveGizmo,
drawGizmoFrameState.moveContext,
showingRotateGizmo,
m_rotateGizmo,
drawGizmoFrameState.rotateContext,
showingScaleGizmo,
m_scaleGizmo,
drawGizmoFrameState.scaleContext));
viewportHostService->SetSceneViewTransformGizmoOverlayState(
BuildSceneViewportTransformGizmoOverlayState(
BuildSceneViewportTransformGizmoHandleBuildInputs(
showingMoveGizmo,
m_moveGizmo,
drawGizmoFrameState.moveContext,
showingRotateGizmo,
m_rotateGizmo,
drawGizmoFrameState.rotateContext,
showingScaleGizmo,
m_scaleGizmo,
drawGizmoFrameState.scaleContext)));
DrawSceneViewportOverlay(
ImGui::GetWindowDrawList(),