Files

120 lines
3.4 KiB
C++

#include "camera_2d.h"
#include <cmath>
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