1use std::fs::{self, File};
25use std::io::{self, BufReader, Cursor, Read, Seek, SeekFrom};
26use std::path::Path;
27
28use draco_core::draco_types::DataType;
29use draco_core::geometry_attribute::{GeometryAttributeType, PointAttribute};
30use draco_core::geometry_indices::{FaceIndex, PointIndex};
31use draco_core::mesh::Mesh;
32
33use crate::traits::ReadFromBytes;
34
35const FBX_MAGIC: &[u8; 21] = b"Kaydara FBX Binary \0";
37
38pub struct FbxReader<R: Read + Seek = BufReader<File>> {
40 reader: R,
41 version: u32,
42}
43
44pub type FbxMemoryReader = FbxReader<Cursor<Vec<u8>>>;
46
47#[derive(Debug, Clone)]
49pub struct FbxNode {
50 pub name: String,
52 pub properties: Vec<FbxProperty>,
54 pub children: Vec<FbxNode>,
56}
57
58#[derive(Debug, Clone)]
60pub enum FbxProperty {
61 Bool(bool),
63 I16(i16),
65 I32(i32),
67 I64(i64),
69 F32(f32),
71 F64(f64),
73 String(String),
75 Raw(Vec<u8>),
77 BoolArray(Vec<bool>),
79 I32Array(Vec<i32>),
81 I64Array(Vec<i64>),
83 F32Array(Vec<f32>),
85 F64Array(Vec<f64>),
87}
88
89impl FbxReader<BufReader<File>> {
90 pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
92 let file = File::open(path)?;
93 let reader = BufReader::new(file);
94 Self::new(reader)
95 }
96}
97
98impl FbxReader<Cursor<Vec<u8>>> {
99 pub fn from_bytes(bytes: impl Into<Vec<u8>>) -> io::Result<Self> {
101 Self::new(Cursor::new(bytes.into()))
102 }
103
104 pub fn read_from_bytes(bytes: &[u8]) -> io::Result<Vec<Mesh>> {
106 let mut reader = Self::from_bytes(bytes.to_vec())?;
107 reader.read_meshes()
108 }
109}
110
111impl crate::traits::Reader for FbxReader<BufReader<File>> {
113 fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
114 FbxReader::open(path)
115 }
116
117 fn read_meshes(&mut self) -> io::Result<Vec<draco_core::mesh::Mesh>> {
118 FbxReader::read_meshes(self)
121 }
122}
123
124impl crate::traits::Reader for FbxReader<Cursor<Vec<u8>>> {
125 fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
126 Self::from_bytes(fs::read(path)?)
127 }
128
129 fn read_meshes(&mut self) -> io::Result<Vec<draco_core::mesh::Mesh>> {
130 FbxReader::read_meshes(self)
131 }
132}
133
134impl crate::scene::SceneReader for FbxReader<BufReader<File>> {
135 fn read_scene(&mut self) -> io::Result<crate::scene::Scene> {
136 let nodes = self.read_nodes()?;
137
138 use std::collections::HashMap;
140 let mut model_map: HashMap<i64, &FbxNode> = HashMap::new();
141 let mut geometry_map: HashMap<i64, &FbxNode> = HashMap::new();
142 let mut connections: Vec<(i64, i64)> = Vec::new(); for n in &nodes {
145 if n.name == "Objects" {
146 for child in &n.children {
147 match child.name.as_str() {
148 "Model" => {
149 if let Some(FbxProperty::I64(id)) = child.properties.first() {
150 model_map.insert(*id, child);
151 }
152 }
153 "Geometry" => {
154 if let Some(FbxProperty::I64(id)) = child.properties.first() {
155 geometry_map.insert(*id, child);
156 }
157 }
158 _ => {}
159 }
160 }
161 } else if n.name == "Connections" {
162 for c in &n.children {
163 if let (
165 Some(FbxProperty::String(_kind)),
166 Some(FbxProperty::I64(child)),
167 Some(FbxProperty::I64(parent)),
168 ) = (
169 c.properties.first(),
170 c.properties.get(1),
171 c.properties.get(2),
172 ) {
173 connections.push((*child, *parent));
174 }
175 }
176 }
177 }
178
179 let mut model_children: HashMap<i64, Vec<i64>> = HashMap::new();
181 for (child, parent) in connections.iter() {
182 if model_map.contains_key(child) || model_map.contains_key(parent) {
183 model_children.entry(*parent).or_default().push(*child);
184 }
185 }
186
187 fn parse_transform(node: &FbxNode) -> Option<crate::scene::Transform> {
189 let mut translation = None;
190 let mut rotation = None;
191 let mut scaling = None;
192
193 for child in &node.children {
194 if child.name == "Properties70" {
195 for prop in &child.children {
196 if let Some(crate::fbx_reader::FbxProperty::String(name)) =
198 prop.properties.first()
199 {
200 if name.contains("Lcl Translation") {
201 for p in &prop.properties {
203 if let crate::fbx_reader::FbxProperty::F64Array(arr) = p {
204 if arr.len() >= 3 {
205 translation =
206 Some([arr[0] as f32, arr[1] as f32, arr[2] as f32]);
207 }
208 }
209 }
210 }
211 if name.contains("Lcl Rotation") {
212 for p in &prop.properties {
213 if let crate::fbx_reader::FbxProperty::F64Array(arr) = p {
214 if arr.len() >= 3 {
215 rotation =
216 Some([arr[0] as f32, arr[1] as f32, arr[2] as f32]);
217 }
218 }
219 }
220 }
221 if name.contains("Lcl Scaling") {
222 for p in &prop.properties {
223 if let crate::fbx_reader::FbxProperty::F64Array(arr) = p {
224 if arr.len() >= 3 {
225 scaling =
226 Some([arr[0] as f32, arr[1] as f32, arr[2] as f32]);
227 }
228 }
229 }
230 }
231 }
232 }
233 }
234 }
235
236 if translation.is_none() && rotation.is_none() && scaling.is_none() {
237 return None;
238 }
239
240 let t = translation.unwrap_or([0.0, 0.0, 0.0]);
242 let r_deg = rotation.unwrap_or([0.0, 0.0, 0.0]);
243 let s = scaling.unwrap_or([1.0, 1.0, 1.0]);
244
245 let rx = r_deg[0].to_radians();
246 let ry = r_deg[1].to_radians();
247 let rz = r_deg[2].to_radians();
248
249 let (sx, cx) = rx.sin_cos();
250 let (sy, cy) = ry.sin_cos();
251 let (sz, cz) = rz.sin_cos();
252
253 let m00 = cz * cy;
255 let m01 = cz * sy * sx - sz * cx;
256 let m02 = cz * sy * cx + sz * sx;
257
258 let m10 = sz * cy;
259 let m11 = sz * sy * sx + cz * cx;
260 let m12 = sz * sy * cx - cz * sx;
261
262 let m20 = -sy;
263 let m21 = cy * sx;
264 let m22 = cy * cx;
265
266 let mat = [
267 [m00 * s[0], m01 * s[1], m02 * s[2], 0.0],
268 [m10 * s[0], m11 * s[1], m12 * s[2], 0.0],
269 [m20 * s[0], m21 * s[1], m22 * s[2], 0.0],
270 [t[0], t[1], t[2], 1.0],
271 ];
272
273 Some(crate::scene::Transform { matrix: mat })
274 }
275
276 fn build_model_node(
278 id: i64,
279 model_map: &std::collections::HashMap<i64, &FbxNode>,
280 model_children: &std::collections::HashMap<i64, Vec<i64>>,
281 model_mesh_instances: &std::collections::HashMap<i64, Vec<crate::scene::MeshInstance>>,
282 ) -> crate::scene::SceneNode {
283 let node_src = model_map.get(&id).unwrap();
284 let mut node = crate::scene::SceneNode::new(Some(node_src.name.clone()));
285 node.transform = parse_transform(node_src);
286 if let Some(mesh_instances) = model_mesh_instances.get(&id) {
287 node.mesh_instances.extend(mesh_instances.clone());
288 }
289
290 if let Some(children) = model_children.get(&id) {
291 for &cid in children {
292 if model_map.contains_key(&cid) {
293 node.children.push(build_model_node(
294 cid,
295 model_map,
296 model_children,
297 model_mesh_instances,
298 ));
299 }
300 }
301 }
302 node
303 }
304
305 let mut model_mesh_instances: std::collections::HashMap<
307 i64,
308 Vec<crate::scene::MeshInstance>,
309 > = std::collections::HashMap::new();
310 for (geom_id, geom_node) in geometry_map.iter() {
311 if let Some(mesh) = self.geometry_to_mesh(geom_node)? {
312 for (child, parent) in connections.iter() {
314 if *child == *geom_id && model_map.contains_key(parent) {
315 let mesh_instance = crate::scene::MeshInstance {
316 name: Some(geom_node.name.clone()),
317 mesh: mesh.clone(),
318 transform: None,
319 };
320 model_mesh_instances
321 .entry(*parent)
322 .or_default()
323 .push(mesh_instance);
324 }
325 }
326 }
327 }
328
329 let mut root_nodes = Vec::new();
331 let top_level: Vec<i64> = model_map
333 .keys()
334 .cloned()
335 .filter(|id| {
336 !connections
337 .iter()
338 .any(|(child, parent)| child == id && model_map.contains_key(parent))
339 })
340 .collect();
341
342 for id in top_level {
343 root_nodes.push(build_model_node(
344 id,
345 &model_map,
346 &model_children,
347 &model_mesh_instances,
348 ));
349 }
350
351 Ok(crate::scene::Scene {
352 name: None,
353 root_nodes,
354 })
355 }
356}
357
358impl ReadFromBytes for FbxReader<Cursor<Vec<u8>>> {
359 fn from_bytes(bytes: &[u8]) -> io::Result<Self> {
360 Self::from_bytes(bytes.to_vec())
361 }
362}
363
364impl<R: Read + Seek> FbxReader<R> {
365 pub fn new(mut reader: R) -> io::Result<Self> {
367 let mut magic = [0u8; 21];
369 reader.read_exact(&mut magic)?;
370 if &magic != FBX_MAGIC {
371 return Err(io::Error::new(
372 io::ErrorKind::InvalidData,
373 "Not a valid binary FBX file",
374 ));
375 }
376
377 reader.seek(SeekFrom::Current(2))?;
379
380 let mut version_bytes = [0u8; 4];
382 reader.read_exact(&mut version_bytes)?;
383 let version = u32::from_le_bytes(version_bytes);
384
385 Ok(Self { reader, version })
386 }
387
388 pub fn version(&self) -> u32 {
390 self.version
391 }
392
393 fn is_64bit(&self) -> bool {
395 self.version >= 7500
396 }
397
398 fn read_node(&mut self) -> io::Result<Option<FbxNode>> {
400 let (end_offset, num_properties, _property_list_len, name_len) = if self.is_64bit() {
401 let mut buf = [0u8; 25];
402 self.reader.read_exact(&mut buf)?;
403 let end_offset = u64::from_le_bytes([
404 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
405 ]);
406 let num_properties = u64::from_le_bytes([
407 buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15],
408 ]);
409 let property_list_len = u64::from_le_bytes([
410 buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23],
411 ]);
412 let name_len = buf[24];
413 (
414 end_offset,
415 num_properties as u32,
416 property_list_len,
417 name_len,
418 )
419 } else {
420 let mut buf = [0u8; 13];
421 self.reader.read_exact(&mut buf)?;
422 let end_offset = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]) as u64;
423 let num_properties = u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]);
424 let _property_list_len = u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]) as u64;
425 let name_len = buf[12];
426 (end_offset, num_properties, _property_list_len, name_len)
427 };
428
429 if end_offset == 0 {
431 return Ok(None);
432 }
433
434 let mut name_bytes = vec![0u8; name_len as usize];
436 self.reader.read_exact(&mut name_bytes)?;
437 let name = String::from_utf8_lossy(&name_bytes).to_string();
438
439 let mut properties = Vec::with_capacity(num_properties as usize);
441 for _ in 0..num_properties {
442 properties.push(self.read_property()?);
443 }
444
445 let mut children = Vec::new();
447 let current_pos = self.reader.stream_position()?;
448 if current_pos < end_offset {
449 while let Some(child) = self.read_node()? {
450 children.push(child);
451 }
452 }
453
454 self.reader.seek(SeekFrom::Start(end_offset))?;
456
457 Ok(Some(FbxNode {
458 name,
459 properties,
460 children,
461 }))
462 }
463
464 fn read_property(&mut self) -> io::Result<FbxProperty> {
466 let mut type_code = [0u8; 1];
467 self.reader.read_exact(&mut type_code)?;
468
469 match type_code[0] {
470 b'C' => {
471 let mut v = [0u8; 1];
472 self.reader.read_exact(&mut v)?;
473 Ok(FbxProperty::Bool(v[0] != 0))
474 }
475 b'Y' => {
476 let mut v = [0u8; 2];
477 self.reader.read_exact(&mut v)?;
478 Ok(FbxProperty::I16(i16::from_le_bytes(v)))
479 }
480 b'I' => {
481 let mut v = [0u8; 4];
482 self.reader.read_exact(&mut v)?;
483 Ok(FbxProperty::I32(i32::from_le_bytes(v)))
484 }
485 b'L' => {
486 let mut v = [0u8; 8];
487 self.reader.read_exact(&mut v)?;
488 Ok(FbxProperty::I64(i64::from_le_bytes(v)))
489 }
490 b'F' => {
491 let mut v = [0u8; 4];
492 self.reader.read_exact(&mut v)?;
493 Ok(FbxProperty::F32(f32::from_le_bytes(v)))
494 }
495 b'D' => {
496 let mut v = [0u8; 8];
497 self.reader.read_exact(&mut v)?;
498 Ok(FbxProperty::F64(f64::from_le_bytes(v)))
499 }
500 b'S' | b'R' => {
501 let mut len_bytes = [0u8; 4];
502 self.reader.read_exact(&mut len_bytes)?;
503 let len = u32::from_le_bytes(len_bytes) as usize;
504 let mut data = vec![0u8; len];
505 self.reader.read_exact(&mut data)?;
506 if type_code[0] == b'S' {
507 Ok(FbxProperty::String(
508 String::from_utf8_lossy(&data).to_string(),
509 ))
510 } else {
511 Ok(FbxProperty::Raw(data))
512 }
513 }
514 b'b' => Ok(FbxProperty::BoolArray(self.read_array_bool()?)),
515 b'i' => Ok(FbxProperty::I32Array(self.read_array_i32()?)),
516 b'l' => Ok(FbxProperty::I64Array(self.read_array_i64()?)),
517 b'f' => Ok(FbxProperty::F32Array(self.read_array_f32()?)),
518 b'd' => Ok(FbxProperty::F64Array(self.read_array_f64()?)),
519 _ => Err(io::Error::new(
520 io::ErrorKind::InvalidData,
521 format!("Unknown property type: {}", type_code[0] as char),
522 )),
523 }
524 }
525
526 fn read_array_header(&mut self) -> io::Result<(u32, u32, u32)> {
528 let mut buf = [0u8; 12];
529 self.reader.read_exact(&mut buf)?;
530 let array_len = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
531 let encoding = u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]);
532 let compressed_len = u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]);
533 Ok((array_len, encoding, compressed_len))
534 }
535
536 fn read_array_data(
538 &mut self,
539 encoding: u32,
540 compressed_len: u32,
541 uncompressed_size: usize,
542 ) -> io::Result<Vec<u8>> {
543 if encoding == 0 {
544 let mut data = vec![0u8; uncompressed_size];
545 self.reader.read_exact(&mut data)?;
546 Ok(data)
547 } else if encoding == 1 {
548 let mut compressed = vec![0u8; compressed_len as usize];
550 self.reader.read_exact(&mut compressed)?;
551
552 #[cfg(feature = "compression")]
553 {
554 use miniz_oxide::inflate::decompress_to_vec_zlib;
555 decompress_to_vec_zlib(&compressed).map_err(|e| {
556 io::Error::new(
557 io::ErrorKind::InvalidData,
558 format!("Decompression error: {:?}", e),
559 )
560 })
561 }
562
563 #[cfg(not(feature = "compression"))]
564 {
565 Err(io::Error::new(
566 io::ErrorKind::Unsupported,
567 "FBX array compression not supported (enable 'compression' feature)",
568 ))
569 }
570 } else {
571 Err(io::Error::new(
572 io::ErrorKind::InvalidData,
573 format!("Unknown array encoding: {}", encoding),
574 ))
575 }
576 }
577
578 fn read_array_bool(&mut self) -> io::Result<Vec<bool>> {
579 let (len, encoding, compressed_len) = self.read_array_header()?;
580 let data = self.read_array_data(encoding, compressed_len, len as usize)?;
581 Ok(data.into_iter().map(|b| b != 0).collect())
582 }
583
584 fn read_array_i32(&mut self) -> io::Result<Vec<i32>> {
585 let (len, encoding, compressed_len) = self.read_array_header()?;
586 let data = self.read_array_data(encoding, compressed_len, len as usize * 4)?;
587 Ok(data
588 .chunks_exact(4)
589 .map(|c| i32::from_le_bytes([c[0], c[1], c[2], c[3]]))
590 .collect())
591 }
592
593 fn read_array_i64(&mut self) -> io::Result<Vec<i64>> {
594 let (len, encoding, compressed_len) = self.read_array_header()?;
595 let data = self.read_array_data(encoding, compressed_len, len as usize * 8)?;
596 Ok(data
597 .chunks_exact(8)
598 .map(|c| i64::from_le_bytes([c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]]))
599 .collect())
600 }
601
602 fn read_array_f32(&mut self) -> io::Result<Vec<f32>> {
603 let (len, encoding, compressed_len) = self.read_array_header()?;
604 let data = self.read_array_data(encoding, compressed_len, len as usize * 4)?;
605 Ok(data
606 .chunks_exact(4)
607 .map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
608 .collect())
609 }
610
611 fn read_array_f64(&mut self) -> io::Result<Vec<f64>> {
612 let (len, encoding, compressed_len) = self.read_array_header()?;
613 let data = self.read_array_data(encoding, compressed_len, len as usize * 8)?;
614 Ok(data
615 .chunks_exact(8)
616 .map(|c| f64::from_le_bytes([c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]]))
617 .collect())
618 }
619
620 pub fn read_nodes(&mut self) -> io::Result<Vec<FbxNode>> {
622 self.reader.seek(SeekFrom::Start(27))?;
624
625 let mut nodes = Vec::new();
626 while let Some(node) = self.read_node()? {
627 nodes.push(node);
628 }
629 Ok(nodes)
630 }
631
632 pub fn read_meshes(&mut self) -> io::Result<Vec<Mesh>> {
634 let nodes = self.read_nodes()?;
635 let mut meshes = Vec::new();
636
637 for node in &nodes {
639 if node.name == "Objects" {
640 for child in &node.children {
641 if child.name == "Geometry" {
642 if let Some(mesh) = self.geometry_to_mesh(child)? {
643 meshes.push(mesh);
644 }
645 }
646 }
647 }
648 }
649
650 Ok(meshes)
651 }
652
653 fn geometry_to_mesh(&self, geometry: &FbxNode) -> io::Result<Option<Mesh>> {
655 let mut vertices: Option<Vec<f64>> = None;
656 let mut polygon_indices: Option<Vec<i32>> = None;
657
658 for child in &geometry.children {
659 match child.name.as_str() {
660 "Vertices" => {
661 if let Some(FbxProperty::F64Array(arr)) = child.properties.first() {
662 vertices = Some(arr.clone());
663 }
664 }
665 "PolygonVertexIndex" => {
666 if let Some(FbxProperty::I32Array(arr)) = child.properties.first() {
667 polygon_indices = Some(arr.clone());
668 }
669 }
670 _ => {}
671 }
672 }
673
674 let vertices = match vertices {
675 Some(v) => v,
676 None => return Ok(None),
677 };
678 let polygon_indices = match polygon_indices {
679 Some(p) => p,
680 None => return Ok(None),
681 };
682
683 let mut mesh = Mesh::new();
685
686 let num_vertices = vertices.len() / 3;
688 let mut pos_att = PointAttribute::new();
689 pos_att.init(
690 GeometryAttributeType::Position,
691 3,
692 DataType::Float32,
693 false,
694 num_vertices,
695 );
696 let buffer = pos_att.buffer_mut();
697 for i in 0..num_vertices {
698 let x = vertices[i * 3] as f32;
699 let y = vertices[i * 3 + 1] as f32;
700 let z = vertices[i * 3 + 2] as f32;
701 let bytes: Vec<u8> = [x, y, z].iter().flat_map(|v| v.to_le_bytes()).collect();
702 buffer.write(i * 12, &bytes);
703 }
704 mesh.add_attribute(pos_att);
705
706 let mut faces: Vec<[u32; 3]> = Vec::new();
708 let mut current_polygon: Vec<i32> = Vec::new();
709
710 for &idx in &polygon_indices {
711 if idx < 0 {
712 let actual_idx = !idx;
714 current_polygon.push(actual_idx);
715
716 if current_polygon.len() >= 3 {
718 let v0 = current_polygon[0] as u32;
719 for i in 1..current_polygon.len() - 1 {
720 let v1 = current_polygon[i] as u32;
721 let v2 = current_polygon[i + 1] as u32;
722 faces.push([v0, v1, v2]);
723 }
724 }
725 current_polygon.clear();
726 } else {
727 current_polygon.push(idx);
728 }
729 }
730
731 mesh.set_num_faces(faces.len());
733 for (i, face) in faces.iter().enumerate() {
734 mesh.set_face(
735 FaceIndex(i as u32),
736 [
737 PointIndex(face[0]),
738 PointIndex(face[1]),
739 PointIndex(face[2]),
740 ],
741 );
742 }
743
744 mesh.deduplicate_point_ids();
747
748 Ok(Some(mesh))
749 }
750}
751
752#[cfg(test)]
753mod tests {
754 use super::*;
755 use std::io::Cursor;
756
757 #[test]
758 fn test_fbx_magic() {
759 let mut data = Vec::new();
760 data.extend_from_slice(FBX_MAGIC);
761 data.extend_from_slice(&[0x1A, 0x00]); data.extend_from_slice(&7300u32.to_le_bytes()); data.extend_from_slice(&[0u8; 13]);
765
766 let cursor = Cursor::new(data);
767 let reader = FbxReader::new(cursor).unwrap();
768 assert_eq!(reader.version(), 7300);
769 }
770
771 #[test]
772 fn test_invalid_magic() {
773 let data = b"Not an FBX file at all";
774 let cursor = Cursor::new(data.to_vec());
775 assert!(FbxReader::new(cursor).is_err());
776 }
777}