Formalize scene viewport interaction frame helpers

This commit is contained in:
2026-04-04 01:42:35 +08:00
parent a920ca7a6a
commit e636abb76d
5 changed files with 531 additions and 79 deletions

View File

@@ -0,0 +1,132 @@
#pragma once
#include "Core/IEditorContext.h"
#include "IViewportHostService.h"
#include "SceneViewportEditorModes.h"
#include "SceneViewportHudOverlay.h"
#include "SceneViewportInteractionResolver.h"
#include "SceneViewportTransformGizmoCoordinator.h"
namespace XCEngine {
namespace Editor {
struct SceneViewportToolState {
bool usingViewMoveTool = false;
bool usingTransformTool = false;
bool showingMoveGizmo = false;
bool showingRotateGizmo = false;
bool showingScaleGizmo = false;
bool useCenterPivot = false;
bool localSpace = false;
SceneViewportTransformGizmoFrameOptions gizmoFrameOptions = {};
};
inline SceneViewportToolState BuildSceneViewportToolState(
SceneViewportToolMode toolMode,
SceneViewportPivotMode pivotMode,
SceneViewportTransformSpaceMode transformSpaceMode) {
SceneViewportToolState state = {};
state.usingViewMoveTool = toolMode == SceneViewportToolMode::ViewMove;
state.usingTransformTool = toolMode == SceneViewportToolMode::Transform;
state.showingMoveGizmo = toolMode == SceneViewportToolMode::Move || state.usingTransformTool;
state.showingRotateGizmo = toolMode == SceneViewportToolMode::Rotate || state.usingTransformTool;
state.showingScaleGizmo = toolMode == SceneViewportToolMode::Scale || state.usingTransformTool;
state.useCenterPivot = pivotMode == SceneViewportPivotMode::Center;
state.localSpace = transformSpaceMode == SceneViewportTransformSpaceMode::Local;
state.gizmoFrameOptions = BuildSceneViewportTransformGizmoFrameOptions(
state.useCenterPivot,
state.localSpace,
state.usingTransformTool,
state.showingMoveGizmo,
state.showingRotateGizmo,
state.showingScaleGizmo);
return state;
}
struct SceneViewportFrameGeometry {
Math::Vector2 viewportSize = Math::Vector2::Zero();
Math::Vector2 localMousePosition = Math::Vector2::Zero();
};
inline SceneViewportFrameGeometry BuildSceneViewportFrameGeometry(
const ImVec2& viewportSize,
const ImVec2& viewportMin,
const ImVec2& absoluteMousePosition) {
SceneViewportFrameGeometry geometry = {};
geometry.viewportSize = Math::Vector2(viewportSize.x, viewportSize.y);
geometry.localMousePosition = Math::Vector2(
absoluteMousePosition.x - viewportMin.x,
absoluteMousePosition.y - viewportMin.y);
return geometry;
}
struct SceneViewportInteractionFrameState {
bool hasInteractiveViewport = false;
SceneViewportOverlayData overlay = {};
SceneViewportTransformGizmoFrameUpdate gizmoFrameUpdate = {};
SceneViewportTransformGizmoFrameState gizmoFrameState = {};
const SceneViewportOverlayFrameData* overlayFrameData = nullptr;
SceneViewportActiveGizmoKind activeGizmoKind = SceneViewportActiveGizmoKind::None;
bool gizmoActive = false;
SceneViewportHudOverlayData hudOverlay = {};
};
inline SceneViewportInteractionFrameState BuildSceneViewportInteractionFrameState(
IEditorContext& context,
IViewportHostService& viewportHostService,
bool hasInteractiveViewport,
const SceneViewportFrameGeometry& geometry,
const SceneViewportTransformGizmoFrameOptions& gizmoFrameOptions,
SceneViewportMoveGizmo& moveGizmo,
SceneViewportRotateGizmo& rotateGizmo,
SceneViewportScaleGizmo& scaleGizmo,
const SceneViewportOverlayFrameData& emptyOverlayFrameData) {
SceneViewportInteractionFrameState state = {};
state.hasInteractiveViewport = hasInteractiveViewport;
state.overlayFrameData = &emptyOverlayFrameData;
if (!hasInteractiveViewport) {
CancelSceneViewportTransformGizmoFrame(context, moveGizmo, rotateGizmo, scaleGizmo);
state.hudOverlay = BuildSceneViewportHudOverlayData(state.overlay);
return state;
}
state.overlay = viewportHostService.GetSceneViewOverlayData();
state.gizmoFrameUpdate = RefreshAndSubmitSceneViewportTransformGizmoFrame(
viewportHostService,
BuildSceneViewportTransformGizmoRefreshRequest(
context,
state.overlay,
geometry.viewportSize,
geometry.localMousePosition,
gizmoFrameOptions),
moveGizmo,
rotateGizmo,
scaleGizmo);
state.gizmoFrameState = state.gizmoFrameUpdate.frameState;
state.overlayFrameData = &viewportHostService.GetSceneViewEditorOverlayFrameData(context);
state.activeGizmoKind = state.gizmoFrameUpdate.overlaySubmission.activeGizmoKind;
state.gizmoActive = state.gizmoFrameUpdate.overlaySubmission.GizmoActive();
state.hudOverlay = BuildSceneViewportHudOverlayData(state.overlay);
return state;
}
inline SceneViewportInteractionResolveRequest BuildSceneViewportInteractionResolveRequest(
const SceneViewportInteractionFrameState& frameState,
const SceneViewportFrameGeometry& geometry,
const ImVec2& viewportMin,
const ImVec2& viewportMax,
const ImVec2& absoluteMousePosition) {
SceneViewportInteractionResolveRequest request = {};
request.overlayFrameData = frameState.overlayFrameData;
request.viewportSize = geometry.viewportSize;
request.localMousePosition = geometry.localMousePosition;
request.hudOverlay = &frameState.hudOverlay;
request.viewportMin = viewportMin;
request.viewportMax = viewportMax;
request.absoluteMousePosition = absoluteMousePosition;
return request;
}
} // namespace Editor
} // namespace XCEngine

