use std::io::Read;
use flate2::read::GzDecoder;
use roxlap_core::{
opticast, rasterizer::ScratchPool, scalar_rasterizer::ScalarRasterizer, Camera,
OpticastOutcome, OpticastSettings,
};
use roxlap_formats::vxl;
const ORACLE_VXL_GZ: &[u8] = include_bytes!("../../../assets/oracle.vxl.gz");
const XRES: u32 = 320;
const YRES: u32 = 240;
fn load_oracle_world() -> vxl::Vxl {
let mut decoder = GzDecoder::new(ORACLE_VXL_GZ);
let mut bytes = Vec::with_capacity(40 * 1024 * 1024);
decoder
.read_to_end(&mut bytes)
.expect("ungzip oracle.vxl.gz");
vxl::parse(&bytes).expect("parse oracle.vxl")
}
fn fnv1a64(bytes: &[u8]) -> u64 {
let mut h = 0xcbf2_9ce4_8422_2325u64;
for &b in bytes {
h ^= u64::from(b);
h = h.wrapping_mul(0x100_0000_01b3);
}
h
}
#[allow(clippy::cast_precision_loss)]
fn render_and_hash(vxl: &vxl::Vxl, mip_levels: u32) -> u64 {
let pixel_count = (XRES as usize) * (YRES as usize);
let mut framebuffer = vec![0u32; pixel_count];
let mut zbuffer = vec![0.0f32; pixel_count];
let mut pool = ScratchPool::new(XRES, YRES, vxl.vsid);
let cam = Camera {
pos: [1024.0, 850.0, 100.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
};
let half_w = (XRES as f32) * 0.5;
let half_h = (YRES as f32) * 0.5;
let settings = OpticastSettings {
xres: XRES,
yres: YRES,
y_start: 0,
y_end: YRES,
hx: half_w,
hy: half_h,
hz: half_w,
anginc: 1,
mip_levels,
mip_scan_dist: 4,
max_scan_dist: 1024,
};
{
let mut rasterizer = ScalarRasterizer::new(
&mut framebuffer,
&mut zbuffer,
XRES as usize,
&vxl.data,
&vxl.column_offset,
&vxl.mip_base_offsets,
vxl.vsid,
);
let outcome = opticast(
&mut rasterizer,
&mut pool,
&cam,
&settings,
vxl.vsid,
&vxl.data,
&vxl.column_offset,
);
assert_eq!(outcome, OpticastOutcome::Rendered);
}
let bytes = bytemuck_cast(&framebuffer);
fnv1a64(bytes)
}
fn bytemuck_cast(fb: &[u32]) -> &[u8] {
let (head, body, tail) = unsafe { fb.align_to::<u8>() };
assert!(head.is_empty() && tail.is_empty(), "u32 → u8 reslice clean");
body
}
#[test]
fn multi_mip_renders_without_panic_and_diverges_from_single_mip() {
let mut vxl = load_oracle_world();
let baseline = render_and_hash(&vxl, 1);
vxl.generate_mips(4);
assert_eq!(vxl.mip_count(), 4);
let multi = render_and_hash(&vxl, 4);
assert_ne!(baseline, 0);
assert_ne!(multi, 0);
assert_ne!(
baseline, multi,
"multi-mip render should differ from single-mip baseline \
(baseline={baseline:#x}, multi={multi:#x})"
);
}
const BASELINE_SINGLE_MIP: u64 = 0xc287_c058_2874_9ce7;
const MULTI_MIP_4: u64 = 0x156e_d408_6987_e325;
#[test]
fn multi_mip_hashes_match_pinned_goldens() {
let mut vxl = load_oracle_world();
let baseline = render_and_hash(&vxl, 1);
assert_eq!(
baseline, BASELINE_SINGLE_MIP,
"single-mip baseline drift: {baseline:#018x}"
);
vxl.generate_mips(4);
let multi = render_and_hash(&vxl, 4);
assert_eq!(multi, MULTI_MIP_4, "multi-mip golden drift: {multi:#018x}");
}