use super::*;
use crate::optics::studio_rig::StudioRig;
const LAMBDAS: [f32; 3] = [450.0, 550.0, 650.0];
const EXPOSURE: f32 = 1.3;
const POSES: [(f32, f32); 2] = [(0.84, 0.94), (-2.1, 0.3)];
const SHADOWS_DEG: [f32; 3] = [0.0, 16.0, 30.0];
const AZIMUTHS: [(f32, f32); 4] = [(1.0, 0.0), (0.0, 1.0), (0.6, 0.8), (-0.8, -0.6)];
const HEIGHTS: [[f32; 4]; 3] = [
[0.98, 0.7, 0.35, 0.1],
[0.049, 0.02, -0.02, -0.049],
[-0.1, -0.35, -0.7, -0.98],
];
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0100_0000_01b3;
const PINS: [u64; 180] = [
0x4bbf_4f01_a2ac_4d0d, 0x4bbf_4f01_a2ac_4d0d, 0x4bbf_4f01_a2ac_4d0d, 0x4bbf_4f01_a2ac_4d0d, 0x5a2e_8322_8229_f076, 0x178f_459a_1b35_55db, 0x2e5c_fe83_0967_4f4f, 0x2e5c_fe83_0967_4f4f, 0x2e5c_fe83_0967_4f4f, 0x2e5c_fe83_0967_4f4f, 0x171b_e73e_e874_f14f, 0x5704_63e8_694d_6b45, 0x6b8e_f66d_51fa_cf95, 0x6b8e_f66d_51fa_cf95, 0x6b8e_f66d_51fa_cf95, 0x6b8e_f66d_51fa_cf95, 0x69e0_7608_30ea_2421, 0xc197_7a07_dd98_96f6, 0x6b8e_f66d_51fa_cf95, 0x6b8e_f66d_51fa_cf95, 0x6b8e_f66d_51fa_cf95, 0x6b8e_f66d_51fa_cf95, 0x69e0_7608_30ea_2421, 0xc197_7a07_dd98_96f6, 0xeb65_7e58_f073_d005, 0xeb65_7e58_f073_d005, 0xeb65_7e58_f073_d005, 0xeb65_7e58_f073_d005, 0x5e3c_499e_f461_c4a5, 0x4d18_d615_2db2_a2dd, 0xeb65_7e58_f073_d005, 0xeb65_7e58_f073_d005, 0xeb65_7e58_f073_d005, 0xeb65_7e58_f073_d005, 0x5e3c_499e_f461_c4a5, 0x4d18_d615_2db2_a2dd, 0x8e57_6d4d_771c_0fab, 0x8e57_6d4d_771c_0fab, 0x8e57_6d4d_771c_0fab, 0x8e57_6d4d_771c_0fab, 0x2864_d1fc_cd9b_5bdb, 0xe87f_4634_70ce_8734, 0x5d22_5e2f_e9ab_2cb1, 0x5d22_5e2f_e9ab_2cb1, 0x5d22_5e2f_e9ab_2cb1, 0x5d22_5e2f_e9ab_2cb1, 0x6899_eeb5_6e26_7e75, 0x62d0_0081_2b57_a084, 0x1bb0_fdd4_d5f4_705e, 0x1bb0_fdd4_d5f4_705e, 0x1bb0_fdd4_d5f4_705e, 0x1bb0_fdd4_d5f4_705e, 0x0381_a399_f0a4_7ce6, 0x4149_b587_db6e_fcd8, 0x271b_b8a2_2703_5500, 0x271b_b8a2_2703_5500, 0x271b_b8a2_2703_5500, 0x271b_b8a2_2703_5500, 0x5fcf_9d1a_d970_8294, 0x943a_9406_0104_2b23, 0xc7fa_3633_20da_8f59, 0xc7fa_3633_20da_8f59, 0xc7fa_3633_20da_8f59, 0xc7fa_3633_20da_8f59, 0x0b73_795e_43a9_e00d, 0x68fa_5e77_e73a_08ab, 0x2f56_d761_0622_c355, 0x2f56_d761_0622_c355, 0x2f56_d761_0622_c355, 0x2f56_d761_0622_c355, 0x5802_b636_7455_7b65, 0x189c_225c_32da_d14f, 0xc7fa_3633_20da_8f59, 0xc7fa_3633_20da_8f59, 0xc7fa_3633_20da_8f59, 0xc7fa_3633_20da_8f59, 0x0b73_795e_43a9_e00d, 0x68fa_5e77_e73a_08ab, 0x2f56_d761_0622_c355, 0x2f56_d761_0622_c355, 0x2f56_d761_0622_c355, 0x2f56_d761_0622_c355, 0x5802_b636_7455_7b65, 0x189c_225c_32da_d14f, 0x705f_43be_7ca7_cca0, 0x705f_43be_7ca7_cca0, 0x705f_43be_7ca7_cca0, 0x705f_43be_7ca7_cca0, 0x705f_43be_7ca7_cca0, 0x705f_43be_7ca7_cca0, 0x4b8a_9577_fe5b_f416, 0x4b8a_9577_fe5b_f416, 0x4b8a_9577_fe5b_f416, 0x4b8a_9577_fe5b_f416, 0x4b8a_9577_fe5b_f416, 0x4b8a_9577_fe5b_f416, 0xb21c_9087_1ab5_6b58, 0xb21c_9087_1ab5_6b58, 0xb21c_9087_1ab5_6b58, 0xb21c_9087_1ab5_6b58, 0xb21c_9087_1ab5_6b58, 0xb21c_9087_1ab5_6b58, 0x8d6e_6991_5278_0665, 0x8d6e_6991_5278_0665, 0x8d6e_6991_5278_0665, 0x8d6e_6991_5278_0665, 0x8d6e_6991_5278_0665, 0x8d6e_6991_5278_0665, 0xfdfd_0994_9973_a410, 0xfdfd_0994_9973_a410, 0xfdfd_0994_9973_a410, 0xfdfd_0994_9973_a410, 0xfdfd_0994_9973_a410, 0xfdfd_0994_9973_a410, 0x513c_1bc6_2228_085c, 0x513c_1bc6_2228_085c, 0x513c_1bc6_2228_085c, 0x513c_1bc6_2228_085c, 0x513c_1bc6_2228_085c, 0x513c_1bc6_2228_085c, 0x267e_ba12_797a_c638, 0x267e_ba12_797a_c638, 0x267e_ba12_797a_c638, 0x267e_ba12_797a_c638, 0x267e_ba12_797a_c638, 0x267e_ba12_797a_c638, 0x393c_7768_63a6_afb6, 0x393c_7768_63a6_afb6, 0x393c_7768_63a6_afb6, 0x393c_7768_63a6_afb6, 0x393c_7768_63a6_afb6, 0x393c_7768_63a6_afb6, 0x4910_4652_9b65_ab65, 0x4910_4652_9b65_ab65, 0x4910_4652_9b65_ab65, 0x4910_4652_9b65_ab65, 0x4910_4652_9b65_ab65, 0x4910_4652_9b65_ab65, 0x84e3_28f2_dadc_ce2d, 0x84e3_28f2_dadc_ce2d, 0x84e3_28f2_dadc_ce2d, 0x84e3_28f2_dadc_ce2d, 0x84e3_28f2_dadc_ce2d, 0x84e3_28f2_dadc_ce2d, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xe3ff_5bbc_0bb6_b305, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xb9d0_059d_6108_3025, 0xeba4_66cb_6a2c_ddbe, 0xeba4_66cb_6a2c_ddbe, 0xeba4_66cb_6a2c_ddbe, 0xeba4_66cb_6a2c_ddbe, 0xeba4_66cb_6a2c_ddbe, 0xeba4_66cb_6a2c_ddbe, 0x8375_7d95_8ded_d556, 0x8375_7d95_8ded_d556, 0x8375_7d95_8ded_d556, 0x8375_7d95_8ded_d556, 0x8375_7d95_8ded_d556, 0x8375_7d95_8ded_d556, ];
fn pin_literal(hash: u64) -> String {
format!(
"0x{:04x}_{:04x}_{:04x}_{:04x}",
(hash >> 48) & 0xffff,
(hash >> 32) & 0xffff,
(hash >> 16) & 0xffff,
hash & 0xffff
)
}
fn fold(hash: &mut u64, bits: u32) {
for byte in bits.to_le_bytes() {
*hash ^= u64::from(byte);
*hash = hash.wrapping_mul(FNV_PRIME);
}
}
fn directions() -> Vec<Vec3> {
let mut out = Vec::with_capacity(48);
for group in HEIGHTS {
for y in group {
let radius = y.mul_add(-y, 1.0).sqrt();
for (hx, hz) in AZIMUTHS {
out.push(Vec3::new(hx * radius, y, hz * radius));
}
}
}
out
}
fn row_hash(
preset: LightingPreset,
pose: (f32, f32),
observer: Vec3,
shadow_deg: f32,
dirs: &[Vec3],
) -> u64 {
let rig = StudioRig::new(pose.0, pose.1);
let environment = preset
.studio(EXPOSURE, pose.0, pose.1)
.with_head_shadow(shadow_deg)
.with_backdrop(0.0);
let cosines = head_shadow_cosines(environment.head_shadow_deg());
let mut hash = FNV_OFFSET;
for &dir in dirs {
let channels =
sample_environment_channels(environment, dir, &LAMBDAS, Some(&rig), observer);
for (&lambda, &value) in LAMBDAS.iter().zip(&channels) {
let single = sample_studio_environment_with_rig_shadow(
dir, lambda, preset, EXPOSURE, &rig, observer, cosines,
);
assert_eq!(
value.to_bits(),
single.to_bits(),
"{preset:?} dir {dir:?} at {lambda} nm: channels vs single-wavelength entry"
);
fold(&mut hash, value.to_bits());
}
}
hash
}
#[test]
fn as_lit_is_bit_identical() {
let dirs = directions();
assert_eq!(dirs.len(), 48);
let observers = [Vec3::ZERO, Vec3::new(0.1, 0.95, 0.3).normalize()];
let mut rows = Vec::with_capacity(PINS.len());
println!("const PINS: [u64; {}] = [", PINS.len());
for preset in LightingPreset::ALL {
for (pose_index, &pose) in POSES.iter().enumerate() {
for (observer_index, &observer) in observers.iter().enumerate() {
for &shadow_deg in &SHADOWS_DEG {
let hash = row_hash(preset, pose, observer, shadow_deg, &dirs);
println!(
" {}, // {preset:?} pose {pose_index} observer {observer_index} head shadow {shadow_deg}",
pin_literal(hash)
);
rows.push((
format!(
"{preset:?} pose {pose_index} observer {observer_index} head shadow {shadow_deg}"
),
hash,
));
}
}
}
}
println!("];");
assert_eq!(rows.len(), PINS.len());
if PINS.iter().all(|&pin| pin == 0) {
println!("PINS is still all zeros: copy the table above into PINS, then remove #[ignore].");
return;
}
for ((label, hash), &pin) in rows.iter().zip(&PINS) {
assert_eq!(*hash, pin, "As Lit radiance bits moved for {label}");
}
}