use crate::error::FbxError;
use crate::material::process_materials;
use crate::mesh::process_meshes;
use crate::node::{process_nodes, process_skins};
use crate::scene::build_scene;
use crate::types::{Fbx, FbxAxisSystem, FbxMeta, Handedness};
use bevy::asset::{io::Reader, AssetLoader, LoadContext, RenderAssetUsages};
use bevy::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize)]
pub struct FbxLoaderSettings {
pub load_meshes: RenderAssetUsages,
pub load_materials: RenderAssetUsages,
pub load_cameras: bool,
pub load_lights: bool,
pub include_source: bool,
pub convert_coordinates: bool,
}
impl Default for FbxLoaderSettings {
fn default() -> Self {
Self {
load_meshes: RenderAssetUsages::default(),
load_materials: RenderAssetUsages::default(),
load_cameras: true,
load_lights: true,
include_source: false,
convert_coordinates: false,
}
}
}
#[derive(Default, bevy::reflect::TypePath)]
pub struct FbxLoader;
impl AssetLoader for FbxLoader {
type Asset = Fbx;
type Settings = FbxLoaderSettings;
type Error = FbxError;
async fn load(
&self,
reader: &mut dyn Reader,
settings: &Self::Settings,
load_context: &mut LoadContext<'_>,
) -> Result<Fbx, FbxError> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
if bytes.is_empty() {
return Err(FbxError::InvalidData("Empty FBX file".to_string()));
}
if bytes.len() < 32 {
return Err(FbxError::InvalidData("FBX file too small".to_string()));
}
let root = ufbx::load_memory(
&bytes,
ufbx::LoadOpts {
target_unit_meters: 1.0,
target_axes: ufbx::CoordinateAxes::right_handed_y_up(),
..Default::default()
},
)
.map_err(|e| FbxError::UfbxError(format!("{:?}", e)))?;
let scene: &ufbx::Scene = &*root;
let (meshes, named_meshes, mesh_transforms, mesh_material_info) =
process_meshes(scene, settings, load_context)?;
let (materials, named_materials) = if !settings.load_materials.is_empty() {
process_materials(scene, settings, load_context)?
} else {
(Vec::new(), HashMap::new())
};
let (nodes, named_nodes, node_map) = process_nodes(scene, &meshes, load_context)?;
let (skins, named_skins) = process_skins(scene, &node_map, load_context)?;
let scene_handle = build_scene(
scene,
&meshes,
&materials,
&named_materials,
&mesh_transforms,
&mesh_material_info,
settings,
load_context,
)?;
let metadata = FbxMeta::default();
Ok(Fbx {
scenes: vec![scene_handle.clone()],
named_scenes: HashMap::new(),
meshes,
named_meshes,
materials,
named_materials,
nodes,
named_nodes,
skins,
named_skins,
default_scene: Some(scene_handle),
axis_system: FbxAxisSystem {
up: Vec3::Y,
front: Vec3::Z,
handedness: Handedness::Right,
},
unit_scale: 1.0,
metadata,
})
}
fn extensions(&self) -> &[&str] {
&["fbx"]
}
}