From 6b2d76bd8ac04681a3576d36da8acc6313326b54 Mon Sep 17 00:00:00 2001 From: samstalhandske Date: Mon, 26 Jan 2026 20:43:45 +0100 Subject: [PATCH] Perspective + orthographic cameras --- camera.odin | 130 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 113 insertions(+), 17 deletions(-) diff --git a/camera.odin b/camera.odin index cedc8bf..980245f 100644 --- a/camera.odin +++ b/camera.odin @@ -6,27 +6,95 @@ import "core:math/linalg" Camera :: struct { position: [3]f32, forward, up, right: [3]f32, - - fov_degrees: f32, near, far: f32, + yaw, pitch, roll: f32, + + type: Camera_Type, } -create_camera :: proc(renderer: ^Renderer, position: [3]f32, forward, up: [3]f32, fov_degrees, near, far: f32) -> Camera { +Camera_Type :: union { + Camera_Perspective, + Camera_Orthographic, +} + +Camera_Perspective :: struct { + fov_degrees: f32, +} + +Camera_Orthographic :: struct { + height: f32, +} + +@(private) create_camera_base :: proc( + renderer: ^Renderer, + position: [3]f32, + forward, up: [3]f32, + near, far: f32, + type: Camera_Type, + yaw: f32 = 0, pitch: f32 = 0, roll: f32 = 0 +) -> Camera +{ c: Camera c.position = position c.forward = forward c.up = up - c.fov_degrees = fov_degrees c.near = near c.far = far + c.type = type + c.yaw = yaw + c.pitch = pitch + c.roll = roll assert(c.forward != {}) assert(c.up != {}) + assert(c.near > 0) + assert(c.far > 0) + + return c +} + +create_camera_perspective :: proc( + renderer: ^Renderer, + position: [3]f32, + forward, up: [3]f32, + near, far: f32, + fov_degrees: f32, + yaw: f32 = 0, pitch: f32 = 0, roll: f32 = 0 +) -> Camera +{ assert(fov_degrees > 0) assert(near > 0) assert(far > 0) - return c + return create_camera_base( + renderer, position, forward, up, + near, far, + Camera_Perspective { + fov_degrees = fov_degrees, + }, + yaw, pitch, roll + ) +} + +create_camera_orthographic :: proc( + renderer: ^Renderer, + position: [3]f32, + forward, up: [3]f32, + near, far: f32, + height: f32, + yaw: f32 = 0, pitch: f32 = 0, roll: f32 = 0 +) -> Camera +{ + assert(height > 0) + + return create_camera_base( + renderer, position, forward, up, + near, far, + Camera_Orthographic { + height = height, + }, + yaw, pitch, roll + ) } get_camera_view_matrix :: proc(camera: ^Camera) -> (linalg.Matrix4f32, bool) { @@ -48,23 +116,50 @@ get_camera_projection_matrix :: proc(renderer: ^Renderer, camera: ^Camera) -> (l } projection_matrix := linalg.MATRIX4F32_IDENTITY - projection_matrix *= linalg.matrix4_perspective( - linalg.to_radians(f32(camera.fov_degrees)), - get_aspect_ratio(renderer), - camera.near, - camera.far, - flip_z_axis = true, - ) + switch t in camera.type { + case Camera_Perspective: { + projection_matrix *= linalg.matrix4_perspective( + linalg.to_radians(f32(t.fov_degrees)), + get_aspect_ratio(renderer), + camera.near, + camera.far, + flip_z_axis = true, + ) + } + case Camera_Orthographic: { + half_h := t.height / 2 + half_w := half_h * get_aspect_ratio(renderer) + + projection_matrix *= linalg.matrix_ortho3d( + -half_w, half_w, + -half_h, half_h, + camera.near, + camera.far, + flip_z_axis = true, + ) + } + } + return projection_matrix, true } rotate_camera :: proc(camera: ^Camera, yaw, pitch, roll: f32) { - pitch := clamp(pitch, -89.0, 89.0) + new_yaw := camera.yaw + yaw + new_pitch := camera.pitch + pitch + new_roll := camera.roll + roll - pitch_rad := linalg.to_radians(pitch) - yaw_rad := linalg.to_radians(yaw) - roll_rad := linalg.to_radians(roll) + set_camera_rotation(camera, new_yaw, new_pitch, new_roll) +} + +set_camera_rotation :: proc(camera: ^Camera, yaw, pitch, roll: f32) { + camera.yaw = yaw + camera.pitch = pitch + camera.roll = roll + + pitch_rad := linalg.to_radians(camera.pitch) + yaw_rad := linalg.to_radians(camera.yaw) + roll_rad := linalg.to_radians(camera.roll) camera.forward = [3]f32{ math.cos(pitch_rad) * math.cos(yaw_rad), @@ -73,9 +168,9 @@ rotate_camera :: proc(camera: ^Camera, yaw, pitch, roll: f32) { } WORLD_UP :: [3]f32{0, 1, 0} + camera.right = linalg.normalize(linalg.cross(camera.forward, WORLD_UP)) camera.up = linalg.normalize(linalg.cross(camera.right, camera.forward)) - // TODO: SS - Support 'roll'. } set_active_camera :: proc(renderer: ^Renderer, camera: ^Camera) { @@ -83,5 +178,6 @@ set_active_camera :: proc(renderer: ^Renderer, camera: ^Camera) { } get_active_camera :: proc(renderer: ^Renderer) -> ^Camera { + assert(renderer != nil) return renderer.active_camera } \ No newline at end of file