1use draco_core::draco_types::DataType;
13use draco_core::geometry_attribute::{GeometryAttributeType, PointAttribute};
14use draco_core::geometry_indices::{FaceIndex, PointIndex};
15use draco_core::mesh::Mesh;
16
17use crate::fbx_scene::{
18 FbxBinormalSet, FbxColorSet, FbxCreaseLayer, FbxLayerSet, FbxMeshInstance, FbxMeshLayers,
19 FbxNormalSet, FbxSmoothingLayer, FbxTangentSet, FbxUvSet,
20};
21
22#[derive(Debug, Clone, Default, PartialEq)]
24pub struct FbxRenderLayer<T> {
25 pub name: Option<String>,
27 pub values: Vec<T>,
29}
30
31#[derive(Debug, Clone, Default, PartialEq)]
36pub struct FbxRenderMesh {
37 pub positions: Vec<[f32; 3]>,
39 pub normals: Vec<FbxRenderLayer<[f32; 3]>>,
41 pub uvs: Vec<FbxRenderLayer<[f32; 2]>>,
43 pub colors: Vec<FbxRenderLayer<[f32; 4]>>,
45 pub tangents: Vec<FbxRenderLayer<[f32; 4]>>,
48 pub binormals: Vec<FbxRenderLayer<[f32; 4]>>,
50 pub indices: Vec<u32>,
52 pub polygon_sizes: Vec<u32>,
57 pub corner_to_control_point: Vec<u32>,
62 pub corner_to_polygon: Vec<u32>,
64}
65
66impl FbxRenderMesh {
67 pub fn corner_count(&self) -> usize {
69 self.positions.len()
70 }
71}
72
73#[derive(Debug, Clone, Copy, Default)]
85pub struct FbxGeometryLayers<'a> {
86 pub control_points: &'a [[f32; 3]],
88 pub polygon_vertex_indices: &'a [i32],
90 pub uv_sets: &'a [FbxUvSet],
92 pub normal_sets: &'a [FbxNormalSet],
94 pub color_sets: &'a [FbxColorSet],
96 pub tangent_sets: &'a [FbxTangentSet],
98 pub binormal_sets: &'a [FbxBinormalSet],
100 pub smoothing_layers: &'a [FbxSmoothingLayer],
105 pub crease_layers: &'a [FbxCreaseLayer],
107}
108
109impl<'a> FbxGeometryLayers<'a> {
110 pub fn from_instance(instance: &'a FbxMeshInstance) -> Self {
112 Self::new(
113 &instance.control_points,
114 &instance.polygon_vertex_indices,
115 &instance.layers,
116 )
117 }
118
119 pub fn new(
121 control_points: &'a [[f32; 3]],
122 polygon_vertex_indices: &'a [i32],
123 layers: &'a FbxMeshLayers,
124 ) -> Self {
125 Self {
126 control_points,
127 polygon_vertex_indices,
128 uv_sets: &layers.uv_sets,
129 normal_sets: &layers.normal_sets,
130 color_sets: &layers.color_sets,
131 tangent_sets: &layers.tangent_sets,
132 binormal_sets: &layers.binormal_sets,
133 smoothing_layers: &layers.smoothing_layers,
134 crease_layers: &layers.crease_layers,
135 }
136 }
137}
138
139#[derive(Debug, Clone, Copy)]
142struct EmittedCorner {
143 control_point: u32,
145 source_corner: usize,
147 polygon: u32,
149}
150
151fn resolve_layer_value<const N: usize>(
156 mapping: Option<&str>,
157 reference: Option<&str>,
158 indices: &[i32],
159 values: &[[f32; N]],
160 corner: EmittedCorner,
161) -> [f32; N] {
162 let logical = match mapping {
163 Some("ByPolygonVertex") => corner.source_corner,
164 Some("ByPolygon") => corner.polygon as usize,
167 Some("AllSame") | Some("AllSameOrPolygon") => 0,
168 _ => corner.control_point as usize,
171 };
172 let value_index = if reference == Some("IndexToDirect") {
173 match indices.get(logical).copied() {
174 Some(index) if index < 0 => return [0.0; N],
176 Some(index) => index as usize,
177 None => logical,
178 }
179 } else {
180 logical
181 };
182 values.get(value_index).copied().unwrap_or([0.0; N])
183}
184
185fn resolve_layer<const N: usize>(
191 set: &FbxLayerSet<N>,
192 corners: &[EmittedCorner],
193) -> FbxRenderLayer<[f32; N]> {
194 FbxRenderLayer {
195 name: set.name.clone(),
196 values: corners
197 .iter()
198 .map(|&corner| {
199 resolve_layer_value(
200 set.mapping.as_deref(),
201 set.reference.as_deref(),
202 &set.indices,
203 &set.values,
204 corner,
205 )
206 })
207 .collect(),
208 }
209}
210
211pub fn expand_to_render_mesh(source: FbxGeometryLayers<'_>) -> FbxRenderMesh {
219 let FbxGeometryLayers {
220 control_points,
221 polygon_vertex_indices,
222 uv_sets,
223 normal_sets,
224 color_sets,
225 tangent_sets,
226 binormal_sets,
227 smoothing_layers: _,
230 crease_layers: _,
231 } = source;
232 let mut render = FbxRenderMesh::default();
233 if control_points.is_empty() || polygon_vertex_indices.is_empty() {
234 return render;
235 }
236
237 let mut emitted: Vec<EmittedCorner> = Vec::new();
241 let mut polygon: Vec<EmittedCorner> = Vec::new();
242 let mut polygon_index = 0u32;
243
244 for (corner, encoded) in polygon_vertex_indices.iter().enumerate() {
245 let control_point = if *encoded < 0 {
246 !*encoded as u32
247 } else {
248 *encoded as u32
249 };
250 polygon.push(EmittedCorner {
251 control_point,
252 source_corner: corner,
253 polygon: polygon_index,
254 });
255
256 if *encoded < 0 {
258 render.polygon_sizes.push(polygon.len() as u32);
259 for offset in 1..polygon.len().saturating_sub(1) {
260 for &vertex in &[polygon[0], polygon[offset], polygon[offset + 1]] {
261 emitted.push(vertex);
262 render.corner_to_polygon.push(polygon_index);
263 }
264 }
265 polygon.clear();
266 polygon_index += 1;
267 }
268 }
269
270 render.positions = emitted
271 .iter()
272 .map(|corner| {
273 control_points
274 .get(corner.control_point as usize)
275 .copied()
276 .unwrap_or([0.0; 3])
277 })
278 .collect();
279 render.corner_to_control_point = emitted.iter().map(|c| c.control_point).collect();
280 render.indices = (0..emitted.len() as u32).collect();
281 render.uvs = uv_sets.iter().map(|s| resolve_layer(s, &emitted)).collect();
282 render.normals = normal_sets
283 .iter()
284 .map(|s| resolve_layer(s, &emitted))
285 .collect();
286 render.colors = color_sets
287 .iter()
288 .map(|s| resolve_layer(s, &emitted))
289 .collect();
290 render.tangents = tangent_sets
291 .iter()
292 .map(|s| resolve_layer(&s.layer, &emitted))
293 .collect();
294 render.binormals = binormal_sets
295 .iter()
296 .map(|s| resolve_layer(&s.layer, &emitted))
297 .collect();
298 render
299}
300
301pub struct DracoMeshWithCornerMap {
306 pub mesh: Mesh,
308 pub corner_to_point: Vec<u32>,
311 pub point_to_corner: Vec<u32>,
315}
316
317pub fn build_draco_mesh(render: &FbxRenderMesh) -> Mesh {
327 build_draco_mesh_with_corner_map(render).mesh
328}
329
330pub fn build_draco_mesh_with_corner_map(render: &FbxRenderMesh) -> DracoMeshWithCornerMap {
339 let normals = render.normals.first();
340 let uvs = render.uvs.first();
341 let colors = render.colors.first();
342 let corner_count = render.corner_count();
343
344 let mut mesh = Mesh::new();
345 mesh.set_num_points(corner_count);
346
347 let mut position = PointAttribute::new();
348 position.init(
349 GeometryAttributeType::Position,
350 3,
351 DataType::Float32,
352 false,
353 corner_count,
354 );
355 for (corner, value) in render.positions.iter().enumerate() {
356 let bytes: Vec<u8> = value.iter().flat_map(|v| v.to_le_bytes()).collect();
357 position.buffer_mut().write(corner * 12, &bytes);
358 }
359 mesh.add_attribute(position);
360
361 if let Some(layer) = normals {
362 let mut normal = PointAttribute::new();
363 normal.init(
364 GeometryAttributeType::Normal,
365 3,
366 DataType::Float32,
367 false,
368 corner_count,
369 );
370 for (corner, value) in layer.values.iter().enumerate() {
371 let bytes: Vec<u8> = value.iter().flat_map(|v| v.to_le_bytes()).collect();
372 normal.buffer_mut().write(corner * 12, &bytes);
373 }
374 mesh.add_attribute(normal);
375 }
376
377 if let Some(layer) = uvs {
378 let mut tex_coord = PointAttribute::new();
379 tex_coord.init(
380 GeometryAttributeType::TexCoord,
381 2,
382 DataType::Float32,
383 false,
384 corner_count,
385 );
386 for (corner, value) in layer.values.iter().enumerate() {
387 let bytes: Vec<u8> = value.iter().flat_map(|v| v.to_le_bytes()).collect();
388 tex_coord.buffer_mut().write(corner * 8, &bytes);
389 }
390 mesh.add_attribute(tex_coord);
391 }
392
393 if let Some(layer) = colors {
394 let mut color = PointAttribute::new();
395 color.init(
396 GeometryAttributeType::Color,
397 4,
398 DataType::Float32,
399 false,
400 corner_count,
401 );
402 for (corner, value) in layer.values.iter().enumerate() {
403 let bytes: Vec<u8> = value.iter().flat_map(|v| v.to_le_bytes()).collect();
404 color.buffer_mut().write(corner * 16, &bytes);
405 }
406 mesh.add_attribute(color);
407 }
408
409 mesh.set_num_faces(corner_count / 3);
410 for face in 0..corner_count / 3 {
411 let base = (face * 3) as u32;
412 mesh.set_face(
413 FaceIndex(face as u32),
414 [PointIndex(base), PointIndex(base + 1), PointIndex(base + 2)],
415 );
416 }
417
418 let corner_to_point = mesh
419 .finalize_returning_corner_map()
420 .expect("deduplicating an in-memory mesh cannot fail on I/O");
421
422 let mut point_to_corner = vec![u32::MAX; mesh.num_points()];
427 for (corner, &point) in corner_to_point.iter().enumerate() {
428 if point_to_corner[point as usize] == u32::MAX {
429 point_to_corner[point as usize] = corner as u32;
430 }
431 }
432
433 DracoMeshWithCornerMap {
434 mesh,
435 corner_to_point,
436 point_to_corner,
437 }
438}
439
440impl FbxMeshInstance {
441 pub fn to_render_mesh(&self) -> FbxRenderMesh {
443 expand_to_render_mesh(FbxGeometryLayers::from_instance(self))
444 }
445
446 pub fn to_draco_mesh(&self) -> Mesh {
451 let render = self.to_render_mesh();
452 if render.positions.is_empty() {
453 return self.mesh.clone();
454 }
455 build_draco_mesh(&render)
456 }
457
458 pub fn to_draco_mesh_with_corner_map(&self) -> DracoMeshWithCornerMap {
468 let render = self.to_render_mesh();
469 if render.positions.is_empty() {
470 let identity: Vec<u32> = (0..self.mesh.num_points() as u32).collect();
471 return DracoMeshWithCornerMap {
472 mesh: self.mesh.clone(),
473 corner_to_point: identity.clone(),
474 point_to_corner: identity,
475 };
476 }
477 build_draco_mesh_with_corner_map(&render)
478 }
479}
480
481#[cfg(test)]
482mod tests {
483 use super::*;
484 use crate::fbx_scene::FbxMeshInstance;
485
486 fn seamed_quad() -> FbxMeshInstance {
489 FbxMeshInstance {
490 control_points: vec![
491 [0.0, 0.0, 0.0],
492 [1.0, 0.0, 0.0],
493 [1.0, 1.0, 0.0],
494 [0.0, 1.0, 0.0],
495 ],
496 polygon_vertex_indices: vec![0, 1, 2, !3],
498 layers: FbxMeshLayers {
499 uv_sets: vec![FbxUvSet {
500 name: Some("map1".to_string()),
501 mapping: Some("ByPolygonVertex".to_string()),
502 reference: Some("Direct".to_string()),
503 values: vec![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.5, 0.5]],
504 indices: Vec::new(),
505 }],
506 ..Default::default()
507 },
508 ..Default::default()
509 }
510 }
511
512 #[test]
513 fn a_quad_fans_into_two_triangles_over_four_corners() {
514 let render = seamed_quad().to_render_mesh();
515 assert_eq!(render.polygon_sizes, vec![4]);
516 assert_eq!(render.corner_count(), 6, "two fan triangles");
517 assert_eq!(render.indices, vec![0, 1, 2, 3, 4, 5]);
518 assert_eq!(render.corner_to_control_point, vec![0, 1, 2, 0, 2, 3]);
519 assert!(render.corner_to_polygon.iter().all(|&p| p == 0));
520 }
521
522 #[test]
523 fn per_corner_uvs_follow_the_corner_not_the_control_point() {
524 let render = seamed_quad().to_render_mesh();
525 let uv = &render.uvs[0].values;
526 assert_eq!(uv[0], [0.0, 0.0]);
528 assert_eq!(uv[1], [1.0, 0.0]);
529 assert_eq!(uv[2], [1.0, 1.0]);
530 assert_eq!(render.uvs[0].name.as_deref(), Some("map1"));
531 }
532
533 #[test]
534 fn welding_keeps_seams_but_drops_exact_duplicates() {
535 let mesh = seamed_quad().to_draco_mesh();
536 assert_eq!(mesh.num_points(), 4);
539 assert_eq!(mesh.num_faces(), 2);
540 }
541
542 #[test]
547 fn welding_gives_the_same_mesh_whatever_the_hasher_seed_is() {
548 let first = seamed_quad().to_draco_mesh();
549 let second = seamed_quad().to_draco_mesh();
550
551 assert_eq!(first.num_points(), second.num_points());
552 assert_eq!(first.num_faces(), second.num_faces());
553 for face in 0..first.num_faces() {
554 assert_eq!(
555 first.face(FaceIndex(face as u32)),
556 second.face(FaceIndex(face as u32)),
557 "face {face} came out differently"
558 );
559 }
560 }
561
562 fn probe_corner() -> EmittedCorner {
564 EmittedCorner {
565 control_point: 1,
566 source_corner: 5,
567 polygon: 2,
568 }
569 }
570
571 #[test]
572 fn a_negative_index_to_direct_entry_yields_a_default_rather_than_panicking() {
573 let value: [f32; 2] = resolve_layer_value(
574 Some("ByPolygonVertex"),
575 Some("IndexToDirect"),
576 &[-31],
577 &[[1.0, 2.0]],
578 EmittedCorner {
579 control_point: 0,
580 source_corner: 0,
581 polygon: 0,
582 },
583 );
584 assert_eq!(value, [0.0, 0.0]);
585 }
586
587 #[test]
588 fn each_mapping_addresses_its_own_domain() {
589 let values: Vec<[f32; 1]> = (0..8).map(|i| [i as f32]).collect();
591 let resolve = |mapping| {
592 resolve_layer_value(Some(mapping), Some("Direct"), &[], &values, probe_corner())
593 };
594 assert_eq!(resolve("ByPolygonVertex"), [5.0]);
595 assert_eq!(resolve("ByVertice"), [1.0]);
596 assert_eq!(resolve("AllSame"), [0.0]);
597 assert_eq!(resolve("ByPolygon"), [2.0]);
600 }
601
602 #[test]
603 fn an_instance_without_raw_geometry_expands_to_nothing() {
604 let mut instance = seamed_quad();
605 instance.control_points.clear();
606 instance.polygon_vertex_indices.clear();
607 assert_eq!(instance.to_render_mesh(), FbxRenderMesh::default());
608 }
609}