├── .github
├── FUNDING.yml
└── workflows
│ └── ci.yml
├── src
├── utils
│ ├── mod.rs
│ ├── triangulate.rs
│ └── fbx_extend.rs
├── lib.rs
├── data.rs
├── fbx_transform.rs
├── material_loader.rs
└── loader.rs
├── assets
└── cube.fbx
├── .gitignore
├── scripts
├── test_wasm.html
└── test_wasm.sh
├── .editorconfig
├── LICENSE-MIT
├── Cargo.toml
├── dev_docs
├── fbx_tree_printer.rs
└── custom_material_design.md
├── examples
├── cube.rs
└── scene_viewer.rs
├── CONTRIBUTING.md
├── README.md
└── LICENSE-APACHE
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [nicopap, HeavyRain266]
2 |
--------------------------------------------------------------------------------
/src/utils/mod.rs:
--------------------------------------------------------------------------------
1 | pub(crate) mod fbx_extend;
2 | pub(crate) mod triangulate;
3 |
--------------------------------------------------------------------------------
/assets/cube.fbx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicopap/bevy_mod_fbx/HEAD/assets/cube.fbx
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Cargo
2 | /target
3 | Cargo.lock
4 |
5 | # Nix/NixOS
6 | .envrc # direnv
7 | shell.nix
8 | flake.nix
9 | flake.lock
10 |
11 | # VScode
12 | .vscode
13 |
14 | # Jetbrains
15 | .idea
16 | .fleet
17 |
--------------------------------------------------------------------------------
/scripts/test_wasm.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_size = 4
6 | indent_style = tab
7 | end_of_line = crlf
8 | max_line_length = 80
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.rs]
13 | indent_size = 4
14 | indent_style = space
15 |
16 | [*.toml]
17 | indent_size = 2
18 | indent_style = space
19 |
20 | [*.yml]
21 | indent_size = 2
22 | indent_style = space
23 |
24 | [*.md]
25 | indent_style = space
26 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "bevy_mod_fbx"
3 | authors = ["Nicola Papale", "HeavyRain266"]
4 | description = "Autodesk Filmbox (*.fbx) loader for Bevy Engine"
5 | license = "MIT OR Apache-2.0"
6 | readme = "README.md"
7 | keywords = ["bevy", "bevy_plugin", "fbx_loader"]
8 | categories = ["game-development"]
9 | repository = "https://github.com/nicopap/bevy_mod_fbx"
10 | exclude = ["assets/**/*", "scripts/**/*", ".github/**/*"]
11 | version = "0.4.0"
12 | edition = "2021"
13 |
14 | [features]
15 | profile = []
16 | maya_3dsmax_pbr = []
17 |
18 | [dependencies]
19 | rgb = "0.8"
20 | anyhow = "1.0.58"
21 | glam = { version = "0.23", features = ["mint"] }
22 | mint = "0.5"
23 | # fbxcel-dom = { version = "0.0.9", path = "../fbxcel-dom" }
24 | fbxcel-dom = "0.0.9"
25 |
26 | [dependencies.bevy]
27 | version = "0.10"
28 | default-features = false
29 | features = [
30 | "bevy_pbr",
31 | "bevy_asset",
32 | "bevy_render",
33 | "bevy_scene",
34 | ]
35 |
36 | [dev-dependencies.bevy]
37 | version = "0.10"
38 | default-features = false
39 | features = [
40 | "x11", #"wayland",
41 |
42 | "tga", "dds",
43 | "bevy_pbr",
44 | "bevy_render",
45 | "bevy_winit",
46 | "bevy_scene",
47 | "filesystem_watcher",
48 | "bevy_core_pipeline"
49 | ]
50 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::{AddAsset, App, Plugin, Resource};
2 |
3 | pub use data::{FbxMesh, FbxScene};
4 | pub use loader::FbxLoader;
5 |
6 | pub(crate) mod data;
7 | pub(crate) mod fbx_transform;
8 | pub(crate) mod loader;
9 | pub mod material_loader;
10 | pub(crate) mod utils;
11 |
12 | use material_loader::MaterialLoader;
13 |
14 | /// Adds support for FBX file loading to the app.
15 | #[derive(Default)]
16 | pub struct FbxPlugin;
17 |
18 | /// Resource to control which material loaders the `FbxLoader`
19 | /// uses.
20 | ///
21 | /// See [`MaterialLoader`] documentation for more details.
22 | ///
23 | /// You can define your own by inserting this as a resource
24 | /// **before** adding the `FbxPlugin` to the app.
25 | /// If you define your own, make sure to add back the default
26 | /// fallback methods if you need them!
27 | ///
28 | /// The default loaders are defined by [`material_loader::default_loader_order`].
29 | #[derive(Clone, Resource)]
30 | pub struct FbxMaterialLoaders(pub Vec);
31 | impl Default for FbxMaterialLoaders {
32 | fn default() -> Self {
33 | Self(material_loader::default_loader_order().into())
34 | }
35 | }
36 |
37 | impl Plugin for FbxPlugin {
38 | fn build(&self, app: &mut App) {
39 | app.init_asset_loader::()
40 | .add_asset::()
41 | .add_asset::();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/dev_docs/fbx_tree_printer.rs:
--------------------------------------------------------------------------------
1 | fn print_children(depth: usize, children: Children) {
2 | let show_depth = depth * 3;
3 | for child in children {
4 | print!("{space:>depth$}", space = "", depth = show_depth);
5 | if child.name().len() > 1 {
6 | print!("{name} ", name = child.name(),);
7 | }
8 | let attr_display = |att: &AttributeValue| match att {
9 | AttributeValue::Bool(v) => v.to_string(),
10 | AttributeValue::I16(v) => v.to_string(),
11 | AttributeValue::I32(v) => v.to_string(),
12 | AttributeValue::I64(v) => v.to_string(),
13 | AttributeValue::F32(v) => v.to_string(),
14 | AttributeValue::F64(v) => v.to_string(),
15 | AttributeValue::ArrBool(_) => "[bool]".to_owned(),
16 | AttributeValue::ArrI32(_) => "[i32]".to_owned(),
17 | AttributeValue::ArrI64(_) => "[i64]".to_owned(),
18 | AttributeValue::ArrF32(_) => "[f32]".to_owned(),
19 | AttributeValue::ArrF64(_) => "[f64]".to_owned(),
20 | AttributeValue::String(s) => s.clone(),
21 | AttributeValue::Binary(_) => "[u8]".to_owned(),
22 | };
23 | print!("[");
24 | for (i, attr) in child.attributes().iter().map(attr_display).enumerate() {
25 | // if matches!(i, 1 | 2 | 3) {
26 | // continue;
27 | // }
28 | if i == 0 {
29 | print!("{attr}: ");
30 | } else {
31 | print!("{attr}, ");
32 | }
33 | }
34 | println!("]");
35 | if child.children().next().is_some() {
36 | println!("{:>depth$}{{", "", depth = show_depth);
37 | }
38 | print_children(depth + 1, child.children());
39 | if child.children().next().is_some() {
40 | println!("{:>depth$}}}", "", depth = show_depth);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/data.rs:
--------------------------------------------------------------------------------
1 | use bevy::{
2 | prelude::{Handle, Image, Mesh, StandardMaterial, Transform},
3 | reflect::TypeUuid,
4 | utils::HashMap,
5 | };
6 | use fbxcel_dom::v7400::object::ObjectId;
7 |
8 | #[derive(Debug, Clone, TypeUuid)]
9 | #[uuid = "966d55c0-515b-4141-97a1-de30ac8ee44c"]
10 | pub struct FbxMesh {
11 | pub name: Option,
12 | pub bevy_mesh_handles: Vec>,
13 | pub materials: Vec>,
14 | }
15 |
16 | /// The data loaded from a FBX scene.
17 | ///
18 | /// Note that the loader spawns a [`Scene`], with all the
19 | /// FBX nodes spawned as entities (with their corresponding [`Name`] set)
20 | /// in the ECS,
21 | /// and you should absolutely use the ECS entities over
22 | /// manipulating this data structure.
23 | /// It is provided publicly, because it might be a good store for strong handles.
24 | ///
25 | /// [`Scene`]: bevy::scene::Scene
26 | /// [`Name`]: bevy::core::Name
27 | #[derive(Default, Debug, Clone, TypeUuid)]
28 | #[uuid = "e87d49b6-8d6a-43c7-bb33-5315db8516eb"]
29 | pub struct FbxScene {
30 | pub name: Option,
31 | pub bevy_meshes: HashMap, String>,
32 | pub materials: HashMap>,
33 | pub textures: HashMap>,
34 | pub meshes: HashMap>,
35 | pub hierarchy: HashMap,
36 | pub roots: Vec,
37 | }
38 |
39 | /// An FBX object in the scene tree.
40 | ///
41 | /// This serves as a node in the transform hierarchy.
42 | #[derive(Default, Debug, Clone)]
43 | pub struct FbxObject {
44 | pub name: Option,
45 | pub transform: Transform,
46 | /// The children of this node.
47 | ///
48 | /// # Notes
49 | /// Not all [`ObjectId`] declared as child of an `FbxObject`
50 | /// are relevant to Bevy.
51 | /// Meaning that you won't find the `ObjectId` in `hierarchy` or `meshes`
52 | /// `HashMap`s of the [`FbxScene`] structure.
53 | pub children: Vec,
54 | }
55 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Continous Integration
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 |
9 | env:
10 | CARGO_TERM_COLORS: always
11 |
12 | jobs:
13 | clippy_check:
14 | name: Clippy
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Checkout sources
18 | uses: actions/checkout@v3
19 | - name: Cache
20 | uses: actions/cache@v2
21 | with:
22 | path: |
23 | ~/.cargo/bin/
24 | ~/.cargo/registry/index/
25 | ~/.cargo/registry/cache/
26 | ~/.cargo/git/db/
27 | target/
28 | key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('**/Cargo.toml') }}
29 | - name: Install stable toolchain
30 | uses: actions-rs/toolchain@v1
31 | with:
32 | toolchain: stable
33 | profile: minimal
34 | components: clippy
35 | override: true
36 | - name: Install Dependencies
37 | run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev
38 | - name: Run clippy
39 | uses: actions-rs/clippy-check@v1
40 | with:
41 | token: ${{ secrets.GITHUB_TOKEN }}
42 | args: --examples -- -D warnings
43 | - name: Run clippy for optional features
44 | uses: actions-rs/clippy-check@v1
45 | with:
46 | token: ${{ secrets.GITHUB_TOKEN }}
47 | args: --examples --features maya_3dsmax_pbr -- -D warnings
48 |
49 | format:
50 | name: Format
51 | runs-on: ubuntu-latest
52 | steps:
53 | - name: Checkout sources
54 | uses: actions/checkout@v2
55 | - name: Install stable toolchain
56 | uses: actions-rs/toolchain@v1
57 | with:
58 | toolchain: stable
59 | profile: minimal
60 | components: rustfmt
61 | override: true
62 | - name: Run cargo fmt
63 | uses: actions-rs/cargo@v1
64 | with:
65 | command: fmt
66 | args: --all -- --check
67 |
--------------------------------------------------------------------------------
/examples/cube.rs:
--------------------------------------------------------------------------------
1 | use bevy::{
2 | log::{Level, LogPlugin},
3 | prelude::*,
4 | render::camera::ScalingMode,
5 | window::{close_on_esc, WindowResolution},
6 | };
7 | use bevy_mod_fbx::FbxPlugin;
8 |
9 | #[derive(Component)]
10 | pub struct Spin;
11 |
12 | fn main() {
13 | let mut app = App::new();
14 |
15 | app.add_plugins(
16 | DefaultPlugins
17 | .set(LogPlugin {
18 | level: Level::INFO,
19 | filter: "bevy_mod_fbx=trace,wgpu=warn".to_owned(),
20 | })
21 | .set(WindowPlugin {
22 | primary_window: Some(Window {
23 | title: "Spinning Cube".into(),
24 | resolution: WindowResolution::new(756., 574.),
25 | ..default()
26 | }),
27 | ..default()
28 | }),
29 | )
30 | .add_plugin(FbxPlugin)
31 | .add_startup_system(setup)
32 | .add_system(spin_cube)
33 | .add_system(close_on_esc);
34 |
35 | app.run();
36 | }
37 |
38 | fn spin_cube(time: Res