Refine editor viewport and interaction workflow

This commit is contained in:
2026-03-29 15:12:38 +08:00
parent b0427b7091
commit 2651bad080
42 changed files with 3888 additions and 570 deletions

View File

@@ -41,6 +41,22 @@ TEST(SceneViewportCameraController_Test, ApplyToMatchesComputedPositionAndForwar
EXPECT_GT(Vector3::Dot(cameraObject.GetTransform()->GetUp().Normalized(), Vector3::Up()), 0.0f);
}
TEST(SceneViewportCameraController_Test, ApplyToLooksAtControllerFocalPointInViewSpace) {
SceneViewportCameraController controller;
controller.Reset();
controller.Focus(Vector3(2.0f, 1.0f, -3.0f));
GameObject cameraObject("EditorCamera");
controller.ApplyTo(*cameraObject.GetTransform());
const Vector3 focalPointInViewSpace =
cameraObject.GetTransform()->InverseTransformPoint(controller.GetFocalPoint());
EXPECT_NEAR(focalPointInViewSpace.x, 0.0f, 1e-3f);
EXPECT_NEAR(focalPointInViewSpace.y, 0.0f, 1e-3f);
EXPECT_NEAR(focalPointInViewSpace.z, controller.GetDistance(), 1e-3f);
}
TEST(SceneViewportCameraController_Test, LookInputRotatesCameraInPlaceAndKeepsDistance) {
SceneViewportCameraController controller;
controller.Reset();
@@ -57,7 +73,7 @@ TEST(SceneViewportCameraController_Test, LookInputRotatesCameraInPlaceAndKeepsDi
EXPECT_TRUE(NearlyEqual(controller.GetPosition(), initialPosition));
EXPECT_FALSE(NearlyEqual(controller.GetFocalPoint(), initialFocus));
EXPECT_LT(controller.GetPitchDegrees(), initialPitch);
EXPECT_GT(controller.GetPitchDegrees(), initialPitch);
EXPECT_TRUE(NearlyEqual(
controller.GetFocalPoint(),
controller.GetPosition() + controller.GetForward() * controller.GetDistance(),
@@ -81,7 +97,7 @@ TEST(SceneViewportCameraController_Test, OrbitInputRotatesAroundFocalPointAndKee
EXPECT_FALSE(NearlyEqual(controller.GetPosition(), initialPosition));
EXPECT_TRUE(NearlyEqual(controller.GetFocalPoint(), initialFocus));
EXPECT_LT(controller.GetPitchDegrees(), initialPitch);
EXPECT_GT(controller.GetPitchDegrees(), initialPitch);
EXPECT_NEAR((controller.GetFocalPoint() - controller.GetPosition()).Magnitude(), initialDistance, 1e-3f);
}
@@ -107,8 +123,8 @@ TEST(SceneViewportCameraController_Test, PanAndZoomUpdateCameraStateConsistently
EXPECT_LT(controller.GetDistance(), initialDistance);
const Vector3 panDelta = controller.GetFocalPoint() - initialFocus;
EXPECT_NEAR(Vector3::Dot(panDelta, controller.GetForward()), 0.0f, 1e-3f);
EXPECT_GT(std::abs(Vector3::Dot(panDelta, right)), 0.0f);
EXPECT_GT(std::abs(Vector3::Dot(panDelta, up)), 0.0f);
EXPECT_LT(Vector3::Dot(panDelta, right), 0.0f);
EXPECT_LT(Vector3::Dot(panDelta, up), 0.0f);
EXPECT_TRUE(NearlyEqual(
controller.GetFocalPoint(),
controller.GetPosition() + controller.GetForward() * controller.GetDistance(),
@@ -135,11 +151,65 @@ TEST(SceneViewportCameraController_Test, FlyInputMovesCameraAndFocalPointTogethe
EXPECT_FALSE(NearlyEqual(controller.GetPosition(), initialPosition));
EXPECT_FALSE(NearlyEqual(controller.GetFocalPoint(), initialFocus));
const Vector3 positionDelta = controller.GetPosition() - initialPosition;
EXPECT_GT(std::abs(Vector3::Dot(positionDelta, forward)), 0.0f);
EXPECT_GT(std::abs(Vector3::Dot(positionDelta, right)), 0.0f);
EXPECT_GT(Vector3::Dot(positionDelta, forward), 0.0f);
EXPECT_GT(Vector3::Dot(positionDelta, right), 0.0f);
EXPECT_TRUE(NearlyEqual(controller.GetFocalPoint() - controller.GetPosition(), initialOffset, 1e-3f));
}
TEST(SceneViewportCameraController_Test, ZoomDoesNotChangeFlySpeed) {
SceneViewportCameraController zoomedController;
zoomedController.Reset();
const Vector3 zoomedInitialPosition = zoomedController.GetPosition();
SceneViewportCameraController baselineController;
baselineController.Reset();
const Vector3 baselineInitialPosition = baselineController.GetPosition();
SceneViewportCameraInputState zoomInput = {};
zoomInput.viewportHeight = 720.0f;
zoomInput.zoomDelta = 8.0f;
zoomedController.ApplyInput(zoomInput);
SceneViewportCameraInputState moveInput = {};
moveInput.viewportHeight = 720.0f;
moveInput.deltaTime = 0.5f;
moveInput.moveForward = 1.0f;
zoomedController.ApplyInput(moveInput);
baselineController.ApplyInput(moveInput);
EXPECT_FLOAT_EQ(zoomedController.GetFlySpeed(), baselineController.GetFlySpeed());
const float zoomedTravel = (zoomedController.GetPosition() - zoomedInitialPosition).Magnitude();
const float baselineTravel = (baselineController.GetPosition() - baselineInitialPosition).Magnitude();
EXPECT_NEAR(zoomedTravel, baselineTravel, 1e-3f);
}
TEST(SceneViewportCameraController_Test, FlySpeedDeltaAdjustsMovementSpeedIndependentlyFromZoom) {
SceneViewportCameraController fasterController;
fasterController.Reset();
const Vector3 fasterInitialPosition = fasterController.GetPosition();
SceneViewportCameraController baselineController;
baselineController.Reset();
const Vector3 baselineInitialPosition = baselineController.GetPosition();
SceneViewportCameraInputState speedInput = {};
speedInput.viewportHeight = 720.0f;
speedInput.flySpeedDelta = 4.0f;
fasterController.ApplyInput(speedInput);
SceneViewportCameraInputState moveInput = {};
moveInput.viewportHeight = 720.0f;
moveInput.deltaTime = 0.5f;
moveInput.moveForward = 1.0f;
fasterController.ApplyInput(moveInput);
baselineController.ApplyInput(moveInput);
const float fasterTravel = (fasterController.GetPosition() - fasterInitialPosition).Magnitude();
const float baselineTravel = (baselineController.GetPosition() - baselineInitialPosition).Magnitude();
EXPECT_GT(fasterController.GetFlySpeed(), baselineController.GetFlySpeed());
EXPECT_GT(fasterTravel, baselineTravel);
}
TEST(SceneViewportCameraController_Test, FocusMovesPivotWithoutChangingDistance) {
SceneViewportCameraController controller;
controller.Reset();