use fnv::FnvHashMap; use crate::MAX_DAG_DEPTH; use crate::VChildDescriptor; use crate::Vec3; use crate::VoxelChunk; use crate::log_2; impl VoxelChunk { /// process a 3D array (`data` with dimensions `dim`) into a SVDAG, mapping values to materials using `f` pub fn from_dense_voxels(data : &[i32], dim : [usize; 3]) -> Self { assert!(dim[0] > 0 && dim[1] > 0 && dim[2] > 0); let depth = log_2(dim.iter().cloned().max().unwrap_or(0) - 1) as usize + 1; assert!(depth < MAX_DAG_DEPTH, "Depth is too large: {} >= {}", depth, MAX_DAG_DEPTH); let size = 1 << depth; fn recursive_create_dense( s : &mut VoxelChunk, d : usize, min : [usize; 3], size : usize, data : &[i32], dim : [usize; 3], dedup : &mut FnvHashMap ) -> i32 { if min[0] >= dim[0] || min[1] >= dim[1] || min[2] >= dim[2] { // air if the voxel does not intersect the voxel data return 0; } if size <= 1 { // once we reach size 1, take the material from the data let v = data[min[0] + dim[0] * (min[1] + dim[1] * min[2])]; // prevent awful fractal voxel graphs return -v.abs(); } const BOX_OFFSETS : [[usize; 3]; 8] = [ [0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1] ]; let mut voxel = VChildDescriptor{ sub_voxels : [0; 8], }; let half_size = size >> 1; let mut is_uniform = true; for i in 0..8 { let bmin = [ min[0] + BOX_OFFSETS[i][0] * half_size, min[1] + BOX_OFFSETS[i][1] * half_size, min[2] + BOX_OFFSETS[i][2] * half_size ]; voxel.sub_voxels[i] = recursive_create_dense(s, d - 1, bmin, half_size, data, dim, dedup); if voxel.sub_voxels[i] != voxel.sub_voxels[0] || voxel.sub_voxels[i] > 0 { // the subvoxels are not all the same leaf node, so this voxel is not uniform is_uniform = false; } } if is_uniform { return voxel.sub_voxels[0]; } if let Some(&id) = dedup.get(&voxel) { // this node is a duplicate id } else { // this node is new, so add it s.voxels.push(voxel); let id = s.voxels.len() as i32; dedup.insert(voxel, id); id } } let mut chunk = VoxelChunk::empty(); chunk.voxels.clear(); // we build a list of unique voxels and store them in here let mut dedup = FnvHashMap::default(); recursive_create_dense(&mut chunk, depth, [0,0,0], size, data, dim, &mut dedup); chunk.voxels.reverse(); //fixup the subvoxel pointers (we reversed the order) let n = chunk.voxels.len() as i32; for i in 0..(chunk.voxels.len()) { for j in 0..8 { let sv = chunk.voxels[i].sub_voxels[j]; if sv > 0 { let svi = n - sv + 1; chunk.voxels[i].sub_voxels[j] = svi; } } } chunk } /// process an implicit 3D array into a DAG. pub fn from_implicit_array i32>(depth : usize, mut implicit : F) -> Self { assert!(depth < MAX_DAG_DEPTH, "Depth is too large: {} >= {}", depth, MAX_DAG_DEPTH); let size = 1 << depth; fn recursive_create_dense_implicit i32>( s : &mut VoxelChunk, min : [usize; 3], size : usize, implicit : &mut F, dedup : &mut FnvHashMap ) -> i32 { if size <= 1 { // once we reach size 1, evaluate the material at the implicit surface let v = implicit(min[0], min[1], min[2]); // prevent awful fractal voxel graphs return -v.abs(); } const BOX_OFFSETS : [[usize; 3]; 8] = [ [0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1] ]; let mut voxel = VChildDescriptor{ sub_voxels : [0; 8], }; let half_size = size >> 1; let mut is_uniform = true; for i in 0..8 { let bmin = [ min[0] + BOX_OFFSETS[i][0] * half_size, min[1] + BOX_OFFSETS[i][1] * half_size, min[2] + BOX_OFFSETS[i][2] * half_size ]; voxel.sub_voxels[i] = recursive_create_dense_implicit(s, bmin, half_size, implicit, dedup); if voxel.sub_voxels[i] != voxel.sub_voxels[0] || voxel.sub_voxels[i] > 0 { // the subvoxels are not all the same leaf node, so this voxel is not uniform is_uniform = false; } } if is_uniform { return voxel.sub_voxels[0]; } if let Some(&id) = dedup.get(&voxel) { // this node is a duplicate id } else { // this node is new, so add it s.voxels.push(voxel); let id = s.voxels.len() as i32; dedup.insert(voxel, id); id } } let mut chunk = VoxelChunk::empty(); chunk.voxels.clear(); // we build a list of unique voxels and store them in here let mut dedup = FnvHashMap::default(); recursive_create_dense_implicit(&mut chunk, [0,0,0], size, &mut implicit, &mut dedup); chunk.voxels.reverse(); //fixup the subvoxel pointers (we reversed the order) let n = chunk.voxels.len() as i32; for i in 0..(chunk.voxels.len()) { for j in 0..8 { let sv = chunk.voxels[i].sub_voxels[j]; if sv > 0 { let svi = n - sv + 1; chunk.voxels[i].sub_voxels[j] = svi; } } } chunk } /// process a distance equation array into a DAG. /// pub fn from_distance_equation f32>(depth : usize, mut implicit : F) -> Self { assert!(depth < MAX_DAG_DEPTH, "Depth is too large: {} >= {}", depth, MAX_DAG_DEPTH); let size = 1 << depth; fn recurse_distance_equation f32>( s : &mut VoxelChunk, min : [usize; 3], size : usize, implicit : &mut F, rscale : f32, dedup : &mut FnvHashMap ) -> i32 { const SQRT_THREE : f32 = 1.732050807568877293527446341505872366942805253810380628055; let v = implicit( rscale * (min[0] as f32 + 0.5 * size as f32), rscale * (min[1] as f32 + 0.5 * size as f32), rscale * (min[2] as f32 + 0.5 * size as f32) ); let bounding_radius = rscale * size as f32 * SQRT_THREE; if size <= 1 { // once we reach size 1, check if the object intersects the implicit region if min[0] == 0 && min[1] == 0 && min[2] == 0 { // println!("maybe intersection {} < {}", v, bounding_radius); } return if v < bounding_radius { -1 } else { 0 }; } if v > bounding_radius { // the voxel does not intersect the cube at all based on the distance equation // println!("no intersection {} {}", v, bounding_radius); return 0; } const BOX_OFFSETS : [[usize; 3]; 8] = [ [0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1] ]; let mut voxel = VChildDescriptor{ sub_voxels : [0; 8], }; let half_size = size >> 1; let mut is_uniform = true; for i in 0..8 { let bmin = [ min[0] + BOX_OFFSETS[i][0] * half_size, min[1] + BOX_OFFSETS[i][1] * half_size, min[2] + BOX_OFFSETS[i][2] * half_size ]; voxel.sub_voxels[i] = recurse_distance_equation(s, bmin, half_size, implicit, rscale, dedup); if voxel.sub_voxels[i] != voxel.sub_voxels[0] || voxel.sub_voxels[i] > 0 { // the subvoxels are not all the same leaf node, so this voxel is not uniform is_uniform = false; } } if is_uniform { return voxel.sub_voxels[0]; } if let Some(&id) = dedup.get(&voxel) { // this node is a duplicate id } else { // this node is new, so add it s.voxels.push(voxel); let id = s.voxels.len() as i32; dedup.insert(voxel, id); id } } let mut chunk = VoxelChunk::empty(); chunk.voxels.clear(); // we build a list of unique voxels and store them in here let mut dedup : FnvHashMap = FnvHashMap::default(); recurse_distance_equation(&mut chunk, [0,0,0], size, &mut implicit, 1.0 / (size as f32), &mut dedup); chunk.voxels.reverse(); //fixup the subvoxel pointers (we reversed the order) let n = chunk.voxels.len() as i32; for i in 0..(chunk.voxels.len()) { for j in 0..8 { let sv = chunk.voxels[i].sub_voxels[j]; if sv > 0 { let svi = n - sv + 1; chunk.voxels[i].sub_voxels[j] = svi; } } } chunk } /// process a distance equation array into a DAG. pub fn from_intersection_test bool>(depth : usize, mut intersect_test : F) -> Self { assert!(depth < MAX_DAG_DEPTH, "Depth is too large: {} >= {}", depth, MAX_DAG_DEPTH); let size = 1 << depth; fn recurse_intersection_test bool>( s : &mut VoxelChunk, min : [usize; 3], size : usize, intersect_test : &mut F, rscale : f32, dedup : &mut FnvHashMap ) -> i32 { let intersects = intersect_test( Vec3::new( rscale * (min[0] as f32 + 0.5 * size as f32), rscale * (min[1] as f32 + 0.5 * size as f32), rscale * (min[2] as f32 + 0.5 * size as f32) ), 0.5 * rscale * size as f32 ); if size <= 1 { // once we reach size 1, check if the object intersects the implicit region if min[0] == 0 && min[1] == 0 && min[2] == 0 { // println!("maybe intersection {} < {}", v, bounding_radius); } return if intersects { -1 } else { 0 }; } if !intersects { // the voxel does not intersect the cube at all based on the distance equation // println!("no intersection {} {}", v, bounding_radius); return 0; } const BOX_OFFSETS : [[usize; 3]; 8] = [ [0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1] ]; let mut voxel = VChildDescriptor{ sub_voxels : [0; 8], }; let half_size = size >> 1; let mut is_uniform = true; for i in 0..8 { let bmin = [ min[0] + BOX_OFFSETS[i][0] * half_size, min[1] + BOX_OFFSETS[i][1] * half_size, min[2] + BOX_OFFSETS[i][2] * half_size ]; voxel.sub_voxels[i] = recurse_intersection_test(s, bmin, half_size, intersect_test, rscale, dedup); if voxel.sub_voxels[i] != voxel.sub_voxels[0] || voxel.sub_voxels[i] > 0 { // the subvoxels are not all the same leaf node, so this voxel is not uniform is_uniform = false; } } if is_uniform { return voxel.sub_voxels[0]; } if let Some(&id) = dedup.get(&voxel) { // this node is a duplicate id } else { // this node is new, so add it s.voxels.push(voxel); let id = s.voxels.len() as i32; dedup.insert(voxel, id); id } } let mut chunk = VoxelChunk::empty(); chunk.voxels.clear(); // we build a list of unique voxel subtrees and store them in here let mut dedup : FnvHashMap = FnvHashMap::default(); recurse_intersection_test(&mut chunk, [0,0,0], size, &mut intersect_test, 1.0 / (size as f32), &mut dedup); chunk.voxels.reverse(); //fixup the subvoxel pointers (we reversed the order) let n = chunk.voxels.len() as i32; for i in 0..(chunk.voxels.len()) { for j in 0..8 { let sv = chunk.voxels[i].sub_voxels[j]; if sv > 0 { let svi = n - sv + 1; chunk.voxels[i].sub_voxels[j] = svi; } } } chunk } }