use crate::camera_math::CameraState;
use crate::fixed::ftol;
pub const PREC: i32 = 256 * 4096;
#[derive(Debug, Clone)]
pub struct OpticastPrelude {
pub forward_z_sign: i32,
pub li_pos: [i32; 3],
pub column_index: u32,
pub pos_xfrac: [f32; 2],
pub pos_yfrac: [f32; 2],
pub pos_z: i32,
pub y_lookup: Vec<i32>,
pub x_mip: i32,
pub max_scan_dist: i32,
}
#[allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
// pos_xfrac / pos_yfrac differ by one letter; that one letter is
// load-bearing (matches voxlap's gposxfrac / gposyfrac names) so
// suppressing the similar_names lint is the right call here.
clippy::similar_names
)]
#[must_use]
pub fn derive_prelude(
camera_state: &CameraState,
vsid: u32,
mip_levels: u32,
mip_scan_dist: i32,
max_scan_dist: i32,
) -> OpticastPrelude {
let forward_z_sign = if camera_state.forward[2] < 0.0 { -1 } else { 1 };
let li_pos = [
camera_state.pos[0] as i32,
camera_state.pos[1] as i32,
camera_state.pos[2] as i32,
];
let column_index = (li_pos[1] as u32) * vsid + (li_pos[0] as u32);
let xfrac1 = camera_state.pos[0] - li_pos[0] as f32;
let yfrac1 = camera_state.pos[1] - li_pos[1] as f32;
let pos_xfrac = [1.0 - xfrac1, xfrac1];
let pos_yfrac = [1.0 - yfrac1, yfrac1];
let pos_z = ftol(camera_state.pos[2] * PREC as f32 - 0.5);
let mut y_lookup = Vec::new();
for j in 0..mip_levels {
let count = (512u32 >> j) + 4;
let pz_shifted = pos_z >> j;
let shift = 16i32 - j as i32;
for i in 0..count {
let val = ((pz_shifted - (i as i32) * PREC) >> shift) & 0xFFFF;
y_lookup.push(val);
}
}
let x_mip = mip_scan_dist.max(4).wrapping_mul(PREC);
let max_scan_dist_clamped = max_scan_dist.clamp(1, 4095).wrapping_mul(PREC);
OpticastPrelude {
forward_z_sign,
li_pos,
column_index,
pos_xfrac,
pos_yfrac,
pos_z,
y_lookup,
x_mip,
max_scan_dist: max_scan_dist_clamped,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::camera_math;
use crate::Camera;
fn bits2(a: [f32; 2]) -> [u32; 2] {
a.map(f32::to_bits)
}
fn oracle_north_state() -> CameraState {
let cam = Camera {
pos: [1024.0, 1024.0, 128.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
};
camera_math::derive(&cam, 640, 480, 320.0, 240.0, 320.0)
}
#[test]
fn integer_position_and_column_index() {
let s = oracle_north_state();
let p = derive_prelude(&s, 2048, 1, 4, 1024);
assert_eq!(p.li_pos, [1024, 1024, 128]);
assert_eq!(p.column_index, 1024 * 2048 + 1024);
assert_eq!(p.forward_z_sign, 1); }
#[test]
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
fn integer_position_pos_at_voxel_centre() {
let cam = Camera {
pos: [10.25, 20.75, 30.5],
right: [1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
};
let s = camera_math::derive(&cam, 640, 480, 320.0, 240.0, 320.0);
let p = derive_prelude(&s, 2048, 1, 4, 1024);
assert_eq!(p.li_pos, [10, 20, 30]);
assert_eq!(bits2(p.pos_xfrac), bits2([0.75, 0.25]));
assert_eq!(bits2(p.pos_yfrac), bits2([0.25, 0.75]));
let want_pz = (30.5_f32 * PREC as f32 - 0.5).round_ties_even() as i32;
assert_eq!(p.pos_z, want_pz);
}
#[test]
fn forward_z_sign_negative_when_looking_down() {
let cam = Camera {
pos: [0.0, 0.0, 0.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 1.0, 0.0],
forward: [0.0, 0.0, -1.0],
};
let s = camera_math::derive(&cam, 640, 480, 320.0, 240.0, 320.0);
let p = derive_prelude(&s, 2048, 1, 4, 1024);
assert_eq!(p.forward_z_sign, -1);
}
#[test]
fn y_lookup_table_for_pos_z_at_voxel_grid() {
let s = oracle_north_state();
let p = derive_prelude(&s, 2048, 1, 4, 1024);
assert_eq!(p.y_lookup.len(), 516);
assert_eq!(p.y_lookup[0], 2048);
assert_eq!(p.y_lookup[128], 0);
assert_eq!(p.y_lookup[129], 65520);
}
#[test]
fn y_lookup_two_mip_levels_have_correct_lengths() {
let s = oracle_north_state();
let p = derive_prelude(&s, 2048, 2, 4, 1024);
assert_eq!(p.y_lookup.len(), 516 + 260);
}
#[test]
fn xmip_and_maxscandist_clamping() {
let s = oracle_north_state();
let p = derive_prelude(&s, 2048, 1, 0, 99999);
assert_eq!(p.x_mip, 4 * PREC);
assert_eq!(p.max_scan_dist, 4095_i32.wrapping_mul(PREC));
let q = derive_prelude(&s, 2048, 1, 16, 0);
assert_eq!(q.x_mip, 16 * PREC);
assert_eq!(q.max_scan_dist, PREC);
let r = derive_prelude(&s, 2048, 1, 4, 1024);
assert_eq!(r.max_scan_dist, 1024 * PREC);
}
}