use super::{SparseVoxelOctree, SvoNode};
use crate::compiled::AabbPacked;
pub struct LinearizedSvo {
pub nodes: Vec<SvoNode>,
pub level_counts: Vec<u32>,
pub level_offsets: Vec<u32>,
pub depth: u32,
pub bounds: AabbPacked,
}
impl LinearizedSvo {
#[inline]
pub fn node_count(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn memory_bytes(&self) -> usize {
self.nodes.len() * std::mem::size_of::<SvoNode>()
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
unsafe {
std::slice::from_raw_parts(
self.nodes.as_ptr() as *const u8,
self.nodes.len() * std::mem::size_of::<SvoNode>(),
)
}
}
pub fn nodes_at_level(&self, level: u32) -> &[SvoNode] {
if level as usize >= self.level_offsets.len() {
return &[];
}
let start = self.level_offsets[level as usize] as usize;
let count = self.level_counts[level as usize] as usize;
let end = (start + count).min(self.nodes.len());
&self.nodes[start..end]
}
}
pub fn linearize_svo(svo: &SparseVoxelOctree) -> LinearizedSvo {
if svo.nodes.is_empty() {
return LinearizedSvo {
nodes: Vec::new(),
level_counts: Vec::new(),
level_offsets: Vec::new(),
depth: 0,
bounds: svo.bounds,
};
}
let mut level_counts: Vec<u32> = Vec::new();
let mut level_offsets: Vec<u32> = Vec::new();
let mut current_level_start = 0usize;
let mut current_level_count = 1usize; let mut depth = 0u32;
loop {
level_offsets.push(current_level_start as u32);
level_counts.push(current_level_count as u32);
let mut next_level_count = 0usize;
for i in current_level_start..(current_level_start + current_level_count) {
if i >= svo.nodes.len() {
break;
}
let node = &svo.nodes[i];
if node.is_leaf == 0 {
next_level_count += node.child_mask.count_ones() as usize;
}
}
if next_level_count == 0 {
break;
}
current_level_start += current_level_count;
current_level_count = next_level_count;
depth += 1;
}
LinearizedSvo {
nodes: svo.nodes.clone(),
level_counts,
level_offsets,
depth,
bounds: svo.bounds,
}
}
pub fn validate_linearized(lin: &LinearizedSvo) -> Result<(), String> {
for (i, node) in lin.nodes.iter().enumerate() {
if node.is_leaf == 0 {
let num_children = node.child_mask.count_ones();
if num_children == 0 {
return Err(format!("Interior node {} has no children", i));
}
let first = node.first_child as usize;
let last = first + num_children as usize - 1;
if last >= lin.nodes.len() {
return Err(format!(
"Node {} references children {}-{} but only {} nodes exist",
i,
first,
last,
lin.nodes.len()
));
}
}
}
Ok(())
}
pub fn compact_svo(svo: &SparseVoxelOctree) -> SparseVoxelOctree {
if svo.nodes.is_empty() {
return SparseVoxelOctree {
nodes: Vec::new(),
bounds: svo.bounds,
max_depth: 0,
leaf_count: 0,
interior_count: 0,
};
}
let mut reachable = vec![false; svo.nodes.len()];
let mut queue: std::collections::VecDeque<usize> = std::collections::VecDeque::new();
reachable[0] = true;
queue.push_back(0);
while let Some(idx) = queue.pop_front() {
let node = &svo.nodes[idx];
if node.is_leaf == 0 {
for octant in 0..8u8 {
if let Some(child_idx) = node.child_index(octant) {
let ci = child_idx as usize;
if ci < svo.nodes.len() && !reachable[ci] {
reachable[ci] = true;
queue.push_back(ci);
}
}
}
}
}
let mut old_to_new = vec![0u32; svo.nodes.len()];
let mut new_idx = 0u32;
for (i, &r) in reachable.iter().enumerate() {
if r {
old_to_new[i] = new_idx;
new_idx += 1;
}
}
let mut new_nodes = Vec::with_capacity(new_idx as usize);
let mut leaf_count = 0u32;
let mut interior_count = 0u32;
for (i, &r) in reachable.iter().enumerate() {
if !r {
continue;
}
let mut node = svo.nodes[i];
if node.is_leaf == 1 {
leaf_count += 1;
} else {
interior_count += 1;
let old_first = node.first_child as usize;
if old_first < svo.nodes.len() {
node.first_child = old_to_new[old_first];
}
}
new_nodes.push(node);
}
SparseVoxelOctree {
nodes: new_nodes,
bounds: svo.bounds,
max_depth: svo.max_depth,
leaf_count,
interior_count,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::svo::SvoBuildConfig;
use crate::types::SdfNode;
use glam::Vec3;
fn make_test_svo() -> SparseVoxelOctree {
let sphere = SdfNode::sphere(1.0);
let config = SvoBuildConfig {
max_depth: 3,
bounds_min: Vec3::splat(-2.0),
bounds_max: Vec3::splat(2.0),
use_compiled: false,
..Default::default()
};
super::super::build::build_svo(&sphere, &config)
}
#[test]
fn test_linearize() {
let svo = make_test_svo();
let lin = linearize_svo(&svo);
assert_eq!(lin.node_count(), svo.node_count());
assert!(lin.level_counts.len() > 1, "Should have multiple levels");
assert_eq!(lin.level_counts[0], 1, "Level 0 should have 1 node (root)");
}
#[test]
fn test_linearize_level_offsets() {
let svo = make_test_svo();
let lin = linearize_svo(&svo);
assert_eq!(lin.level_offsets[0], 0);
for i in 1..lin.level_offsets.len() {
assert_eq!(
lin.level_offsets[i],
lin.level_offsets[i - 1] + lin.level_counts[i - 1],
"Level offsets should be cumulative at level {}",
i
);
}
}
#[test]
fn test_validate() {
let svo = make_test_svo();
let lin = linearize_svo(&svo);
assert!(validate_linearized(&lin).is_ok());
}
#[test]
fn test_nodes_at_level() {
let svo = make_test_svo();
let lin = linearize_svo(&svo);
let root_level = lin.nodes_at_level(0);
assert_eq!(root_level.len(), 1);
let level1 = lin.nodes_at_level(1);
assert!(level1.len() <= 8);
}
#[test]
fn test_as_bytes() {
let svo = make_test_svo();
let lin = linearize_svo(&svo);
let bytes = lin.as_bytes();
assert_eq!(bytes.len(), lin.node_count() * 32);
}
#[test]
fn test_compact() {
let svo = make_test_svo();
let compacted = compact_svo(&svo);
assert!(compacted.node_count() <= svo.node_count());
assert_eq!(
compacted.leaf_count + compacted.interior_count,
compacted.node_count() as u32
);
}
#[test]
fn test_linearize_empty() {
let svo = SparseVoxelOctree {
nodes: Vec::new(),
bounds: AabbPacked::empty(),
max_depth: 0,
leaf_count: 0,
interior_count: 0,
};
let lin = linearize_svo(&svo);
assert_eq!(lin.node_count(), 0);
assert_eq!(lin.depth, 0);
}
#[test]
fn test_memory_bytes() {
let svo = make_test_svo();
let lin = linearize_svo(&svo);
assert_eq!(lin.memory_bytes(), lin.node_count() * 32);
}
}