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