Camera Math
Utilities that turn a SafiCamera component into the matrices and rays every renderer, picker, and editor tool needs. Kept in one place so the render system, physics picking, and the upcoming gizmo handles all agree on the same eye/forward/up convention — no subtle mismatches between "what the user sees" and "what the raycast hits".
API
safi_camera_build_view_proj
Computes the same view and projection matrices the render system uses each frame. Prefers the explicit pose (cam->eye, cam->forward, cam->up); when cam->eye is all-zero, falls back to the legacy eye = target + (0,0,3) / look-at-origin convention so older scene files keep rendering.
safi_camera_screen_ray
Unprojects a cursor position (pixel coordinates, SDL origin top-left) into a world-space ray. out_origin is the camera eye; out_dir is the unit-length vector from the eye through the far-plane point under the cursor. Pair with safi_physics_raycast or any future AABB/mesh picker.
safi_camera_world_to_screen
The inverse direction: project a world-space point to pixel coordinates (SDL origin: top-left). Returns false when the point sits behind the camera or on the clip boundary; the gizmo hit-test uses it to find the on-screen distance from the cursor to each axis handle.
Why this lives here
Before the helper existed, the same ~30 lines of unproject math lived in the gltf_viewer demo's input system. Every new caller (editor gizmo drag, click-to-select, brush stamping, …) would duplicate it. The helper folds that math into one place; the demo's picking is now eight lines.
Convention
The engine uses cglm defaults: right-handed, +X right, +Y up, -Z forward (see Render Overview). safi_camera_screen_ray flips the cursor Y internally to account for SDL's top-left origin, so callers pass raw pixel coordinates.
See also
SafiCamera— the component the helpers read- Render Overview — coordinate conventions
- Editor Camera — uses this for click-to-fly-to-point workflows
- Physics —
safi_physics_raycast