Files
renderer/camera.odin

183 lines
4.4 KiB
Odin

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, .Render),
camera.near,
camera.far,
flip_z_axis = true,
)
}
case Camera_Orthographic: {
half_h := t.height / 2
half_w := half_h * get_aspect_ratio(renderer, .Render)
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
}