#include "camera_2d.h" #include namespace fn::cam { using fn::math2d::Vec2; using fn::math2d::Rect; Vec2 world_to_screen(const Camera2D& c, Vec2 world) { float dx = world.x - c.pos.x; float dy = world.y - c.pos.y; if (c.rotation != 0.0f) { float cs = std::cos(-c.rotation); float sn = std::sin(-c.rotation); float rx = dx * cs - dy * sn; float ry = dx * sn + dy * cs; dx = rx; dy = ry; } float sx = dx * c.zoom + (float)c.viewport_w * 0.5f; float sy = dy * c.zoom + (float)c.viewport_h * 0.5f; return {sx, sy}; } Vec2 screen_to_world(const Camera2D& c, Vec2 screen) { float dx = (screen.x - (float)c.viewport_w * 0.5f) / c.zoom; float dy = (screen.y - (float)c.viewport_h * 0.5f) / c.zoom; if (c.rotation != 0.0f) { float cs = std::cos(c.rotation); float sn = std::sin(c.rotation); float rx = dx * cs - dy * sn; float ry = dx * sn + dy * cs; dx = rx; dy = ry; } return {c.pos.x + dx, c.pos.y + dy}; } Rect visible_world_rect(const Camera2D& c) { // For rotated cameras, return the AABB that contains the rotated viewport. float hw = (float)c.viewport_w * 0.5f / c.zoom; float hh = (float)c.viewport_h * 0.5f / c.zoom; if (c.rotation == 0.0f) { return {c.pos.x - hw, c.pos.y - hh, hw * 2.0f, hh * 2.0f}; } float cs = std::fabs(std::cos(c.rotation)); float sn = std::fabs(std::sin(c.rotation)); float ehw = hw * cs + hh * sn; float ehh = hw * sn + hh * cs; return {c.pos.x - ehw, c.pos.y - ehh, ehw * 2.0f, ehh * 2.0f}; } void view_proj_matrix(const Camera2D& c, float out[16]) { // Orthographic projection mapping a viewport-sized box around camera pos // to clip space [-1, 1]. float hw = (float)c.viewport_w * 0.5f / c.zoom; float hh = (float)c.viewport_h * 0.5f / c.zoom; float l = c.pos.x - hw; float r = c.pos.x + hw; // Screen Y goes down, world Y can go either; we pick world-Y-up convention: // top of screen = pos.y + hh, bottom = pos.y - hh. float b = c.pos.y - hh; float t = c.pos.y + hh; float cs = std::cos(-c.rotation); float sn = std::sin(-c.rotation); // Build column-major: // M = Ortho(l,r,b,t) * Rotate(-rotation around camera center) // Compose by hand to avoid temporaries. float ortho[16] = { 2.0f / (r - l), 0.0f, 0.0f, 0.0f, 0.0f, 2.0f / (t - b), 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, -(r + l) / (r - l), -(t + b) / (t - b), 0.0f, 1.0f, }; if (c.rotation == 0.0f) { for (int i = 0; i < 16; ++i) out[i] = ortho[i]; return; } // Rotation around camera pos in world space: // T(pos) * R * T(-pos) // We bake it as a column-major 4x4 then multiply: out = ortho * rot. float px = c.pos.x; float py = c.pos.y; float rot[16] = { cs, sn, 0.0f, 0.0f, -sn, cs, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, px - cs * px + sn * py, py - sn * px - cs * py, 0.0f, 1.0f, }; // out = ortho * rot (column-major). for (int col = 0; col < 4; ++col) { for (int row = 0; row < 4; ++row) { float sum = 0.0f; for (int k = 0; k < 4; ++k) { sum += ortho[k * 4 + row] * rot[col * 4 + k]; } out[col * 4 + row] = sum; } } } } // namespace fn::cam