feat: add scene view fly controls
This commit is contained in:
@@ -33,9 +33,14 @@ struct SceneViewportInput {
|
||||
ImVec2 viewportSize = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 mouseDelta = ImVec2(0.0f, 0.0f);
|
||||
float mouseWheel = 0.0f;
|
||||
float deltaTime = 0.0f;
|
||||
float moveForward = 0.0f;
|
||||
float moveRight = 0.0f;
|
||||
float moveUp = 0.0f;
|
||||
bool hovered = false;
|
||||
bool focused = false;
|
||||
bool looking = false;
|
||||
bool fastMove = false;
|
||||
bool orbiting = false;
|
||||
bool panning = false;
|
||||
bool focusSelectionRequested = false;
|
||||
|
||||
@@ -19,7 +19,12 @@ struct SceneViewportCameraInputState {
|
||||
float panDeltaX = 0.0f;
|
||||
float panDeltaY = 0.0f;
|
||||
float zoomDelta = 0.0f;
|
||||
float deltaTime = 0.0f;
|
||||
float moveForward = 0.0f;
|
||||
float moveRight = 0.0f;
|
||||
float moveUp = 0.0f;
|
||||
float viewportHeight = 0.0f;
|
||||
bool fastMove = false;
|
||||
};
|
||||
|
||||
class SceneViewportCameraController {
|
||||
@@ -93,6 +98,23 @@ public:
|
||||
m_position += ((right * -input.panDeltaX) + (up * input.panDeltaY)) * worldUnitsPerPixel;
|
||||
}
|
||||
|
||||
if (input.deltaTime > 0.0f &&
|
||||
(std::abs(input.moveForward) > Math::EPSILON ||
|
||||
std::abs(input.moveRight) > Math::EPSILON ||
|
||||
std::abs(input.moveUp) > Math::EPSILON)) {
|
||||
const Math::Vector3 movement =
|
||||
GetForward() * input.moveForward +
|
||||
GetRight() * input.moveRight +
|
||||
Math::Vector3::Up() * input.moveUp;
|
||||
if (movement.SqrMagnitude() > Math::EPSILON) {
|
||||
const float speedMultiplier = input.fastMove ? 4.0f : 1.0f;
|
||||
const float flySpeed = (std::max)(5.0f, m_distance * 2.0f) * speedMultiplier;
|
||||
const Math::Vector3 delta = Math::Vector3::Normalize(movement) * flySpeed * input.deltaTime;
|
||||
m_position += delta;
|
||||
m_focalPoint += delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (std::abs(input.zoomDelta) > Math::EPSILON) {
|
||||
const float zoomFactor = std::pow(0.85f, input.zoomDelta);
|
||||
m_distance = std::clamp(m_distance * zoomFactor, 0.5f, 500.0f);
|
||||
@@ -111,7 +133,7 @@ public:
|
||||
private:
|
||||
void ApplyRotationDelta(float deltaX, float deltaY) {
|
||||
m_yawDegrees += deltaX * 0.30f;
|
||||
m_pitchDegrees = std::clamp(m_pitchDegrees - deltaY * 0.20f, -89.0f, 89.0f);
|
||||
m_pitchDegrees = std::clamp(m_pitchDegrees + deltaY * 0.20f, -89.0f, 89.0f);
|
||||
}
|
||||
|
||||
Math::Vector3 GetRight() const {
|
||||
|
||||
@@ -91,6 +91,11 @@ public:
|
||||
SceneViewportCameraInputState controllerInput = {};
|
||||
controllerInput.viewportHeight = input.viewportSize.y;
|
||||
controllerInput.zoomDelta = input.hovered ? input.mouseWheel : 0.0f;
|
||||
controllerInput.deltaTime = input.deltaTime;
|
||||
controllerInput.moveForward = input.moveForward;
|
||||
controllerInput.moveRight = input.moveRight;
|
||||
controllerInput.moveUp = input.moveUp;
|
||||
controllerInput.fastMove = input.fastMove;
|
||||
|
||||
if (input.looking) {
|
||||
controllerInput.lookDeltaX = input.mouseDelta.x;
|
||||
|
||||
@@ -263,15 +263,29 @@ void SceneViewPanel::Render() {
|
||||
|
||||
SceneViewportInput input = {};
|
||||
input.viewportSize = content.availableSize;
|
||||
input.deltaTime = io.DeltaTime;
|
||||
input.hovered = content.hovered;
|
||||
input.focused = content.focused;
|
||||
input.mouseWheel = content.hovered ? io.MouseWheel : 0.0f;
|
||||
input.mouseWheel = content.hovered ? -io.MouseWheel : 0.0f;
|
||||
input.looking = m_lookDragging;
|
||||
input.orbiting = m_orbitDragging;
|
||||
input.panning = m_panDragging;
|
||||
input.fastMove = io.KeyShift;
|
||||
input.focusSelectionRequested =
|
||||
content.focused && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_F, false);
|
||||
|
||||
if (m_lookDragging && content.focused && !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_orbitDragging || m_panDragging) {
|
||||
input.mouseDelta = io.MouseDelta;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ TEST(SceneViewportCameraController_Test, LookInputRotatesCameraInPlaceAndKeepsDi
|
||||
|
||||
const Vector3 initialPosition = controller.GetPosition();
|
||||
const Vector3 initialFocus = controller.GetFocalPoint();
|
||||
const float initialPitch = controller.GetPitchDegrees();
|
||||
|
||||
SceneViewportCameraInputState input = {};
|
||||
input.viewportHeight = 720.0f;
|
||||
@@ -56,6 +57,7 @@ TEST(SceneViewportCameraController_Test, LookInputRotatesCameraInPlaceAndKeepsDi
|
||||
|
||||
EXPECT_TRUE(NearlyEqual(controller.GetPosition(), initialPosition));
|
||||
EXPECT_FALSE(NearlyEqual(controller.GetFocalPoint(), initialFocus));
|
||||
EXPECT_LT(controller.GetPitchDegrees(), initialPitch);
|
||||
EXPECT_TRUE(NearlyEqual(
|
||||
controller.GetFocalPoint(),
|
||||
controller.GetPosition() + controller.GetForward() * controller.GetDistance(),
|
||||
@@ -69,6 +71,7 @@ TEST(SceneViewportCameraController_Test, OrbitInputRotatesAroundFocalPointAndKee
|
||||
const Vector3 initialPosition = controller.GetPosition();
|
||||
const Vector3 initialFocus = controller.GetFocalPoint();
|
||||
const float initialDistance = controller.GetDistance();
|
||||
const float initialPitch = controller.GetPitchDegrees();
|
||||
|
||||
SceneViewportCameraInputState input = {};
|
||||
input.viewportHeight = 720.0f;
|
||||
@@ -78,6 +81,7 @@ TEST(SceneViewportCameraController_Test, OrbitInputRotatesAroundFocalPointAndKee
|
||||
|
||||
EXPECT_FALSE(NearlyEqual(controller.GetPosition(), initialPosition));
|
||||
EXPECT_TRUE(NearlyEqual(controller.GetFocalPoint(), initialFocus));
|
||||
EXPECT_LT(controller.GetPitchDegrees(), initialPitch);
|
||||
EXPECT_NEAR((controller.GetFocalPoint() - controller.GetPosition()).Magnitude(), initialDistance, 1e-3f);
|
||||
}
|
||||
|
||||
@@ -105,6 +109,26 @@ TEST(SceneViewportCameraController_Test, PanAndZoomUpdateCameraStateConsistently
|
||||
1e-3f));
|
||||
}
|
||||
|
||||
TEST(SceneViewportCameraController_Test, FlyInputMovesCameraAndFocalPointTogether) {
|
||||
SceneViewportCameraController controller;
|
||||
controller.Reset();
|
||||
|
||||
const Vector3 initialPosition = controller.GetPosition();
|
||||
const Vector3 initialFocus = controller.GetFocalPoint();
|
||||
const Vector3 initialOffset = initialFocus - initialPosition;
|
||||
|
||||
SceneViewportCameraInputState input = {};
|
||||
input.viewportHeight = 720.0f;
|
||||
input.deltaTime = 0.5f;
|
||||
input.moveForward = 1.0f;
|
||||
input.moveRight = 1.0f;
|
||||
controller.ApplyInput(input);
|
||||
|
||||
EXPECT_FALSE(NearlyEqual(controller.GetPosition(), initialPosition));
|
||||
EXPECT_FALSE(NearlyEqual(controller.GetFocalPoint(), initialFocus));
|
||||
EXPECT_TRUE(NearlyEqual(controller.GetFocalPoint() - controller.GetPosition(), initialOffset, 1e-3f));
|
||||
}
|
||||
|
||||
TEST(SceneViewportCameraController_Test, FocusMovesPivotWithoutChangingDistance) {
|
||||
SceneViewportCameraController controller;
|
||||
controller.Reset();
|
||||
|
||||
Reference in New Issue
Block a user