/** * @file test_camera.cpp * @brief Camera 系统单元测试 * * 测试正交相机的投影矩阵、坐标转换等功能。 */ #include "test_framework.h" #include #include #include #include #include #include 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)); }