#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub enum OcclusionResult {
Visible,
Occluded,
Pending,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct OcclusionQuery {
pub id: u32,
pub result: OcclusionResult,
pub sample_count: u64,
}
impl OcclusionQuery {
#[allow(dead_code)]
pub fn new(id: u32) -> Self {
Self {
id,
result: OcclusionResult::Pending,
sample_count: 0,
}
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct OcclusionAabb {
pub min: [f32; 3],
pub max: [f32; 3],
}
impl OcclusionAabb {
#[allow(dead_code)]
pub fn new(min: [f32; 3], max: [f32; 3]) -> Self {
Self { min, max }
}
#[allow(dead_code)]
pub fn inflate(&self, amount: f32) -> Self {
Self {
min: [
self.min[0] - amount,
self.min[1] - amount,
self.min[2] - amount,
],
max: [
self.max[0] + amount,
self.max[1] + amount,
self.max[2] + amount,
],
}
}
#[allow(dead_code)]
pub fn contains_point(&self, p: [f32; 3]) -> bool {
p[0] >= self.min[0]
&& p[0] <= self.max[0]
&& p[1] >= self.min[1]
&& p[1] <= self.max[1]
&& p[2] >= self.min[2]
&& p[2] <= self.max[2]
}
#[allow(dead_code)]
pub fn intersects(&self, other: &OcclusionAabb) -> bool {
self.min[0] <= other.max[0]
&& self.max[0] >= other.min[0]
&& self.min[1] <= other.max[1]
&& self.max[1] >= other.min[1]
&& self.min[2] <= other.max[2]
&& self.max[2] >= other.min[2]
}
#[allow(dead_code)]
pub fn surface_area(&self) -> f32 {
let dx = (self.max[0] - self.min[0]).max(0.0);
let dy = (self.max[1] - self.min[1]).max(0.0);
let dz = (self.max[2] - self.min[2]).max(0.0);
2.0 * (dx * dy + dy * dz + dz * dx)
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct SoftOcclusionBuffer {
pub width: u32,
pub height: u32,
depth: Vec<f32>,
}
impl SoftOcclusionBuffer {
#[allow(dead_code)]
pub fn new(width: u32, height: u32) -> Self {
let count = (width * height) as usize;
Self {
width,
height,
depth: vec![1.0; count],
}
}
#[allow(dead_code)]
pub fn clear(&mut self) {
for d in self.depth.iter_mut() {
*d = 1.0;
}
}
#[allow(dead_code)]
pub fn write(&mut self, x: u32, y: u32, depth: f32) {
let idx = (y * self.width + x) as usize;
if idx < self.depth.len() {
let d = &mut self.depth[idx];
if depth < *d {
*d = depth;
}
}
}
#[allow(dead_code)]
pub fn test(&self, x: u32, y: u32, depth: f32) -> bool {
let idx = (y * self.width + x) as usize;
if idx < self.depth.len() {
depth < self.depth[idx]
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn query_starts_pending() {
let q = OcclusionQuery::new(1);
assert_eq!(q.result, OcclusionResult::Pending);
}
#[test]
fn aabb_contains_center() {
let aabb = OcclusionAabb::new([-1.0; 3], [1.0; 3]);
assert!(aabb.contains_point([0.0, 0.0, 0.0]));
}
#[test]
fn aabb_excludes_outside() {
let aabb = OcclusionAabb::new([-1.0; 3], [1.0; 3]);
assert!(!aabb.contains_point([2.0, 0.0, 0.0]));
}
#[test]
fn aabb_inflate_grows() {
let aabb = OcclusionAabb::new([0.0; 3], [1.0; 3]);
let inflated = aabb.inflate(1.0);
assert!((inflated.min[0] + 1.0).abs() < 1e-6);
}
#[test]
fn aabb_intersects_overlapping() {
let a = OcclusionAabb::new([0.0; 3], [2.0; 3]);
let b = OcclusionAabb::new([1.0; 3], [3.0; 3]);
assert!(a.intersects(&b));
}
#[test]
fn aabb_not_intersects_separate() {
let a = OcclusionAabb::new([0.0; 3], [1.0; 3]);
let b = OcclusionAabb::new([2.0; 3], [3.0; 3]);
assert!(!a.intersects(&b));
}
#[test]
fn surface_area_unit_cube() {
let aabb = OcclusionAabb::new([0.0; 3], [1.0; 3]);
assert!((aabb.surface_area() - 6.0).abs() < 1e-5);
}
#[test]
fn soft_buffer_write_test() {
let mut buf = SoftOcclusionBuffer::new(4, 4);
buf.write(0, 0, 0.5);
assert!(buf.test(0, 0, 0.3));
assert!(!buf.test(0, 0, 0.8));
}
#[test]
fn soft_buffer_clear_resets() {
let mut buf = SoftOcclusionBuffer::new(2, 2);
buf.write(0, 0, 0.1);
buf.clear();
assert!(buf.test(0, 0, 0.99));
}
}