#![allow(clippy::similar_names)]
use glam::DVec3;
use roxlap_core::opticast::{opticast, OpticastOutcome, OpticastSettings};
use roxlap_core::rasterizer::ScratchPool;
use roxlap_core::scalar_rasterizer::ScalarRasterizer;
use roxlap_core::sky::Sky;
use roxlap_core::Camera;
use crate::{GridTransform, Scene, CHUNK_SIZE_XY};
const SKY_MASK_SENTINEL: u32 = 0x00_DE_AD_BE;
fn world_camera_to_grid_local(camera: &Camera, transform: &GridTransform) -> Camera {
let inv = transform.rotation.inverse();
let world_offset = DVec3::from_array(camera.pos) - transform.origin;
let local_pos = inv * world_offset;
let local_right = inv * DVec3::from_array(camera.right);
let local_down = inv * DVec3::from_array(camera.down);
let local_forward = inv * DVec3::from_array(camera.forward);
Camera {
pos: local_pos.to_array(),
right: local_right.to_array(),
down: local_down.to_array(),
forward: local_forward.to_array(),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RenderOutcome {
Rendered {
grids_drawn: usize,
},
Empty,
}
#[allow(clippy::too_many_arguments)]
pub fn render_scene(
fb: &mut [u32],
zb: &mut [f32],
pitch_pixels: usize,
width: u32,
height: u32,
pool: &mut ScratchPool,
scene: &mut Scene,
camera: &Camera,
settings: &OpticastSettings,
sky: Option<&Sky>,
) -> RenderOutcome {
debug_assert_eq!(fb.len(), zb.len());
let pixel_count = (width as usize) * (height as usize);
debug_assert_eq!(fb.len(), pixel_count);
let mut grids_drawn = 0usize;
for (_id, grid) in scene.grids_mut() {
let Some(backing) = grid.chunk_xyz_backing() else {
continue;
};
let local_cam = world_camera_to_grid_local(camera, &grid.transform);
let cg = roxlap_core::ChunkGrid {
chunks: &backing.chunks,
origin_chunk_xy: backing.origin_chunk_xy,
origin_chunk_z: backing.origin_chunk_z,
chunks_x: backing.chunks_x,
chunks_y: backing.chunks_y,
chunks_z: backing.chunks_z,
};
let grid_view = roxlap_core::GridView::from_chunk_grid(&cg, CHUNK_SIZE_XY);
let outcome = {
let mut rasterizer = ScalarRasterizer::new(fb, zb, pitch_pixels, grid_view);
if let Some(sky_ref) = sky {
rasterizer = rasterizer.with_sky(sky_ref);
}
opticast(&mut rasterizer, pool, &local_cam, settings, grid_view)
};
if outcome == OpticastOutcome::Rendered {
grids_drawn += 1;
}
}
if grids_drawn == 0 {
RenderOutcome::Empty
} else {
RenderOutcome::Rendered { grids_drawn }
}
}
pub fn compose_into(
shared_fb: &mut [u32],
shared_zb: &mut [f32],
temp_fb: &[u32],
temp_zb: &[f32],
) {
debug_assert_eq!(shared_fb.len(), shared_zb.len());
debug_assert_eq!(shared_fb.len(), temp_fb.len());
debug_assert_eq!(shared_fb.len(), temp_zb.len());
for i in 0..shared_fb.len() {
if temp_zb[i] < shared_zb[i] {
shared_fb[i] = temp_fb[i];
shared_zb[i] = temp_zb[i];
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn render_scene_composed(
fb: &mut [u32],
zb: &mut [f32],
pitch_pixels: usize,
width: u32,
height: u32,
pool: &mut ScratchPool,
scene: &mut Scene,
camera: &Camera,
settings: &OpticastSettings,
sky_color: u32,
sky: Option<&Sky>,
) -> RenderOutcome {
debug_assert_eq!(fb.len(), zb.len());
let pixel_count = (width as usize) * (height as usize);
debug_assert_eq!(fb.len(), pixel_count);
let mut grids_drawn = 0usize;
let mut temp_fb = vec![sky_color; pixel_count];
let mut temp_zb = vec![f32::INFINITY; pixel_count];
for (_id, grid) in scene.grids_mut() {
let Some(backing) = grid.chunk_xyz_backing() else {
continue;
};
let owns_sky = grid.render_sky;
let local_sky_color = if owns_sky {
sky_color
} else {
SKY_MASK_SENTINEL
};
if !owns_sky {
pool.set_skycast(SKY_MASK_SENTINEL as i32, 0);
}
temp_fb.fill(local_sky_color);
temp_zb.fill(f32::INFINITY);
let local_cam = world_camera_to_grid_local(camera, &grid.transform);
let cg = roxlap_core::ChunkGrid {
chunks: &backing.chunks,
origin_chunk_xy: backing.origin_chunk_xy,
origin_chunk_z: backing.origin_chunk_z,
chunks_x: backing.chunks_x,
chunks_y: backing.chunks_y,
chunks_z: backing.chunks_z,
};
let grid_view = roxlap_core::GridView::from_chunk_grid(&cg, CHUNK_SIZE_XY);
let per_grid_settings;
let active_settings = match grid.mip_levels_override {
Some(cap) => {
let clamped = cap.clamp(1, settings.mip_levels);
per_grid_settings = OpticastSettings {
mip_levels: clamped,
..*settings
};
&per_grid_settings
}
None => settings,
};
let outcome = {
let mut rasterizer =
ScalarRasterizer::new(&mut temp_fb, &mut temp_zb, pitch_pixels, grid_view);
if owns_sky {
if let Some(sky_ref) = sky {
rasterizer = rasterizer.with_sky(sky_ref);
}
}
opticast(
&mut rasterizer,
pool,
&local_cam,
active_settings,
grid_view,
)
};
if !owns_sky {
for (px, z) in temp_fb.iter().zip(temp_zb.iter_mut()) {
if *px == SKY_MASK_SENTINEL {
*z = f32::INFINITY;
}
}
pool.set_skycast(sky_color as i32, 0);
}
if outcome == OpticastOutcome::Rendered {
compose_into(fb, zb, &temp_fb, &temp_zb);
grids_drawn += 1;
}
}
if grids_drawn == 0 {
RenderOutcome::Empty
} else {
RenderOutcome::Rendered { grids_drawn }
}
}
#[cfg(test)]
#[allow(clippy::float_cmp)]
mod tests {
use super::*;
use crate::{GridTransform, Scene, CHUNK_SIZE_XY};
use glam::{DVec3, IVec3};
use roxlap_core::opticast::{opticast as core_opticast, OpticastSettings};
use roxlap_core::rasterizer::ScratchPool;
use roxlap_core::scalar_rasterizer::ScalarRasterizer;
use roxlap_core::{Camera, Engine};
const XRES: u32 = 320;
const YRES: u32 = 200;
fn build_one_grid_scene(world_origin: DVec3) -> (Scene, crate::GridId) {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(world_origin));
let grid = scene.grid_mut(id).unwrap();
grid.set_rect(
IVec3::new(40, 40, 40),
IVec3::new(55, 55, 55),
Some(0x80_88_88_88),
);
grid.set_sphere(IVec3::new(80, 80, 80), 6, Some(0x80_22_aa_22));
(scene, id)
}
fn camera_at(pos: [f64; 3]) -> Camera {
Camera {
pos,
right: [-1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
}
}
fn render_setup(pool_vsid: u32) -> (Engine, ScratchPool, Vec<u32>, Vec<f32>) {
let engine = Engine::new();
let mut pool = ScratchPool::new(XRES, YRES, pool_vsid);
let sky = engine.sky_color();
let sky_col_i = i32::from_ne_bytes(sky.to_ne_bytes());
pool.set_skycast(sky_col_i, 0);
let fog_col_i = i32::from_ne_bytes(engine.fog_color().to_ne_bytes());
pool.set_fog(fog_col_i, engine.fog_max_scan_dist());
pool.set_treat_z_max_as_air(true);
let pixel_count = (XRES as usize) * (YRES as usize);
let framebuffer = vec![sky; pixel_count];
let zbuffer = vec![0.0f32; pixel_count];
(engine, pool, framebuffer, zbuffer)
}
fn render_via_scene(scene: &mut Scene, camera: &Camera) -> Vec<u32> {
let (_engine, mut pool, mut fb, mut zb) = render_setup(CHUNK_SIZE_XY);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
scene,
camera,
&settings,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
fb
}
fn render_via_direct_opticast(scene: &Scene, local_camera: &Camera) -> Vec<u32> {
let (_engine, mut pool, mut fb, mut zb) = render_setup(CHUNK_SIZE_XY);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let grid = scene.grids().next().unwrap().1;
let chunk = grid.chunk(IVec3::ZERO).unwrap();
let grid_view = roxlap_core::GridView::from_single_vxl(chunk);
let mut rasterizer = ScalarRasterizer::new(&mut fb, &mut zb, XRES as usize, grid_view);
let _ = core_opticast(
&mut rasterizer,
&mut pool,
local_camera,
&settings,
grid_view,
);
drop(rasterizer);
fb
}
#[test]
fn world_camera_to_grid_local_identity_rotation_translates_pos_only() {
let camera = Camera {
pos: [110.0, 220.0, 330.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
};
let transform = GridTransform::at(DVec3::new(100.0, 200.0, 300.0));
let local = super::world_camera_to_grid_local(&camera, &transform);
assert_eq!(local.right, camera.right);
assert_eq!(local.down, camera.down);
assert_eq!(local.forward, camera.forward);
for (got, want) in local.pos.iter().zip([10.0, 20.0, 30.0].iter()) {
assert!((got - want).abs() < 1e-12, "pos got={got} want={want}");
}
}
#[test]
fn world_camera_to_grid_local_90deg_z_rotates_basis_and_pos() {
use glam::DQuat;
let camera = Camera {
pos: [0.0, 10.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 transform = GridTransform {
origin: DVec3::ZERO,
rotation: DQuat::from_rotation_z(std::f64::consts::FRAC_PI_2),
};
let local = super::world_camera_to_grid_local(&camera, &transform);
let approx_eq =
|a: [f64; 3], b: [f64; 3]| a.iter().zip(b.iter()).all(|(x, y)| (x - y).abs() < 1e-9);
assert!(
approx_eq(local.pos, [10.0, 0.0, 0.0]),
"pos={:?} expected ~(10, 0, 0)",
local.pos
);
assert!(
approx_eq(local.right, [0.0, -1.0, 0.0]),
"right={:?} expected ~(0, -1, 0)",
local.right
);
assert!(
approx_eq(local.down, [0.0, 0.0, 1.0]),
"down={:?} expected ~(0, 0, 1)",
local.down
);
assert!(
approx_eq(local.forward, [1.0, 0.0, 0.0]),
"forward={:?} expected ~(1, 0, 0)",
local.forward
);
}
#[test]
fn world_camera_to_grid_local_preserves_basis_orthonormality() {
use glam::DQuat;
let camera = Camera {
pos: [3.0, -5.0, 7.0],
right: [-1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
};
let transform = GridTransform {
origin: DVec3::new(1.0, 2.0, 3.0),
rotation: DQuat::from_axis_angle(glam::DVec3::new(0.3, 0.8, 0.5).normalize(), 0.7),
};
let local = super::world_camera_to_grid_local(&camera, &transform);
let r = DVec3::from_array(local.right);
let d = DVec3::from_array(local.down);
let f = DVec3::from_array(local.forward);
for v in [r, d, f] {
assert!(
(v.length_squared() - 1.0).abs() < 1e-12,
"basis vec {v:?} not unit length"
);
}
assert!(r.dot(d).abs() < 1e-12, "right·down = {}", r.dot(d));
assert!(r.dot(f).abs() < 1e-12, "right·forward = {}", r.dot(f));
assert!(d.dot(f).abs() < 1e-12, "down·forward = {}", d.dot(f));
let cross = r.cross(d);
assert!(
(cross - f).length() < 1e-12,
"right×down={cross:?} forward={f:?}"
);
}
fn build_one_grid_marker_scene(transform: GridTransform) -> (Scene, crate::GridId, u32) {
let mut scene = Scene::new();
let id = scene.add_grid(transform);
let grid = scene.grid_mut(id).unwrap();
grid.set_rect(
IVec3::new(40, 40, 40),
IVec3::new(55, 55, 55),
Some(0x80_55_aa_22), );
(scene, id, 0x80_55_aa_22)
}
#[test]
fn s5_1_180deg_z_rotated_grid_byte_identical_to_axis_aligned() {
use glam::DQuat;
let axis_aligned_camera = Camera {
pos: [40.0, -20.0, 50.0],
right: [-1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
};
let rotated_camera = Camera {
pos: [-40.0, 20.0, 50.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, -1.0, 0.0],
};
let q = DQuat::from_xyzw(0.0, 0.0, 1.0, 0.0);
let rot_pos = q * DVec3::from_array(axis_aligned_camera.pos);
let rot_fwd = q * DVec3::from_array(axis_aligned_camera.forward);
assert_eq!(rot_pos.to_array(), rotated_camera.pos);
assert_eq!(rot_fwd.to_array(), rotated_camera.forward);
let (mut scene_a, _, _) = build_one_grid_marker_scene(GridTransform::identity());
let fb_a = render_via_scene(&mut scene_a, &axis_aligned_camera);
let (mut scene_b, _, _) = build_one_grid_marker_scene(GridTransform {
origin: DVec3::ZERO,
rotation: q,
});
let fb_b = render_via_scene(&mut scene_b, &rotated_camera);
assert_eq!(
fb_a, fb_b,
"rotating both grid and camera by R about the grid origin must leave the framebuffer unchanged"
);
}
#[test]
fn s5_1_45deg_z_rotated_grid_renders_marker() {
use glam::DQuat;
let rotation = DQuat::from_rotation_z(std::f64::consts::FRAC_PI_4);
let (mut scene, _, marker) = build_one_grid_marker_scene(GridTransform {
origin: DVec3::ZERO,
rotation,
});
let marker_world = rotation * DVec3::new(47.5, 47.5, 47.5);
let camera = Camera {
pos: [marker_world.x, marker_world.y - 80.0, marker_world.z],
right: [-1.0, 0.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [0.0, 1.0, 0.0],
};
let (_engine, mut pool, mut fb, mut zb) = render_setup(CHUNK_SIZE_XY);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let marker_count = fb.iter().filter(|&&p| p == marker).count();
assert!(
marker_count > 50,
"45°-rotated marker box should be visible — got {marker_count} marker pixels"
);
}
#[test]
fn render_sky_false_drops_grid_sky_pixels() {
use crate::{GridId, GridTransform};
let mut scene = Scene::new();
let _b_id: GridId = scene.add_grid(GridTransform::at(DVec3::new(0.0, 600.0, 0.0)));
let b_id = scene.grids().next().unwrap().0;
scene.grid_mut(b_id).unwrap().set_rect(
IVec3::new(0, 0, 100),
IVec3::new(127, 127, 110),
Some(0x80_22_88_22), );
let a_id = scene.add_grid(GridTransform::at(DVec3::new(0.0, 200.0, 0.0)));
scene.grid_mut(a_id).unwrap().set_rect(
IVec3::new(60, 60, 60),
IVec3::new(67, 67, 67),
Some(0x80_aa_22_22), );
scene.grid_mut(a_id).unwrap().render_sky = false;
let unique_sky: u32 = 0xFF_AB_CD_EF;
let (_engine, mut pool, _) = make_composed_pool(CHUNK_SIZE_XY);
let mut fb = vec![unique_sky; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
let camera = camera_at([64.0, 0.0, 100.0]);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
unique_sky,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 2 });
let leaked = fb
.iter()
.filter(|&&p| p == super::SKY_MASK_SENTINEL)
.count();
assert_eq!(
leaked, 0,
"SKY_MASK_SENTINEL leaked into composed framebuffer ({leaked} pixels)"
);
let red_count = fb.iter().filter(|&&p| p == 0x80_aa_22_22).count();
assert!(
red_count > 0,
"red cube from sky-disabled grid A is missing — render_sky=false should only mask sky"
);
let green_count = fb.iter().filter(|&&p| p == 0x80_22_88_22).count();
assert!(
green_count > 0,
"grid B's floor invisible — grid A's masked sky may have overwritten it"
);
}
#[test]
fn render_sky_false_single_grid_no_sentinel_leak() {
let (mut scene, id, _) = build_one_grid_marker_scene(GridTransform::identity());
scene.grid_mut(id).unwrap().render_sky = false;
let unique_sky: u32 = 0xFF_12_34_56;
let (_engine, mut pool, _) = make_composed_pool(CHUNK_SIZE_XY);
let mut fb = vec![unique_sky; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
let camera = camera_at([64.0, 0.0, 64.0]);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
unique_sky,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let leaked = fb
.iter()
.filter(|&&p| p == super::SKY_MASK_SENTINEL)
.count();
assert_eq!(leaked, 0, "SKY_MASK_SENTINEL leaked ({leaked} pixels)");
let prefill_count = fb.iter().filter(|&&p| p == unique_sky).count();
assert!(
prefill_count > 0,
"no pre-fill pixels survived — render_sky=false should leave non-hit pixels untouched"
);
}
#[test]
fn render_scene_at_origin_matches_direct_opticast() {
let (mut scene, _) = build_one_grid_scene(DVec3::ZERO);
let cam = camera_at([64.0, 0.0, 64.0]);
let via_scene = render_via_scene(&mut scene, &cam);
let via_direct = render_via_direct_opticast(&scene, &cam);
assert_eq!(
via_scene, via_direct,
"render_scene with single 1-chunk grid at origin should match direct opticast"
);
}
#[test]
fn render_scene_translated_grid_matches_grid_local_opticast() {
let world_origin = DVec3::new(1000.0, 2000.0, 3000.0);
let (mut scene, _) = build_one_grid_scene(world_origin);
let world_cam = camera_at([1064.0, 2000.0, 3064.0]);
let local_cam = camera_at([64.0, 0.0, 64.0]);
let via_scene = render_via_scene(&mut scene, &world_cam);
let via_direct = render_via_direct_opticast(&scene, &local_cam);
assert_eq!(
via_scene, via_direct,
"render_scene of translated grid should match opticast with grid-local camera"
);
}
#[test]
fn empty_scene_returns_empty_outcome() {
let mut scene = Scene::new();
let (_engine, mut pool, mut fb, mut zb) = render_setup(CHUNK_SIZE_XY);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera_at([0.0, 0.0, 0.0]),
&settings,
None,
);
assert_eq!(outcome, RenderOutcome::Empty);
}
fn build_two_grid_side_by_side() -> (Scene, u32, u32) {
let mut scene = Scene::new();
let g0 = scene.add_grid(GridTransform::at(DVec3::new(0.0, 200.0, 0.0)));
scene.grid_mut(g0).unwrap().set_rect(
IVec3::new(56, 56, 92),
IVec3::new(71, 71, 107),
Some(0x80_88_22_22), );
let _g1 = scene.add_grid(GridTransform::at(DVec3::new(200.0, 200.0, 0.0)));
let g1_id = scene
.grids()
.filter(|(id, _)| *id != g0)
.map(|(id, _)| id)
.next()
.unwrap();
scene.grid_mut(g1_id).unwrap().set_rect(
IVec3::new(56, 56, 92),
IVec3::new(71, 71, 107),
Some(0x80_22_22_88), );
(scene, 0x80_88_22_22, 0x80_22_22_88)
}
fn make_composed_pool(pool_vsid: u32) -> (Engine, ScratchPool, u32) {
let engine = Engine::new();
let mut pool = ScratchPool::new(XRES, YRES, pool_vsid);
let sky_color = engine.sky_color();
let sky_col_i = i32::from_ne_bytes(sky_color.to_ne_bytes());
pool.set_skycast(sky_col_i, 0);
let fog_col_i = i32::from_ne_bytes(engine.fog_color().to_ne_bytes());
pool.set_fog(fog_col_i, engine.fog_max_scan_dist());
pool.set_treat_z_max_as_air(true);
(engine, pool, sky_color)
}
fn pixel_count(width: u32, height: u32) -> usize {
(width as usize) * (height as usize)
}
#[test]
fn compose_into_takes_smaller_z() {
let mut shared_fb = vec![0xff_ff_ff_ff_u32; 4];
let mut shared_zb = vec![10.0f32; 4];
let temp_fb = [0xaa_aa_aa_aa, 0x11_22_33_44, 0x55_66_77_88, 0xde_ad_be_ef];
let temp_zb = [5.0f32, 20.0, 10.0, f32::INFINITY];
compose_into(&mut shared_fb, &mut shared_zb, &temp_fb, &temp_zb);
assert_eq!(shared_fb[0], 0xaa_aa_aa_aa);
assert_eq!(shared_zb[0], 5.0);
assert_eq!(shared_fb[1], 0xff_ff_ff_ff);
assert_eq!(shared_zb[1], 10.0);
assert_eq!(shared_fb[2], 0xff_ff_ff_ff);
assert_eq!(shared_fb[3], 0xff_ff_ff_ff);
}
#[test]
fn render_scene_composed_two_grids_both_visible() {
let (mut scene, red, blue) = build_two_grid_side_by_side();
let (_engine, mut pool, sky_color) = make_composed_pool(CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
let camera = camera_at([160.0, 100.0, 100.0]);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 2 });
let red_count = fb.iter().filter(|&&p| p == red).count();
let blue_count = fb.iter().filter(|&&p| p == blue).count();
assert!(
red_count > 0,
"no red pixels: grid 0 (red box) not visible after compose"
);
assert!(
blue_count > 0,
"no blue pixels: grid 1 (blue box) not visible after compose"
);
}
#[test]
fn render_scene_composed_grid_a_in_front_of_grid_b() {
let mut scene = Scene::new();
let g_a = scene.add_grid(GridTransform::at(DVec3::new(0.0, 50.0, 0.0)));
scene.grid_mut(g_a).unwrap().set_rect(
IVec3::new(56, 56, 92),
IVec3::new(71, 71, 107),
Some(0x80_aa_00_00), );
let _g_b = scene.add_grid(GridTransform::at(DVec3::new(0.0, 200.0, 0.0)));
let g_b_id = scene
.grids()
.filter(|(id, _)| *id != g_a)
.map(|(id, _)| id)
.next()
.unwrap();
scene.grid_mut(g_b_id).unwrap().set_rect(
IVec3::new(56, 56, 92),
IVec3::new(71, 71, 107),
Some(0x80_00_00_aa), );
let (_engine, mut pool, sky_color) = make_composed_pool(CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
let camera = camera_at([64.0, -10.0, 100.0]);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 2 });
let red_count = fb.iter().filter(|&&p| p == 0x80_aa_00_00).count();
assert!(
red_count > 0,
"expected red pixels (closer box should win z-test)"
);
let mut scene2 = Scene::new();
let g_b2 = scene2.add_grid(GridTransform::at(DVec3::new(0.0, 200.0, 0.0)));
scene2.grid_mut(g_b2).unwrap().set_rect(
IVec3::new(56, 56, 92),
IVec3::new(71, 71, 107),
Some(0x80_00_00_aa),
);
let g_a2 = scene2.add_grid(GridTransform::at(DVec3::new(0.0, 50.0, 0.0)));
scene2.grid_mut(g_a2).unwrap().set_rect(
IVec3::new(56, 56, 92),
IVec3::new(71, 71, 107),
Some(0x80_aa_00_00),
);
let mut fb2 = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb2 = vec![f32::INFINITY; pixel_count(XRES, YRES)];
let outcome2 = render_scene_composed(
&mut fb2,
&mut zb2,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene2,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome2, RenderOutcome::Rendered { grids_drawn: 2 });
assert_eq!(
fb, fb2,
"composition should be order-independent — same scene in different add order should produce identical output"
);
}
#[test]
fn render_scene_composed_empty_scene_returns_empty() {
let mut scene = Scene::new();
let (_engine, mut pool, sky_color) = make_composed_pool(CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
let camera = camera_at([0.0, 0.0, 0.0]);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Empty);
assert!(fb.iter().all(|&p| p == sky_color));
}
fn fnv1a64(data: &[u8]) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for &b in data {
h ^= u64::from(b);
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
h
}
#[test]
fn render_scene_two_chunk_x_grid_no_seam() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(DVec3::new(0.0, 200.0, 0.0)));
let g = scene.grid_mut(id).unwrap();
g.set_rect(
IVec3::new(120, 60, 200),
IVec3::new(136, 67, 215),
Some(0x80_aa_55_22),
);
assert_eq!(g.chunk_count(), 2);
let (_engine, mut pool, sky_color) = make_composed_pool(2 * CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
let camera = camera_at([128.0, 100.0, 207.0]);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let stripe = 0x80_aa_55_22;
let stripe_count = fb.iter().filter(|&&p| p == stripe).count();
assert!(
stripe_count > 200,
"stripe rendered too few pixels ({stripe_count}) — chunks may not be stitching"
);
let centre_y = (YRES / 2) as usize;
let row_start = centre_y * (XRES as usize);
let row = &fb[row_start..row_start + (XRES as usize)];
let mut in_stripe = false;
let mut seam_gaps = 0usize;
for &px in row {
if px == stripe {
in_stripe = true;
} else if in_stripe && px == sky_color {
if row.iter().skip_while(|&&p| p != px).any(|&p| p == stripe) {
seam_gaps += 1;
}
in_stripe = false;
}
}
assert!(
seam_gaps <= 1,
"centre row has {seam_gaps} disjoint stripe runs — expected 1 (chunk-edge seam suspected)"
);
}
#[test]
fn vxl_generate_mips_on_set_voxel_chunk_renders() {
let mut grid = crate::Grid::new(GridTransform::identity());
grid.set_rect(
IVec3::new(0, 0, 100),
IVec3::new(127, 127, 254),
Some(0x80_88_88_88),
);
let chunk = grid.chunks.get_mut(&IVec3::ZERO).unwrap();
chunk.generate_mips(3);
let (_engine, mut pool, sky_color) = make_composed_pool(CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
let camera = camera_at([64.0, 0.0, 64.0]);
let mut settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
settings.mip_levels = 3;
settings.mip_scan_dist = 32;
let grid_view = roxlap_core::GridView::from_single_vxl(&chunk);
let mut rasterizer = ScalarRasterizer::new(&mut fb, &mut zb, XRES as usize, grid_view);
let _ = core_opticast(&mut rasterizer, &mut pool, &camera, &settings, grid_view);
drop(rasterizer);
let non_sky = fb.iter().filter(|&&p| p != sky_color).count();
assert!(
non_sky > 0,
"Vxl::generate_mips on a set_voxel-built chunk should render to something non-sky (got {non_sky})"
);
}
#[test]
fn render_with_mips_present_still_renders_mip0() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(DVec3::ZERO));
scene.grid_mut(id).unwrap().set_rect(
IVec3::new(40, 40, 40),
IVec3::new(55, 55, 55),
Some(0x80_88_88_88),
);
{
let grid = scene.grid_mut(id).unwrap();
let chunk = grid.chunks.get_mut(&IVec3::ZERO).unwrap();
chunk.generate_mips(3);
}
let (_engine, mut pool, sky_color) = make_composed_pool(CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
let camera = camera_at([64.0, 0.0, 64.0]);
let mut settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
settings.mip_scan_dist = 100_000;
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let non_sky = fb.iter().filter(|&&p| p != sky_color).count();
assert!(
non_sky > 0,
"render of single-grid scene with mips present rendered all-sky: mip-0 may be corrupted by generate_mips"
);
}
#[test]
fn render_scene_two_chunk_x_grid_hash_is_stable() {
const GOLDEN: u64 = 0x215e_d66d_7359_4725;
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(DVec3::new(0.0, 200.0, 0.0)));
scene.grid_mut(id).unwrap().set_rect(
IVec3::new(120, 60, 200),
IVec3::new(136, 67, 215),
Some(0x80_aa_55_22),
);
let (_engine, mut pool, sky_color) = make_composed_pool(2 * CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
let camera = camera_at([128.0, 100.0, 207.0]);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let bytes: Vec<u8> = fb.iter().flat_map(|p| p.to_ne_bytes()).collect();
let hash = fnv1a64(&bytes);
if GOLDEN == SENTINEL {
eprintln!("render_scene_two_chunk_x_grid_hash_is_stable: capture hash = 0x{hash:016x}");
panic!("GOLDEN is the SENTINEL placeholder — paste 0x{hash:016x} into GOLDEN above");
}
assert_eq!(
hash, GOLDEN,
"2-chunk render hash drifted: expected 0x{GOLDEN:016x}, got 0x{hash:016x}"
);
}
const SENTINEL: u64 = 0xDEAD_BEEF_DEAD_BEEF;
#[test]
fn approach_b_renders_two_chunk_x_stripe_via_chunk_grid() {
const SENTINEL_B: u64 = 0xDEAD_BEEF_DEAD_BEEF;
const GOLDEN_B: u64 = 0x5ee1_e81c_66a8_d1f1;
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::identity());
let g = scene.grid_mut(id).unwrap();
g.set_rect(
IVec3::new(0, 0, 200),
IVec3::new(127, 127, 205),
Some(0x80_44_44_aa),
);
g.set_rect(
IVec3::new(160, 50, 150),
IVec3::new(170, 60, 165),
Some(0x80_aa_55_22),
);
assert_eq!(g.chunk_count(), 2);
let backing = g.chunk_xyz_backing().expect("at least one chunk populated");
assert_eq!(backing.chunks_x, 2);
assert_eq!(backing.chunks_y, 1);
assert_eq!(backing.origin_chunk_xy, [0, 0]);
let cg = roxlap_core::ChunkGrid {
chunks: &backing.chunks,
origin_chunk_xy: backing.origin_chunk_xy,
origin_chunk_z: backing.origin_chunk_z,
chunks_x: backing.chunks_x,
chunks_y: backing.chunks_y,
chunks_z: backing.chunks_z,
};
let grid_view = roxlap_core::GridView::from_chunk_grid(&cg, CHUNK_SIZE_XY);
let camera = Camera {
pos: [10.0, 64.0, 160.0],
right: [0.0, 1.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [1.0, 0.0, 0.0],
};
let (_engine, mut pool, mut fb, mut zb) = render_setup(2 * CHUNK_SIZE_XY);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let mut rasterizer = ScalarRasterizer::new(&mut fb, &mut zb, XRES as usize, grid_view);
let outcome = core_opticast(&mut rasterizer, &mut pool, &camera, &settings, grid_view);
drop(rasterizer);
assert_eq!(outcome, OpticastOutcome::Rendered);
let floor_count = fb.iter().filter(|&&p| p == 0x80_44_44_aa).count();
let box_count = fb.iter().filter(|&&p| p == 0x80_aa_55_22).count();
assert!(
floor_count > 1000,
"floor not visible — only {floor_count} floor pixels (single-chunk path?)"
);
assert!(
box_count > 50,
"box in chunk (1, 0) not visible — only {box_count} box pixels — cross-chunk DDA may have failed to fire"
);
let bytes: Vec<u8> = fb.iter().flat_map(|p| p.to_ne_bytes()).collect();
let hash = fnv1a64(&bytes);
if GOLDEN_B == SENTINEL_B {
eprintln!("approach_b_renders_two_chunk_x_stripe_via_chunk_grid: capture hash = 0x{hash:016x}");
panic!(
"GOLDEN_B is the SENTINEL placeholder — paste 0x{hash:016x} into GOLDEN_B above"
);
}
assert_eq!(
hash, GOLDEN_B,
"Approach B 2-chunk render hash drifted: expected 0x{GOLDEN_B:016x}, got 0x{hash:016x}"
);
}
#[test]
fn approach_b_camera_in_chunk_1_0_renders_neighbour() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::identity());
let g = scene.grid_mut(id).unwrap();
g.set_rect(
IVec3::new(128, 0, 200),
IVec3::new(255, 127, 205),
Some(0x80_44_44_aa),
);
g.set_rect(
IVec3::new(20, 50, 150),
IVec3::new(30, 60, 165),
Some(0x80_aa_55_22),
);
assert_eq!(g.chunk_count(), 2);
let backing = g.chunk_xyz_backing().expect("populated");
let cg = roxlap_core::ChunkGrid {
chunks: &backing.chunks,
origin_chunk_xy: backing.origin_chunk_xy,
origin_chunk_z: backing.origin_chunk_z,
chunks_x: backing.chunks_x,
chunks_y: backing.chunks_y,
chunks_z: backing.chunks_z,
};
let grid_view = roxlap_core::GridView::from_chunk_grid(&cg, CHUNK_SIZE_XY);
let (aabb_min, aabb_max) = grid_view.aabb_xy();
assert_eq!(aabb_min, [0, 0]);
assert_eq!(aabb_max, [256, 128]);
let camera = Camera {
pos: [200.0, 64.0, 160.0],
right: [0.0, -1.0, 0.0],
down: [0.0, 0.0, 1.0],
forward: [-1.0, 0.0, 0.0],
};
let (_engine, mut pool, mut fb, mut zb) = render_setup(2 * CHUNK_SIZE_XY);
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let mut rasterizer = ScalarRasterizer::new(&mut fb, &mut zb, XRES as usize, grid_view);
let outcome = core_opticast(&mut rasterizer, &mut pool, &camera, &settings, grid_view);
drop(rasterizer);
assert_eq!(outcome, OpticastOutcome::Rendered);
let floor_count = fb.iter().filter(|&&p| p == 0x80_44_44_aa).count();
let box_count = fb.iter().filter(|&&p| p == 0x80_aa_55_22).count();
assert!(
floor_count > 1000,
"floor under camera in chunk (1, 0) not visible — only {floor_count} floor pixels — in_bounds_xy fix may not have taken effect"
);
assert!(
box_count > 50,
"box in chunk (0, 0) not visible — only {box_count} box pixels — westward cross-chunk DDA failed"
);
}
#[test]
fn stacked_two_chunk_z_camera_in_chz1_sees_own_chunk_floor() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(DVec3::ZERO));
let g = scene.grid_mut(id).unwrap();
g.ensure_chunk(IVec3::new(0, 0, 0));
g.set_rect(
IVec3::new(60, 60, 306),
IVec3::new(72, 72, 310),
Some(0x80_33_66_99),
);
assert!(g.chunk(IVec3::new(0, 0, 1)).is_some());
let (_engine, mut pool, sky_color) = make_composed_pool(2 * CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
pool.set_treat_z_max_as_air(true);
let camera = Camera {
pos: [66.0, 66.0, 280.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 1.0, 0.0],
forward: [0.0, 0.0, 1.0],
};
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let floor_count = fb.iter().filter(|&&p| p == 0x80_33_66_99).count();
assert!(
floor_count > 100,
"camera at chz=1 with floor in same chunk should see it — got {floor_count} floor pixels"
);
}
#[test]
fn stacked_two_chunk_z_camera_in_chz0_sees_chz1_floor() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(DVec3::ZERO));
let g = scene.grid_mut(id).unwrap();
g.ensure_chunk(IVec3::new(0, 0, 0));
g.set_rect(
IVec3::new(60, 60, 306),
IVec3::new(72, 72, 310),
Some(0x80_77_aa_44),
);
assert!(g.chunk(IVec3::new(0, 0, 1)).is_some());
let (_engine, mut pool, sky_color) = make_composed_pool(2 * CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
pool.set_treat_z_max_as_air(true);
let camera = Camera {
pos: [66.0, 66.0, 100.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 1.0, 0.0],
forward: [0.0, 0.0, 1.0],
};
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let floor_count = fb.iter().filter(|&&p| p == 0x80_77_aa_44).count();
assert!(
floor_count > 50,
"camera in chz=0 air-gap should see chz=1 floor via cross-chunk look-down — got {floor_count} floor pixels"
);
}
#[test]
#[ignore = "S4B.6.l: known limitation — needs cf-splitting at chz boundaries"]
fn stacked_chz0_distant_mountain_visible_from_chz0_camera() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(DVec3::ZERO));
let g = scene.grid_mut(id).unwrap();
g.set_rect(
IVec3::new(100, 100, 100),
IVec3::new(124, 124, 200),
Some(0x80_aa_55_22), );
g.set_rect(
IVec3::new(0, 0, 336),
IVec3::new(128, 128, 360),
Some(0x80_22_88_44),
);
g.set_rect(IVec3::new(100, 100, 336), IVec3::new(124, 124, 360), None);
assert!(g.chunk(IVec3::new(0, 0, 0)).is_some());
assert!(g.chunk(IVec3::new(0, 0, 1)).is_some());
let (_engine, mut pool, sky_color) = make_composed_pool(CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
pool.set_treat_z_max_as_air(true);
let (sy, cy) = (std::f64::consts::FRAC_PI_4).sin_cos();
let (sp, cp) = 0.72_f64.sin_cos();
let camera = Camera {
pos: [40.0, 40.0, 60.0],
right: [-sy, cy, 0.0],
down: [-cy * sp, -sy * sp, cp],
forward: [cy * cp, sy * cp, sp],
};
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let mountain_count = fb.iter().filter(|&&p| p == 0x80_aa_55_22).count();
let hill_count = fb.iter().filter(|&&p| p == 0x80_22_88_44).count();
eprintln!("chz0-distant-mountain: mountain_chz0={mountain_count} hill_chz1={hill_count}");
assert!(
hill_count > 50,
"expected chz=1 hills via cross-chunk look-down — got {hill_count}"
);
assert!(
mountain_count > 50,
"expected chz=0 distant mountain visible — got {mountain_count} (S4B.6.l limitation)"
);
}
#[test]
fn mid_render_handoff_reveals_chz1_hills_under_mountain_camera() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(DVec3::ZERO));
let g = scene.grid_mut(id).unwrap();
g.set_rect(
IVec3::new(60, 60, 150),
IVec3::new(72, 72, 200),
Some(0x80_88_44_22), );
g.set_rect(
IVec3::new(0, 0, 336),
IVec3::new(128, 128, 360),
Some(0x80_22_88_44), );
g.set_rect(IVec3::new(60, 60, 336), IVec3::new(72, 72, 360), None);
assert!(g.chunk(IVec3::new(0, 0, 0)).is_some());
assert!(g.chunk(IVec3::new(0, 0, 1)).is_some());
let (_engine, mut pool, sky_color) = make_composed_pool(2 * CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
pool.set_treat_z_max_as_air(true);
let camera = Camera {
pos: [66.0, 66.0, 100.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 1.0, 0.0],
forward: [0.0, 0.0, 1.0],
};
let settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let mountain_count = fb.iter().filter(|&&p| p == 0x80_88_44_22).count();
let hill_count = fb.iter().filter(|&&p| p == 0x80_22_88_44).count();
let mut hill_depths: Vec<f32> = fb
.iter()
.zip(zb.iter())
.filter_map(|(&p, &d)| if p == 0x80_22_88_44 { Some(d) } else { None })
.collect();
hill_depths.sort_by(|a, b| a.partial_cmp(b).unwrap());
let median_hill_depth = hill_depths[hill_depths.len() / 2];
eprintln!(
"mid-render handoff: mountain={mountain_count} hill={hill_count} median_hill_depth={median_hill_depth:.1}"
);
assert!(
mountain_count > 50,
"should see mountain peak via chz=0 — got {mountain_count} mountain pixels"
);
assert!(
hill_count > 50,
"should see chz=1 hills via mid-render handoff — got {hill_count} hill pixels"
);
assert!(
(median_hill_depth - 236.0).abs() < 80.0,
"hill median depth should be ≈236 (camera→z=336); got {median_hill_depth:.1} — state.z1 may be stale at the mountain peak's z"
);
}
#[test]
fn stacked_two_chunk_z_camera_in_chz0_sees_chz1_floor_multi_mip() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(DVec3::ZERO));
let g = scene.grid_mut(id).unwrap();
g.ensure_chunk(IVec3::new(0, 0, 0));
g.set_rect(
IVec3::new(60, 60, 306),
IVec3::new(72, 72, 310),
Some(0x80_77_aa_44),
);
assert!(g.chunk(IVec3::new(0, 0, 1)).is_some());
let (_engine, mut pool, sky_color) = make_composed_pool(2 * CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
pool.set_treat_z_max_as_air(true);
let camera = Camera {
pos: [66.0, 66.0, 100.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 1.0, 0.0],
forward: [0.0, 0.0, 1.0],
};
let mut settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
settings.mip_levels = 2;
settings.mip_scan_dist = 16;
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let floor_count = fb.iter().filter(|&&p| p == 0x80_77_aa_44).count();
assert!(
floor_count > 50,
"multi-mip cross-chunk look-down should still see chz=1 floor — got {floor_count} floor pixels"
);
}
#[test]
fn stacked_three_chunk_z_camera_in_chz2_sees_own_chunk_floor_multi_mip() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(DVec3::ZERO));
let g = scene.grid_mut(id).unwrap();
g.ensure_chunk(IVec3::new(0, 0, 0));
g.ensure_chunk(IVec3::new(0, 0, 1));
g.set_rect(
IVec3::new(60, 60, 562),
IVec3::new(72, 72, 566),
Some(0x80_aa_55_22),
);
assert!(g.chunk(IVec3::new(0, 0, 2)).is_some());
let (_engine, mut pool, sky_color) = make_composed_pool(2 * CHUNK_SIZE_XY);
let mut fb = vec![sky_color; pixel_count(XRES, YRES)];
let mut zb = vec![f32::INFINITY; pixel_count(XRES, YRES)];
pool.set_treat_z_max_as_air(true);
let camera = Camera {
pos: [66.0, 66.0, 540.0],
right: [1.0, 0.0, 0.0],
down: [0.0, 1.0, 0.0],
forward: [0.0, 0.0, 1.0],
};
let mut settings = OpticastSettings::for_oracle_framebuffer(XRES, YRES);
settings.mip_levels = 2;
settings.mip_scan_dist = 16;
let outcome = render_scene_composed(
&mut fb,
&mut zb,
XRES as usize,
XRES,
YRES,
&mut pool,
&mut scene,
&camera,
&settings,
sky_color,
None,
);
assert_eq!(outcome, RenderOutcome::Rendered { grids_drawn: 1 });
let floor_count = fb.iter().filter(|&&p| p == 0x80_aa_55_22).count();
assert!(
floor_count > 100,
"camera at chz=2 with floor in same chunk should see it — got {floor_count} floor pixels"
);
}
}