pub mod build;
pub mod linearize;
pub mod query;
pub mod streaming;
use crate::compiled::AabbPacked;
use glam::Vec3;
pub use build::{build_svo, build_svo_compiled};
pub use linearize::{linearize_svo, LinearizedSvo};
pub use query::{svo_nearest_surface, svo_query_point, svo_ray_query, SvoRayHit};
pub use streaming::{SvoChunk, SvoStreamingCache};
#[repr(C, align(32))]
#[derive(Copy, Clone, Debug)]
pub struct SvoNode {
pub distance: f32,
pub nx: f32,
pub ny: f32,
pub nz: f32,
pub child_mask: u8,
pub is_leaf: u8,
pub material_id: u16,
pub first_child: u32,
pub _pad: [f32; 2],
}
impl Default for SvoNode {
fn default() -> Self {
Self {
distance: f32::MAX,
nx: 0.0,
ny: 0.0,
nz: 0.0,
child_mask: 0,
is_leaf: 1,
material_id: 0,
first_child: 0,
_pad: [0.0; 2],
}
}
}
impl SvoNode {
#[inline]
pub const fn leaf(distance: f32, normal: Vec3) -> Self {
Self {
distance,
nx: normal.x,
ny: normal.y,
nz: normal.z,
child_mask: 0,
is_leaf: 1,
material_id: 0,
first_child: 0,
_pad: [0.0; 2],
}
}
#[inline]
pub const fn interior(distance: f32, child_mask: u8, first_child: u32) -> Self {
Self {
distance,
nx: 0.0,
ny: 0.0,
nz: 0.0,
child_mask,
is_leaf: 0,
material_id: 0,
first_child,
_pad: [0.0; 2],
}
}
#[inline]
pub const fn normal(&self) -> Vec3 {
Vec3::new(self.nx, self.ny, self.nz)
}
#[inline]
pub const fn child_count(&self) -> u32 {
self.child_mask.count_ones()
}
#[inline]
pub const fn child_index(&self, octant: u8) -> Option<u32> {
if self.child_mask & (1 << octant) == 0 {
return None;
}
let bits_before = self.child_mask & ((1 << octant) - 1);
Some(self.first_child + bits_before.count_ones())
}
}
#[derive(Debug, Clone)]
pub struct SvoBuildConfig {
pub max_depth: u32,
pub distance_threshold: f32,
pub surface_detail: f32,
pub use_compiled: bool,
pub bounds_min: Vec3,
pub bounds_max: Vec3,
}
impl Default for SvoBuildConfig {
fn default() -> Self {
Self {
max_depth: 8,
distance_threshold: 1.5,
surface_detail: 1.0,
use_compiled: true,
bounds_min: Vec3::splat(-2.0),
bounds_max: Vec3::splat(2.0),
}
}
}
pub struct SparseVoxelOctree {
pub nodes: Vec<SvoNode>,
pub bounds: AabbPacked,
pub max_depth: u32,
pub leaf_count: u32,
pub interior_count: u32,
}
impl SparseVoxelOctree {
pub fn build(node: &crate::types::SdfNode, config: &SvoBuildConfig) -> Self {
build_svo(node, config)
}
pub fn build_compiled(
compiled: &crate::compiled::CompiledSdf,
config: &SvoBuildConfig,
) -> Self {
build_svo_compiled(compiled, config)
}
#[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 query_point(&self, point: Vec3) -> f32 {
svo_query_point(self, point)
}
#[inline]
pub fn ray_query(&self, origin: Vec3, direction: Vec3, max_distance: f32) -> Option<SvoRayHit> {
svo_ray_query(self, origin, direction, max_distance)
}
#[inline]
pub fn nearest_surface(&self, point: Vec3) -> (f32, Vec3) {
svo_nearest_surface(self, point)
}
pub fn linearize(&self) -> LinearizedSvo {
linearize_svo(self)
}
}
#[inline]
pub fn octant_for_point(point: Vec3, center: Vec3) -> u8 {
let mut octant = 0u8;
if point.x > center.x {
octant |= 1;
}
if point.y > center.y {
octant |= 2;
}
if point.z > center.z {
octant |= 4;
}
octant
}
#[inline]
pub fn child_center(parent_center: Vec3, parent_half_size: Vec3, octant: u8) -> Vec3 {
let quarter = parent_half_size * 0.5;
Vec3::new(
parent_center.x
+ if octant & 1 != 0 {
quarter.x
} else {
-quarter.x
},
parent_center.y
+ if octant & 2 != 0 {
quarter.y
} else {
-quarter.y
},
parent_center.z
+ if octant & 4 != 0 {
quarter.z
} else {
-quarter.z
},
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_svo_node_size() {
assert_eq!(std::mem::size_of::<SvoNode>(), 32);
}
#[test]
fn test_svo_node_default() {
let node = SvoNode::default();
assert_eq!(node.distance, f32::MAX);
assert_eq!(node.is_leaf, 1);
assert_eq!(node.child_mask, 0);
}
#[test]
fn test_svo_node_leaf() {
let node = SvoNode::leaf(1.5, Vec3::new(0.0, 1.0, 0.0));
assert_eq!(node.distance, 1.5);
assert_eq!(node.ny, 1.0);
assert_eq!(node.is_leaf, 1);
assert_eq!(node.child_count(), 0);
}
#[test]
fn test_svo_node_interior() {
let node = SvoNode::interior(0.5, 0b10110101, 42);
assert_eq!(node.is_leaf, 0);
assert_eq!(node.child_mask, 0b10110101);
assert_eq!(node.first_child, 42);
assert_eq!(node.child_count(), 5);
}
#[test]
fn test_child_index() {
let node = SvoNode::interior(0.0, 0b00110101, 10);
assert_eq!(node.child_index(0), Some(10)); assert_eq!(node.child_index(1), None); assert_eq!(node.child_index(2), Some(11)); assert_eq!(node.child_index(3), None); assert_eq!(node.child_index(4), Some(12)); assert_eq!(node.child_index(5), Some(13)); assert_eq!(node.child_index(6), None); assert_eq!(node.child_index(7), None); }
#[test]
fn test_octant_for_point() {
let center = Vec3::ZERO;
assert_eq!(octant_for_point(Vec3::new(-1.0, -1.0, -1.0), center), 0);
assert_eq!(octant_for_point(Vec3::new(1.0, -1.0, -1.0), center), 1);
assert_eq!(octant_for_point(Vec3::new(-1.0, 1.0, -1.0), center), 2);
assert_eq!(octant_for_point(Vec3::new(1.0, 1.0, -1.0), center), 3);
assert_eq!(octant_for_point(Vec3::new(-1.0, -1.0, 1.0), center), 4);
assert_eq!(octant_for_point(Vec3::new(1.0, 1.0, 1.0), center), 7);
}
#[test]
fn test_child_center() {
let parent = Vec3::ZERO;
let half = Vec3::splat(1.0);
let c0 = child_center(parent, half, 0);
assert_eq!(c0, Vec3::new(-0.5, -0.5, -0.5));
let c7 = child_center(parent, half, 7);
assert_eq!(c7, Vec3::new(0.5, 0.5, 0.5));
let c3 = child_center(parent, half, 3); assert_eq!(c3, Vec3::new(0.5, 0.5, -0.5));
}
}