use crate::Camera;
#[derive(Debug, Clone, Copy)]
pub struct CameraState {
pub pos: [f32; 3],
pub right: [f32; 3],
pub down: [f32; 3],
pub forward: [f32; 3],
pub xs: [f32; 3],
pub ys: [f32; 3],
pub zs: [f32; 3],
pub add: [f32; 3],
pub corn: [[f32; 3]; 4],
pub nor: [[f32; 3]; 4],
}
#[allow(
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::cast_lossless
)]
#[must_use]
pub fn derive(camera: &Camera, xres: u32, yres: u32, hx: f32, hy: f32, hz: f32) -> CameraState {
let pos = camera.pos.map(|v| v as f32);
let right = camera.right.map(|v| v as f32);
let down = camera.down.map(|v| v as f32);
let forward = camera.forward.map(|v| v as f32);
let xs = [right[0], down[0], forward[0]];
let ys = [right[1], down[1], forward[1]];
let zs = [right[2], down[2], forward[2]];
let add = [
-(pos[0] * right[0] + pos[1] * right[1] + pos[2] * right[2]),
-(pos[0] * down[0] + pos[1] * down[1] + pos[2] * down[2]),
-(pos[0] * forward[0] + pos[1] * forward[1] + pos[2] * forward[2]),
];
let xres_f = xres as f32;
let yres_f = yres as f32;
let c0 = [
-hx * right[0] - hy * down[0] + hz * forward[0],
-hx * right[1] - hy * down[1] + hz * forward[1],
-hx * right[2] - hy * down[2] + hz * forward[2],
];
let c1 = [
xres_f * right[0] + c0[0],
xres_f * right[1] + c0[1],
xres_f * right[2] + c0[2],
];
let c2 = [
yres_f * down[0] + c1[0],
yres_f * down[1] + c1[1],
yres_f * down[2] + c1[2],
];
let c3 = [
yres_f * down[0] + c0[0],
yres_f * down[1] + c0[1],
yres_f * down[2] + c0[2],
];
let corn = [c0, c1, c2, c3];
let mut nor = [[0.0f32; 3]; 4];
for i in 0..4 {
let a = corn[i];
let b = corn[(i + 1) % 4];
nor[i] = [
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
];
}
CameraState {
pos,
right,
down,
forward,
xs,
ys,
zs,
add,
corn,
nor,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn bits3(a: [f32; 3]) -> [u32; 3] {
a.map(f32::to_bits)
}
#[test]
fn identity_camera_origin_basic_derivations() {
let cam = Camera {
pos: [0.0, 0.0, 0.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
};
let s = derive(&cam, 640, 480, 320.0, 240.0, 320.0);
assert_eq!(bits3(s.right), bits3([1.0, 0.0, 0.0]));
assert_eq!(bits3(s.down), bits3([0.0, 0.0, 1.0]));
assert_eq!(bits3(s.forward), bits3([0.0, 1.0, 0.0]));
assert_eq!(bits3(s.xs), bits3([1.0, 0.0, 0.0]));
assert_eq!(bits3(s.ys), bits3([0.0, 0.0, 1.0]));
assert_eq!(bits3(s.zs), bits3([0.0, 1.0, 0.0]));
assert_eq!(bits3(s.add), bits3([-0.0, -0.0, -0.0]));
assert_eq!(bits3(s.corn[0]), bits3([-320.0, 320.0, -240.0]));
assert_eq!(bits3(s.corn[1]), bits3([320.0, 320.0, -240.0]));
assert_eq!(bits3(s.corn[2]), bits3([320.0, 320.0, 240.0]));
assert_eq!(bits3(s.corn[3]), bits3([-320.0, 320.0, 240.0]));
}
#[test]
fn identity_camera_frustum_edge_normals() {
let cam = Camera {
pos: [0.0, 0.0, 0.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
};
let s = derive(&cam, 640, 480, 320.0, 240.0, 320.0);
assert_eq!(bits3(s.nor[0]), bits3([0.0, -153_600.0, -204_800.0]));
assert_eq!(bits3(s.nor[2]), bits3([0.0, -153_600.0, 204_800.0]));
}
#[test]
fn translated_camera_add_is_negative_dot() {
let cam = Camera {
pos: [10.0, 20.0, 30.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
};
let s = derive(&cam, 640, 480, 320.0, 240.0, 320.0);
assert_eq!(bits3(s.add), bits3([-10.0, -30.0, -20.0]));
assert_eq!(bits3(s.corn[0]), bits3([-320.0, 320.0, -240.0]));
}
#[test]
fn yawed_camera_basis_propagates() {
let cam = Camera {
pos: [0.0, 0.0, 0.0],
right: [0.0, 1.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [-1.0, 0.0, 0.0],
};
let s = derive(&cam, 640, 480, 320.0, 240.0, 320.0);
assert_eq!(bits3(s.corn[0]), bits3([-320.0, -320.0, -240.0]));
assert_eq!(bits3(s.xs), bits3([0.0, 0.0, -1.0])); assert_eq!(bits3(s.ys), bits3([1.0, 0.0, 0.0]));
assert_eq!(bits3(s.zs), bits3([0.0, 1.0, 0.0]));
}
}