Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Voxel shader (initial impl.) #6

Merged
merged 5 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]
},
);
Comment on lines +65 to +81
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored add_triangle method to accept a closure for color calculation.

This change enhances flexibility by allowing dynamic color calculations. However, consider encapsulating color calculation logic into a separate function or module for better maintainability.

}
}
}
}

// 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