use bzvx::*; use bzvx::voxelize::*; use bzvx::voxelize::Triangle; use cgmath::InnerSpace; use pbr::ProgressBar; use noise; use noise::NoiseFn; use std::path::PathBuf; #[test] fn test_voxel_dag_obj_shell_buff_doge() { println!("Converting {:?}", "./data/obj/BuffDoge.OBJ"); convert_obj_file( PathBuf::from("./data/obj/BuffDoge.OBJ"), PathBuf::from("./data/dag/BuffDoge.svdag"), 12 ); println!(""); println!("Converting {:?}", "./data/obj/BuffDoge.OBJ"); convert_obj_file( PathBuf::from("./data/obj/MegaBuffDoge.OBJ"), PathBuf::from("./data/dag/MegaBuffDoge.svdag"), 12 ); println!(""); println!("Converting {:?}", "./data/obj/BuffDoge.OBJ"); convert_obj_file( PathBuf::from("./data/obj/Cheem.OBJ"), PathBuf::from("./data/dag/Cheem.svdag"), 12 ); } #[test] fn test_voxel_dag_obj_shell_teapot() { convert_obj_file( PathBuf::from("./data/obj/teapot.obj"), PathBuf::from("./data/dag/teapot.svdag"), 12 ); } #[test] fn test_voxel_dag_obj_shell_sponza() { convert_obj_file_with_materials( PathBuf::from("./data/obj/Sponza/sponza.obj"), PathBuf::from("./data/dag/sponza_mats.svdag"), PathBuf::from("./data/dag/sponza_mats.mats"), 12 ); } #[test] fn test_voxel_dag_obj_shell_sponza_textured() { convert_obj_file_textured( PathBuf::from("./data/obj/sponza-modified/sponza.obj"), PathBuf::from("./data/dag/sponza_tex_1k.svdag"), PathBuf::from("./data/dag/sponza_tex_1k.mats"), 10 ); } #[test] fn test_voxel_dag_obj_shell_sibenik() { convert_obj_file_with_materials( PathBuf::from("./data/obj/sibenik/sibenik.obj"), PathBuf::from("./data/dag/sibenik_mats.svdag"), PathBuf::from("./data/dag/sibenik_mats.mats"), 10 ); } #[test] fn test_voxel_dag_obj_shell_hairball() { convert_obj_file( PathBuf::from("./data/obj/hairball.obj"), PathBuf::from("./data/dag/hairball.svdag"), 9 ); } #[test] fn test_voxel_dag_tri_shell() { use std::path::Path; use std::fs; let v0 = Vec3::new(1.0, 0.0, 0.0); let v1 = Vec3::new(0.0, 1.0, 0.0); let v2 = Vec3::new(0.0, 0.0, 1.0); let triangles = vec![ Triangle{ points : [v0, v1, v2], normal : (v0 - v1).cross(v1 - v2), mat : 1, ..Default::default() } ]; let min = Vec3::new(0.0, 0.0, 0.0); let size = 1.0; println!("Triangles: {}", triangles.len()); use std::time::*; let mut pb = ProgressBar::new(8*8*8*8); let start = Instant::now(); let vchunk = VoxelChunk::from_mesh(8, &triangles, min, size, &mut |t| { pb.total = t; pb.inc(); }); let elapsed = start.elapsed(); pb.finish(); println!("DAG nodes: {}", vchunk.len()); println!("Time to assemble: {:?}", elapsed); let serialized = bincode::serialize(&vchunk).unwrap(); fs::write("./data/dag/tri.svdag", serialized).unwrap(); } /// Construct an SVDAG of a ct-scan of the stanford bunny; #[test] fn test_voxel_dag_bunny() { let mut data : Vec = Vec::with_capacity(512 * 512 * 361); use std::path::Path; use std::fs; use std::u16; use std::time::*; let dir = Path::new("./data/dense/bunny/"); if dir.is_dir() { for entry in fs::read_dir(dir).unwrap() { let entry = entry.unwrap(); let path = entry.path(); if !path.is_dir() { println!("Loading {:?}", path); let slice = fs::read(path).unwrap(); assert_eq!(slice.len(), 512*512*2); data.append(&mut slice.chunks_exact(2).map(|v| u16::from_be_bytes([v[0],v[1]])).collect::>()); } } } assert_eq!(data.len(), 512 * 512 * 361); println!("Converting..."); // scan data has a solid cylinder around the bunny, so this code removes that. for z in 0..361 { for y in 0..512 { for x in 0..512 { let dx = x as i32 - 256; let dy = y as i32 - 256; let i = x + 512 * (y + 512 * z); if dx * dx + dy * dy > 255 * 255 { data[i] = 0; } } } } // threshold for the ct scan data let data = data.iter().map(|&v| if v > 0x06ff {1i32} else {0i32}).collect::>(); let start = Instant::now(); let mut chunk = VoxelChunk::from_dense_voxels(&data, [512, 512, 361]); let runtime = start.elapsed(); println!("Compression took {:?}", runtime); chunk.topological_sort(); let out_path = Path::new("./data/dag/bunny.svdag"); println!("Writing File... ({:?})", out_path); use bincode; let serialized = bincode::serialize(&chunk).unwrap(); fs::write(out_path, serialized).unwrap(); println!("Num Voxels: {} (from {})", chunk.voxels.len(), 512*512*361); } #[test] fn test_voxel_dag_implicit() { use std::time::*; use std::fs; use bincode; use std::path::*; println!("Compressing implicit gyroid..."); let start = Instant::now(); let chunk = VoxelChunk::from_implicit_array(8, |x, y, z| { let scale = 1.0/16.0; let threshold = 0.05; let x = x as f32 * scale; let y = y as f32 * scale; let z = z as f32 * scale; let sdf = x.sin() * y.cos() + y.sin() * z.cos() + z.sin() * x.cos(); if sdf.abs() < threshold { 1 } else { 0 } }); let runtime = start.elapsed(); println!("Compression took {:?}", runtime); let out_path = Path::new("./data/dag/gyroid.svdag"); println!("Writing File... ({:?})", out_path); let serialized = bincode::serialize(&chunk).unwrap(); fs::write(out_path, serialized).unwrap(); println!("Num Voxels: {} (from {})", chunk.voxels.len(), 512*512*512); } #[test] fn test_voxel_dag_de_gyroid() { use std::time::*; use std::fs; use bincode; use std::path::*; println!("Compressing DE gyroid..."); let start = Instant::now(); let chunk = VoxelChunk::from_distance_equation(10, |x, y, z| { let scale = std::f32::consts::PI * 4.0; let x = x as f32 * scale; let y = y as f32 * scale; let z = z as f32 * scale; let sdf = x.sin() * y.cos() + y.sin() * z.cos() + z.sin() * x.cos(); sdf.abs() / scale }); let runtime = start.elapsed(); println!("Compression took {:?}", runtime); println!("Num Voxels: {} (uncompress: {})", chunk.voxels.len(), 512*512*512); assert!(!chunk.detect_cycles(), "Cycle Detected!"); let out_path = Path::new("./data/dag/gyroid_de.svdag"); println!("Writing File... ({:?})", out_path); let serialized = bincode::serialize(&chunk).unwrap(); fs::write(out_path, serialized).unwrap(); } #[test] fn test_voxel_dag_intersection_sphere() { use std::time::*; use std::fs; use bincode; use std::path::*; println!("Creating SVDAG from sphere intersection test..."); let start = Instant::now(); let sc = Vec3::new(0.5, 0.5, 0.5); let sr = 0.01; let depth = 10; let uncompressed_size = (1 << depth) * (1 << depth) * (1 << depth); let chunk = VoxelChunk::from_intersection_test(depth, |v, s| { let mut sq_dist = 0.0; if sc.x < v.x - s { sq_dist += (v.x - s - sc.x).powi(2); } if sc.x > v.x + s { sq_dist += (v.x + s - sc.x).powi(2); } if sc.y < v.y - s { sq_dist += (v.y - s - sc.y).powi(2); } if sc.y > v.y + s { sq_dist += (v.y + s - sc.y).powi(2); } if sc.z < v.z - s { sq_dist += (v.z - s - sc.z).powi(2); } if sc.z > v.z + s { sq_dist += (v.z + s - sc.z).powi(2); } sq_dist < sr * sr }); let runtime = start.elapsed(); println!("Compression took {:?}", runtime); println!("Num Voxels: {} (uncompressed: {} - ) [{:3.3}%]", chunk.voxels.len(), uncompressed_size, 100.0 * chunk.voxels.len() as f32 / uncompressed_size as f32); assert!(!chunk.detect_cycles(), "Cycle Detected!"); let out_path = Path::new("./data/dag/sphere.svdag"); println!("Writing File... ({:?})", out_path); let serialized = bincode::serialize(&chunk).unwrap(); fs::write(out_path, serialized).unwrap(); } #[test] fn test_voxel_dag_de_mandelbulb() { use std::time::*; use std::fs; use bincode; use std::path::*; println!("Compressing DE mandelbulb..."); let start = Instant::now(); const DEPTH : usize = 11; const DIM : usize = 1 << DEPTH; let chunk = VoxelChunk::from_distance_equation(DEPTH, |x, y, z| { const SCALE : f32 = 4.0; let pos = Vec3::new(x - 0.5, y - 0.5, z - 0.5) * SCALE; let mut z = pos; let mut dr = 1.0; let mut r = 0.0; const ITERS : usize = 8; const POWER : f32 = 8.0; const BAILOUT : f32 = 2.0; for _ in 0..ITERS { r = z.magnitude(); //length(z); if r>BAILOUT { break }; // convert to polar coordinates let mut theta = (z.z/r).acos(); let mut phi = (z.y).atan2(z.x); dr = r.powf( POWER -1.0) * POWER * dr + 1.0; // scale and rotate the point let zr = r.powf(POWER); theta = theta*POWER; phi = phi*POWER; // convert back to cartesian coordinates z = zr*Vec3::new(theta.sin()*phi.cos(), phi.sin()*theta.sin(), theta.cos()); z += pos; } return 0.5*r.ln()*r/dr / SCALE; }); let runtime = start.elapsed(); println!("Compression took {:?}", runtime); println!("Num Voxels: {} (uncompressed: {} ({:2.1}%))", chunk.voxels.len(), DIM*DIM*DIM,chunk.voxels.len() as f32 / (DIM*DIM*DIM) as f32); assert!(!chunk.detect_cycles(), "Cycle Detected!"); let out_path = Path::new("./data/dag/mandelbulb.svdag"); println!("Writing File... ({:?})", out_path); let serialized = bincode::serialize(&chunk).unwrap(); fs::write(out_path, serialized).unwrap(); } /// This test constructs a simple sphere as a test #[test] fn test_voxel_dag_sphere() { use std::path::Path; use std::fs; let data : [i32; 8*8*8]= [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; let chunk = VoxelChunk::from_dense_voxels(&data, [8, 8, 8]); let out_path = Path::new("./data/dag/sphere.svdag"); { use bincode; use serde_json::to_string_pretty; let serialized = bincode::serialize(&chunk).unwrap(); fs::write(out_path, serialized).unwrap(); println!("{}", to_string_pretty(&chunk).unwrap()); } println!("Num Voxels: {} (from {})", chunk.voxels.len(), 8*8*8); } /// This test constructs a simple sphere as a test #[test] fn test_voxel_dag_checkers() { use std::path::Path; use std::fs; for i in 1..6 { let d = 1 << i; let data : Vec = (0..d).map(|z| { (0..d).map(move |y| { (0..d).map(move |x| (x + y + z) % 2) }).flatten() }).flatten().collect(); let chunk = VoxelChunk::from_dense_voxels(&data, [d as usize, d as usize, d as usize]); let path = format!("./data/dag/checker{:0>2}.svdag", d); let out_path = Path::new(&path); { use bincode; use serde_json::to_string_pretty; let serialized = bincode::serialize(&chunk).unwrap(); fs::write(out_path, serialized).unwrap(); println!("Checker (d={})", d); println!("{}", to_string_pretty(&chunk).unwrap()); println!("Num Voxels: {} (from {})", chunk.voxels.len(), d*d*d); } } } #[test] fn test_voxel_dag_octohedron() { use std::path::Path; use std::fs; for i in 1..6 { let d = 1 << i; let dd = d >> 1; let data : Vec = (0..d).map(|z| { (0..d).map(move |y| { (0..d).map(move |x| if (x == dd || x == dd - 1) && (y == dd || y == dd - 1) && (z == dd || z == dd - 1) {1} else {0}) }).flatten() }).flatten().collect(); let chunk = VoxelChunk::from_dense_voxels(&data, [d as usize, d as usize, d as usize]); let path = format!("./data/dag/octohedron{:0>2}.svdag", d); let out_path = Path::new(&path); { use bincode; use serde_json::to_string_pretty; let serialized = bincode::serialize(&chunk).unwrap(); fs::write(out_path, serialized).unwrap(); println!("Checker (d={})", d); println!("{}", to_string_pretty(&chunk).unwrap()); println!("Num Voxels: {} (from {})", chunk.voxels.len(), d*d*d); } } } #[test] fn test_voxel_index_distance() { let data = std::fs::read("./data/dag/hairball.svdag").expect("could not read file"); let mut chunk : VoxelChunk = bincode::deserialize(&data).expect("error deserializing voxels"); let mut n = 0usize; let mut avg = 0.0; let mut avg_sq = 0.0; let mut max = 0.0; let mut min = chunk.voxels.len() as f64; let mut n_less_16bit = 0; chunk.topological_sort(); for i in 0..(chunk.voxels.len()) { for j in 0..8 { let v = chunk.voxels[i].sub_voxels[j]; if v > 0 { let d = (v as isize - 1 - i as isize).abs() ; if d < (1<<16) { n_less_16bit += 1; } let d = d as f64; max = if d > max {d} else {max}; min = if d < min {d} else {min}; avg += d; avg_sq += d*d; n += 1; } } } avg /= n as f64; avg_sq /= n as f64; println!("AVG: {}", avg); println!("STD: {}", (avg_sq - avg*avg).sqrt()); println!("MAX: {}", max); println!("MIN: {}", min); println!("16B: {}", n_less_16bit); println!("LEN: {}", n); }