#![allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub struct ShadowEdge {
pub v0: u32,
pub v1: u32,
pub face_a: u32,
pub face_b: u32,
}
#[derive(Debug, Default, Clone)]
pub struct ShadowVolumeMesh {
pub front_cap: Vec<[f32; 3]>,
pub back_cap: Vec<[f32; 3]>,
pub fins: Vec<[f32; 3]>,
pub light_direction: [f32; 3],
pub extrude_length: f32,
}
impl ShadowVolumeMesh {
pub fn new(light_direction: [f32; 3], extrude_length: f32) -> Self {
Self {
light_direction,
extrude_length,
..Default::default()
}
}
pub fn total_vertex_count(&self) -> usize {
self.front_cap.len() + self.back_cap.len() + self.fins.len()
}
pub fn clear(&mut self) {
self.front_cap.clear();
self.back_cap.clear();
self.fins.clear();
}
}
pub fn extrude_in_light_dir(position: [f32; 3], light_dir: [f32; 3], length: f32) -> [f32; 3] {
[
position[0] + light_dir[0] * length,
position[1] + light_dir[1] * length,
position[2] + light_dir[2] * length,
]
}
pub fn face_faces_light(normal: [f32; 3], light_dir: [f32; 3]) -> bool {
let dot = normal[0] * light_dir[0] + normal[1] * light_dir[1] + normal[2] * light_dir[2];
dot > 0.0
}
pub fn find_shadow_silhouette_edges(edges: &[ShadowEdge], lit_faces: &[bool]) -> Vec<ShadowEdge> {
edges
.iter()
.copied()
.filter(|e| {
let fa = e.face_a as usize;
let fb = e.face_b as usize;
let a_lit = lit_faces.get(fa).copied().unwrap_or(false);
let b_lit = lit_faces.get(fb).copied().unwrap_or(false);
a_lit != b_lit
})
.collect()
}
pub fn generate_shadow_volume(
positions: &[[f32; 3]],
light_dir: [f32; 3],
extrude_length: f32,
) -> ShadowVolumeMesh {
let mut sv = ShadowVolumeMesh::new(light_dir, extrude_length);
sv.front_cap.extend_from_slice(positions);
sv.back_cap = positions
.iter()
.map(|&p| extrude_in_light_dir(p, light_dir, extrude_length))
.collect();
sv
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_shadow_volume_empty() {
let sv = ShadowVolumeMesh::new([0.0, -1.0, 0.0], 5.0);
assert_eq!(sv.total_vertex_count(), 0);
}
#[test]
fn test_extrude_in_light_dir() {
let pos = [0.0f32, 0.0, 0.0];
let dir = [0.0f32, 1.0, 0.0];
let result = extrude_in_light_dir(pos, dir, 2.0);
assert!((result[1] - 2.0).abs() < f32::EPSILON);
}
#[test]
fn test_face_faces_light_true() {
assert!(face_faces_light([0.0, 1.0, 0.0], [0.0, 1.0, 0.0]));
}
#[test]
fn test_face_faces_light_false() {
assert!(!face_faces_light([0.0, -1.0, 0.0], [0.0, 1.0, 0.0]));
}
#[test]
fn test_generate_shadow_volume_caps() {
let pos = vec![[0.0f32; 3]; 6];
let sv = generate_shadow_volume(&pos, [0.0, -1.0, 0.0], 3.0);
assert_eq!(sv.front_cap.len(), 6);
assert_eq!(sv.back_cap.len(), 6);
}
#[test]
fn test_clear() {
let pos = vec![[0.0f32; 3]; 3];
let mut sv = generate_shadow_volume(&pos, [0.0, -1.0, 0.0], 1.0);
sv.clear();
assert_eq!(sv.total_vertex_count(), 0);
}
#[test]
fn test_find_shadow_silhouette_edges_empty() {
assert!(find_shadow_silhouette_edges(&[], &[true, false]).is_empty());
}
#[test]
fn test_find_shadow_silhouette_edges_filter() {
let edges = vec![ShadowEdge {
v0: 0,
v1: 1,
face_a: 0,
face_b: 1,
}];
let lit_faces = vec![true, false];
let result = find_shadow_silhouette_edges(&edges, &lit_faces);
assert_eq!(result.len(), 1);
}
#[test]
fn test_extrude_zero_length() {
let p = [1.0f32, 2.0, 3.0];
assert_eq!(extrude_in_light_dir(p, [1.0, 0.0, 0.0], 0.0), p);
}
#[test]
fn test_total_vertex_count() {
let mut sv = ShadowVolumeMesh::new([0.0, -1.0, 0.0], 1.0);
sv.front_cap.push([0.0; 3]);
sv.back_cap.push([0.0; 3]);
sv.fins.push([0.0; 3]);
assert_eq!(sv.total_vertex_count(), 3);
}
}