View File

@@ -5,6 +5,7 @@
#include "SceneViewPanel.h"
#include "Viewport/SceneViewportEditorOverlayData.h"
#include "Viewport/SceneViewportHudOverlay.h"
#include "Viewport/SceneViewportInteractionFrame.h"
#include "Viewport/SceneViewportInteractionActions.h"
#include "Viewport/SceneViewportInteractionResolver.h"
#include "Viewport/SceneViewportMath.h"
@@ -292,91 +293,51 @@ void SceneViewPanel::Render() {
m_toolMode = toolShortcutAction.targetTool;
}
const bool usingViewMoveTool = m_toolMode == SceneViewportToolMode::ViewMove;
const bool usingTransformTool = m_toolMode == SceneViewportToolMode::Transform;
const bool showingMoveGizmo = m_toolMode == SceneViewportToolMode::Move || usingTransformTool;
const bool showingRotateGizmo = m_toolMode == SceneViewportToolMode::Rotate || usingTransformTool;
const bool showingScaleGizmo = m_toolMode == SceneViewportToolMode::Scale || usingTransformTool;
const bool useCenterPivot = m_pivotMode == SceneViewportPivotMode::Center;
const bool localSpace = m_transformSpaceMode == SceneViewportTransformSpaceMode::Local;
const SceneViewportTransformGizmoFrameOptions gizmoFrameOptions =
BuildSceneViewportTransformGizmoFrameOptions(
useCenterPivot,
localSpace,
usingTransformTool,
showingMoveGizmo,
showingRotateGizmo,
showingScaleGizmo);
const Math::Vector2 viewportSize(content.availableSize.x, content.availableSize.y);
const Math::Vector2 localMousePosition(
io.MousePos.x - content.itemMin.x,
io.MousePos.y - content.itemMin.y);
SceneViewportOverlayData overlay = {};
SceneViewportTransformGizmoFrameUpdate interactionGizmoFrame = {};
SceneViewportTransformGizmoFrameState gizmoFrameState = {};
const SceneViewportToolState toolState = BuildSceneViewportToolState(
m_toolMode,
m_pivotMode,
m_transformSpaceMode);
const SceneViewportFrameGeometry frameGeometry = BuildSceneViewportFrameGeometry(
content.availableSize,
content.itemMin,
io.MousePos);
SceneViewportOverlayFrameData emptySceneOverlayFrameData = {};
if (hasInteractiveViewport) {
overlay = viewportHostService->GetSceneViewOverlayData();
interactionGizmoFrame = RefreshAndSubmitSceneViewportTransformGizmoFrame(
*viewportHostService,
BuildSceneViewportTransformGizmoRefreshRequest(
*m_context,
overlay,
viewportSize,
localMousePosition,
gizmoFrameOptions),
m_moveGizmo,
m_rotateGizmo,
m_scaleGizmo);
gizmoFrameState = interactionGizmoFrame.frameState;
} else {
CancelSceneViewportTransformGizmoFrame(
const SceneViewportInteractionFrameState interactionFrameState =
BuildSceneViewportInteractionFrameState(
*m_context,
*viewportHostService,
hasInteractiveViewport,
frameGeometry,
toolState.gizmoFrameOptions,
m_moveGizmo,
m_rotateGizmo,
m_scaleGizmo);
}
const SceneViewportTransformGizmoOverlaySubmission interactionGizmoSubmission =
hasInteractiveViewport
? interactionGizmoFrame.overlaySubmission
: SceneViewportTransformGizmoOverlaySubmission{};
const SceneViewportOverlayFrameData& interactionOverlayFrameData =
hasInteractiveViewport
? viewportHostService->GetSceneViewEditorOverlayFrameData(*m_context)
: emptySceneOverlayFrameData;
const SceneViewportActiveGizmoKind activeGizmoKind = interactionGizmoSubmission.activeGizmoKind;
const bool gizmoActive = interactionGizmoSubmission.GizmoActive();
const SceneViewportHudOverlayData interactionHudOverlay =
BuildSceneViewportHudOverlayData(overlay);
m_scaleGizmo,
emptySceneOverlayFrameData);
SceneViewportInteractionResult hoveredInteraction = {};
const bool canResolveViewportInteraction = CanResolveSceneViewportInteraction(
hasInteractiveViewport,
viewportContentHovered,
usingViewMoveTool,
toolState.usingViewMoveTool,
m_navigationState,
gizmoActive);
interactionFrameState.gizmoActive);
if (canResolveViewportInteraction) {
SceneViewportInteractionResolveRequest interactionRequest = {};
interactionRequest.overlayFrameData = &interactionOverlayFrameData;
interactionRequest.viewportSize = viewportSize;
interactionRequest.localMousePosition = localMousePosition;
interactionRequest.hudOverlay = &interactionHudOverlay;
interactionRequest.viewportMin = content.itemMin;
interactionRequest.viewportMax = content.itemMax;
interactionRequest.absoluteMousePosition = io.MousePos;
hoveredInteraction = ResolveSceneViewportInteraction(interactionRequest);
hoveredInteraction = ResolveSceneViewportInteraction(
BuildSceneViewportInteractionResolveRequest(
interactionFrameState,
frameGeometry,
content.itemMin,
content.itemMax,
io.MousePos));
}
ApplySceneViewportHoveredHandleState(
BuildSceneViewportHoveredHandleState(hoveredInteraction),
gizmoActive,
showingMoveGizmo,
interactionFrameState.gizmoActive,
toolState.showingMoveGizmo,
m_moveGizmo,
showingRotateGizmo,
toolState.showingRotateGizmo,
m_rotateGizmo,
showingScaleGizmo,
toolState.showingScaleGizmo,
m_scaleGizmo);
const SceneViewportInteractionActions interactionActions =
@@ -389,8 +350,8 @@ void SceneViewPanel::Render() {
navigationRequest.state = m_navigationState;
navigationRequest.hasInteractiveViewport = hasInteractiveViewport;
navigationRequest.viewportHovered = viewportContentHovered;
navigationRequest.usingViewMoveTool = usingViewMoveTool;
navigationRequest.gizmoActive = gizmoActive;
navigationRequest.usingViewMoveTool = toolState.usingViewMoveTool;
navigationRequest.gizmoActive = interactionFrameState.gizmoActive;
navigationRequest.clickedLeft = content.clickedLeft;
navigationRequest.clickedRight = content.clickedRight;
navigationRequest.clickedMiddle = content.clickedMiddle;
@@ -409,7 +370,7 @@ void SceneViewPanel::Render() {
ExecuteSceneViewportTransformGizmoLifecycleCommand(
BuildBeginSceneViewportTransformGizmoLifecycleCommand(interactionActions),
m_context->GetUndoManager(),
gizmoFrameState,
interactionFrameState.gizmoFrameState,
m_moveGizmo,
m_rotateGizmo,
m_scaleGizmo);
@@ -419,14 +380,14 @@ void SceneViewPanel::Render() {
*m_context,
*viewportHostService,
content.availableSize,
localMousePosition);
frameGeometry.localMousePosition);
ExecuteSceneViewportTransformGizmoLifecycleCommand(
BuildFrameSceneViewportTransformGizmoLifecycleCommand(
activeGizmoKind,
interactionFrameState.activeGizmoKind,
ImGui::IsMouseDown(ImGuiMouseButton_Left)),
m_context->GetUndoManager(),
gizmoFrameState,
interactionFrameState.gizmoFrameState,
m_moveGizmo,
m_rotateGizmo,
m_scaleGizmo);
@@ -466,15 +427,15 @@ void SceneViewPanel::Render() {
viewportHostService->UpdateSceneViewInput(*m_context, input);
if (content.hasViewportArea && content.frame.hasTexture) {
overlay = viewportHostService->GetSceneViewOverlayData();
const SceneViewportOverlayData overlay = viewportHostService->GetSceneViewOverlayData();
RefreshAndSubmitSceneViewportTransformGizmoFrame(
*viewportHostService,
BuildSceneViewportTransformGizmoRefreshRequest(
*m_context,
overlay,
viewportSize,
localMousePosition,
gizmoFrameOptions),
frameGeometry.viewportSize,
frameGeometry.localMousePosition,
toolState.gizmoFrameOptions),
m_moveGizmo,
m_rotateGizmo,
m_scaleGizmo);