refactor(): restructured code to turn the process_gltf (core) part into a crate (#1)

* refactor(): restructured code to turn the process_gltf (core) part into a crate
 * changed process_gltf into a lib/crate basics
 * changed current demo setup into an example that is importing the new crate
 * updated imports in the crate side
 * updated dependencies
 * cleanups
 * added more clear information about preUpdate vs setup
 * improved README/ added use as crate examples
This commit is contained in:
Mark Moissette 2023-08-02 01:45:57 +02:00 committed by GitHub
parent 2eb6daece8
commit 1417d5f389
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 284 additions and 71 deletions

22
Cargo.lock generated
View File

@ -675,6 +675,17 @@ dependencies = [
"thiserror",
]
[[package]]
name = "bevy_gltf_components"
version = "0.2.0"
dependencies = [
"bevy",
"bevy_editor_pls",
"bevy_rapier3d",
"ron",
"serde",
]
[[package]]
name = "bevy_hierarchy"
version = "0.11.0"
@ -1208,17 +1219,6 @@ version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "blender-worklfow"
version = "0.1.0"
dependencies = [
"bevy",
"bevy_editor_pls",
"bevy_rapier3d",
"ron",
"serde",
]
[[package]]
name = "block"
version = "0.1.6"

View File

@ -1,17 +1,23 @@
[package]
name = "blender-worklfow"
version = "0.1.0"
name = "bevy_gltf_components"
version = "0.2.0"
edition = "2021"
license = "MIT OR Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bevy="0.11.0"
serde = "*"
ron="*"
[dev-dependencies]
bevy="0.11"
bevy_rapier3d = { version = "0.22.0", features = [ "serde-serialize", "debug-render-3d", "enhanced-determinism"] }
bevy_editor_pls = { git="https://github.com/jakobhellermann/bevy_editor_pls.git" }
[dependencies]
bevy = { version = "0.11", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
serde = "*"
ron="*"
[[example]]
name = "general"
path = "examples/general/main.rs"
#### --------------------Dev/ debug-------------------------------
# Enable high optimizations for dependencies (incl. Bevy), but not for our code:
[profile.dev.package."*"]

190
README.md
View File

@ -1,8 +1,190 @@
# Blender_bevy_components_worklflow
# bevy_gltf_components
![demo](./docs/blender_gltf_components.png)
A plugin & tools for adding components from gltf files in the [bevy](https://bevyengine.org/) game engine.
It enables minimalistic [Blender](https://www.blender.org/) (gltf) centric workflow for Bevy, ie defining entites & their components
inside Blender using Blender's objects **custom properties**.
Aka "Blender as editor for Bevy"
It also allows you to setup 'blueprints' in Blender by using collections (the recomended way to go most of the time), or directly on single use objects .
## Features
* Useful if you want to use Blender (or any editor allowing to export gltf with configurable gltf_extras) as your Editor
* define Bevy components as custom properties in Blender (RON, though an older JSON version is also available)
* no plugin or extra tools needed in Blender (but I provide a little Blender plugin to auto-export to gltf on save if you want !)
* define components in Blender Collections & override any of them in your collection instances if you want
* minimal setup & code, you can have something basic running fast
## Usage
1. Add the crate to your dependencies
```toml
[dependencies]
bevy_gltf_components = { git="https://github.com/kaosat-dev/Blender_bevy_components_worklflow.git" }
```
(not on crates.io yet !)
2. Import
```rust
use bevy_gltf_components::ComponentsFromGltfPlugin;
```
3. Add the plugin
```rust
.add_plugin(ComponentsFromGltfPlugin)
```
### Example (without bevy_asset_loader)
See [here](./examples/general/main.rs) for more details
```rust
use bevy::prelude::*;
use bevy_gltf_components::ComponentsFromGltfPlugin;
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
#[reflect(Component)]
struct TuppleTestF32(f32);
#[derive(Component, Reflect, Default, Debug, )]
#[reflect(Component)]
/// helper marker component, for demo only
pub struct LoadedMarker;
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)]
enum AppState {
#[default]
Loading,
Running,
}
fn main(){
App::new()
.add_plugins((
DefaultPlugins,
ComponentsFromGltfPlugin,
))
.add_state::<AppState>()
.add_systems(Startup, setup)
.add_systems(Update, (
spawn_level.run_if(in_state(AppState::Loading)),
))
.run();
}
#[derive(Resource)]
struct AssetLoadHelper(Handle<Scene>);
// we preload the data here, but this is for DEMO PURPOSES ONLY !! Please use https://github.com/NiklasEi/bevy_asset_loader or a similar logic to seperate loading / pre processing
// of assets from the spawning
// AssetLoadHelper is also just for the same purpose, you do not need it in a real scenario
// the states here are also for demo purposes only,
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
let tmp: Handle<Scene> = asset_server.load("models/level1.glb#Scene0");
commands.insert_resource(AssetLoadHelper(tmp));
}
fn spawn_level(
mut commands: Commands,
scene_markers: Query<&LoadedMarker>,
preloaded_scene: Res<AssetLoadHelper>,
mut asset_event_reader: EventReader<AssetEvent<Gltf>>,
mut next_state: ResMut<NextState<AppState>>,
){
if let Some(asset_event) = asset_event_reader.iter().next() {
match asset_event {
AssetEvent::Created { handle: _ } => {
info!("GLTF loaded");
if scene_markers.is_empty() {
info!("spawning scene");
commands.spawn(
(
SceneBundle {
scene: preloaded_scene.0.clone(),
..default()
},
LoadedMarker,
Name::new("Level1")
)
);
next_state.set(AppState::Running);
}
}
_ => ()
}
}
}
```
### Example (with bevy_asset_loader, recommended for ease of use)
- follow [bevy_asset_loader's](https://github.com/NiklasEi/bevy_asset_loader) docs to setup your gltf asset loading
- also add this plugin
```rust
use bevy::prelude::*;
use bevy_gltf_components::ComponentsFromGltfPlugin;
// replace this with your actual states !
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)]
enum AppState {
#[default]
Loading,
Running,
}
fn main(){
App::new()
.add_plugins((
DefaultPlugins,
ComponentsFromGltfPlugin,
))
// do the setup for bevy_asset_loader
.add_system(setup_game.in_schedule(OnEnter(AppState::Running)))
.run();
}
pub fn setup_game(
mut commands: Commands,
game_assets: Res<GameAssets>, // assets with your "world" or "level" gltf for example
) {
// more fast approach, load the game seperatly
commands.spawn((
SceneBundle {
scene: game_assets.world.clone(),
..default()
},
bevy::prelude::Name::from("world"),
));
}
```
# Workflow with blender
This example, is actually closer to a boilerplate + tooling showcases how to use a minimalistic [Blender](https://www.blender.org/) (gltf) centric workflow for [Bevy](https://bevyengine.org/), ie defining entites & their components
inside Blender using Blender's objects **custom properties**.
@ -12,7 +194,7 @@ It also allows you to setup 'blueprints' in Blender by using collections (the re
## Features
* Useful if you want to use Blender as your Editor
* Useful if you want to use Blender (or any editor allowing to export gltf with configurable gltf_extras) as your Editor
* define Bevy components as custom properties in Blender (RON, though an older JSON version is also available)
* no plugin or extra tools needed in Blender (but I provide a little Blender plugin to auto-export to gltf on save if you want !)
* define components in Blender Collections & override any of them in your collection instances if you want
@ -22,6 +204,10 @@ It also allows you to setup 'blueprints' in Blender by using collections (the re
There is a [video tutorial/explanation](https://youtu.be/-lcScjQCA3c) if you want, or you can skip to the text version ahead
important : the plugin for processing gltf files runs in *preUpdate* , so you cannot use the components directly if you spawn your scene from gltf in *setup* (the additional components will not show up)
Please see the included example or use bevy_asset_loader for a reliable workflow
## Workflow
The workflow goes as follows (once you got your Bevy code setup)

View File

@ -1,6 +1,3 @@
pub mod process_gltf;
pub use process_gltf::*;
pub mod camera;
pub use camera::*;
@ -19,7 +16,6 @@ impl Plugin for CorePlugin {
fn build(&self, app: &mut App) {
app
.add_plugins((
ProcessGltfPlugin,
LightingPlugin,
CameraPlugin,
PhysicsPlugin

View File

@ -2,6 +2,7 @@ use std::time::Duration;
use bevy::{prelude::*, asset::ChangeWatcher, gltf::Gltf};
use bevy_editor_pls::prelude::*;
use bevy_rapier3d::prelude::*;
use bevy_gltf_components::ComponentsFromGltfPlugin;
mod core;
use crate::core::*;
@ -45,6 +46,7 @@ fn main(){
RapierPhysicsPlugin::<NoUserData>::default(),
RapierDebugRenderPlugin::default(),
// our custom plugins
ComponentsFromGltfPlugin,
CorePlugin, // reusable plugins
DemoPlugin, // specific to our game
ComponentsTestPlugin // Showcases different type of components /structs
@ -61,10 +63,10 @@ fn main(){
#[derive(Resource)]
struct AssetLoadHack(Handle<Scene>);
struct AssetLoadHelper(Handle<Scene>);
// we preload the data here, but this is for DEMO PURPOSES ONLY !! Please use https://github.com/NiklasEi/bevy_asset_loader or a similar logic to seperate loading / pre processing
// of assets from the spawning
// AssetLoadHack is also just for the same purpose, you do not need it in a real scenario
// AssetLoadHelper is also just for the same purpose, you do not need it in a real scenario
// the states here are also for demo purposes only,
fn setup(
mut commands: Commands,
@ -72,13 +74,13 @@ fn setup(
) {
let tmp: Handle<Scene> = asset_server.load("models/level1.glb#Scene0");
commands.insert_resource(AssetLoadHack(tmp));
commands.insert_resource(AssetLoadHelper(tmp));
}
fn spawn_level(
mut commands: Commands,
scene_markers: Query<&LoadedMarker>,
preloaded_scene: Res<AssetLoadHack>,
preloaded_scene: Res<AssetLoadHelper>,
mut asset_event_reader: EventReader<AssetEvent<Gltf>>,
mut next_state: ResMut<NextState<AppState>>,
@ -106,7 +108,4 @@ fn spawn_level(
_ => ()
}
}
}

View File

@ -1,34 +0,0 @@
pub mod utils;
pub use utils::*;
pub mod gltf_to_components;
pub use gltf_to_components::*;
pub mod process_gltfs;
pub use process_gltfs::*;
// pub mod models_replace_proxies;
// pub use models_replace_proxies::*;
use bevy::prelude::*;
// use crate::state::{AppState};
pub struct ProcessGltfPlugin;
impl Plugin for ProcessGltfPlugin {
fn build(&self, app: &mut App) {
app
.insert_resource(GltfLoadingTracker::new())
.add_systems(PreUpdate, (
track_new_gltf,
process_loaded_scenes,
))
// .add_systems((models_replace_proxies,).in_set(OnUpdate(AppState::GameRunning)))
// compute the aabbs of a whole hierarchy
//.add_systems((compute_compound_aabb,).in_set(OnUpdate(AppState::GameRunning)))
;
}
}

View File

@ -1,12 +1,15 @@
use std::collections::HashMap;
use core::ops::Deref;
use ron::Value;
use serde::de::DeserializeSeed;
use bevy::prelude::*;
use bevy::reflect::serde::{UntypedReflectDeserializer, ReflectSerializer};
use bevy::reflect::{TypeRegistryInternal, TypeInfo};
use bevy::prelude::{ResMut, Assets, info, debug, Name, Parent, warn};
use bevy::ecs::{entity::Entity, reflect::ReflectComponent};
use bevy::scene::Scene;
use bevy::utils::HashMap;
use bevy::reflect::serde::UntypedReflectDeserializer;
use bevy::reflect::{TypeRegistryInternal, TypeInfo, Reflect};
use bevy::gltf::{Gltf, GltfExtras};
use super::capitalize_first_letter;

57
src/lib.rs Normal file
View File

@ -0,0 +1,57 @@
pub mod utils;
pub use utils::*;
pub mod gltf_to_components;
pub use gltf_to_components::*;
pub mod process_gltfs;
pub use process_gltfs::*;
use bevy::prelude::{
App,Plugin, PreUpdate
};
/// A Bevy plugin for extracting components from gltf files and automatically adding them to the relevant entities
/// It will automatically run every time you load a gltf file
/// Add this plugin to your Bevy app to get access to this feature
/// ```
/// # use bevy::prelude::*;
/// # use bevy_gltf_components::ComponentsFromGltfPlugin::*;
///
///
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_plugin(GltfComponentsPlugin)
/// .add_startup_system(setup)
/// .add_system(spawn_level)
/// .run();
/// }
///
/// fn setup(
/// mut state: ResMut<State>,
/// asset_server: Res<AssetServer>,
/// mut commands: bevy::prelude::Commands
/// ){
/// asset_server.load("models/level1.glb#Scene0")
/// }
///
/// fn spawn_level(){
///
///}
/// ```
#[derive(Default)]
pub struct ComponentsFromGltfPlugin;
impl Plugin for ComponentsFromGltfPlugin {
fn build(&self, app: &mut App) {
app
.insert_resource(GltfLoadingTracker::new())
.add_systems(PreUpdate, (
track_new_gltf,
process_loaded_scenes,
))
;
}
}

View File

@ -1,4 +1,4 @@
use std::collections::HashSet;
use bevy::utils::HashSet;
use bevy::{prelude::*, asset::LoadState};
use bevy::gltf::Gltf;