Extra2D/Tests/test_camera.cpp

329 lines
9.0 KiB
C++
Raw Normal View History

/**
* @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));
}