package renderer import "core:math" import "core:math/linalg" Camera :: struct { position: [3]f32, forward, up, right: [3]f32, near, far: f32, yaw, pitch, roll: f32, type: Camera_Type, } 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.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 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) { if camera == nil { return {}, false } center := camera.position + camera.forward view_matrix := linalg.MATRIX4F32_IDENTITY view_matrix *= linalg.matrix4_look_at_f32(camera.position, center, camera.up, flip_z_axis = true) return view_matrix, true } get_camera_projection_matrix :: proc(renderer: ^Renderer, camera: ^Camera) -> (linalg.Matrix4f32, bool) { if camera == nil { return {}, false } projection_matrix := linalg.MATRIX4F32_IDENTITY 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) { new_yaw := camera.yaw + yaw new_pitch := camera.pitch + pitch new_roll := camera.roll + 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), math.sin(pitch_rad), math.cos(pitch_rad) * math.sin(yaw_rad), } 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)) } set_active_camera :: proc(renderer: ^Renderer, camera: ^Camera) { renderer.active_camera = camera } get_active_camera :: proc(renderer: ^Renderer) -> ^Camera { assert(renderer != nil) return renderer.active_camera }