Formalize scene viewport navigation input helpers

This commit is contained in:
2026-04-04 01:28:53 +08:00
parent c3680258e0
commit a920ca7a6a
7 changed files with 558 additions and 188 deletions

View File

@@ -8,6 +8,7 @@
#include "Viewport/SceneViewportInteractionActions.h"
#include "Viewport/SceneViewportInteractionResolver.h"
#include "Viewport/SceneViewportMath.h"
#include "Viewport/SceneViewportNavigation.h"
#include "Viewport/SceneViewportTransformGizmoCoordinator.h"
#include "ViewportPanelContent.h"
#include "Platform/Win32Utf8.h"
@@ -243,21 +244,6 @@ SceneViewportToolOverlayResult RenderSceneViewportToolOverlay(
return result;
}
bool ShouldBeginSceneViewportNavigationDrag(
bool hasInteractiveViewport,
bool hovered,
bool activeDrag,
bool otherDrag,
bool gizmoActive,
ImGuiMouseButton button) {
return hasInteractiveViewport &&
hovered &&
!activeDrag &&
!otherDrag &&
!gizmoActive &&
ImGui::IsMouseClicked(button);
}
} // namespace
SceneViewPanel::SceneViewPanel() : Panel("Scene") {}
@@ -279,56 +265,31 @@ void SceneViewPanel::Render() {
const bool viewportContentHovered = content.hovered && !toolOverlay.hovered;
if (toolOverlay.clicked) {
if (m_moveGizmo.IsActive()) {
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (m_rotateGizmo.IsActive()) {
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (m_scaleGizmo.IsActive()) {
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
}
CancelSceneViewportTransformGizmoFrame(*m_context, m_moveGizmo, m_rotateGizmo, m_scaleGizmo);
m_toolMode = toolOverlay.clickedTool;
}
const bool allowToolShortcut = !io.WantTextInput && !m_lookDragging && !m_panDragging;
if (allowToolShortcut) {
if (ImGui::IsKeyPressed(ImGuiKey_Q, false)) {
if (m_moveGizmo.IsActive()) {
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (m_rotateGizmo.IsActive()) {
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (m_scaleGizmo.IsActive()) {
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
}
m_toolMode = SceneViewportToolMode::ViewMove;
} else if (ImGui::IsKeyPressed(ImGuiKey_W, false)) {
if (m_rotateGizmo.IsActive()) {
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (m_scaleGizmo.IsActive()) {
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
}
m_toolMode = SceneViewportToolMode::Move;
} else if (ImGui::IsKeyPressed(ImGuiKey_E, false)) {
if (m_moveGizmo.IsActive()) {
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (m_scaleGizmo.IsActive()) {
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
}
m_toolMode = SceneViewportToolMode::Rotate;
} else if (ImGui::IsKeyPressed(ImGuiKey_R, false)) {
if (m_moveGizmo.IsActive()) {
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (m_rotateGizmo.IsActive()) {
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
}
m_toolMode = SceneViewportToolMode::Scale;
SceneViewportToolShortcutRequest toolShortcutRequest = {};
toolShortcutRequest.wantTextInput = io.WantTextInput;
toolShortcutRequest.lookDragging = m_navigationState.lookDragging;
toolShortcutRequest.panDragging = m_navigationState.panDragging;
toolShortcutRequest.pressedViewMove = ImGui::IsKeyPressed(ImGuiKey_Q, false);
toolShortcutRequest.pressedMove = ImGui::IsKeyPressed(ImGuiKey_W, false);
toolShortcutRequest.pressedRotate = ImGui::IsKeyPressed(ImGuiKey_E, false);
toolShortcutRequest.pressedScale = ImGui::IsKeyPressed(ImGuiKey_R, false);
const SceneViewportToolShortcutAction toolShortcutAction =
BuildSceneViewportToolShortcutAction(toolShortcutRequest);
if (toolShortcutAction.HasAction()) {
if (toolShortcutAction.cancelMoveGizmo && m_moveGizmo.IsActive()) {
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (toolShortcutAction.cancelRotateGizmo && m_rotateGizmo.IsActive()) {
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (toolShortcutAction.cancelScaleGizmo && m_scaleGizmo.IsActive()) {
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
}
m_toolMode = toolShortcutAction.targetTool;
}
const bool usingViewMoveTool = m_toolMode == SceneViewportToolMode::ViewMove;
@@ -390,14 +351,12 @@ void SceneViewPanel::Render() {
const SceneViewportHudOverlayData interactionHudOverlay =
BuildSceneViewportHudOverlayData(overlay);
SceneViewportInteractionResult hoveredInteraction = {};
const bool canResolveViewportInteraction =
hasInteractiveViewport &&
viewportContentHovered &&
!usingViewMoveTool &&
!m_lookDragging &&
!m_panDragging &&
!toolOverlay.hovered &&
!gizmoActive;
const bool canResolveViewportInteraction = CanResolveSceneViewportInteraction(
hasInteractiveViewport,
viewportContentHovered,
usingViewMoveTool,
m_navigationState,
gizmoActive);
if (canResolveViewportInteraction) {
SceneViewportInteractionResolveRequest interactionRequest = {};
interactionRequest.overlayFrameData = &interactionOverlayFrameData;
@@ -426,33 +385,24 @@ void SceneViewPanel::Render() {
hasInteractiveViewport,
content.clickedLeft,
canResolveViewportInteraction);
const bool beginLeftPanDrag = usingViewMoveTool
? ShouldBeginSceneViewportNavigationDrag(
hasInteractiveViewport,
viewportContentHovered,
m_panDragging,
m_lookDragging,
gizmoActive,
ImGuiMouseButton_Left)
: false;
const bool beginLookDrag = ShouldBeginSceneViewportNavigationDrag(
hasInteractiveViewport,
viewportContentHovered,
m_lookDragging,
m_panDragging,
gizmoActive,
ImGuiMouseButton_Right);
const bool beginMiddlePanDrag = ShouldBeginSceneViewportNavigationDrag(
hasInteractiveViewport,
viewportContentHovered,
m_panDragging,
m_lookDragging,
gizmoActive,
ImGuiMouseButton_Middle);
const bool beginPanDrag = beginLeftPanDrag || beginMiddlePanDrag;
SceneViewportNavigationRequest navigationRequest = {};
navigationRequest.state = m_navigationState;
navigationRequest.hasInteractiveViewport = hasInteractiveViewport;
navigationRequest.viewportHovered = viewportContentHovered;
navigationRequest.usingViewMoveTool = usingViewMoveTool;
navigationRequest.gizmoActive = gizmoActive;
navigationRequest.clickedLeft = content.clickedLeft;
navigationRequest.clickedRight = content.clickedRight;
navigationRequest.clickedMiddle = content.clickedMiddle;
navigationRequest.leftMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
navigationRequest.rightMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Right);
navigationRequest.middleMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
const SceneViewportNavigationUpdate navigationUpdate =
UpdateSceneViewportNavigationState(navigationRequest);
m_navigationState = navigationUpdate.state;
if (toolOverlay.clicked || interactionActions.HasClickAction() || beginLookDrag ||
beginPanDrag) {
if (toolOverlay.clicked || interactionActions.HasClickAction() || navigationUpdate.beginLookDrag ||
navigationUpdate.beginPanDrag) {
ImGui::SetWindowFocus();
}
@@ -481,81 +431,37 @@ void SceneViewPanel::Render() {
m_rotateGizmo,
m_scaleGizmo);
if (beginLookDrag) {
m_lookDragging = true;
m_panDragging = false;
m_loggedLookDelta = false;
m_loggedPanDelta = false;
}
if (beginPanDrag) {
m_panDragging = true;
m_lookDragging = false;
m_panDragButton = beginLeftPanDrag ? ImGuiMouseButton_Left : ImGuiMouseButton_Middle;
m_loggedPanDelta = false;
m_loggedLookDelta = false;
}
if (m_lookDragging && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
m_lookDragging = false;
m_loggedLookDelta = false;
}
if (m_panDragging && !ImGui::IsMouseDown(m_panDragButton)) {
m_panDragging = false;
m_panDragButton = ImGuiMouseButton_Middle;
m_loggedPanDelta = false;
}
if (m_lookDragging || m_panDragging || m_moveGizmo.IsActive() || m_rotateGizmo.IsActive() ||
m_scaleGizmo.IsActive()) {
const SceneViewportCaptureFlags captureFlags = BuildSceneViewportCaptureFlags(
SceneViewportCaptureRequest{
m_navigationState,
m_moveGizmo.IsActive(),
m_rotateGizmo.IsActive(),
m_scaleGizmo.IsActive()});
if (captureFlags.captureMouse) {
ImGui::SetNextFrameWantCaptureMouse(true);
}
if (m_lookDragging) {
if (captureFlags.captureKeyboard) {
ImGui::SetNextFrameWantCaptureKeyboard(true);
}
SceneViewportInput input = {};
input.viewportSize = content.availableSize;
input.deltaTime = io.DeltaTime;
input.hovered = viewportContentHovered;
input.focused = content.focused || m_lookDragging || m_panDragging;
input.mouseWheel = (viewportContentHovered && !m_lookDragging) ? io.MouseWheel : 0.0f;
input.flySpeedDelta = (viewportContentHovered && m_lookDragging) ? io.MouseWheel : 0.0f;
input.looking = m_lookDragging;
input.orbiting = false;
input.panning = m_panDragging;
input.fastMove = io.KeyShift;
input.focusSelectionRequested =
input.focused && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_F, false);
if (m_lookDragging && !io.WantTextInput) {
input.moveForward =
(ImGui::IsKeyDown(ImGuiKey_W) ? 1.0f : 0.0f) -
(ImGui::IsKeyDown(ImGuiKey_S) ? 1.0f : 0.0f);
input.moveRight =
(ImGui::IsKeyDown(ImGuiKey_D) ? 1.0f : 0.0f) -
(ImGui::IsKeyDown(ImGuiKey_A) ? 1.0f : 0.0f);
input.moveUp =
(ImGui::IsKeyDown(ImGuiKey_E) ? 1.0f : 0.0f) -
(ImGui::IsKeyDown(ImGuiKey_Q) ? 1.0f : 0.0f);
}
if (m_lookDragging || m_panDragging) {
if (m_lookDragging) {
input.mouseDelta = io.MouseDelta;
if (!m_loggedLookDelta &&
(input.mouseDelta.x != 0.0f || input.mouseDelta.y != 0.0f)) {
m_loggedLookDelta = true;
}
}
if (m_panDragging) {
input.mouseDelta = io.MouseDelta;
if (!m_loggedPanDelta &&
(input.mouseDelta.x != 0.0f || input.mouseDelta.y != 0.0f)) {
m_loggedPanDelta = true;
}
}
}
SceneViewportInputBuildRequest inputRequest = {};
inputRequest.state = m_navigationState;
inputRequest.viewportSize = content.availableSize;
inputRequest.mouseDelta = io.MouseDelta;
inputRequest.mouseWheel = io.MouseWheel;
inputRequest.deltaTime = io.DeltaTime;
inputRequest.viewportHovered = viewportContentHovered;
inputRequest.viewportFocused = content.focused;
inputRequest.wantTextInput = io.WantTextInput;
inputRequest.fastMove = io.KeyShift;
inputRequest.focusSelectionKeyPressed = ImGui::IsKeyPressed(ImGuiKey_F, false);
inputRequest.moveForwardKeyDown = ImGui::IsKeyDown(ImGuiKey_W);
inputRequest.moveBackwardKeyDown = ImGui::IsKeyDown(ImGuiKey_S);
inputRequest.moveRightKeyDown = ImGui::IsKeyDown(ImGuiKey_D);
inputRequest.moveLeftKeyDown = ImGui::IsKeyDown(ImGuiKey_A);
inputRequest.moveUpKeyDown = ImGui::IsKeyDown(ImGuiKey_E);
inputRequest.moveDownKeyDown = ImGui::IsKeyDown(ImGuiKey_Q);
const SceneViewportInput input = BuildSceneViewportInput(inputRequest);
viewportHostService->UpdateSceneViewInput(*m_context, input);