329 lines
9.0 KiB
C++
329 lines
9.0 KiB
C++
|
|
/**
|
||
|
|
* @file test_camera.cpp
|
||
|
|
* @brief Camera 系统单元测试
|
||
|
|
*
|
||
|
|
* 测试正交相机的投影矩阵、坐标转换等功能。
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "test_framework.h"
|
||
|
|
#include <extra2d/cam/cam.h>
|
||
|
|
#include <extra2d/cam/ortho_cam.h>
|
||
|
|
#include <extra2d/core/math_extended.h>
|
||
|
|
#include <glm/glm.hpp>
|
||
|
|
#include <glm/gtc/matrix_transform.hpp>
|
||
|
|
#include <cmath>
|
||
|
|
|
||
|
|
using namespace extra2d;
|
||
|
|
using namespace extra2d::test;
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
constexpr f32 EPSILON = 0.001f;
|
||
|
|
|
||
|
|
bool vec2Equal(const Vec2& a, const Vec2& b, f32 eps = EPSILON) {
|
||
|
|
return std::abs(a.x - b.x) < eps && std::abs(a.y - b.y) < eps;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool mat4Equal(const Mat4& a, const Mat4& b, f32 eps = EPSILON) {
|
||
|
|
for (int i = 0; i < 4; ++i) {
|
||
|
|
for (int j = 0; j < 4; ++j) {
|
||
|
|
if (std::abs(a[i][j] - b[i][j]) > eps) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// OrthoCam 基本测试
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
TEST(OrthoCam, Create) {
|
||
|
|
OrthoCam cam;
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(cam.type() == CamType::Ortho);
|
||
|
|
TEST_ASSERT_TRUE(vec2Equal(Vec2(0, 0), Vec2(cam.position().x, cam.position().y)));
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.zoom() - 1.0f) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, SetPosition) {
|
||
|
|
OrthoCam cam;
|
||
|
|
|
||
|
|
cam.setPosition(Vec2(100, 200));
|
||
|
|
TEST_ASSERT_TRUE(vec2Equal(Vec2(100, 200), Vec2(cam.position().x, cam.position().y)));
|
||
|
|
|
||
|
|
cam.setPosition(Vec2(50, 75));
|
||
|
|
TEST_ASSERT_TRUE(vec2Equal(Vec2(50, 75), Vec2(cam.position().x, cam.position().y)));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, SetZoom) {
|
||
|
|
OrthoCam cam;
|
||
|
|
|
||
|
|
cam.setZoom(2.0f);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.zoom() - 2.0f) < EPSILON);
|
||
|
|
|
||
|
|
cam.setZoom(0.5f);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.zoom() - 0.5f) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, SetRotation) {
|
||
|
|
OrthoCam cam;
|
||
|
|
|
||
|
|
cam.setRotation(45.0f);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.rotation() - 45.0f) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// 正交投影设置测试
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
TEST(OrthoCam, Ortho) {
|
||
|
|
OrthoCam cam;
|
||
|
|
|
||
|
|
cam.ortho(-640, 640, -360, 360);
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.left() - (-640)) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.right() - 640) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.bottom() - (-360)) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.top() - 360) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, SetOrthoFromSize) {
|
||
|
|
OrthoCam cam;
|
||
|
|
|
||
|
|
cam.setOrthoFromSize(1280, 720);
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.width() - 1280) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.height() - 720) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(vec2Equal(cam.center(), Vec2(0, 0)));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, SetCenter) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.setOrthoFromSize(1280, 720);
|
||
|
|
|
||
|
|
cam.setCenter(100, 200);
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(vec2Equal(cam.center(), Vec2(100, 200)));
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.left() - (100 - 640)) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(cam.right() - (100 + 640)) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// 视图矩阵测试
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
TEST(OrthoCam, ViewMatrixIdentity) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.setPosition(Vec2(0, 0));
|
||
|
|
cam.setRotation(0);
|
||
|
|
cam.setZoom(1.0f);
|
||
|
|
|
||
|
|
Mat4 view = cam.view();
|
||
|
|
Mat4 identity(1.0f);
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(mat4Equal(view, identity));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, ViewMatrixTranslation) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.setPosition(Vec2(100, 200));
|
||
|
|
cam.setRotation(0);
|
||
|
|
cam.setZoom(1.0f);
|
||
|
|
|
||
|
|
Mat4 view = cam.view();
|
||
|
|
|
||
|
|
Vec4 origin(0, 0, 0, 1);
|
||
|
|
Vec4 transformed = view * origin;
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(std::abs(transformed.x - (-100)) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(transformed.y - (-200)) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, ViewMatrixZoom) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.setPosition(Vec2(0, 0));
|
||
|
|
cam.setRotation(0);
|
||
|
|
cam.setZoom(2.0f);
|
||
|
|
|
||
|
|
Mat4 view = cam.view();
|
||
|
|
|
||
|
|
Vec4 point(100, 100, 0, 1);
|
||
|
|
Vec4 transformed = view * point;
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(std::abs(transformed.x - 200) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(transformed.y - 200) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// 投影矩阵测试
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
TEST(OrthoCam, ProjMatrix) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.ortho(-640, 640, -360, 360, -1, 1);
|
||
|
|
|
||
|
|
Mat4 proj = cam.proj();
|
||
|
|
|
||
|
|
Vec4 corner1(-640, -360, 0, 1);
|
||
|
|
Vec4 transformed1 = proj * corner1;
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(std::abs(transformed1.x - (-1)) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(transformed1.y - (-1)) < EPSILON);
|
||
|
|
|
||
|
|
Vec4 corner2(640, 360, 0, 1);
|
||
|
|
Vec4 transformed2 = proj * corner2;
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(std::abs(transformed2.x - 1) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(transformed2.y - 1) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, ProjMatrixWithZoom) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.ortho(-640, 640, -360, 360);
|
||
|
|
cam.setZoom(2.0f);
|
||
|
|
|
||
|
|
Mat4 proj = cam.proj();
|
||
|
|
|
||
|
|
Vec4 center(0, 0, 0, 1);
|
||
|
|
Vec4 transformed = proj * center;
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(std::abs(transformed.x) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(transformed.y) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// 坐标转换测试
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
TEST(OrthoCam, ScreenToWorld) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.ortho(-640, 640, -360, 360);
|
||
|
|
cam.viewport(0, 0, 1280, 720);
|
||
|
|
cam.setPosition(Vec2(0, 0));
|
||
|
|
cam.setZoom(1.0f);
|
||
|
|
|
||
|
|
Vec2 screenCenter(640, 360);
|
||
|
|
Vec2 worldPos = cam.screenToWorld(screenCenter);
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(vec2Equal(worldPos, Vec2(0, 0)));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, ScreenToWorldCorner) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.ortho(-640, 640, -360, 360);
|
||
|
|
cam.viewport(0, 0, 1280, 720);
|
||
|
|
cam.setPosition(Vec2(0, 0));
|
||
|
|
cam.setZoom(1.0f);
|
||
|
|
|
||
|
|
Vec2 screenTopLeft(0, 0);
|
||
|
|
Vec2 worldPos = cam.screenToWorld(screenTopLeft);
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(std::abs(worldPos.x - (-640)) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(worldPos.y - 360) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, WorldToScreen) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.ortho(-640, 640, -360, 360);
|
||
|
|
cam.viewport(0, 0, 1280, 720);
|
||
|
|
cam.setPosition(Vec2(0, 0));
|
||
|
|
cam.setZoom(1.0f);
|
||
|
|
|
||
|
|
Vec2 worldCenter(0, 0);
|
||
|
|
Vec2 screenPos = cam.worldToScreen(worldCenter);
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(vec2Equal(screenPos, Vec2(640, 360)));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, WorldToScreenCorner) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.ortho(-640, 640, -360, 360);
|
||
|
|
cam.viewport(0, 0, 1280, 720);
|
||
|
|
cam.setPosition(Vec2(0, 0));
|
||
|
|
cam.setZoom(1.0f);
|
||
|
|
|
||
|
|
Vec2 worldTopLeft(-640, 360);
|
||
|
|
Vec2 screenPos = cam.worldToScreen(worldTopLeft);
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(std::abs(screenPos.x) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(screenPos.y) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(OrthoCam, ScreenToWorldRoundTrip) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.ortho(-640, 640, -360, 360);
|
||
|
|
cam.viewport(0, 0, 1280, 720);
|
||
|
|
cam.setPosition(Vec2(100, 200));
|
||
|
|
cam.setZoom(1.5f);
|
||
|
|
|
||
|
|
Vec2 originalWorld(300, 400);
|
||
|
|
Vec2 screen = cam.worldToScreen(originalWorld);
|
||
|
|
Vec2 backToWorld = cam.screenToWorld(screen);
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(vec2Equal(originalWorld, backToWorld, 0.1f));
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// 视口测试
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
TEST(OrthoCam, SetViewport) {
|
||
|
|
OrthoCam cam;
|
||
|
|
|
||
|
|
cam.viewport(100, 200, 800, 600);
|
||
|
|
|
||
|
|
Vec2 vpPos = cam.viewportPos();
|
||
|
|
Vec2 vpSize = cam.viewportSize();
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(std::abs(vpPos.x - 100) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(vpPos.y - 200) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(vpSize.x - 800) < EPSILON);
|
||
|
|
TEST_ASSERT_TRUE(std::abs(vpSize.y - 600) < EPSILON);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// 脏标记测试
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
TEST(OrthoCam, DirtyFlag) {
|
||
|
|
OrthoCam cam;
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(cam.dirty());
|
||
|
|
|
||
|
|
cam.setDirty(false);
|
||
|
|
TEST_ASSERT_FALSE(cam.dirty());
|
||
|
|
|
||
|
|
cam.setPosition(Vec2(100, 100));
|
||
|
|
TEST_ASSERT_TRUE(cam.dirty());
|
||
|
|
|
||
|
|
cam.setDirty(false);
|
||
|
|
TEST_ASSERT_FALSE(cam.dirty());
|
||
|
|
|
||
|
|
cam.setZoom(2.0f);
|
||
|
|
TEST_ASSERT_TRUE(cam.dirty());
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// VP 矩阵测试
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
TEST(OrthoCam, VPMatrix) {
|
||
|
|
OrthoCam cam;
|
||
|
|
cam.ortho(-640, 640, -360, 360);
|
||
|
|
cam.viewport(0, 0, 1280, 720);
|
||
|
|
cam.setPosition(Vec2(0, 0));
|
||
|
|
cam.setRotation(0);
|
||
|
|
cam.setZoom(1.0f);
|
||
|
|
cam.setDirty(false);
|
||
|
|
|
||
|
|
Mat4 vp = cam.vp();
|
||
|
|
|
||
|
|
Mat4 view = cam.view();
|
||
|
|
Mat4 proj = cam.proj();
|
||
|
|
Mat4 expectedVP = proj * view;
|
||
|
|
|
||
|
|
TEST_ASSERT_TRUE(mat4Equal(vp, expectedVP));
|
||
|
|
}
|