Skip to content

Commit

Permalink
Voxel shader (initial impl.) (#6)
Browse files Browse the repository at this point in the history
rel: #5 


![shader](https://github.com/MIERUNE/dda-voxelize-rs/assets/5351911/50a6a0ba-bf09-4ef5-b896-f684cac93999)



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added dependency on `palette` version `0.7.6` for enhanced color
management.
- Updated `glam` dependency to version `0.28` for improved performance
and features.

- **Bug Fixes**
- Clarified README to specify that the 3D mesh voxelizer only voxelizes
surfaces, not volumes.

- **Chores**
  - Updated `.gitignore` to exclude `.DS_Store` files.
  - Removed unnecessary release profile settings in `Cargo.toml`.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
ciscorn authored Jun 13, 2024
1 parent 118b2fe commit df94df7
Show file tree
Hide file tree
Showing 5 changed files with 359 additions and 491 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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/
Expand Down
10 changes: 3 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <nokonoko.1203.777@gmail.com>", "Taku Fukada <naninunenor@gmail.com>", "MIERUNE Inc. <info@mierune.co.jp>"]
Expand All @@ -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"

Expand All @@ -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"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
205 changes: 147 additions & 58 deletions examples/voxelize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
];
Expand All @@ -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<u32> = 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() {
Expand All @@ -64,70 +61,148 @@ 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-------------------

// 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(
[
Expand All @@ -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::<LittleEndian>(pos[0]).unwrap();
bin_file.write_u32::<LittleEndian>(pos[1]).unwrap();
bin_file.write_u32::<LittleEndian>(pos[2]).unwrap();
}

for &idx in &indices {
bin_file.write_u32::<LittleEndian>(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::<LittleEndian>(x).unwrap();
bin_file.write_u32::<LittleEndian>(y).unwrap();
bin_file.write_u32::<LittleEndian>(z).unwrap();
bin_file.write_u32::<LittleEndian>(r).unwrap();
bin_file.write_u32::<LittleEndian>(g).unwrap();
bin_file.write_u32::<LittleEndian>(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!( {
Expand All @@ -185,8 +263,11 @@ fn main() {
{
"primitives": [
{
"attributes": {"POSITION": 0},
"indices": 1,
"attributes": {
"POSITION": 1,
"COLOR_0": 2,
},
"indices": 0,
"mode": 4, // TRIANGLES
},
],
Expand All @@ -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",
},
],
});
Expand Down
Loading

0 comments on commit df94df7

Please sign in to comment.