Skip to content

Commit 1e0844a

Browse files
authored
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 -->
1 parent df94df7 commit 1e0844a

File tree

3 files changed

+373
-63
lines changed

3 files changed

+373
-63
lines changed

examples/voxelize.rs

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ fn main() {
1919
[0.5, 0.5, 26.5],
2020
[-25.5, 0.5, 0.5],
2121
[0.5, 0.5, -25.5],
22-
[0.5, 45.5, 0.5],
23-
[0.5, -44.5, 0.5],
22+
[0.5, 55.5, 0.5],
23+
[0.5, -54.5, 0.5],
2424
];
2525

2626
let mut mpoly = MultiPolygon::<u32>::new();
@@ -72,8 +72,8 @@ fn main() {
7272
let [x, y, z] = [x as f32, y as f32, z as f32];
7373
let color_lab = palette::Okhsl::new(
7474
x.atan2(z).to_degrees(),
75-
1.0 - (x * x + z * z) / 2500.,
76-
y / 90. + 0.5,
75+
1.0 - (x * x + z * z) / 3050.,
76+
y / 110. + 0.5,
7777
);
7878
let color_srgb = palette::Srgb::from_color(color_lab);
7979
[color_srgb.red, color_srgb.green, color_srgb.blue]
@@ -84,45 +84,6 @@ fn main() {
8484
}
8585
}
8686

87-
// voxelizer.add_triangle(
88-
// &[[0., 40.4, 40.4], [0., 40.6, 40.5], [0., 40.4, 40.6]],
89-
// &|_, [x, y, z], _| {
90-
// let [x, y, z] = [x as f32, y as f32, z as f32];
91-
// let color_lab = palette::Okhsl::new(
92-
// x.atan2(z).to_degrees(),
93-
// 1.0 - (x * x + z * z) / 2200.,
94-
// y / 90. + 0.5,
95-
// );
96-
// let color_srgb = palette::Srgb::from_color(color_lab);
97-
// [color_srgb.red, color_srgb.green, color_srgb.blue]
98-
// },
99-
// );
100-
101-
// voxelizer.add_triangle(
102-
// &[[0., 30.6, 30.4], [0., 30.4, 30.5], [0., 30.6, 30.6]],
103-
// &|_, [x, y, z], _| {
104-
// let [x, y, z] = [x as f32, y as f32, z as f32];
105-
// let color_lab = palette::Okhsl::new(
106-
// x.atan2(z).to_degrees(),
107-
// 1.0 - (x * x + z * z) / 2200.,
108-
// y / 90. + 0.5,
109-
// );
110-
// let color_srgb = palette::Srgb::from_color(color_lab);
111-
// [color_srgb.red, color_srgb.green, color_srgb.blue]
112-
// },
113-
// );
114-
115-
// voxelizer.add_line([40., 40., 40.], [40., 40., 40.], &|_, [x, y, z], _| {
116-
// let [x, y, z] = [x as f32, y as f32, z as f32];
117-
// let color_lab = palette::Okhsl::new(
118-
// x.atan2(z).to_degrees(),
119-
// 1.0 - (x * x + z * z) / 2200.,
120-
// y / 90. + 0.5,
121-
// );
122-
// let color_srgb = palette::Srgb::from_color(color_lab);
123-
// [color_srgb.red, color_srgb.green, color_srgb.blue]
124-
// });
125-
12687
let occupied_voxels = voxelizer.finalize();
12788

12889
// -------------------make glTF-------------------

