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

@@ -0,0 +1,27 @@
#pragma once
#include <cstdint>
namespace XCEngine {
namespace Editor {
enum class SceneViewportToolMode : uint8_t {
ViewMove = 0,
Move,
Rotate,
Scale,
Transform
};
enum class SceneViewportPivotMode : uint8_t {
Pivot = 0,
Center
};
enum class SceneViewportTransformSpaceMode : uint8_t {
Global = 0,
Local
};
} // namespace Editor
} // namespace XCEngine

View File

@@ -0,0 +1,279 @@
#pragma once
#include "IViewportHostService.h"
#include "SceneViewportEditorModes.h"
#include <imgui.h>
namespace XCEngine {
namespace Editor {
struct SceneViewportToolShortcutRequest {
bool wantTextInput = false;
bool lookDragging = false;
bool panDragging = false;
bool pressedViewMove = false;
bool pressedMove = false;
bool pressedRotate = false;
bool pressedScale = false;
};
struct SceneViewportToolShortcutAction {
SceneViewportToolMode targetTool = SceneViewportToolMode::Move;
bool triggered = false;
bool cancelMoveGizmo = false;
bool cancelRotateGizmo = false;
bool cancelScaleGizmo = false;
bool HasAction() const {
return triggered;
}
};
inline bool CanUseSceneViewportToolShortcut(const SceneViewportToolShortcutRequest& request) {
return !request.wantTextInput && !request.lookDragging && !request.panDragging;
}
inline SceneViewportToolShortcutAction BuildSceneViewportToolShortcutAction(
const SceneViewportToolShortcutRequest& request) {
SceneViewportToolShortcutAction action = {};
if (!CanUseSceneViewportToolShortcut(request)) {
return action;
}
if (request.pressedViewMove) {
action.targetTool = SceneViewportToolMode::ViewMove;
action.triggered = true;
action.cancelMoveGizmo = true;
action.cancelRotateGizmo = true;
action.cancelScaleGizmo = true;
} else if (request.pressedMove) {
action.targetTool = SceneViewportToolMode::Move;
action.triggered = true;
action.cancelRotateGizmo = true;
action.cancelScaleGizmo = true;
} else if (request.pressedRotate) {
action.targetTool = SceneViewportToolMode::Rotate;
action.triggered = true;
action.cancelMoveGizmo = true;
action.cancelScaleGizmo = true;
} else if (request.pressedScale) {
action.targetTool = SceneViewportToolMode::Scale;
action.triggered = true;
action.cancelMoveGizmo = true;
action.cancelRotateGizmo = true;
}
return action;
}
struct SceneViewportNavigationState {
bool lookDragging = false;
bool panDragging = false;
int panDragButton = ImGuiMouseButton_Middle;
};
struct SceneViewportNavigationRequest {
SceneViewportNavigationState state = {};
bool hasInteractiveViewport = false;
bool viewportHovered = false;
bool usingViewMoveTool = false;
bool gizmoActive = false;
bool clickedLeft = false;
bool clickedRight = false;
bool clickedMiddle = false;
bool leftMouseDown = false;
bool rightMouseDown = false;
bool middleMouseDown = false;
};
struct SceneViewportNavigationUpdate {
SceneViewportNavigationState state = {};
bool beginLookDrag = false;
bool beginPanDrag = false;
bool beginLeftPanDrag = false;
bool beginMiddlePanDrag = false;
};
inline bool ShouldBeginSceneViewportNavigationDrag(
bool hasInteractiveViewport,
bool hovered,
bool activeDrag,
bool otherDrag,
bool gizmoActive,
bool clicked) {
return hasInteractiveViewport && hovered && !activeDrag && !otherDrag && !gizmoActive && clicked;
}
inline bool IsSceneViewportPanDragButtonDown(
const SceneViewportNavigationRequest& request,
int button) {
switch (button) {
case ImGuiMouseButton_Left:
return request.leftMouseDown;
case ImGuiMouseButton_Right:
return request.rightMouseDown;
case ImGuiMouseButton_Middle:
default:
return request.middleMouseDown;
}
}
inline SceneViewportNavigationUpdate UpdateSceneViewportNavigationState(
const SceneViewportNavigationRequest& request) {
SceneViewportNavigationUpdate update = {};
update.state = request.state;
update.beginLeftPanDrag = request.usingViewMoveTool
? ShouldBeginSceneViewportNavigationDrag(
request.hasInteractiveViewport,
request.viewportHovered,
request.state.panDragging,
request.state.lookDragging,
request.gizmoActive,
request.clickedLeft)
: false;
update.beginLookDrag = ShouldBeginSceneViewportNavigationDrag(
request.hasInteractiveViewport,
request.viewportHovered,
request.state.lookDragging,
request.state.panDragging,
request.gizmoActive,
request.clickedRight);
update.beginMiddlePanDrag = ShouldBeginSceneViewportNavigationDrag(
request.hasInteractiveViewport,
request.viewportHovered,
request.state.panDragging,
request.state.lookDragging,
request.gizmoActive,
request.clickedMiddle);
update.beginPanDrag = update.beginLeftPanDrag || update.beginMiddlePanDrag;
if (update.beginLookDrag) {
update.state.lookDragging = true;
update.state.panDragging = false;
}
if (update.beginPanDrag) {
update.state.panDragging = true;
update.state.lookDragging = false;
update.state.panDragButton = update.beginLeftPanDrag
? ImGuiMouseButton_Left
: ImGuiMouseButton_Middle;
}
if (update.state.lookDragging && !request.rightMouseDown) {
update.state.lookDragging = false;
}
if (update.state.panDragging &&
!IsSceneViewportPanDragButtonDown(request, update.state.panDragButton)) {
update.state.panDragging = false;
update.state.panDragButton = ImGuiMouseButton_Middle;
}
return update;
}
struct SceneViewportCaptureRequest {
SceneViewportNavigationState state = {};
bool moveGizmoActive = false;
bool rotateGizmoActive = false;
bool scaleGizmoActive = false;
};
struct SceneViewportCaptureFlags {
bool captureMouse = false;
bool captureKeyboard = false;
};
inline SceneViewportCaptureFlags BuildSceneViewportCaptureFlags(
const SceneViewportCaptureRequest& request) {
SceneViewportCaptureFlags flags = {};
flags.captureMouse =
request.state.lookDragging ||
request.state.panDragging ||
request.moveGizmoActive ||
request.rotateGizmoActive ||
request.scaleGizmoActive;
flags.captureKeyboard = request.state.lookDragging;
return flags;
}
inline bool CanResolveSceneViewportInteraction(
bool hasInteractiveViewport,
bool viewportHovered,
bool usingViewMoveTool,
const SceneViewportNavigationState& navigationState,
bool gizmoActive) {
return hasInteractiveViewport &&
viewportHovered &&
!usingViewMoveTool &&
!navigationState.lookDragging &&
!navigationState.panDragging &&
!gizmoActive;
}
struct SceneViewportInputBuildRequest {
SceneViewportNavigationState state = {};
ImVec2 viewportSize = ImVec2(0.0f, 0.0f);
ImVec2 mouseDelta = ImVec2(0.0f, 0.0f);
float mouseWheel = 0.0f;
float deltaTime = 0.0f;
bool viewportHovered = false;
bool viewportFocused = false;
bool wantTextInput = false;
bool fastMove = false;
bool focusSelectionKeyPressed = false;
bool moveForwardKeyDown = false;
bool moveBackwardKeyDown = false;
bool moveRightKeyDown = false;
bool moveLeftKeyDown = false;
bool moveUpKeyDown = false;
bool moveDownKeyDown = false;
};
inline SceneViewportInput BuildSceneViewportInput(const SceneViewportInputBuildRequest& request) {
SceneViewportInput input = {};
input.viewportSize = request.viewportSize;
input.deltaTime = request.deltaTime;
input.hovered = request.viewportHovered;
input.focused =
request.viewportFocused ||
request.state.lookDragging ||
request.state.panDragging;
input.mouseWheel =
(request.viewportHovered && !request.state.lookDragging)
? request.mouseWheel
: 0.0f;
input.flySpeedDelta =
(request.viewportHovered && request.state.lookDragging)
? request.mouseWheel
: 0.0f;
input.looking = request.state.lookDragging;
input.orbiting = false;
input.panning = request.state.panDragging;
input.fastMove = request.fastMove;
input.focusSelectionRequested =
input.focused &&
!request.wantTextInput &&
request.focusSelectionKeyPressed;
if (request.state.lookDragging && !request.wantTextInput) {
input.moveForward =
(request.moveForwardKeyDown ? 1.0f : 0.0f) -
(request.moveBackwardKeyDown ? 1.0f : 0.0f);
input.moveRight =
(request.moveRightKeyDown ? 1.0f : 0.0f) -
(request.moveLeftKeyDown ? 1.0f : 0.0f);
input.moveUp =
(request.moveUpKeyDown ? 1.0f : 0.0f) -
(request.moveDownKeyDown ? 1.0f : 0.0f);
}
if (request.state.lookDragging || request.state.panDragging) {
input.mouseDelta = request.mouseDelta;
}
return input;
}
} // namespace Editor
} // namespace XCEngine