use std::sync::Arc;
use glam::Vec3;
use crate::scene::camera::ResolvedCamera;
use crate::tree::Rect;
const DEPTH_BIAS: f32 = 1.0e-3;
#[derive(Clone, Debug)]
pub struct SceneDepthMap {
pub camera: ResolvedCamera,
pub rect: Rect,
pub width: u32,
pub height: u32,
pub depth: Arc<[f32]>,
}
impl SceneDepthMap {
pub fn occludes(&self, world: Vec3) -> bool {
if self.width == 0 || self.height == 0 || self.depth.is_empty() {
return true;
}
let Some((p, z)) = self.camera.project_to_screen_with_depth(world, self.rect) else {
return true; };
let fx = (p.x - self.rect.x) / self.rect.w.max(f32::EPSILON);
let fy = (p.y - self.rect.y) / self.rect.h.max(f32::EPSILON);
if !(0.0..1.0).contains(&fx) || !(0.0..1.0).contains(&fy) {
return true; }
let tx = ((fx * self.width as f32) as u32).min(self.width - 1);
let ty = ((fy * self.height as f32) as u32).min(self.height - 1);
let Some(&surface_z) = self.depth.get((ty * self.width + tx) as usize) else {
return true;
};
z > surface_z + DEPTH_BIAS
}
}
#[cfg(test)]
mod tests {
use super::*;
fn camera() -> ResolvedCamera {
ResolvedCamera {
eye: Vec3::new(0.0, 0.0, 5.0),
target: Vec3::ZERO,
up: Vec3::Y,
fov_y: std::f32::consts::FRAC_PI_4,
near: 0.1,
far: 100.0,
}
}
fn rect() -> Rect {
Rect::new(0.0, 0.0, 200.0, 200.0)
}
fn map_with(surface_z: f32) -> SceneDepthMap {
SceneDepthMap {
camera: camera(),
rect: rect(),
width: 1,
height: 1,
depth: Arc::from(vec![surface_z]),
}
}
#[test]
fn far_surface_does_not_occlude_a_point_in_front() {
assert!(!map_with(1.0).occludes(Vec3::ZERO));
}
#[test]
fn near_surface_occludes_a_point_behind_it() {
assert!(map_with(0.0).occludes(Vec3::ZERO));
}
#[test]
fn point_behind_camera_is_occluded() {
assert!(map_with(1.0).occludes(Vec3::new(0.0, 0.0, 20.0)));
}
#[test]
fn point_outside_viewport_is_occluded() {
assert!(map_with(1.0).occludes(Vec3::new(100.0, 0.0, 0.0)));
}
#[test]
fn empty_map_occludes_everything() {
let empty = SceneDepthMap {
camera: camera(),
rect: rect(),
width: 0,
height: 0,
depth: Arc::from(Vec::<f32>::new()),
};
assert!(empty.occludes(Vec3::ZERO));
}
}