examples/voxelize_uv.rs

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
use std::{
2+
fs::File,
3+
io::{BufWriter, Write},
4+
};
5+
6+
use byteorder::{LittleEndian, WriteBytesExt};
7+
use earcut::{utils3d::project3d_to_2d, Earcut};
8+
use flatgeom::MultiPolygon;
9+
use indexmap::IndexSet;
10+
use serde_json::json;
11+
12+
use dda_voxelize::DdaVoxelizer;
13+
14+
fn main() {
15+
let vertices: Vec<[f64; 3]> = vec![
16+
// exterior
17+
[26.5, 26.5, 0.5],
18+
[0.5, 26.5, 26.5],
19+
[-25.5, 26.5, 0.5],
20+
[0.5, 26.5, -25.5],
21+
[0.5, 75.5, 0.5],
22+
];
23+
24+
let mut mpoly = MultiPolygon::<u32>::new();
25+
26+
// index
27+
mpoly.add_exterior([0, 1, 2, 3]);
28+
mpoly.add_exterior([0, 1, 4]);
29+
mpoly.add_exterior([1, 2, 4]);
30+
mpoly.add_exterior([2, 3, 4]);
31+
mpoly.add_exterior([3, 0, 4]);
32+
33+
let mut voxelizer = DdaVoxelizer::new();
34+
35+
let mut earcutter = Earcut::new();
36+
let mut buf3d: Vec<[f32; 3]> = Vec::new();
37+
let mut buf2d: Vec<[f32; 2]> = Vec::new();
38+
let mut index_buf: Vec<u32> = Vec::new();
39+
40+
for idx_poly in mpoly.iter() {
41+
let poly = idx_poly.transform(|idx| vertices[*idx as usize]);
42+
let num_outer = match poly.hole_indices().first() {
43+
Some(&v) => v as usize,
44+
None => poly.raw_coords().len(),
45+
};
46+
47+
for axis in 0..=5 {
48+
buf3d.clear();
49+
buf3d.extend(poly.raw_coords().iter().map(|v| match axis {
50+
0 => [v[0] as f32, v[1] as f32, v[2] as f32],
51+
1 => [v[1] as f32, v[0] as f32, v[2] as f32],
52+
2 => [v[0] as f32, v[2] as f32, v[1] as f32],
53+
3 => [v[0] as f32, -v[1] as f32, v[2] as f32],
54+
4 => [-v[1] as f32, v[0] as f32, v[2] as f32],
55+
5 => [v[0] as f32, v[2] as f32, -v[1] as f32],
56+
_ => unreachable!(),
57+
}));
58+
if project3d_to_2d(&buf3d, num_outer, &mut buf2d) {
59+
// earcut
60+
earcutter.earcut(buf2d.iter().cloned(), poly.hole_indices(), &mut index_buf);
61+
for index in index_buf.chunks_exact(3) {
62+
voxelizer.add_triangle(
63+
&[
64+
buf3d[index[0] as usize],
65+
buf3d[index[1] as usize],
66+
buf3d[index[2] as usize],
67+
],
68+
&|_current_value, _pos, [w0, w1, w2]| {
69+
// You could use the vertex weight to calculate the UV of the voxel.
70+
// uv = v0.uv * w0 + v1.uv * w1 + v2.uv * w2;
71+
72+
[w0, w1, w2]
73+
},
74+
);
75+
}
76+
}
77+
}
78+
}
79+
80+
let occupied_voxels = voxelizer.finalize();
81+
82+
// -------------------make glTF-------------------
83+
84+
// voxel is an integer value, but componentType of accessors is 5126 (floating point number),
85+
// and INTEGER type cannot be used due to primitives constraints
86+
87+
let mut indices = Vec::new();
88+
let mut vertices = IndexSet::new(); // [x, y, z, r, g, b]
89+
90+
for (position, voxel) in occupied_voxels.iter() {
91+
let [x, y, z] = [position[0] as f32, position[1] as f32, position[2] as f32];
92+
let [r, g, b] = voxel;
93+
94+
let [r_bits, g_bits, b_bits] = [r.to_bits(), g.to_bits(), b.to_bits()];
95+
96+
// Make a voxel cube
97+
let (idx0, _) = vertices.insert_full([
98+
(x + 0.5).to_bits(),
99+
(y - 0.5).to_bits(),
100+
(z + 0.5).to_bits(),
101+
r_bits,
102+
g_bits,
103+
b_bits,
104+
]);
105+
let (idx1, _) = vertices.insert_full([
106+
(x - 0.5).to_bits(),
107+
(y - 0.5).to_bits(),
108+
(z + 0.5).to_bits(),
109+
r_bits,
110+
g_bits,
111+
b_bits,
112+
]);
113+
let (idx2, _) = vertices.insert_full([
114+
(x + 0.5).to_bits(),
115+
(y - 0.5).to_bits(),
116+
(z - 0.5).to_bits(),
117+
r_bits,
118+
g_bits,
119+
b_bits,
120+
]);
121+
let (idx3, _) = vertices.insert_full([
122+
(x - 0.5).to_bits(),
123+
(y - 0.5).to_bits(),
124+
(z - 0.5).to_bits(),
125+
r_bits,
126+
g_bits,
127+
b_bits,
128+
]);
129+
let (idx4, _) = vertices.insert_full([
130+
(x + 0.5).to_bits(),
131+
(y + 0.5).to_bits(),
132+
(z + 0.5).to_bits(),
133+
r_bits,
134+
g_bits,
135+
b_bits,
136+
]);
137+
let (idx5, _) = vertices.insert_full([
138+
(x - 0.5).to_bits(),
139+
(y + 0.5).to_bits(),
140+
(z + 0.5).to_bits(),
141+
r_bits,
142+
g_bits,
143+
b_bits,
144+
]);
145+
let (idx6, _) = vertices.insert_full([
146+
(x + 0.5).to_bits(),
147+
(y + 0.5).to_bits(),
148+
(z - 0.5).to_bits(),
149+
r_bits,
150+
g_bits,
151+
b_bits,
152+
]);
153+
let (idx7, _) = vertices.insert_full([
154+
(x - 0.5).to_bits(),
155+
(y + 0.5).to_bits(),
156+
(z - 0.5).to_bits(),
157+
r_bits,
158+
g_bits,
159+
b_bits,
160+
]);
161+
indices.extend(
162+
[
163+
idx0, idx1, idx2, idx2, idx1, idx3, idx6, idx5, idx4, idx5, idx6, idx7, idx2, idx3,
164+
idx6, idx7, idx6, idx3, idx4, idx1, idx0, idx1, idx4, idx5, idx0, idx2, idx4, idx6,
165+
idx4, idx2, idx5, idx3, idx1, idx3, idx5, idx7,
166+
]
167+
.iter()
168+
.map(|&idx| idx as u32),
169+
);
170+
}
171+
172+
let mut min_position = [f32::MAX; 3];
173+
let mut max_position = [f32::MIN; 3];
174+
{
175+
let mut bin_file = BufWriter::new(File::create("output.bin").unwrap());
176+
177+
for &idx in &indices {
178+
bin_file.write_u32::<LittleEndian>(idx).unwrap();
179+
}
180+
181+
for &[x, y, z, r, g, b] in &vertices {
182+
min_position[0] = f32::min(min_position[0], f32::from_bits(x));
183+
min_position[1] = f32::min(min_position[1], f32::from_bits(y));
184+
min_position[2] = f32::min(min_position[2], f32::from_bits(z));
185+
max_position[0] = f32::max(max_position[0], f32::from_bits(x));
186+
max_position[1] = f32::max(max_position[1], f32::from_bits(y));
187+
max_position[2] = f32::max(max_position[2], f32::from_bits(z));
188+
189+
bin_file.write_u32::<LittleEndian>(x).unwrap();
190+
bin_file.write_u32::<LittleEndian>(y).unwrap();
191+
bin_file.write_u32::<LittleEndian>(z).unwrap();
192+
bin_file.write_u32::<LittleEndian>(r).unwrap();
193+
bin_file.write_u32::<LittleEndian>(g).unwrap();
194+
bin_file.write_u32::<LittleEndian>(b).unwrap();
195+
}
196+
}
197+
198+
let indices_size = indices.len() * 4;
199+
let vertices_size = vertices.len() * 6 * 4;
200+
let total_size = indices_size + vertices_size;
201+
202+
// make glTF
203+
let gltf_json = json!( {
204+
"asset": {
205+
"version": "2.0",
206+
},
207+
"scene": 0,
208+
"scenes": [
209+
{
210+
"nodes": [0],
211+
},
212+
],
213+
"nodes": [
214+
{"mesh": 0},
215+
],
216+
"meshes": [
217+
{
218+
"primitives": [
219+
{
220+
"attributes": {
221+
"POSITION": 1,
222+
"COLOR_0": 2,
223+
},
224+
"indices": 0,
225+
"mode": 4, // TRIANGLES
226+
},
227+
],
228+
},
229+
],
230+
"buffers": [
231+
{
232+
"uri": "./output.bin",
233+
"byteLength": total_size,
234+
},
235+
],
236+
"bufferViews": [
237+
{
238+
"buffer": 0,
239+
"byteOffset": 0,
240+
"byteLength": indices_size,
241+
"target": 34963, // ELEMENT_ARRAY_BUFFER
242+
},
243+
{
244+
"buffer": 0,
245+
"byteStride": 6 * 4,
246+
"byteOffset": indices_size,
247+
"byteLength": vertices_size,
248+
"target": 34962, // ARRAY_BUFFER
249+
},
250+
],
251+
"accessors": [
252+
{
253+
"bufferView": 0,
254+
"byteOffset": 0,
255+
"componentType": 5125, // UNSIGNED_INT
256+
"count": indices.len(),
257+
"type": "SCALAR",
258+
},
259+
{
260+
"bufferView": 1,
261+
"byteOffset": 0,
262+
"componentType": 5126, // FLOAT
263+
"count": vertices.len(),
264+
"type": "VEC3",
265+
"min": [min_position[0], min_position[1], min_position[2]],
266+
"max": [max_position[0], max_position[1], max_position[2]],
267+
},
268+
{
269+
"bufferView": 1,
270+
"byteOffset": 4 * 3,
271+
"componentType": 5126, // FLOAT
272+
"count": vertices.len(),
273+
"type": "VEC3",
274+
},
275+
],
276+
});
277+
278+
// write glTF
279+
println!("write glTF");
280+
let mut gltf_file = File::create("output.gltf").unwrap();
281+
let _ = gltf_file.write_all(gltf_json.to_string().as_bytes());
282+
}

0 commit comments

Comments
 (0)