Skip to content

Commit

Permalink
Load scene data from FBX tree (#27)
Browse files Browse the repository at this point in the history
Read the FBX transform tree and assign entities for each node in the hierarchy,
computing and setting the bevy `Transform` according to FBX's completely
insane transform propagation algorithm.

Also a few unrelated things (I'm sorry)

This is also a first step toward loading skeleton rigs.

- Correct the way the scene's `UnitScaleFactor` is applied: instead of dividing
  the vertices position by it, we just add the scale to the root node of the scene,
  this also requires dividing by default by 100.0 the scale of all FBX files.
- Add a tool to print the FBX file content in the `dev_docs` directory
- Add a "Contributing" section to the README
- Fixes #26
- The Bistro scene's normals are broken, but this is a bevy bug: bevyengine/bevy#5514
  • Loading branch information
nicopap authored Aug 4, 2022
1 parent aef75eb commit d00b15f
Show file tree
Hide file tree
Showing 12 changed files with 731 additions and 109 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ maya_3dsmax_pbr = []
rgb = "0.8.33"
anyhow = "1.0.58"
glam = { version = "0.21", features = ["mint"] }
mint = "0.5"
# fbxcel-dom = { version = "0.0.9", path = "../fbxcel-dom" }
fbxcel-dom = "0.0.9"

Expand All @@ -30,8 +31,8 @@ features = [
"bevy_scene",
]

# [dev-dependencies]
# bevy-inspector-egui = "0.11"
[dev-dependencies]
bevy-inspector-egui = "0.12"

[dev-dependencies.bevy]
version = "0.8.0"
Expand Down
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ This material loader do not work with every type of texture files,
the textures must be readable from CPU and have each component (color channel)
be exactly 8 bits (such as PNG).

### Version matrix

| bevy | latest supporting version |
|------|--------|
| 0.8 | 0.1.0-dev |

### Limitations

> **NOTE**: If you find more limitations, file an issue!
Expand All @@ -38,6 +44,7 @@ be exactly 8 bits (such as PNG).
- [X] Support arbitrary material loading.
- [X] Support Maya's PBR
- [X] Proper scaling based on FBX config scale ([#10](https://github.com/HeavyRain266/bevy_fbx/issues/10))
- [X] Load complex scenes with a transform tree hierarchy
- [ ] Proper handling of Coordinate system
- [ ] Support `bevy_animation` as optional feature ([#13](https://github.com/HeavyRain266/bevy_fbx/issues/13))
- [ ] Provide examples with usage of complex scenes ([#6](https://github.com/HeavyRain266/bevy_fbx/issues/6))
Expand All @@ -46,8 +53,8 @@ be exactly 8 bits (such as PNG).

### Examples

- cube: Load simple cube with point light
- scene_viwer: Load any FBX files from `/path/to/file.fbx`, defaults to `assets/cube.fbx`
- `cube`: Load simple cube with point light
- `scene_viewer`: Load any FBX files from `/path/to/file.fbx`, defaults to `assets/cube.fbx`

Run example:

Expand All @@ -59,6 +66,30 @@ cargo run --example <example_name>
cargu run --example <example_name> --release --features bevy/dynamic
```

### Contributing

FBX is a very widely used and flexible file format.
We currently expect most models **to not load properly**.
We cannot test how `bevy_fbx` handles the export formats of every software out there.
If your model loads properly, thank your lucky start,
if you encounter any issue please do the following:

- Try opening the model using the `scene_viewer` (run `cargo run --example scene_viewer --release -- /path/to/file.fbx`)
(if your file has textures, make sure to enable the correct file formats using `--features bevy/png bevy/jpg bevy/tga` etc.)
- If it fails, open an issue on our Github repo with a screenshot in the scene viewer
and a screenshot of how the model should look like
<https://github.com/HeavyRain266/bevy_fbx/issues/new/choose>
- Ideally provide a download link to your FBX model

#### Further troubleshooting tools

If you want to help us figure out how to load a particularly tricky model,
the following tools may be useful:

- <https://github.com/lo48576/fbx_objects_depviz>
- <https://github.com/lo48576/fbx-tree-view>
- Use the `profile` feature with those instructions: https://github.com/bevyengine/bevy/blob/main/docs/profiling.md

## License

bevy_fbx is licensed under either:
Expand Down
43 changes: 43 additions & 0 deletions dev_docs/fbx_tree_printer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
fn print_children(depth: usize, children: Children) {
let show_depth = depth * 3;
for child in children {
print!("{space:>depth$}", space = "", depth = show_depth);
if child.name().len() > 1 {
print!("{name} ", name = child.name(),);
}
let attr_display = |att: &AttributeValue| match att {
AttributeValue::Bool(v) => v.to_string(),
AttributeValue::I16(v) => v.to_string(),
AttributeValue::I32(v) => v.to_string(),
AttributeValue::I64(v) => v.to_string(),
AttributeValue::F32(v) => v.to_string(),
AttributeValue::F64(v) => v.to_string(),
AttributeValue::ArrBool(_) => "[bool]".to_owned(),
AttributeValue::ArrI32(_) => "[i32]".to_owned(),
AttributeValue::ArrI64(_) => "[i64]".to_owned(),
AttributeValue::ArrF32(_) => "[f32]".to_owned(),
AttributeValue::ArrF64(_) => "[f64]".to_owned(),
AttributeValue::String(s) => s.clone(),
AttributeValue::Binary(_) => "[u8]".to_owned(),
};
print!("[");
for (i, attr) in child.attributes().iter().map(attr_display).enumerate() {
// if matches!(i, 1 | 2 | 3) {
// continue;
// }
if i == 0 {
print!("{attr}: ");
} else {
print!("{attr}, ");
}
}
println!("]");
if child.children().next().is_some() {
println!("{:>depth$}{{", "", depth = show_depth);
}
print_children(depth + 1, child.children());
if child.children().next().is_some() {
println!("{:>depth$}}}", "", depth = show_depth);
}
}
}
42 changes: 20 additions & 22 deletions examples/cube.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
use bevy::{prelude::*, render::camera::ScalingMode, window::close_on_esc};
use bevy::{
log::{Level, LogSettings},
prelude::*,
render::camera::ScalingMode,
window::close_on_esc,
};
use bevy_fbx::FbxPlugin;
use bevy_inspector_egui::WorldInspectorPlugin;

fn main() {
let mut app = App::new();
Expand All @@ -10,13 +16,16 @@ fn main() {
height: 574.0,

..default()
});

app.add_plugins(DefaultPlugins);
app.add_plugin(FbxPlugin);

app.add_startup_system(setup);
app.add_system(close_on_esc);
})
.insert_resource(LogSettings {
level: Level::INFO,
filter: "bevy_fbx=trace,wgpu=warn".to_owned(),
})
.add_plugins(DefaultPlugins)
.add_plugin(WorldInspectorPlugin::new())
.add_plugin(FbxPlugin)
.add_startup_system(setup)
.add_system(close_on_esc);

app.run();
}
Expand All @@ -29,33 +38,22 @@ fn setup(mut cmd: Commands, asset_server: Res<AssetServer>) {
projection: OrthographicProjection {
scale: 3.0,
scaling_mode: ScalingMode::FixedVertical(2.0),

..default()
}
.into(),

transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),

..default()
});

// light
cmd.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(3.0, 8.0, 5.0),

..default()
});

// Cube
cmd.spawn_bundle(TransformBundle {
local: Transform::from_xyz(0.0, 0.0, 0.0),
global: GlobalTransform::identity(),
})
.with_children(|parent| {
parent.spawn_bundle(SceneBundle {
scene: cube,

..default()
});
cmd.spawn_bundle(SceneBundle {
scene: cube,
..default()
});
}
20 changes: 12 additions & 8 deletions examples/scene_viewer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// This file is heavily inspired by the Bevy scene_viewer.rs
// found in the repository's tools folder.
//! A simple FBX scene viewer made with Bevy.
//!
//! Just run `cargo run --release --example scene_viewer /path/to/model.fbx#Scene`,
Expand All @@ -15,7 +17,7 @@ use bevy::{
window::close_on_esc,
};
use bevy_fbx::FbxPlugin;
// use bevy_inspector_egui::WorldInspectorPlugin;
use bevy_inspector_egui::WorldInspectorPlugin;

use std::f32::consts::TAU;

Expand Down Expand Up @@ -47,8 +49,8 @@ Controls:
brightness: 1.0 / 5.0f32,
})
.insert_resource(LogSettings {
level: Level::INFO,
..Default::default()
level: Level::WARN,
filter: "bevy_fbx=info".to_owned(),
})
.insert_resource(AssetServerSettings {
asset_folder: std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string()),
Expand All @@ -59,7 +61,7 @@ Controls:
..default()
})
.add_plugins(DefaultPlugins)
// .add_plugin(WorldInspectorPlugin::new())
.add_plugin(WorldInspectorPlugin::new())
.add_plugin(FbxPlugin)
.add_startup_system(setup)
.add_system(update_lights)
Expand Down Expand Up @@ -110,10 +112,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
},
..default()
});
commands.spawn_bundle(SceneBundle {
scene: asset_server.load(&scene_path),
..Default::default()
});
commands
.spawn_bundle(SceneBundle {
scene: asset_server.load(&scene_path),
..default()
})
.insert(Name::new(scene_path));
}

const SCALE_STEP: f32 = 0.1;
Expand Down
44 changes: 37 additions & 7 deletions src/data.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,54 @@
use bevy::{
asset::Handle,
pbr::StandardMaterial,
prelude::{Handle, Image, Mesh, StandardMaterial, Transform},
reflect::TypeUuid,
render::{mesh::Mesh as BevyMesh, texture::Image},
utils::{HashMap, HashSet},
utils::HashMap,
};
use fbxcel_dom::v7400::object::ObjectId;

#[derive(Debug, Clone, TypeUuid)]
#[uuid = "966d55c0-515b-4141-97a1-de30ac8ee44c"]
pub struct FbxMesh {
pub name: Option<String>,
pub bevy_mesh_handles: Vec<Handle<BevyMesh>>,
pub bevy_mesh_handles: Vec<Handle<Mesh>>,
pub materials: Vec<Handle<StandardMaterial>>,
}

/// The data loaded from a FBX scene.
///
/// Note that the loader spawns a [`Scene`], with all the
/// FBX nodes spawned as entities (with their corresponding [`Name`] set)
/// in the ECS,
/// and you should absolutely use the ECS entities over
/// manipulating this data structure.
/// It is provided publicly, because it might be a good store for strong handles.
///
/// [`Scene`]: bevy::scene::Scene
/// [`Name`]: bevy::core::Name
#[derive(Default, Debug, Clone, TypeUuid)]
#[uuid = "e87d49b6-8d6a-43c7-bb33-5315db8516eb"]
pub struct FbxScene {
pub name: Option<String>,
pub bevy_meshes: HashMap<Handle<BevyMesh>, String>,
pub bevy_meshes: HashMap<Handle<Mesh>, String>,
pub materials: HashMap<String, Handle<StandardMaterial>>,
pub textures: HashMap<String, Handle<Image>>,
pub meshes: HashSet<Handle<FbxMesh>>,
pub meshes: HashMap<ObjectId, Handle<FbxMesh>>,
pub hierarchy: HashMap<ObjectId, FbxObject>,
pub roots: Vec<ObjectId>,
}

/// An FBX object in the scene tree.
///
/// This serves as a node in the transform hierarchy.
#[derive(Default, Debug, Clone)]
pub struct FbxObject {
pub name: Option<String>,
pub transform: Transform,
/// The children of this node.
///
/// # Notes
/// Not all [`ObjectId`] declared as child of an `FbxObject`
/// are relevant to Bevy.
/// Meaning that you won't find the `ObjectId` in `hierarchy` or `meshes`
/// `HashMap`s of the [`FbxScene`] structure.
pub children: Vec<ObjectId>,
}
Loading

0 comments on commit d00b15f

Please sign in to comment.