-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Calculate vertex weight for each voxels (for UV calculations) (#7)
Closes #5 This PR adds the calculation of vertex weights for each voxel, which is needed to derive UV coordinates from vertex data. (The calculation logic and algorithms are immature and could be further optimized.) ![Screenshot 2024-06-28 at 15 52 56](https://github.com/MIERUNE/dda-voxelize-rs/assets/5351911/6c250a49-31c4-4320-ad19-1d63cc4ad316) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new file for voxelizing 3D polygons and generating glTF files. - Enhanced voxelization process by incorporating weight parameters for voxels, lines, and triangles. - **Improvements** - Adjusted color calculation logic to improve visual fidelity. - Updated coordinate ranges for better precision. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Loading branch information
Showing
3 changed files
with
373 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,282 @@ | ||
use std::{ | ||
fs::File, | ||
io::{BufWriter, Write}, | ||
}; | ||
|
||
use byteorder::{LittleEndian, WriteBytesExt}; | ||
use earcut::{utils3d::project3d_to_2d, Earcut}; | ||
use flatgeom::MultiPolygon; | ||
use indexmap::IndexSet; | ||
use serde_json::json; | ||
|
||
use dda_voxelize::DdaVoxelizer; | ||
|
||
fn main() { | ||
let vertices: Vec<[f64; 3]> = vec![ | ||
// exterior | ||
[26.5, 26.5, 0.5], | ||
[0.5, 26.5, 26.5], | ||
[-25.5, 26.5, 0.5], | ||
[0.5, 26.5, -25.5], | ||
[0.5, 75.5, 0.5], | ||
]; | ||
|
||
let mut mpoly = MultiPolygon::<u32>::new(); | ||
|
||
// index | ||
mpoly.add_exterior([0, 1, 2, 3]); | ||
mpoly.add_exterior([0, 1, 4]); | ||
mpoly.add_exterior([1, 2, 4]); | ||
mpoly.add_exterior([2, 3, 4]); | ||
mpoly.add_exterior([3, 0, 4]); | ||
|
||
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(); | ||
|
||
for idx_poly in mpoly.iter() { | ||
let poly = idx_poly.transform(|idx| vertices[*idx as usize]); | ||
let num_outer = match poly.hole_indices().first() { | ||
Some(&v) => v as usize, | ||
None => poly.raw_coords().len(), | ||
}; | ||
|
||
for axis in 0..=5 { | ||
buf3d.clear(); | ||
buf3d.extend(poly.raw_coords().iter().map(|v| match axis { | ||
0 => [v[0] as f32, v[1] as f32, v[2] as f32], | ||
1 => [v[1] as f32, v[0] as f32, v[2] as f32], | ||
2 => [v[0] as f32, v[2] as f32, v[1] as f32], | ||
3 => [v[0] as f32, -v[1] as f32, v[2] as f32], | ||
4 => [-v[1] as f32, v[0] as f32, v[2] as f32], | ||
5 => [v[0] as f32, v[2] as f32, -v[1] as f32], | ||
_ => unreachable!(), | ||
})); | ||
if project3d_to_2d(&buf3d, num_outer, &mut buf2d) { | ||
// earcut | ||
earcutter.earcut(buf2d.iter().cloned(), poly.hole_indices(), &mut index_buf); | ||
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, _pos, [w0, w1, w2]| { | ||
// You could use the vertex weight to calculate the UV of the voxel. | ||
// uv = v0.uv * w0 + v1.uv * w1 + v2.uv * w2; | ||
|
||
[w0, w1, w2] | ||
}, | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
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 indices = Vec::new(); | ||
let mut vertices = IndexSet::new(); // [x, y, z, r, g, b] | ||
|
||
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, _) = 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, _) = 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, _) = 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, _) = 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, _) = 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, _) = 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, _) = 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, _) = 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( | ||
[ | ||
idx0, idx1, idx2, idx2, idx1, idx3, idx6, idx5, idx4, idx5, idx6, idx7, idx2, idx3, | ||
idx6, idx7, idx6, idx3, idx4, idx1, idx0, idx1, idx4, idx5, idx0, idx2, idx4, idx6, | ||
idx4, idx2, idx5, idx3, idx1, idx3, idx5, idx7, | ||
] | ||
.iter() | ||
.map(|&idx| idx as u32), | ||
); | ||
} | ||
|
||
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 &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(); | ||
} | ||
} | ||
|
||
let indices_size = indices.len() * 4; | ||
let vertices_size = vertices.len() * 6 * 4; | ||
let total_size = indices_size + vertices_size; | ||
|
||
// make glTF | ||
let gltf_json = json!( { | ||
"asset": { | ||
"version": "2.0", | ||
}, | ||
"scene": 0, | ||
"scenes": [ | ||
{ | ||
"nodes": [0], | ||
}, | ||
], | ||
"nodes": [ | ||
{"mesh": 0}, | ||
], | ||
"meshes": [ | ||
{ | ||
"primitives": [ | ||
{ | ||
"attributes": { | ||
"POSITION": 1, | ||
"COLOR_0": 2, | ||
}, | ||
"indices": 0, | ||
"mode": 4, // TRIANGLES | ||
}, | ||
], | ||
}, | ||
], | ||
"buffers": [ | ||
{ | ||
"uri": "./output.bin", | ||
"byteLength": total_size, | ||
}, | ||
], | ||
"bufferViews": [ | ||
{ | ||
"buffer": 0, | ||
"byteOffset": 0, | ||
"byteLength": indices_size, | ||
"target": 34963, // ELEMENT_ARRAY_BUFFER | ||
}, | ||
{ | ||
"buffer": 0, | ||
"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": vertices.len(), | ||
"type": "VEC3", | ||
"min": [min_position[0], min_position[1], min_position[2]], | ||
"max": [max_position[0], max_position[1], max_position[2]], | ||
}, | ||
{ | ||
"bufferView": 1, | ||
"byteOffset": 4 * 3, | ||
"componentType": 5126, // FLOAT | ||
"count": vertices.len(), | ||
"type": "VEC3", | ||
}, | ||
], | ||
}); | ||
|
||
// write glTF | ||
println!("write glTF"); | ||
let mut gltf_file = File::create("output.gltf").unwrap(); | ||
let _ = gltf_file.write_all(gltf_json.to_string().as_bytes()); | ||
} |
Oops, something went wrong.