Formalize scene viewport interaction actions

This commit is contained in:
2026-04-04 00:21:25 +08:00
parent 308b3b061c
commit 468dbfa7ac
7 changed files with 584 additions and 89 deletions

View File

@@ -83,6 +83,7 @@ add_executable(${PROJECT_NAME} WIN32
src/Viewport/SceneViewportRotateGizmo.cpp
src/Viewport/SceneViewportScaleGizmo.cpp
src/Viewport/SceneViewportHudOverlay.cpp
src/Viewport/SceneViewportInteractionActions.cpp
src/Viewport/SceneViewportInteractionResolver.cpp
src/Viewport/SceneViewportOrientationGizmo.cpp
src/Viewport/SceneViewportOverlayBuilder.cpp

View File

@@ -0,0 +1,128 @@
#include "SceneViewportInteractionActions.h"
#include "IViewportHostService.h"
namespace XCEngine {
namespace Editor {
SceneViewportActiveGizmoKind ToSceneViewportActiveGizmoKind(SceneViewportInteractionKind kind) {
switch (kind) {
case SceneViewportInteractionKind::MoveGizmo:
return SceneViewportActiveGizmoKind::Move;
case SceneViewportInteractionKind::RotateGizmo:
return SceneViewportActiveGizmoKind::Rotate;
case SceneViewportInteractionKind::ScaleGizmo:
return SceneViewportActiveGizmoKind::Scale;
case SceneViewportInteractionKind::OrientationGizmo:
case SceneViewportInteractionKind::SceneIcon:
case SceneViewportInteractionKind::None:
default:
return SceneViewportActiveGizmoKind::None;
}
}
SceneViewportHoveredHandleState BuildSceneViewportHoveredHandleState(
const SceneViewportInteractionResult& interaction) {
SceneViewportHoveredHandleState state = {};
state.hoveredGizmoKind = ToSceneViewportActiveGizmoKind(interaction.kind);
if (interaction.kind == SceneViewportInteractionKind::MoveGizmo) {
state.moveAxis = interaction.moveAxis;
state.movePlane = interaction.movePlane;
} else if (interaction.kind == SceneViewportInteractionKind::RotateGizmo) {
state.rotateAxis = interaction.rotateAxis;
} else if (interaction.kind == SceneViewportInteractionKind::ScaleGizmo) {
state.scaleHandle = interaction.scaleHandle;
}
return state;
}
void ApplySceneViewportHoveredHandleState(
const SceneViewportHoveredHandleState& hoveredHandleState,
bool gizmoActive,
bool showingMoveGizmo,
SceneViewportMoveGizmo& moveGizmo,
bool showingRotateGizmo,
SceneViewportRotateGizmo& rotateGizmo,
bool showingScaleGizmo,
SceneViewportScaleGizmo& scaleGizmo) {
if (gizmoActive) {
return;
}
if (showingMoveGizmo) {
moveGizmo.SetHoveredHandle(hoveredHandleState.moveAxis, hoveredHandleState.movePlane);
}
if (showingRotateGizmo) {
rotateGizmo.SetHoveredHandle(hoveredHandleState.rotateAxis);
}
if (showingScaleGizmo) {
scaleGizmo.SetHoveredHandle(hoveredHandleState.scaleHandle);
}
}
SceneViewportInteractionActions BuildSceneViewportInteractionActions(
const SceneViewportInteractionResult& interaction,
bool hasInteractiveViewport,
bool clickedLeft,
bool canResolveViewportInteraction) {
SceneViewportInteractionActions actions = {};
actions.hoveredGizmoKind = ToSceneViewportActiveGizmoKind(interaction.kind);
actions.orientationAxis = interaction.kind == SceneViewportInteractionKind::OrientationGizmo
? interaction.orientationAxis
: SceneViewportOrientationAxis::None;
actions.sceneIconEntityId = interaction.kind == SceneViewportInteractionKind::SceneIcon
? interaction.entityId
: 0;
actions.beginTransformGizmo =
hasInteractiveViewport &&
clickedLeft &&
actions.hoveredGizmoKind != SceneViewportActiveGizmoKind::None;
actions.orientationGizmoClick =
hasInteractiveViewport &&
clickedLeft &&
actions.orientationAxis != SceneViewportOrientationAxis::None;
actions.sceneIconClick =
hasInteractiveViewport &&
clickedLeft &&
actions.sceneIconEntityId != 0;
actions.selectSceneClick =
hasInteractiveViewport &&
clickedLeft &&
canResolveViewportInteraction &&
!interaction.HasHit();
return actions;
}
void DispatchSceneViewportInteractionActions(
const SceneViewportInteractionActions& actions,
IEditorContext& context,
IViewportHostService& viewportHostService,
const ImVec2& viewportSize,
const Math::Vector2& localMousePosition) {
if (actions.orientationGizmoClick) {
viewportHostService.AlignSceneViewToOrientationAxis(actions.orientationAxis);
}
if (actions.sceneIconClick) {
context.GetSelectionManager().SetSelectedEntity(actions.sceneIconEntityId);
return;
}
if (!actions.selectSceneClick) {
return;
}
const uint64_t selectedEntity = viewportHostService.PickSceneViewEntity(
context,
viewportSize,
ImVec2(localMousePosition.x, localMousePosition.y));
if (selectedEntity != 0) {
context.GetSelectionManager().SetSelectedEntity(selectedEntity);
} else {
context.GetSelectionManager().ClearSelection();
}
}
} // namespace Editor
} // namespace XCEngine

View File

@@ -0,0 +1,65 @@
#pragma once
#include "Core/IEditorContext.h"
#include "SceneViewportInteractionResolver.h"
#include "SceneViewportTransformGizmoFrameBuilder.h"
#include <imgui.h>
#include <cstdint>
namespace XCEngine {
namespace Editor {
struct SceneViewportHoveredHandleState {
SceneViewportActiveGizmoKind hoveredGizmoKind = SceneViewportActiveGizmoKind::None;
SceneViewportGizmoAxis moveAxis = SceneViewportGizmoAxis::None;
SceneViewportGizmoPlane movePlane = SceneViewportGizmoPlane::None;
SceneViewportRotateGizmoAxis rotateAxis = SceneViewportRotateGizmoAxis::None;
SceneViewportScaleGizmoHandle scaleHandle = SceneViewportScaleGizmoHandle::None;
};
struct SceneViewportInteractionActions {
SceneViewportActiveGizmoKind hoveredGizmoKind = SceneViewportActiveGizmoKind::None;
SceneViewportOrientationAxis orientationAxis = SceneViewportOrientationAxis::None;
uint64_t sceneIconEntityId = 0;
bool beginTransformGizmo = false;
bool orientationGizmoClick = false;
bool sceneIconClick = false;
bool selectSceneClick = false;
bool HasClickAction() const {
return beginTransformGizmo || orientationGizmoClick || sceneIconClick || selectSceneClick;
}
};
SceneViewportActiveGizmoKind ToSceneViewportActiveGizmoKind(SceneViewportInteractionKind kind);
SceneViewportHoveredHandleState BuildSceneViewportHoveredHandleState(
const SceneViewportInteractionResult& interaction);
void ApplySceneViewportHoveredHandleState(
const SceneViewportHoveredHandleState& hoveredHandleState,
bool gizmoActive,
bool showingMoveGizmo,
SceneViewportMoveGizmo& moveGizmo,
bool showingRotateGizmo,
SceneViewportRotateGizmo& rotateGizmo,
bool showingScaleGizmo,
SceneViewportScaleGizmo& scaleGizmo);
SceneViewportInteractionActions BuildSceneViewportInteractionActions(
const SceneViewportInteractionResult& interaction,
bool hasInteractiveViewport,
bool clickedLeft,
bool canResolveViewportInteraction);
void DispatchSceneViewportInteractionActions(
const SceneViewportInteractionActions& actions,
IEditorContext& context,
IViewportHostService& viewportHostService,
const ImVec2& viewportSize,
const Math::Vector2& localMousePosition);
} // namespace Editor
} // namespace XCEngine

View File

@@ -5,6 +5,7 @@
#include "SceneViewPanel.h"
#include "Viewport/SceneViewportEditorOverlayData.h"
#include "Viewport/SceneViewportHudOverlay.h"
#include "Viewport/SceneViewportInteractionActions.h"
#include "Viewport/SceneViewportInteractionResolver.h"
#include "Viewport/SceneViewportOverlayHandleBuilder.h"
#include "Viewport/SceneViewportMath.h"
@@ -38,22 +39,6 @@ const char* GetSceneViewportTransformSpaceModeLabel(SceneViewportTransformSpaceM
return mode == SceneViewportTransformSpaceMode::Global ? "Global" : "Local";
}
SceneViewportActiveGizmoKind ToActiveGizmoKind(SceneViewportInteractionKind kind) {
switch (kind) {
case SceneViewportInteractionKind::MoveGizmo:
return SceneViewportActiveGizmoKind::Move;
case SceneViewportInteractionKind::RotateGizmo:
return SceneViewportActiveGizmoKind::Rotate;
case SceneViewportInteractionKind::ScaleGizmo:
return SceneViewportActiveGizmoKind::Scale;
case SceneViewportInteractionKind::OrientationGizmo:
case SceneViewportInteractionKind::SceneIcon:
case SceneViewportInteractionKind::None:
default:
return SceneViewportActiveGizmoKind::None;
}
}
float GetSceneToolbarToggleWidth(const char* firstLabel, const char* secondLabel) {
constexpr float kHorizontalPadding = 10.0f;
constexpr float kMinWidth = 68.0f;
@@ -440,58 +425,22 @@ void SceneViewPanel::Render() {
hoveredInteraction = ResolveSceneViewportInteraction(interactionRequest);
}
if (!gizmoActive) {
if (showingMoveGizmo) {
m_moveGizmo.SetHoveredHandle(
hoveredInteraction.kind == SceneViewportInteractionKind::MoveGizmo
? hoveredInteraction.moveAxis
: SceneViewportGizmoAxis::None,
hoveredInteraction.kind == SceneViewportInteractionKind::MoveGizmo
? hoveredInteraction.movePlane
: SceneViewportGizmoPlane::None);
}
if (showingRotateGizmo) {
m_rotateGizmo.SetHoveredHandle(
hoveredInteraction.kind == SceneViewportInteractionKind::RotateGizmo
? hoveredInteraction.rotateAxis
: SceneViewportRotateGizmoAxis::None);
}
if (showingScaleGizmo) {
m_scaleGizmo.SetHoveredHandle(
hoveredInteraction.kind == SceneViewportInteractionKind::ScaleGizmo
? hoveredInteraction.scaleHandle
: SceneViewportScaleGizmoHandle::None);
}
}
ApplySceneViewportHoveredHandleState(
BuildSceneViewportHoveredHandleState(hoveredInteraction),
gizmoActive,
showingMoveGizmo,
m_moveGizmo,
showingRotateGizmo,
m_rotateGizmo,
showingScaleGizmo,
m_scaleGizmo);
const SceneViewportActiveGizmoKind hoveredGizmoKind =
ToActiveGizmoKind(hoveredInteraction.kind);
const bool gizmoHovering = hoveredGizmoKind != SceneViewportActiveGizmoKind::None;
const SceneViewportOrientationAxis orientationAxisHit =
hoveredInteraction.kind == SceneViewportInteractionKind::OrientationGizmo
? hoveredInteraction.orientationAxis
: SceneViewportOrientationAxis::None;
const uint64_t clickedSceneIconEntity =
hoveredInteraction.kind == SceneViewportInteractionKind::SceneIcon
? hoveredInteraction.entityId
: 0;
const bool beginTransformGizmo =
hasInteractiveViewport &&
content.clickedLeft &&
gizmoHovering;
const bool orientationGizmoClick =
hasInteractiveViewport &&
content.clickedLeft &&
orientationAxisHit != SceneViewportOrientationAxis::None;
const bool sceneIconClick =
hasInteractiveViewport &&
content.clickedLeft &&
clickedSceneIconEntity != 0;
const bool selectClick =
hasInteractiveViewport &&
content.clickedLeft &&
canResolveViewportInteraction &&
!hoveredInteraction.HasHit();
const SceneViewportInteractionActions interactionActions =
BuildSceneViewportInteractionActions(
hoveredInteraction,
hasInteractiveViewport,
content.clickedLeft,
canResolveViewportInteraction);
const bool beginLeftPanDrag = usingViewMoveTool
? ShouldBeginSceneViewportNavigationDrag(
hasInteractiveViewport,
@@ -517,38 +466,27 @@ void SceneViewPanel::Render() {
ImGuiMouseButton_Middle);
const bool beginPanDrag = beginLeftPanDrag || beginMiddlePanDrag;
if (toolOverlay.clicked || beginTransformGizmo || orientationGizmoClick || sceneIconClick || selectClick || beginLookDrag ||
if (toolOverlay.clicked || interactionActions.HasClickAction() || beginLookDrag ||
beginPanDrag) {
ImGui::SetWindowFocus();
}
if (beginTransformGizmo) {
if (hoveredGizmoKind == SceneViewportActiveGizmoKind::Scale) {
if (interactionActions.beginTransformGizmo) {
if (interactionActions.hoveredGizmoKind == SceneViewportActiveGizmoKind::Scale) {
m_scaleGizmo.TryBeginDrag(gizmoFrameState.scaleContext, m_context->GetUndoManager());
} else if (hoveredGizmoKind == SceneViewportActiveGizmoKind::Move) {
} else if (interactionActions.hoveredGizmoKind == SceneViewportActiveGizmoKind::Move) {
m_moveGizmo.TryBeginDrag(gizmoFrameState.moveContext, m_context->GetUndoManager());
} else if (hoveredGizmoKind == SceneViewportActiveGizmoKind::Rotate) {
} else if (interactionActions.hoveredGizmoKind == SceneViewportActiveGizmoKind::Rotate) {
m_rotateGizmo.TryBeginDrag(gizmoFrameState.rotateContext, m_context->GetUndoManager());
}
}
if (orientationGizmoClick) {
viewportHostService->AlignSceneViewToOrientationAxis(orientationAxisHit);
}
if (sceneIconClick) {
m_context->GetSelectionManager().SetSelectedEntity(clickedSceneIconEntity);
} else if (selectClick) {
const uint64_t selectedEntity = viewportHostService->PickSceneViewEntity(
*m_context,
content.availableSize,
ImVec2(localMousePosition.x, localMousePosition.y));
if (selectedEntity != 0) {
m_context->GetSelectionManager().SetSelectedEntity(selectedEntity);
} else {
m_context->GetSelectionManager().ClearSelection();
}
}
DispatchSceneViewportInteractionActions(
interactionActions,
*m_context,
*viewportHostService,
content.availableSize,
localMousePosition);
if (gizmoActive) {
if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) {