1use std::collections::HashMap;
13
14use draco_core::draco_types::DataType;
15use draco_core::geometry_attribute::{GeometryAttributeType, PointAttribute};
16use draco_core::geometry_indices::{FaceIndex, PointIndex};
17use draco_core::mesh::Mesh;
18
19use crate::fbx_scene::{
20 FbxBinormalSet, FbxColorSet, FbxCreaseLayer, FbxLayerSet, FbxMeshInstance, FbxMeshLayers,
21 FbxNormalSet, FbxSmoothingLayer, FbxTangentSet, FbxUvSet,
22};
23
24#[derive(Debug, Clone, Default, PartialEq)]
26pub struct FbxRenderLayer<T> {
27 pub name: Option<String>,
29 pub values: Vec<T>,
31}
32
33#[derive(Debug, Clone, Default, PartialEq)]
38pub struct FbxRenderMesh {
39 pub positions: Vec<[f32; 3]>,
41 pub normals: Vec<FbxRenderLayer<[f32; 3]>>,
43 pub uvs: Vec<FbxRenderLayer<[f32; 2]>>,
45 pub colors: Vec<FbxRenderLayer<[f32; 4]>>,
47 pub tangents: Vec<FbxRenderLayer<[f32; 4]>>,
50 pub binormals: Vec<FbxRenderLayer<[f32; 4]>>,
52 pub indices: Vec<u32>,
54 pub polygon_sizes: Vec<u32>,
59 pub corner_to_control_point: Vec<u32>,
64 pub corner_to_polygon: Vec<u32>,
66}
67
68impl FbxRenderMesh {
69 pub fn corner_count(&self) -> usize {
71 self.positions.len()
72 }
73}
74
75#[derive(Debug, Clone, Copy, Default)]
87pub struct FbxGeometryLayers<'a> {
88 pub control_points: &'a [[f32; 3]],
90 pub polygon_vertex_indices: &'a [i32],
92 pub uv_sets: &'a [FbxUvSet],
94 pub normal_sets: &'a [FbxNormalSet],
96 pub color_sets: &'a [FbxColorSet],
98 pub tangent_sets: &'a [FbxTangentSet],
100 pub binormal_sets: &'a [FbxBinormalSet],
102 pub smoothing_layers: &'a [FbxSmoothingLayer],
107 pub crease_layers: &'a [FbxCreaseLayer],
109}
110
111impl<'a> FbxGeometryLayers<'a> {
112 pub fn from_instance(instance: &'a FbxMeshInstance) -> Self {
114 Self::new(
115 &instance.control_points,
116 &instance.polygon_vertex_indices,
117 &instance.layers,
118 )
119 }
120
121 pub fn new(
123 control_points: &'a [[f32; 3]],
124 polygon_vertex_indices: &'a [i32],
125 layers: &'a FbxMeshLayers,
126 ) -> Self {
127 Self {
128 control_points,
129 polygon_vertex_indices,
130 uv_sets: &layers.uv_sets,
131 normal_sets: &layers.normal_sets,
132 color_sets: &layers.color_sets,
133 tangent_sets: &layers.tangent_sets,
134 binormal_sets: &layers.binormal_sets,
135 smoothing_layers: &layers.smoothing_layers,
136 crease_layers: &layers.crease_layers,
137 }
138 }
139}
140
141#[derive(Debug, Clone, Copy)]
144struct EmittedCorner {
145 control_point: u32,
147 source_corner: usize,
149 polygon: u32,
151}
152
153fn resolve_layer_value<const N: usize>(
158 mapping: Option<&str>,
159 reference: Option<&str>,
160 indices: &[i32],
161 values: &[[f32; N]],
162 corner: EmittedCorner,
163) -> [f32; N] {
164 let logical = match mapping {
165 Some("ByPolygonVertex") => corner.source_corner,
166 Some("ByPolygon") => corner.polygon as usize,
169 Some("AllSame") | Some("AllSameOrPolygon") => 0,
170 _ => corner.control_point as usize,
173 };
174 let value_index = if reference == Some("IndexToDirect") {
175 match indices.get(logical).copied() {
176 Some(index) if index < 0 => return [0.0; N],
178 Some(index) => index as usize,
179 None => logical,
180 }
181 } else {
182 logical
183 };
184 values.get(value_index).copied().unwrap_or([0.0; N])
185}
186
187fn resolve_layer<const N: usize>(
193 set: &FbxLayerSet<N>,
194 corners: &[EmittedCorner],
195) -> FbxRenderLayer<[f32; N]> {
196 FbxRenderLayer {
197 name: set.name.clone(),
198 values: corners
199 .iter()
200 .map(|&corner| {
201 resolve_layer_value(
202 set.mapping.as_deref(),
203 set.reference.as_deref(),
204 &set.indices,
205 &set.values,
206 corner,
207 )
208 })
209 .collect(),
210 }
211}
212
213pub fn expand_to_render_mesh(source: FbxGeometryLayers<'_>) -> FbxRenderMesh {
221 let FbxGeometryLayers {
222 control_points,
223 polygon_vertex_indices,
224 uv_sets,
225 normal_sets,
226 color_sets,
227 tangent_sets,
228 binormal_sets,
229 smoothing_layers: _,
232 crease_layers: _,
233 } = source;
234 let mut render = FbxRenderMesh::default();
235 if control_points.is_empty() || polygon_vertex_indices.is_empty() {
236 return render;
237 }
238
239 let mut emitted: Vec<EmittedCorner> = Vec::new();
243 let mut polygon: Vec<EmittedCorner> = Vec::new();
244 let mut polygon_index = 0u32;
245
246 for (corner, encoded) in polygon_vertex_indices.iter().enumerate() {
247 let control_point = if *encoded < 0 {
248 !*encoded as u32
249 } else {
250 *encoded as u32
251 };
252 polygon.push(EmittedCorner {
253 control_point,
254 source_corner: corner,
255 polygon: polygon_index,
256 });
257
258 if *encoded < 0 {
260 render.polygon_sizes.push(polygon.len() as u32);
261 for offset in 1..polygon.len().saturating_sub(1) {
262 for &vertex in &[polygon[0], polygon[offset], polygon[offset + 1]] {
263 emitted.push(vertex);
264 render.corner_to_polygon.push(polygon_index);
265 }
266 }
267 polygon.clear();
268 polygon_index += 1;
269 }
270 }
271
272 render.positions = emitted
273 .iter()
274 .map(|corner| {
275 control_points
276 .get(corner.control_point as usize)
277 .copied()
278 .unwrap_or([0.0; 3])
279 })
280 .collect();
281 render.corner_to_control_point = emitted.iter().map(|c| c.control_point).collect();
282 render.indices = (0..emitted.len() as u32).collect();
283 render.uvs = uv_sets.iter().map(|s| resolve_layer(s, &emitted)).collect();
284 render.normals = normal_sets
285 .iter()
286 .map(|s| resolve_layer(s, &emitted))
287 .collect();
288 render.colors = color_sets
289 .iter()
290 .map(|s| resolve_layer(s, &emitted))
291 .collect();
292 render.tangents = tangent_sets
293 .iter()
294 .map(|s| resolve_layer(&s.layer, &emitted))
295 .collect();
296 render.binormals = binormal_sets
297 .iter()
298 .map(|s| resolve_layer(&s.layer, &emitted))
299 .collect();
300 render
301}
302
303pub fn build_draco_mesh(render: &FbxRenderMesh) -> Mesh {
313 let normals = render.normals.first();
314 let uvs = render.uvs.first();
315 let colors = render.colors.first();
316
317 type WeldKey = (
320 [u32; 3],
321 Option<[u32; 3]>,
322 Option<[u32; 2]>,
323 Option<[u32; 4]>,
324 );
325 let mut unique: HashMap<WeldKey, u32> = HashMap::new();
326 let mut order: Vec<usize> = Vec::new();
327 let mut remapped: Vec<u32> = Vec::with_capacity(render.corner_count());
328
329 for corner in 0..render.corner_count() {
330 let position = render.positions[corner].map(f32::to_bits);
331 let normal = normals.map(|layer| layer.values[corner].map(f32::to_bits));
332 let uv = uvs.map(|layer| layer.values[corner].map(f32::to_bits));
333 let color = colors.map(|layer| layer.values[corner].map(f32::to_bits));
334 let key: WeldKey = (position, normal, uv, color);
335 let next = unique.len() as u32;
336 let index = *unique.entry(key).or_insert_with(|| {
337 order.push(corner);
338 next
339 });
340 remapped.push(index);
341 }
342
343 let point_count = order.len();
344 let mut mesh = Mesh::new();
345 mesh.set_num_points(point_count);
346
347 let mut position = PointAttribute::new();
348 position.init(
349 GeometryAttributeType::Position,
350 3,
351 DataType::Float32,
352 false,
353 point_count,
354 );
355 for (index, &corner) in order.iter().enumerate() {
356 let bytes: Vec<u8> = render.positions[corner]
357 .iter()
358 .flat_map(|value| value.to_le_bytes())
359 .collect();
360 position.buffer_mut().write(index * 12, &bytes);
361 }
362 mesh.add_attribute(position);
363
364 if let Some(layer) = normals {
365 let mut normal = PointAttribute::new();
366 normal.init(
367 GeometryAttributeType::Normal,
368 3,
369 DataType::Float32,
370 false,
371 point_count,
372 );
373 for (index, &corner) in order.iter().enumerate() {
374 let bytes: Vec<u8> = layer.values[corner]
375 .iter()
376 .flat_map(|value| value.to_le_bytes())
377 .collect();
378 normal.buffer_mut().write(index * 12, &bytes);
379 }
380 mesh.add_attribute(normal);
381 }
382
383 if let Some(layer) = uvs {
384 let mut tex_coord = PointAttribute::new();
385 tex_coord.init(
386 GeometryAttributeType::TexCoord,
387 2,
388 DataType::Float32,
389 false,
390 point_count,
391 );
392 for (index, &corner) in order.iter().enumerate() {
393 let bytes: Vec<u8> = layer.values[corner]
394 .iter()
395 .flat_map(|value| value.to_le_bytes())
396 .collect();
397 tex_coord.buffer_mut().write(index * 8, &bytes);
398 }
399 mesh.add_attribute(tex_coord);
400 }
401
402 if let Some(layer) = colors {
403 let mut color = PointAttribute::new();
404 color.init(
405 GeometryAttributeType::Color,
406 4,
407 DataType::Float32,
408 false,
409 point_count,
410 );
411 for (index, &corner) in order.iter().enumerate() {
412 let bytes: Vec<u8> = layer.values[corner]
413 .iter()
414 .flat_map(|value| value.to_le_bytes())
415 .collect();
416 color.buffer_mut().write(index * 16, &bytes);
417 }
418 mesh.add_attribute(color);
419 }
420
421 mesh.set_num_faces(remapped.len() / 3);
422 for (face, triangle) in remapped.chunks_exact(3).enumerate() {
423 mesh.set_face(
424 FaceIndex(face as u32),
425 [
426 PointIndex(triangle[0]),
427 PointIndex(triangle[1]),
428 PointIndex(triangle[2]),
429 ],
430 );
431 }
432 mesh
433}
434
435impl FbxMeshInstance {
436 pub fn to_render_mesh(&self) -> FbxRenderMesh {
438 expand_to_render_mesh(FbxGeometryLayers::from_instance(self))
439 }
440
441 pub fn to_draco_mesh(&self) -> Mesh {
446 let render = self.to_render_mesh();
447 if render.positions.is_empty() {
448 return self.mesh.clone();
449 }
450 build_draco_mesh(&render)
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457 use crate::fbx_scene::FbxMeshInstance;
458
459 fn seamed_quad() -> FbxMeshInstance {
462 FbxMeshInstance {
463 control_points: vec![
464 [0.0, 0.0, 0.0],
465 [1.0, 0.0, 0.0],
466 [1.0, 1.0, 0.0],
467 [0.0, 1.0, 0.0],
468 ],
469 polygon_vertex_indices: vec![0, 1, 2, !3],
471 layers: FbxMeshLayers {
472 uv_sets: vec![FbxUvSet {
473 name: Some("map1".to_string()),
474 mapping: Some("ByPolygonVertex".to_string()),
475 reference: Some("Direct".to_string()),
476 values: vec![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.5, 0.5]],
477 indices: Vec::new(),
478 }],
479 ..Default::default()
480 },
481 ..Default::default()
482 }
483 }
484
485 #[test]
486 fn a_quad_fans_into_two_triangles_over_four_corners() {
487 let render = seamed_quad().to_render_mesh();
488 assert_eq!(render.polygon_sizes, vec![4]);
489 assert_eq!(render.corner_count(), 6, "two fan triangles");
490 assert_eq!(render.indices, vec![0, 1, 2, 3, 4, 5]);
491 assert_eq!(render.corner_to_control_point, vec![0, 1, 2, 0, 2, 3]);
492 assert!(render.corner_to_polygon.iter().all(|&p| p == 0));
493 }
494
495 #[test]
496 fn per_corner_uvs_follow_the_corner_not_the_control_point() {
497 let render = seamed_quad().to_render_mesh();
498 let uv = &render.uvs[0].values;
499 assert_eq!(uv[0], [0.0, 0.0]);
501 assert_eq!(uv[1], [1.0, 0.0]);
502 assert_eq!(uv[2], [1.0, 1.0]);
503 assert_eq!(render.uvs[0].name.as_deref(), Some("map1"));
504 }
505
506 #[test]
507 fn welding_keeps_seams_but_drops_exact_duplicates() {
508 let mesh = seamed_quad().to_draco_mesh();
509 assert_eq!(mesh.num_points(), 4);
512 assert_eq!(mesh.num_faces(), 2);
513 }
514
515 fn probe_corner() -> EmittedCorner {
517 EmittedCorner {
518 control_point: 1,
519 source_corner: 5,
520 polygon: 2,
521 }
522 }
523
524 #[test]
525 fn a_negative_index_to_direct_entry_yields_a_default_rather_than_panicking() {
526 let value: [f32; 2] = resolve_layer_value(
527 Some("ByPolygonVertex"),
528 Some("IndexToDirect"),
529 &[-31],
530 &[[1.0, 2.0]],
531 EmittedCorner {
532 control_point: 0,
533 source_corner: 0,
534 polygon: 0,
535 },
536 );
537 assert_eq!(value, [0.0, 0.0]);
538 }
539
540 #[test]
541 fn each_mapping_addresses_its_own_domain() {
542 let values: Vec<[f32; 1]> = (0..8).map(|i| [i as f32]).collect();
544 let resolve = |mapping| {
545 resolve_layer_value(Some(mapping), Some("Direct"), &[], &values, probe_corner())
546 };
547 assert_eq!(resolve("ByPolygonVertex"), [5.0]);
548 assert_eq!(resolve("ByVertice"), [1.0]);
549 assert_eq!(resolve("AllSame"), [0.0]);
550 assert_eq!(resolve("ByPolygon"), [2.0]);
553 }
554
555 #[test]
556 fn an_instance_without_raw_geometry_expands_to_nothing() {
557 let mut instance = seamed_quad();
558 instance.control_points.clear();
559 instance.polygon_vertex_indices.clear();
560 assert_eq!(instance.to_render_mesh(), FbxRenderMesh::default());
561 }
562}