diff --git a/.gitignore b/.gitignore index 12d666a..0ac8f1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ ### Generated by gibo (https://github.com/simonwhitaker/gibo) ### https://raw.github.com/github/gitignore/4488915eec0b3a45b5c63ead28f286819c0917de/Rust.gitignore +.DS_Store + # Generated by Cargo # will have compiled files and executables debug/ diff --git a/Cargo.toml b/Cargo.toml index 1c27044..36da38d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dda-voxelize" -version = "0.1.0" +version = "0.2.0-alpha.1" edition = "2021" description = "3D mesh voxelization with the DDA algorithm" authors = ["nokonoko1203 ", "Taku Fukada ", "MIERUNE Inc. "] @@ -9,7 +9,7 @@ repository = "https://github.com/MIERUNE/dda-voxelize-rs" categories = ["algorithms", "graphics", "mathematics", "science::geo"] [dependencies] -glam = "0.27" +glam = "0.28" hashbrown = "0.14" log = "0.4.21" @@ -19,8 +19,4 @@ earcut = "0.4" byteorder = "1.5" serde_json = "1.0" indexmap = "2.2" - -[profile.release] -lto = "fat" -codegen-units = 1 -panic = "abort" +palette = "0.7.6" diff --git a/README.md b/README.md index e634d7d..b0dccb2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Work in progress. -A 3D mesh voxelizer implemented in Rust using the DDA (Digital Differential Analyzer) algorithm. The DDA method is simple and very fast when you only want to voxelize the surfaces of meshes. +A 3D mesh voxelizer implemented in Rust using the DDA (Digital Differential Analyzer) algorithm. The DDA method is simple and very fast when only the surfaces (not volumes) of 3D objects need to be voxelized. ![1716994116122](docs/demo.png) diff --git a/examples/voxelize.rs b/examples/voxelize.rs index 62db92d..50a7ba2 100644 --- a/examples/voxelize.rs +++ b/examples/voxelize.rs @@ -6,19 +6,19 @@ use std::{ use byteorder::{LittleEndian, WriteBytesExt}; use earcut::{utils3d::project3d_to_2d, Earcut}; use flatgeom::MultiPolygon; -use hashbrown::HashMap; use indexmap::IndexSet; +use palette::FromColor; use serde_json::json; -use dda_voxelize::{DdaVoxelizer, MeshVoxelizer}; +use dda_voxelize::DdaVoxelizer; fn main() { let vertices: Vec<[f64; 3]> = vec![ // exterior - [21.5, 0.5, 0.5], - [0.5, 0.5, 21.5], - [-20.5, 0.5, 0.5], - [0.5, 0.5, -20.5], + [26.5, 0.5, 0.5], + [0.5, 0.5, 26.5], + [-25.5, 0.5, 0.5], + [0.5, 0.5, -25.5], [0.5, 45.5, 0.5], [0.5, -44.5, 0.5], ]; @@ -36,16 +36,13 @@ fn main() { mpoly.add_exterior([2, 3, 5]); mpoly.add_exterior([3, 0, 5]); - // triangulation + let mut voxelizer = DdaVoxelizer::new(); + let mut earcutter = Earcut::new(); let mut buf3d: Vec<[f32; 3]> = Vec::new(); let mut buf2d: Vec<[f32; 2]> = Vec::new(); let mut index_buf: Vec = Vec::new(); - let mut voxelizer = DdaVoxelizer { - voxels: HashMap::new(), - }; - for idx_poly in mpoly.iter() { let poly = idx_poly.transform(|idx| vertices[*idx as usize]); let num_outer = match poly.hole_indices().first() { @@ -64,17 +61,68 @@ fn main() { if project3d_to_2d(&buf3d, num_outer, &mut buf2d) { // earcut earcutter.earcut(buf2d.iter().cloned(), poly.hole_indices(), &mut index_buf); - for indx in index_buf.chunks_exact(3) { - voxelizer.add_triangle(&[ - buf3d[indx[0] as usize], - buf3d[indx[1] as usize], - buf3d[indx[2] as usize], - ]); + for index in index_buf.chunks_exact(3) { + voxelizer.add_triangle( + &[ + buf3d[index[0] as usize], + buf3d[index[1] as usize], + buf3d[index[2] as usize], + ], + &|_current_value, [x, y, z], _vertex_weight| { + let [x, y, z] = [x as f32, y as f32, z as f32]; + let color_lab = palette::Okhsl::new( + x.atan2(z).to_degrees(), + 1.0 - (x * x + z * z) / 2500., + y / 90. + 0.5, + ); + let color_srgb = palette::Srgb::from_color(color_lab); + [color_srgb.red, color_srgb.green, color_srgb.blue] + }, + ); } } } } + // voxelizer.add_triangle( + // &[[0., 40.4, 40.4], [0., 40.6, 40.5], [0., 40.4, 40.6]], + // &|_, [x, y, z], _| { + // let [x, y, z] = [x as f32, y as f32, z as f32]; + // let color_lab = palette::Okhsl::new( + // x.atan2(z).to_degrees(), + // 1.0 - (x * x + z * z) / 2200., + // y / 90. + 0.5, + // ); + // let color_srgb = palette::Srgb::from_color(color_lab); + // [color_srgb.red, color_srgb.green, color_srgb.blue] + // }, + // ); + + // voxelizer.add_triangle( + // &[[0., 30.6, 30.4], [0., 30.4, 30.5], [0., 30.6, 30.6]], + // &|_, [x, y, z], _| { + // let [x, y, z] = [x as f32, y as f32, z as f32]; + // let color_lab = palette::Okhsl::new( + // x.atan2(z).to_degrees(), + // 1.0 - (x * x + z * z) / 2200., + // y / 90. + 0.5, + // ); + // let color_srgb = palette::Srgb::from_color(color_lab); + // [color_srgb.red, color_srgb.green, color_srgb.blue] + // }, + // ); + + // voxelizer.add_line([40., 40., 40.], [40., 40., 40.], &|_, [x, y, z], _| { + // let [x, y, z] = [x as f32, y as f32, z as f32]; + // let color_lab = palette::Okhsl::new( + // x.atan2(z).to_degrees(), + // 1.0 - (x * x + z * z) / 2200., + // y / 90. + 0.5, + // ); + // let color_srgb = palette::Srgb::from_color(color_lab); + // [color_srgb.red, color_srgb.green, color_srgb.blue] + // }); + let occupied_voxels = voxelizer.finalize(); // -------------------make glTF------------------- @@ -82,52 +130,79 @@ fn main() { // voxel is an integer value, but componentType of accessors is 5126 (floating point number), // and INTEGER type cannot be used due to primitives constraints - let mut positions = IndexSet::new(); let mut indices = Vec::new(); + let mut vertices = IndexSet::new(); // [x, y, z, r, g, b] - for (position, _) in occupied_voxels.iter() { + for (position, voxel) in occupied_voxels.iter() { let [x, y, z] = [position[0] as f32, position[1] as f32, position[2] as f32]; + let [r, g, b] = voxel; + + let [r_bits, g_bits, b_bits] = [r.to_bits(), g.to_bits(), b.to_bits()]; // Make a voxel cube - let (idx0, _) = positions.insert_full([ + let (idx0, _) = vertices.insert_full([ (x + 0.5).to_bits(), (y - 0.5).to_bits(), (z + 0.5).to_bits(), + r_bits, + g_bits, + b_bits, ]); - let (idx1, _) = positions.insert_full([ + let (idx1, _) = vertices.insert_full([ (x - 0.5).to_bits(), (y - 0.5).to_bits(), (z + 0.5).to_bits(), + r_bits, + g_bits, + b_bits, ]); - let (idx2, _) = positions.insert_full([ + let (idx2, _) = vertices.insert_full([ (x + 0.5).to_bits(), (y - 0.5).to_bits(), (z - 0.5).to_bits(), + r_bits, + g_bits, + b_bits, ]); - let (idx3, _) = positions.insert_full([ + let (idx3, _) = vertices.insert_full([ (x - 0.5).to_bits(), (y - 0.5).to_bits(), (z - 0.5).to_bits(), + r_bits, + g_bits, + b_bits, ]); - let (idx4, _) = positions.insert_full([ + let (idx4, _) = vertices.insert_full([ (x + 0.5).to_bits(), (y + 0.5).to_bits(), (z + 0.5).to_bits(), + r_bits, + g_bits, + b_bits, ]); - let (idx5, _) = positions.insert_full([ + let (idx5, _) = vertices.insert_full([ (x - 0.5).to_bits(), (y + 0.5).to_bits(), (z + 0.5).to_bits(), + r_bits, + g_bits, + b_bits, ]); - let (idx6, _) = positions.insert_full([ + let (idx6, _) = vertices.insert_full([ (x + 0.5).to_bits(), (y + 0.5).to_bits(), (z - 0.5).to_bits(), + r_bits, + g_bits, + b_bits, ]); - let (idx7, _) = positions.insert_full([ + let (idx7, _) = vertices.insert_full([ (x - 0.5).to_bits(), (y + 0.5).to_bits(), (z - 0.5).to_bits(), + r_bits, + g_bits, + b_bits, ]); indices.extend( [ @@ -140,32 +215,35 @@ fn main() { ); } - let mut min_point = [f32::MAX; 3]; - let mut max_point = [f32::MIN; 3]; + let mut min_position = [f32::MAX; 3]; + let mut max_position = [f32::MIN; 3]; { let mut bin_file = BufWriter::new(File::create("output.bin").unwrap()); - for pos in &positions { - min_point[0] = f32::min(min_point[0], f32::from_bits(pos[0])); - min_point[1] = f32::min(min_point[1], f32::from_bits(pos[1])); - min_point[2] = f32::min(min_point[2], f32::from_bits(pos[2])); - max_point[0] = f32::max(max_point[0], f32::from_bits(pos[0])); - max_point[1] = f32::max(max_point[1], f32::from_bits(pos[1])); - max_point[2] = f32::max(max_point[2], f32::from_bits(pos[2])); - - bin_file.write_u32::(pos[0]).unwrap(); - bin_file.write_u32::(pos[1]).unwrap(); - bin_file.write_u32::(pos[2]).unwrap(); - } for &idx in &indices { bin_file.write_u32::(idx).unwrap(); } + + for &[x, y, z, r, g, b] in &vertices { + min_position[0] = f32::min(min_position[0], f32::from_bits(x)); + min_position[1] = f32::min(min_position[1], f32::from_bits(y)); + min_position[2] = f32::min(min_position[2], f32::from_bits(z)); + max_position[0] = f32::max(max_position[0], f32::from_bits(x)); + max_position[1] = f32::max(max_position[1], f32::from_bits(y)); + max_position[2] = f32::max(max_position[2], f32::from_bits(z)); + + bin_file.write_u32::(x).unwrap(); + bin_file.write_u32::(y).unwrap(); + bin_file.write_u32::(z).unwrap(); + bin_file.write_u32::(r).unwrap(); + bin_file.write_u32::(g).unwrap(); + bin_file.write_u32::(b).unwrap(); + } } - // number of voxels x number of vertex coordinates (3) x 4 bytes (f32) - let positions_size = positions.len() * 12; let indices_size = indices.len() * 4; - let total_size = positions_size + indices_size; + let vertices_size = vertices.len() * 6 * 4; + let total_size = indices_size + vertices_size; // make glTF let gltf_json = json!( { @@ -185,8 +263,11 @@ fn main() { { "primitives": [ { - "attributes": {"POSITION": 0}, - "indices": 1, + "attributes": { + "POSITION": 1, + "COLOR_0": 2, + }, + "indices": 0, "mode": 4, // TRIANGLES }, ], @@ -202,32 +283,40 @@ fn main() { { "buffer": 0, "byteOffset": 0, - "byteLength": positions_size, - "target": 34962, + "byteLength": indices_size, + "target": 34963, // ELEMENT_ARRAY_BUFFER }, { "buffer": 0, - "byteOffset": positions_size, - "byteLength": indices_size, - "target": 34963, + "byteStride": 6 * 4, + "byteOffset": indices_size, + "byteLength": vertices_size, + "target": 34962, // ARRAY_BUFFER }, ], "accessors": [ { "bufferView": 0, "byteOffset": 0, + "componentType": 5125, // UNSIGNED_INT + "count": indices.len(), + "type": "SCALAR", + }, + { + "bufferView": 1, + "byteOffset": 0, "componentType": 5126, // FLOAT - "count": positions.len(), + "count": vertices.len(), "type": "VEC3", - "min": [min_point[0], min_point[1], min_point[2]], - "max": [max_point[0], max_point[1], max_point[2]], + "min": [min_position[0], min_position[1], min_position[2]], + "max": [max_position[0], max_position[1], max_position[2]], }, { "bufferView": 1, - "byteOffset": 0, - "componentType": 5125, // UNSIGNED_INT - "count": indices.len(), - "type": "SCALAR", + "byteOffset": 4 * 3, + "componentType": 5126, // FLOAT + "count": vertices.len(), + "type": "VEC3", }, ], }); diff --git a/src/voxelize.rs b/src/voxelize.rs index 3a507e2..19659db 100644 --- a/src/voxelize.rs +++ b/src/voxelize.rs @@ -1,49 +1,86 @@ use glam::Vec3; -use hashbrown::HashMap; - -#[derive(Eq, PartialEq, Hash, Debug, Clone)] -pub struct Voxel { - pub color: [u8; 3], -} +use hashbrown::{hash_map::Entry, HashMap}; pub type VoxelPosition = [i32; 3]; +pub type VertexWeight = [f32; 3]; + +pub trait Shader: Fn(Option<&V>, VoxelPosition, VertexWeight) -> V {} +impl Shader for F where F: Fn(Option<&V>, VoxelPosition, VertexWeight) -> V {} -pub trait MeshVoxelizer { - fn add_triangle(&mut self, triangle: &[[f32; 3]; 3]); - fn finalize(self) -> HashMap; +pub struct DdaVoxelizer { + buffer: HashMap, } -pub struct DdaVoxelizer { - pub voxels: HashMap, +impl DdaVoxelizer { + pub fn new() -> Self { + Self::default() + } } -impl MeshVoxelizer for DdaVoxelizer { - fn add_triangle(&mut self, triangle: &[[f32; 3]; 3]) { - fill_triangle(&mut self.voxels, triangle); +impl Default for DdaVoxelizer { + fn default() -> Self { + Self { + buffer: HashMap::new(), + } + } +} + +impl DdaVoxelizer { + pub fn add_triangle(&mut self, triangle: &[[f32; 3]; 3], shader: &impl Shader) { + fill_triangle(&mut self.buffer, triangle, &shader); + } + + pub fn add_line(&mut self, start: [f32; 3], end: [f32; 3], shader: &impl Shader) { + draw_line( + &mut self.buffer, + Vec3::from_array(start), + Vec3::from_array(end), + shader, + ) + } + + pub fn finalize(self) -> HashMap { + self.buffer } - fn finalize(self) -> HashMap { - self.voxels +} + +fn put_voxel( + buffer: &mut HashMap<[i32; 3], V>, + position: glam::IVec3, + shader: impl Shader, +) { + let position = position.to_array(); + let weight = [1., 0., 0.]; // FIXME: not impletmented yet + match buffer.entry(position) { + Entry::Occupied(mut v) => { + let v = v.get_mut(); + *v = shader(Some(v), position, weight); + } + Entry::Vacant(v) => { + v.insert(shader(None, position, weight)); + } } } -fn draw_line(voxels: &mut HashMap, start: Vec3, end: Vec3) { +fn draw_line( + buffer: &mut HashMap, + start: Vec3, + end: Vec3, + shader: &impl Shader, +) { let difference = end - start; + let mut current_voxel = (start + Vec3::splat(0.5)).floor().as_ivec3(); let last_voxel = (end + Vec3::splat(0.5)).floor().as_ivec3(); - let mut current_voxel = (start + Vec3::splat(0.5)).floor().as_ivec3(); let step = difference.signum().as_ivec3(); let next_voxel_boundary = current_voxel.as_vec3() + 0.5 * step.as_vec3(); let mut tmax = (next_voxel_boundary - start) / difference; let tdelta = step.as_vec3() / difference; - let voxel = Voxel { - color: [255, 255, 255], - }; - // TODO: We can optimize this since we actually need 2D DDA, not 3D DDA while current_voxel != last_voxel { - voxels.insert(current_voxel.to_array(), voxel.clone()); + put_voxel(buffer, current_voxel, shader); if tmax.x < tmax.y { if tmax.x < tmax.z { @@ -62,48 +99,26 @@ fn draw_line(voxels: &mut HashMap, start: Vec3, end: Vec3) } } - voxels.insert(last_voxel.to_array(), voxel.clone()); + put_voxel(buffer, current_voxel, shader); } -fn fill_triangle(voxels: &mut HashMap, triangle: &[[f32; 3]; 3]) { - let p1 = Vec3::from(triangle[0]); - let p2 = Vec3::from(triangle[1]); - let p3 = Vec3::from(triangle[2]); - - // If the triangle is small enough, fill only the voxels corresponding to the three vertices. - if is_small_triangle(&p1, &p2, &p3) { - voxels.insert( - (p1 + Vec3::splat(0.5)).as_ivec3().to_array(), - Voxel { - color: [255, 255, 255], - }, - ); - voxels.insert( - (p2 + Vec3::splat(0.5)).as_ivec3().to_array(), - Voxel { - color: [255, 255, 255], - }, - ); - voxels.insert( - (p3 + Vec3::splat(0.5)).as_ivec3().to_array(), - Voxel { - color: [255, 255, 255], - }, - ); - return; - } +fn fill_triangle( + voxels: &mut HashMap, + triangle: &[[f32; 3]; 3], + shader: &impl Shader, +) { + let v0 = Vec3::from(triangle[0]); + let v1 = Vec3::from(triangle[1]); + let v2 = Vec3::from(triangle[2]); - let mut normal = (p2 - p1).cross(p3 - p1); + let mut normal = (v1 - v0).cross(v2 - v0); let normal_length = normal.length(); if normal_length.is_nan() || normal_length == 0.0 { + // TODO: Should we draw a line when the triangle is colinear? return; } normal /= normal_length; - // Find the axis of maximum length - // yz plane if norm_axis is 0(x) - // zx plane if norm_axis is 1(y) - // xy plane if norm_axis is 2(z) let normal_axis = normal .abs() .to_array() @@ -113,250 +128,105 @@ fn fill_triangle(voxels: &mut HashMap, triangle: &[[f32; 3 .map(|(i, _)| i) .unwrap(); - let mut min_point = p1; - let mut max_point = p1; - for p in &[p2, p3] { - min_point = min_point.min(*p); - max_point = max_point.max(*p); - } - let box_size = max_point - min_point; - - let sweep_axis = match normal_axis { - 0 => { - if box_size[1] >= box_size[2] { - 1 - } else { - 2 - } - } - 1 => { - if box_size[2] >= box_size[0] { - 2 - } else { - 0 - } - } - _ => { - if box_size[0] >= box_size[1] { - 0 - } else { - 1 - } + // Determine the axis to sweep along + let sweep_axis = { + let min_point = v0.min(v1).min(v2); + let max_point = v0.max(v1).max(v2); + let box_size = max_point - min_point; + + // Which axis is the triangle's normal closer to? + match normal_axis { + 0 if box_size[1] >= box_size[2] => 1, + 0 => 2, + 1 if box_size[2] >= box_size[0] => 2, + 1 => 0, + 2 if box_size[0] >= box_size[1] => 0, + 2 => 1, + _ => unreachable!(), } }; - // Draw a virtual grid along the x-axis and find the intersection of the target triangle and the edge - // Paint from the intersection with smaller x to the intersection with larger x - match sweep_axis { - // sweep x - 0 => { - let mut ordered_verts = [triangle[0], triangle[1], triangle[2]]; - if ordered_verts[0][0] > ordered_verts[1][0] { - ordered_verts.swap(0, 1); - } - if ordered_verts[1][0] > ordered_verts[2][0] { - ordered_verts.swap(1, 2); - } - if ordered_verts[0][0] > ordered_verts[1][0] { - ordered_verts.swap(0, 1); - } - debug_assert!(ordered_verts[1][0] >= ordered_verts[0][0]); - - let ordered_verts: [[f32; 3]; 3] = ordered_verts.map(|inner| inner.map(|x| x)); - - let mut start_step; - let mut start_pos; - - if (ordered_verts[1][0] - ordered_verts[0][0]).abs() >= 0.0 - && (ordered_verts[1][0] - ordered_verts[0][0].floor() >= 1.0) - { - start_step = (Vec3::from(ordered_verts[1]) - Vec3::from(ordered_verts[0])) - / (ordered_verts[1][0] - ordered_verts[0][0]); - start_pos = Vec3::from(ordered_verts[0]) - + start_step - * ((1.0 - ordered_verts[0][0] + ordered_verts[0][0].floor()) % 1.0); - } else { - start_step = (Vec3::from(ordered_verts[2]) - Vec3::from(ordered_verts[1])) - / (ordered_verts[2][0] - ordered_verts[1][0]); - start_pos = Vec3::from(ordered_verts[1]) - + start_step - * ((1.0 - ordered_verts[1][0] + ordered_verts[1][0].floor()) % 1.0); - }; - - let end_step = (Vec3::from(ordered_verts[2]) - Vec3::from(ordered_verts[0])) - / (ordered_verts[2][0] - ordered_verts[0][0]); - let mut end_pos = Vec3::from(ordered_verts[0]) - + end_step * ((1.0 - ordered_verts[0][0] + ordered_verts[0][0].floor()) % 1.0); - - let mut end_vertex_x = ordered_verts[1][0]; - - if start_step.length() > 1000.0 || end_step.length() > 1000.0 { - log::debug!("Direction vector magnitude is too large"); - return; - } + // Reorder the vertices to arrange sequentially along the sweep axis + let mut ordered_verts = [triangle[0], triangle[1], triangle[2]]; + if ordered_verts[0][sweep_axis] > ordered_verts[1][sweep_axis] { + ordered_verts.swap(0, 1); + } + if ordered_verts[1][sweep_axis] > ordered_verts[2][sweep_axis] { + ordered_verts.swap(1, 2); + } + if ordered_verts[0][sweep_axis] > ordered_verts[1][sweep_axis] { + ordered_verts.swap(0, 1); + } + debug_assert!(ordered_verts[1][sweep_axis] >= ordered_verts[0][sweep_axis]); + + let p0 = Vec3::from(ordered_verts[0]); + let p1 = Vec3::from(ordered_verts[1]); + let p2 = Vec3::from(ordered_verts[2]); + let v01 = p1 - p0; + let v02 = p2 - p0; + let v12 = p2 - p1; + + let end_step = v02 / (ordered_verts[2][sweep_axis] - ordered_verts[0][sweep_axis]); + let mut end_pos = p0 + + end_step + * ((1.0 - ordered_verts[0][sweep_axis] + ordered_verts[0][sweep_axis].floor()) % 1.0); + + let start_step1 = v01 / (ordered_verts[1][sweep_axis] - ordered_verts[0][sweep_axis]); + let start_step2 = v12 / (ordered_verts[2][sweep_axis] - ordered_verts[1][sweep_axis]); + let to_next_line = + (1.0 - ordered_verts[0][sweep_axis] + ordered_verts[0][sweep_axis].floor()) % 1.0; + let mut start_pos = p0 + start_step1 * to_next_line; + + if start_step1[sweep_axis].is_finite() { + // underjet + if to_next_line > 0.5 { + let d = to_next_line - 0.5; + let mut s = (p0 + d * v01).to_array(); + let mut e = (p0 + d * v02).to_array(); + s[sweep_axis] -= 0.5; + e[sweep_axis] -= 0.5; + draw_line(voxels, Vec3::from_array(s), Vec3::from_array(e), shader); + }; - while end_pos[0] <= ordered_verts[2][0] { - draw_line(voxels, start_pos, end_pos); - - start_pos += start_step; - end_pos += end_step; - - if start_pos[0] >= end_vertex_x { - end_vertex_x = start_pos[0] - ordered_verts[1][0]; - start_pos -= start_step * end_vertex_x; - if (ordered_verts[2][0] - ordered_verts[1][0]).abs() == 0.0 { - continue; - } - start_step = (Vec3::from(ordered_verts[2]) - Vec3::from(ordered_verts[1])) - / (ordered_verts[2][0] - ordered_verts[1][0]); - start_pos += start_step * end_vertex_x; - end_vertex_x = ordered_verts[2][0]; - } - } + // Start position is on the first edge + while start_pos[sweep_axis] <= ordered_verts[1][sweep_axis] { + draw_line(voxels, start_pos, end_pos, shader); + start_pos += start_step1; + end_pos += end_step; } - // sweep y - 1 => { - let mut ordered_verts = [triangle[0], triangle[1], triangle[2]]; - if ordered_verts[0][1] > ordered_verts[1][1] { - ordered_verts.swap(0, 1); - } - if ordered_verts[1][1] > ordered_verts[2][1] { - ordered_verts.swap(1, 2); - } - if ordered_verts[0][1] > ordered_verts[1][1] { - ordered_verts.swap(0, 1); - } - debug_assert!(ordered_verts[1][1] >= ordered_verts[0][1]); - - let ordered_verts: [[f32; 3]; 3] = ordered_verts.map(|inner| inner.map(|x| x)); - - let mut start_step; - let mut start_pos; - if (ordered_verts[1][1] - ordered_verts[0][1]).abs() >= 0.0 - && (ordered_verts[1][1] - ordered_verts[0][1].floor() >= 1.0) - { - start_step = (Vec3::from(ordered_verts[1]) - Vec3::from(ordered_verts[0])) - / (ordered_verts[1][1] - ordered_verts[0][1]); - start_pos = Vec3::from(ordered_verts[0]) - + start_step - * ((1.0 - ordered_verts[0][1] + ordered_verts[0][1].floor()) % 1.0); - } else { - start_step = (Vec3::from(ordered_verts[2]) - Vec3::from(ordered_verts[1])) - / (ordered_verts[2][1] - ordered_verts[1][1]); - start_pos = Vec3::from(ordered_verts[1]) - + start_step - * ((1.0 - ordered_verts[1][1] + ordered_verts[1][1].floor()) % 1.0); - }; - - let end_step = (Vec3::from(ordered_verts[2]) - Vec3::from(ordered_verts[0])) - / (ordered_verts[2][1] - ordered_verts[0][1]); - let mut end_pos = Vec3::from(ordered_verts[0]) - + end_step * ((1.0 - ordered_verts[0][1] + ordered_verts[0][1].floor()) % 1.0); - - let mut end_vertex_y = ordered_verts[1][1]; - - if start_step.length() > 1000.0 || end_step.length() > 1000.0 { - log::debug!("Direction vector magnitude is too large"); - return; - } + // Switch to the second edge + let end_vertex_y = ordered_verts[1][sweep_axis] - start_pos[sweep_axis]; + start_pos += (start_step1 - start_step2) * end_vertex_y; + } else { + // Switch to the second edge + start_pos = Vec3::from(ordered_verts[1]) + + start_step2 + * ((1.0 - ordered_verts[1][sweep_axis] + ordered_verts[1][sweep_axis].floor()) + % 1.0); + } - while end_pos[1] <= ordered_verts[2][1] { - draw_line(voxels, start_pos, end_pos); - - start_pos += start_step; - end_pos += end_step; - - if start_pos[1] >= end_vertex_y { - end_vertex_y = start_pos[1] - ordered_verts[1][1]; - start_pos -= start_step * end_vertex_y; - if (ordered_verts[2][1] - ordered_verts[1][1]).abs() == 0.0 { - continue; - } - start_step = (Vec3::from(ordered_verts[2]) - Vec3::from(ordered_verts[1])) - / (ordered_verts[2][1] - ordered_verts[1][1]); - start_pos += start_step * end_vertex_y; - end_vertex_y = ordered_verts[2][1]; - } - } + if start_step2[sweep_axis].is_finite() { + // Start position is on the second edge + while end_pos[sweep_axis] <= ordered_verts[2][sweep_axis] { + draw_line(voxels, start_pos, end_pos, shader); + start_pos += start_step2; + end_pos += end_step; } - // sweep z - _ => { - let mut ordered_verts = [triangle[0], triangle[1], triangle[2]]; - if ordered_verts[0][2] > ordered_verts[1][2] { - ordered_verts.swap(0, 1); - } - if ordered_verts[1][2] > ordered_verts[2][2] { - ordered_verts.swap(1, 2); - } - if ordered_verts[0][2] > ordered_verts[1][2] { - ordered_verts.swap(0, 1); - } - debug_assert!(ordered_verts[1][2] >= ordered_verts[0][2]); - - let ordered_verts: [[f32; 3]; 3] = ordered_verts.map(|inner| inner.map(|x| x)); - - let mut start_step; - let mut start_pos; - - if (ordered_verts[1][2] - ordered_verts[0][2]).abs() >= 0.0 - && (ordered_verts[1][2] - ordered_verts[0][2].floor() >= 1.0) - { - start_step = (Vec3::from(ordered_verts[1]) - Vec3::from(ordered_verts[0])) - / (ordered_verts[1][2] - ordered_verts[0][2]); - start_pos = Vec3::from(ordered_verts[0]) - + start_step - * ((1.0 - ordered_verts[0][2] + ordered_verts[0][2].floor()) % 1.0); - } else { - start_step = (Vec3::from(ordered_verts[2]) - Vec3::from(ordered_verts[1])) - / (ordered_verts[2][2] - ordered_verts[1][2]); - start_pos = Vec3::from(ordered_verts[1]) - + start_step - * ((1.0 - ordered_verts[1][2] + ordered_verts[1][2].floor()) % 1.0); - }; - let end_step = (Vec3::from(ordered_verts[2]) - Vec3::from(ordered_verts[0])) - / (ordered_verts[2][2] - ordered_verts[0][2]); - let mut end_pos = Vec3::from(ordered_verts[0]) - + end_step * ((1.0 - ordered_verts[0][2] + ordered_verts[0][2].floor()) % 1.0); - - let mut end_vertex_z = ordered_verts[1][2]; - - if start_step.length() > 1000.0 || end_step.length() > 1000.0 { - log::debug!("Direction vector magnitude is too large"); - return; - } - - while end_pos[2] <= ordered_verts[2][2] { - draw_line(voxels, start_pos, end_pos); - - start_pos += start_step; - end_pos += end_step; - - if start_pos[2] >= end_vertex_z { - end_vertex_z = start_pos[2] - ordered_verts[1][2]; - start_pos -= start_step * end_vertex_z; - if (ordered_verts[2][2] - ordered_verts[1][2]).abs() == 0.0 { - continue; - } - start_step = (Vec3::from(ordered_verts[2]) - Vec3::from(ordered_verts[1])) - / (ordered_verts[2][2] - ordered_verts[1][2]); - start_pos += start_step * end_vertex_z; - end_vertex_z = ordered_verts[2][2]; - } - } + // overjet + if end_pos[sweep_axis] - 0.5 < ordered_verts[2][sweep_axis] + && end_pos[sweep_axis] - 0.5 >= ordered_verts[1][sweep_axis] + { + start_pos -= start_step2 * 0.5; + end_pos -= end_step * 0.5; + start_pos[sweep_axis] += 0.5; + end_pos[sweep_axis] += 0.5; + draw_line(voxels, start_pos, end_pos, shader); } } } -fn is_small_triangle(p1: &Vec3, p2: &Vec3, p3: &Vec3) -> bool { - let d12 = p1.distance(*p2); - let d23 = p2.distance(*p3); - let d31 = p3.distance(*p1); - - d12 <= 1.0 && d23 <= 1.0 && d31 <= 1.0 -} - #[cfg(test)] mod tests { use super::*; @@ -382,7 +252,7 @@ mod tests { let mut index_buf: Vec = Vec::new(); let mut voxelizer = DdaVoxelizer { - voxels: HashMap::new(), + buffer: HashMap::new(), }; for idx_poly in mpoly.iter() { @@ -401,24 +271,22 @@ mod tests { if project3d_to_2d(&buf3d, num_outer, &mut buf2d) { earcutter.earcut(buf2d.iter().cloned(), poly.hole_indices(), &mut index_buf); - for indx in index_buf.chunks_exact(3) { - voxelizer.add_triangle(&[ - buf3d[indx[0] as usize], - buf3d[indx[1] as usize], - buf3d[indx[2] as usize], - ]); + for index in index_buf.chunks_exact(3) { + voxelizer.add_triangle( + &[ + buf3d[index[0] as usize], + buf3d[index[1] as usize], + buf3d[index[2] as usize], + ], + &|_, _, _| true, + ); } } } let occupied_voxels = voxelizer.finalize(); - let mut test_voxels: HashMap = HashMap::new(); - test_voxels.insert( - [0, 0, 0], - Voxel { - color: [255, 255, 255], - }, - ); + let mut test_voxels: HashMap = HashMap::new(); + test_voxels.insert([0, 0, 0], true); assert_eq!(occupied_voxels, test_voxels); } @@ -442,7 +310,7 @@ mod tests { let mut index_buf: Vec = Vec::new(); let mut voxelizer = DdaVoxelizer { - voxels: HashMap::new(), + buffer: HashMap::new(), }; for idx_poly in mpoly.iter() { let poly = idx_poly.transform(|idx| vertices[*idx as usize]); @@ -461,42 +329,25 @@ mod tests { if project3d_to_2d(&buf3d, num_outer, &mut buf2d) { earcutter.earcut(buf2d.iter().cloned(), poly.hole_indices(), &mut index_buf); for indx in index_buf.chunks_exact(3) { - voxelizer.add_triangle(&[ - buf3d[indx[0] as usize], - buf3d[indx[1] as usize], - buf3d[indx[2] as usize], - ]); + voxelizer.add_triangle( + &[ + buf3d[indx[0] as usize], + buf3d[indx[1] as usize], + buf3d[indx[2] as usize], + ], + &|_, _, _| true, + ); } } } let occupied_voxels = voxelizer.finalize(); - let mut test_voxels: HashMap = HashMap::new(); - test_voxels.insert( - [0, 0, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( - [1, 0, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( - [0, 1, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( - [1, 1, 0], - Voxel { - color: [255, 255, 255], - }, - ); + let mut test_voxels: HashMap = HashMap::new(); + test_voxels.insert([0, 0, 0], true); + test_voxels.insert([1, 0, 0], true); + test_voxels.insert([0, 1, 0], true); + test_voxels.insert([1, 1, 0], true); assert_eq!(occupied_voxels, test_voxels); } @@ -525,7 +376,7 @@ mod tests { let mut index_buf: Vec = Vec::new(); let mut voxelizer = DdaVoxelizer { - voxels: HashMap::new(), + buffer: HashMap::new(), }; for idx_poly in mpoly.iter() { @@ -545,114 +396,41 @@ mod tests { if project3d_to_2d(&buf3d, num_outer, &mut buf2d) { earcutter.earcut(buf2d.iter().cloned(), poly.hole_indices(), &mut index_buf); for indx in index_buf.chunks_exact(3) { - voxelizer.add_triangle(&[ - buf3d[indx[0] as usize], - buf3d[indx[1] as usize], - buf3d[indx[2] as usize], - ]); + voxelizer.add_triangle( + &[ + buf3d[indx[0] as usize], + buf3d[indx[1] as usize], + buf3d[indx[2] as usize], + ], + &|_, _, _| true, + ); } } } let occupied_voxels = voxelizer.finalize(); - let mut test_voxels: HashMap = HashMap::new(); - test_voxels.insert( + let mut test_voxels: HashMap = HashMap::new(); + for p in [ [0, 0, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [1, 0, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [2, 0, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [3, 0, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [0, 1, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [1, 1, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [2, 1, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [3, 1, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [0, 2, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [1, 2, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [2, 2, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [3, 2, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [0, 3, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [1, 3, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [2, 3, 0], - Voxel { - color: [255, 255, 255], - }, - ); - test_voxels.insert( [3, 3, 0], - Voxel { - color: [255, 255, 255], - }, - ); + ] { + test_voxels.insert(p, true); + } assert_eq!(occupied_voxels, test_voxels); } @@ -704,7 +482,7 @@ mod tests { let mut index_buf: Vec = Vec::new(); let mut voxelizer = DdaVoxelizer { - voxels: HashMap::new(), + buffer: HashMap::new(), }; for idx_poly in mpoly.iter() { @@ -724,11 +502,14 @@ mod tests { if project3d_to_2d(&buf3d, num_outer, &mut buf2d) { earcutter.earcut(buf2d.iter().cloned(), poly.hole_indices(), &mut index_buf); for indx in index_buf.chunks_exact(3) { - voxelizer.add_triangle(&[ - buf3d[indx[0] as usize], - buf3d[indx[1] as usize], - buf3d[indx[2] as usize], - ]); + voxelizer.add_triangle( + &[ + buf3d[indx[0] as usize], + buf3d[indx[1] as usize], + buf3d[indx[2] as usize], + ], + &|_, _, _| true, + ); } } }