224 lines
8.9 KiB
C++
224 lines
8.9 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include "Viewport/SceneViewportCameraController.h"
|
|
|
|
#include <XCEngine/Components/GameObject.h>
|
|
|
|
#include <cmath>
|
|
|
|
namespace {
|
|
|
|
bool NearlyEqual(float lhs, float rhs, float epsilon = 1e-4f) {
|
|
return std::abs(lhs - rhs) <= epsilon;
|
|
}
|
|
|
|
bool NearlyEqual(const XCEngine::Math::Vector3& lhs, const XCEngine::Math::Vector3& rhs, float epsilon = 1e-4f) {
|
|
return NearlyEqual(lhs.x, rhs.x, epsilon) &&
|
|
NearlyEqual(lhs.y, rhs.y, epsilon) &&
|
|
NearlyEqual(lhs.z, rhs.z, epsilon);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
using XCEngine::Components::GameObject;
|
|
using XCEngine::Editor::SceneViewportCameraController;
|
|
using XCEngine::Editor::SceneViewportCameraInputState;
|
|
using XCEngine::Math::Vector3;
|
|
|
|
TEST(SceneViewportCameraController_Test, ApplyToMatchesComputedPositionAndForward) {
|
|
SceneViewportCameraController controller;
|
|
controller.Reset();
|
|
controller.Focus(Vector3(2.0f, 1.0f, -3.0f));
|
|
|
|
GameObject cameraObject("EditorCamera");
|
|
controller.ApplyTo(*cameraObject.GetTransform());
|
|
|
|
EXPECT_TRUE(NearlyEqual(cameraObject.GetTransform()->GetPosition(), controller.GetPosition()));
|
|
EXPECT_TRUE(NearlyEqual(
|
|
cameraObject.GetTransform()->GetForward().Normalized(),
|
|
controller.GetForward(),
|
|
1e-3f));
|
|
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();
|
|
|
|
const Vector3 initialPosition = controller.GetPosition();
|
|
const Vector3 initialFocus = controller.GetFocalPoint();
|
|
const float initialPitch = controller.GetPitchDegrees();
|
|
|
|
SceneViewportCameraInputState input = {};
|
|
input.viewportHeight = 720.0f;
|
|
input.lookDeltaX = 20.0f;
|
|
input.lookDeltaY = -15.0f;
|
|
controller.ApplyInput(input);
|
|
|
|
EXPECT_TRUE(NearlyEqual(controller.GetPosition(), initialPosition));
|
|
EXPECT_FALSE(NearlyEqual(controller.GetFocalPoint(), initialFocus));
|
|
EXPECT_GT(controller.GetPitchDegrees(), initialPitch);
|
|
EXPECT_TRUE(NearlyEqual(
|
|
controller.GetFocalPoint(),
|
|
controller.GetPosition() + controller.GetForward() * controller.GetDistance(),
|
|
1e-3f));
|
|
}
|
|
|
|
TEST(SceneViewportCameraController_Test, OrbitInputRotatesAroundFocalPointAndKeepsDistance) {
|
|
SceneViewportCameraController controller;
|
|
controller.Reset();
|
|
|
|
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;
|
|
input.orbitDeltaX = 20.0f;
|
|
input.orbitDeltaY = -15.0f;
|
|
controller.ApplyInput(input);
|
|
|
|
EXPECT_FALSE(NearlyEqual(controller.GetPosition(), initialPosition));
|
|
EXPECT_TRUE(NearlyEqual(controller.GetFocalPoint(), initialFocus));
|
|
EXPECT_GT(controller.GetPitchDegrees(), initialPitch);
|
|
EXPECT_NEAR((controller.GetFocalPoint() - controller.GetPosition()).Magnitude(), initialDistance, 1e-3f);
|
|
}
|
|
|
|
TEST(SceneViewportCameraController_Test, PanAndZoomUpdateCameraStateConsistently) {
|
|
SceneViewportCameraController controller;
|
|
controller.Reset();
|
|
|
|
const Vector3 initialPosition = controller.GetPosition();
|
|
const Vector3 initialFocus = controller.GetFocalPoint();
|
|
const float initialDistance = controller.GetDistance();
|
|
const Vector3 right = Vector3::Normalize(Vector3::Cross(Vector3::Up(), controller.GetForward()));
|
|
const Vector3 up = Vector3::Normalize(Vector3::Cross(controller.GetForward(), right));
|
|
|
|
SceneViewportCameraInputState input = {};
|
|
input.viewportHeight = 720.0f;
|
|
input.panDeltaX = 16.0f;
|
|
input.panDeltaY = -10.0f;
|
|
input.zoomDelta = 1.0f;
|
|
controller.ApplyInput(input);
|
|
|
|
EXPECT_FALSE(NearlyEqual(controller.GetPosition(), initialPosition));
|
|
EXPECT_FALSE(NearlyEqual(controller.GetFocalPoint(), initialFocus));
|
|
EXPECT_LT(controller.GetDistance(), initialDistance);
|
|
const Vector3 panDelta = controller.GetFocalPoint() - initialFocus;
|
|
EXPECT_NEAR(Vector3::Dot(panDelta, controller.GetForward()), 0.0f, 1e-3f);
|
|
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(),
|
|
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;
|
|
const Vector3 forward = controller.GetForward();
|
|
const Vector3 right = Vector3::Normalize(Vector3::Cross(Vector3::Up(), forward));
|
|
|
|
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));
|
|
const Vector3 positionDelta = controller.GetPosition() - initialPosition;
|
|
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();
|
|
|
|
SceneViewportCameraController baselineController;
|
|
baselineController.Reset();
|
|
|
|
SceneViewportCameraInputState zoomInput = {};
|
|
zoomInput.viewportHeight = 720.0f;
|
|
zoomInput.zoomDelta = 8.0f;
|
|
zoomedController.ApplyInput(zoomInput);
|
|
const Vector3 zoomedPositionBeforeMove = zoomedController.GetPosition();
|
|
const Vector3 baselinePositionBeforeMove = baselineController.GetPosition();
|
|
|
|
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() - zoomedPositionBeforeMove).Magnitude();
|
|
const float baselineTravel = (baselineController.GetPosition() - baselinePositionBeforeMove).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();
|
|
|
|
const float initialDistance = controller.GetDistance();
|
|
const Vector3 target(5.0f, -2.0f, 7.5f);
|
|
controller.Focus(target);
|
|
|
|
EXPECT_TRUE(NearlyEqual(controller.GetFocalPoint(), target));
|
|
EXPECT_FLOAT_EQ(controller.GetDistance(), initialDistance);
|
|
}
|