mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-12-22 07:34:10 +00:00
chore(bevy_gltf_components): Update to Bevy 0.12 (#32)
* chore(bevy_gltf_components): updated code for bevy_main/ v0.12 * refactor(examples): cleanups & tweaks * added compatibility tables
This commit is contained in:
parent
d06aa0042e
commit
c4e83655f3
565
Cargo.lock
generated
565
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
@ -10,16 +10,15 @@ members = [
|
||||
"crates/bevy_gltf_blueprints",
|
||||
]
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
bevy="0.11"
|
||||
bevy_rapier3d = { version = "0.22.0", features = [ "serde-serialize", "debug-render-3d", "enhanced-determinism"] }
|
||||
bevy_editor_pls = { version = "0.5.0" }
|
||||
bevy_asset_loader = { version = "0.17.0", features = ["standard_dynamic_assets" ]} #version = "0.16",
|
||||
bevy="0.12"
|
||||
bevy_asset_loader = { version = "0.18", features = ["standard_dynamic_assets" ]}
|
||||
bevy_rapier3d = { version = "0.23.0", features = [ "serde-serialize", "debug-render-3d", "enhanced-determinism"] }
|
||||
bevy_editor_pls = { version = "0.6" }
|
||||
rand = "0.8.5"
|
||||
|
||||
[dependencies]
|
||||
bevy = { version = "0.11", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
|
||||
bevy = { version = "0.12", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
|
||||
bevy_gltf_components = { path = "crates/bevy_gltf_components" }
|
||||
bevy_gltf_blueprints = { path = "crates/bevy_gltf_blueprints" }
|
||||
|
||||
|
@ -128,11 +128,9 @@ you will get a warning **per entity**
|
||||
## Limitations / issues
|
||||
- some components have to be defined in ```text``` in Blender, might try using the AppTypeRegistry and some Python code on the Blender side for a nicer UI (although this loses the "fast & easy, no tooling" approach)
|
||||
- Some of `bevy_rapier`/physics code / ways to define colliders could perhaps be done better within Blender (currently it also goes via RON)
|
||||
- there seem to be some random system ordering issues that I am still investigating (only when replacing proxy components, no breaking bugs, just restarting your Bevy app is enough)
|
||||
|
||||
## Future work
|
||||
- I have a number of other tools/ code helpers that I have not yet included here, because they need cleanup/ might make this example too complex
|
||||
* simplified animation logic: ie instead of having to manually specify the animations you need from a gltf file, it is integrated with the spawning system above, which creates a ```Animations``` component in all entities that have an ```AnimationPlayer``` and you can simply query for both to easilly control your animations per entity.
|
||||
|
||||
|
||||
## Credits
|
||||
|
@ -1,5 +1,5 @@
|
||||
({
|
||||
"world":File (path: "advanced/models/World.glb#Scene0"),
|
||||
"world":File (path: "advanced/models/World.glb"),
|
||||
"models": Folder (
|
||||
path: "advanced/models/library",
|
||||
),
|
||||
|
@ -1,5 +1,5 @@
|
||||
({
|
||||
"world":File (path: "animation/models/Level1.glb#Scene0"),
|
||||
"world":File (path: "animation/models/Level1.glb"),
|
||||
"models": Folder (
|
||||
path: "animation/models/library",
|
||||
),
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bevy_gltf_blueprints"
|
||||
version = "0.2.1"
|
||||
version = "0.3.0"
|
||||
authors = ["Mark 'kaosat-dev' Moissette"]
|
||||
description = "Adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy."
|
||||
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||
@ -11,8 +11,8 @@ edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
bevy = { version = "0.11", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "bevy_animation", "animation"] }
|
||||
bevy = { version = "0.12", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "bevy_animation", "animation"] }
|
||||
|
||||
[dependencies]
|
||||
bevy_gltf_components = "0.1"
|
||||
bevy = { version = "0.11", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "bevy_animation", "animation"] }
|
||||
bevy_gltf_components = "0.2"
|
||||
bevy = { version = "0.12", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "bevy_animation", "animation"] }
|
||||
|
@ -25,8 +25,8 @@ Here's a minimal usage example:
|
||||
```toml
|
||||
# Cargo.toml
|
||||
[dependencies]
|
||||
bevy="0.11"
|
||||
bevy_gltf_blueprints = { version = "0.2"}
|
||||
bevy="0.12"
|
||||
bevy_gltf_blueprints = { version = "0.3"}
|
||||
|
||||
```
|
||||
|
||||
@ -64,7 +64,7 @@ fn spawn_blueprint(
|
||||
Add the following to your `[dependencies]` section in `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
bevy_gltf_blueprints = "0.2"
|
||||
bevy_gltf_blueprints = "0.3"
|
||||
```
|
||||
|
||||
Or use `cargo add`:
|
||||
@ -246,6 +246,19 @@ https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/example
|
||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/animation
|
||||
|
||||
|
||||
## Compatible Bevy versions
|
||||
|
||||
The main branch is compatible with the latest Bevy release, while the branch `bevy_main` tries to track the `main` branch of Bevy (PRs updating the tracked commit are welcome).
|
||||
|
||||
Compatibility of `bevy_gltf_blueprints` versions:
|
||||
| `bevy_gltf_blueprints` | `bevy` |
|
||||
| :-- | :-- |
|
||||
| `0.3` | `0.12` |
|
||||
| `0.1 -0.2` | `0.11` |
|
||||
| branch `main` | `0.12` |
|
||||
| branch `bevy_main` | `main` |
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This crate, all its code, contents & assets is Dual-licensed under either of
|
||||
|
@ -58,14 +58,15 @@ pub(crate) fn spawn_from_blueprints(
|
||||
Path::new(&blueprints_config.library_folder).join(Path::new(model_file_name.as_str()));
|
||||
|
||||
debug!("attempting to spawn {:?}", model_path);
|
||||
let scene: Handle<Gltf> = asset_server.load(model_path);
|
||||
let model_handle: Handle<Gltf> = asset_server.load(model_path);
|
||||
|
||||
let world = game_world.single_mut();
|
||||
let world = world.1[0]; // FIXME: dangerous hack because our gltf data have a single child like this, but might not always be the case
|
||||
|
||||
let gltf = assets_gltf
|
||||
.get(&scene)
|
||||
.get(&model_handle)
|
||||
.expect("this gltf should have been loaded");
|
||||
|
||||
// WARNING we work under the assumtion that there is ONLY ONE named scene, and that the first one is the right one
|
||||
let main_scene_name = gltf
|
||||
.named_scenes
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bevy_gltf_components"
|
||||
version = "0.1.3"
|
||||
version = "0.2.0"
|
||||
authors = ["Mark 'kaosat-dev' Moissette"]
|
||||
description = "Allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side."
|
||||
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||
@ -11,9 +11,9 @@ edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
bevy = { version = "0.11", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
|
||||
bevy = { version = "0.12", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
|
||||
|
||||
[dependencies]
|
||||
bevy = { version = "0.11", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
|
||||
bevy = { version = "0.12", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
|
||||
serde = "1.0.188"
|
||||
ron = "0.8.1"
|
||||
|
@ -23,8 +23,8 @@ Here's a minimal usage example:
|
||||
```toml
|
||||
# Cargo.toml
|
||||
[dependencies]
|
||||
bevy="0.11"
|
||||
bevy_gltf_components = { version = "0.1"}
|
||||
bevy="0.12"
|
||||
bevy_gltf_components = { version = "0.2"}
|
||||
|
||||
```
|
||||
|
||||
@ -88,6 +88,20 @@ Typically , the order of systems should be
|
||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/basic
|
||||
|
||||
|
||||
|
||||
## Compatible Bevy versions
|
||||
|
||||
The main branch is compatible with the latest Bevy release, while the branch `bevy_main` tries to track the `main` branch of Bevy (PRs updating the tracked commit are welcome).
|
||||
|
||||
Compatibility of `bevy_gltf_components` versions:
|
||||
| `bevy_gltf_components` | `bevy` |
|
||||
| :-- | :-- |
|
||||
| `0.2` | `0.12` |
|
||||
| `0.1` | `0.11` |
|
||||
| branch `main` | `0.12` |
|
||||
| branch `bevy_main` | `main` |
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This crate, all its code, contents & assets is Dual-licensed under either of
|
||||
|
@ -5,24 +5,167 @@ use serde::de::DeserializeSeed;
|
||||
|
||||
use bevy::ecs::{entity::Entity, reflect::ReflectComponent};
|
||||
use bevy::gltf::{Gltf, GltfExtras};
|
||||
use bevy::prelude::{debug, info, warn, Assets, Name, Parent, ResMut};
|
||||
use bevy::reflect::serde::UntypedReflectDeserializer;
|
||||
use bevy::reflect::{Reflect, TypeInfo, TypeRegistryInternal};
|
||||
use bevy::reflect::serde::UntypedReflectDeserializer; // ReflectSerializer
|
||||
use bevy::reflect::{Reflect, TypeInfo, TypeRegistry};
|
||||
use bevy::scene::Scene;
|
||||
use bevy::utils::HashMap;
|
||||
use bevy::{
|
||||
log::{debug, info, warn},
|
||||
prelude::{Assets, Name, Parent, ResMut},
|
||||
};
|
||||
|
||||
use super::capitalize_first_letter;
|
||||
|
||||
pub fn ronstring_to_reflect_component(
|
||||
ron_string: &String,
|
||||
type_registry: &TypeRegistry,
|
||||
) -> Vec<Box<dyn Reflect>> {
|
||||
let lookup: HashMap<String, Value> = ron::from_str(ron_string.as_str()).unwrap();
|
||||
let mut components: Vec<Box<dyn Reflect>> = Vec::new();
|
||||
for (key, value) in lookup.into_iter() {
|
||||
let type_string = key.replace("component: ", "").trim().to_string();
|
||||
let capitalized_type_name = capitalize_first_letter(type_string.as_str());
|
||||
|
||||
let mut parsed_value: String;
|
||||
match value.clone() {
|
||||
Value::String(str) => {
|
||||
parsed_value = str;
|
||||
}
|
||||
_ => parsed_value = ron::to_string(&value).unwrap().to_string(),
|
||||
}
|
||||
|
||||
if let Some(type_registration) =
|
||||
type_registry.get_with_short_type_path(capitalized_type_name.as_str())
|
||||
{
|
||||
debug!("TYPE INFO {:?}", type_registration.type_info());
|
||||
match type_registration.type_info() {
|
||||
TypeInfo::TupleStruct(info) => {
|
||||
// we handle tupple strucs with only one field differently, as Blender's custom properties with custom ui (float, int, bool, etc) always give us a tupple struct
|
||||
if info.field_len() == 1 {
|
||||
let field = info
|
||||
.field_at(0)
|
||||
.expect("we should always have at least one field here");
|
||||
let field_name = field.type_path();
|
||||
// TODO: find a way to cast with typeId instead of this matching
|
||||
/*match field.type_id(){
|
||||
TypeId::of::<f32>() => {
|
||||
println!("WE HAVE A f32");
|
||||
}
|
||||
}
|
||||
Vec3 => {
|
||||
println!("WE HAVE A VEC3");
|
||||
let bla:Vec3 = ron::from_str(&parsed_value).unwrap();
|
||||
println!("bla {}", bla)
|
||||
}
|
||||
_ =>{}
|
||||
}*/
|
||||
|
||||
let mut formated = parsed_value.clone();
|
||||
match field_name {
|
||||
"f32" => {
|
||||
formated = parsed_value.parse::<f32>().unwrap().to_string();
|
||||
}
|
||||
"f64" => {
|
||||
formated = parsed_value.parse::<f64>().unwrap().to_string();
|
||||
}
|
||||
"u8" => {
|
||||
formated = parsed_value.parse::<u8>().unwrap().to_string();
|
||||
}
|
||||
"u16" => {
|
||||
formated = parsed_value.parse::<u16>().unwrap().to_string();
|
||||
}
|
||||
"u32" => {
|
||||
formated = parsed_value.parse::<u32>().unwrap().to_string();
|
||||
}
|
||||
"u64" => {
|
||||
formated = parsed_value.parse::<u64>().unwrap().to_string();
|
||||
}
|
||||
"u128" => {
|
||||
formated = parsed_value.parse::<u128>().unwrap().to_string();
|
||||
}
|
||||
"glam::Vec2" => {
|
||||
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
|
||||
formated = format!("(x:{},y:{})", parsed[0], parsed[1]);
|
||||
}
|
||||
"glam::Vec3" => {
|
||||
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
|
||||
formated =
|
||||
format!("(x:{},y:{},z:{})", parsed[0], parsed[1], parsed[2]);
|
||||
}
|
||||
"bevy_render::color::Color" => {
|
||||
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
|
||||
if parsed.len() == 3 {
|
||||
formated = format!(
|
||||
"Rgba(red:{},green:{},blue:{}, alpha: 1.0)",
|
||||
parsed[0], parsed[1], parsed[2]
|
||||
);
|
||||
}
|
||||
if parsed.len() == 4 {
|
||||
formated = format!(
|
||||
"Rgba(red:{},green:{},blue:{}, alpha:{})",
|
||||
parsed[0], parsed[1], parsed[2], parsed[3]
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
parsed_value = format!("({formated})");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// println!("parsed value {}",parsed_value);
|
||||
if parsed_value.is_empty() {
|
||||
parsed_value = "()".to_string();
|
||||
}
|
||||
|
||||
let ron_string = format!(
|
||||
"{{ \"{}\":{} }}",
|
||||
type_registration.type_info().type_path(),
|
||||
parsed_value
|
||||
);
|
||||
|
||||
// usefull to determine what an entity looks like Serialized
|
||||
/*let test_struct = Enemy::default();
|
||||
let serializer = ReflectSerializer::new(&test_struct, &type_registry);
|
||||
let serialized =
|
||||
ron::ser::to_string_pretty(&serializer, ron::ser::PrettyConfig::default()).unwrap();
|
||||
println!("serialized Component {}", serialized);*/
|
||||
|
||||
debug!("component data ron string {}", ron_string);
|
||||
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str()).unwrap();
|
||||
let reflect_deserializer = UntypedReflectDeserializer::new(type_registry);
|
||||
let component = reflect_deserializer.deserialize(&mut deserializer).expect(
|
||||
format!(
|
||||
"failed to deserialize component {} with value: {:?}",
|
||||
key, value
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
debug!("component {:?}", component);
|
||||
debug!("real type {:?}", component.get_represented_type_info());
|
||||
|
||||
components.push(component);
|
||||
debug!("found type registration for {}", capitalized_type_name);
|
||||
} else {
|
||||
warn!("no type registration for {}", capitalized_type_name);
|
||||
}
|
||||
}
|
||||
components
|
||||
}
|
||||
|
||||
/// main function: injects components into each entity in gltf files that have gltf_extras, using reflection
|
||||
pub fn gltf_extras_to_components(
|
||||
gltf: &mut Gltf,
|
||||
scenes: &mut ResMut<Assets<Scene>>,
|
||||
type_registry: impl Deref<Target = TypeRegistryInternal>,
|
||||
gltf_name: &str,
|
||||
type_registry: impl Deref<Target = TypeRegistry>,
|
||||
) {
|
||||
let mut added_components = 0;
|
||||
for (_name, scene) in &gltf.named_scenes {
|
||||
debug!("gltf: {:?} scene name {:?}", gltf_name, _name);
|
||||
debug!("gltf: scene name {:?}", _name);
|
||||
|
||||
let scene = scenes.get_mut(scene).unwrap();
|
||||
|
||||
@ -81,16 +224,22 @@ pub fn gltf_extras_to_components(
|
||||
}
|
||||
for component in components {
|
||||
let mut entity_mut = scene.world.entity_mut(entity);
|
||||
debug!("------adding {} {:?}", component.type_name(), component);
|
||||
debug!(
|
||||
"------adding {} {:?}",
|
||||
component.get_represented_type_info().unwrap().type_path(),
|
||||
component
|
||||
);
|
||||
|
||||
let component_type_path =
|
||||
component.get_represented_type_info().unwrap().type_path();
|
||||
type_registry
|
||||
.get_with_name(component.type_name())
|
||||
.get_with_type_path(component_type_path)
|
||||
.unwrap() // Component was successfully deserialized, it has to be in the registry
|
||||
.data::<ReflectComponent>()
|
||||
.unwrap() // Hopefully, the component deserializer ensures those are components
|
||||
.insert(&mut entity_mut, &*component);
|
||||
|
||||
// info!("all components {:?}", scene.world.entity(entity).archetype().components());
|
||||
// debug!("all components {:?}", scene.world.entity(entity).archetype().components());
|
||||
// scene.world.components().
|
||||
// TODO: how can we insert any additional components "by hand" here ?
|
||||
}
|
||||
@ -103,142 +252,5 @@ pub fn gltf_extras_to_components(
|
||||
}
|
||||
}
|
||||
}
|
||||
info!("done extracting gltf_extras /n");
|
||||
}
|
||||
|
||||
pub fn ronstring_to_reflect_component(
|
||||
ron_string: &String,
|
||||
type_registry: &TypeRegistryInternal,
|
||||
) -> Vec<Box<dyn Reflect>> {
|
||||
let lookup: HashMap<String, Value> = ron::from_str(ron_string.as_str()).unwrap();
|
||||
let mut components: Vec<Box<dyn Reflect>> = Vec::new();
|
||||
for (key, value) in lookup.into_iter() {
|
||||
let type_string = key.replace("component: ", "").trim().to_string();
|
||||
let capitalized_type_name = capitalize_first_letter(type_string.as_str());
|
||||
|
||||
let mut parsed_value: String;
|
||||
match value.clone() {
|
||||
Value::String(str) => {
|
||||
parsed_value = str;
|
||||
}
|
||||
_ => parsed_value = ron::to_string(&value).unwrap().to_string(),
|
||||
}
|
||||
|
||||
if let Some(type_registration) =
|
||||
type_registry.get_with_short_name(capitalized_type_name.as_str())
|
||||
{
|
||||
// println!("TYPE INFO {:?}", type_registration.type_info());
|
||||
match type_registration.type_info() {
|
||||
TypeInfo::TupleStruct(info) => {
|
||||
// we handle tupple strucs with only one field differently, as Blender's custom properties with custom ui (float, int, bool, etc) always give us a tupple struct
|
||||
if info.field_len() == 1 {
|
||||
let field = info
|
||||
.field_at(0)
|
||||
.expect("we should always have at least one field here");
|
||||
let field_name = field.type_name();
|
||||
// TODO: find a way to cast with typeId instead of this matching
|
||||
/*match field.type_id(){
|
||||
TypeId::of::<f32>() => {
|
||||
println!("WE HAVE A f32");
|
||||
}
|
||||
}
|
||||
Vec3 => {
|
||||
println!("WE HAVE A VEC3");
|
||||
let bla:Vec3 = ron::from_str(&parsed_value).unwrap();
|
||||
println!("bla {}", bla)
|
||||
}
|
||||
_ =>{}
|
||||
}*/
|
||||
let mut formated = parsed_value.clone();
|
||||
match field_name {
|
||||
"f32" => {
|
||||
formated = parsed_value.parse::<f32>().unwrap().to_string();
|
||||
}
|
||||
"f64" => {
|
||||
formated = parsed_value.parse::<f64>().unwrap().to_string();
|
||||
}
|
||||
"u8" => {
|
||||
formated = parsed_value.parse::<u8>().unwrap().to_string();
|
||||
}
|
||||
"u16" => {
|
||||
formated = parsed_value.parse::<u16>().unwrap().to_string();
|
||||
}
|
||||
"u32" => {
|
||||
formated = parsed_value.parse::<u32>().unwrap().to_string();
|
||||
}
|
||||
"u64" => {
|
||||
formated = parsed_value.parse::<u64>().unwrap().to_string();
|
||||
}
|
||||
"u128" => {
|
||||
formated = parsed_value.parse::<u128>().unwrap().to_string();
|
||||
}
|
||||
"glam::f32::vec2::Vec2" => {
|
||||
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
|
||||
formated = format!("(x:{},y:{})", parsed[0], parsed[1]);
|
||||
}
|
||||
"glam::f32::vec3::Vec3" => {
|
||||
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
|
||||
formated =
|
||||
format!("(x:{},y:{},z:{})", parsed[0], parsed[1], parsed[2]);
|
||||
}
|
||||
"bevy_render::color::Color" => {
|
||||
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
|
||||
if parsed.len() == 3 {
|
||||
formated = format!(
|
||||
"Rgba(red:{},green:{},blue:{}, alpha: 1.0)",
|
||||
parsed[0], parsed[1], parsed[2]
|
||||
);
|
||||
}
|
||||
if parsed.len() == 4 {
|
||||
formated = format!(
|
||||
"Rgba(red:{},green:{},blue:{}, alpha:{})",
|
||||
parsed[0], parsed[1], parsed[2], parsed[3]
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
parsed_value = format!("({formated})");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// println!("parsed value {}",parsed_value);
|
||||
if parsed_value.is_empty() {
|
||||
parsed_value = "()".to_string();
|
||||
}
|
||||
|
||||
let ron_string = format!(
|
||||
"{{ \"{}\":{} }}",
|
||||
type_registration.type_name(),
|
||||
parsed_value
|
||||
);
|
||||
|
||||
// usefull to determine what an entity looks like Serialized
|
||||
/*let test_struct = TuppleTestStr::default();
|
||||
let serializer = ReflectSerializer::new(&test_struct, &type_registry);
|
||||
let serialized =
|
||||
ron::ser::to_string_pretty(&serializer, ron::ser::PrettyConfig::default()).unwrap();
|
||||
println!("serialized Component {}", serialized);*/
|
||||
|
||||
// println!("component data ron string {}", ron_string);
|
||||
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str()).unwrap();
|
||||
let reflect_deserializer = UntypedReflectDeserializer::new(type_registry);
|
||||
let component = reflect_deserializer.deserialize(&mut deserializer).expect(
|
||||
format!(
|
||||
"failed to deserialize component {} with value: {:?}",
|
||||
key, value
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
components.push(component);
|
||||
debug!("found type registration for {}", capitalized_type_name);
|
||||
} else {
|
||||
warn!("no type registration for {}", capitalized_type_name);
|
||||
}
|
||||
}
|
||||
components
|
||||
info!("done injecting components from gltf_extras /n");
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use bevy::gltf::Gltf;
|
||||
use bevy::utils::HashSet;
|
||||
use bevy::{asset::LoadState, prelude::*};
|
||||
|
||||
use super::gltf_extras_to_components;
|
||||
use crate::gltf_extras_to_components;
|
||||
|
||||
#[derive(Resource)]
|
||||
/// component to keep track of gltfs' loading state
|
||||
@ -18,7 +18,7 @@ impl GltfLoadingTracker {
|
||||
loading_gltfs: HashSet::new(),
|
||||
}
|
||||
}
|
||||
pub fn add_scene(&mut self, handle: Handle<Gltf>) {
|
||||
pub fn add_gltf(&mut self, handle: Handle<Gltf>) {
|
||||
self.loading_gltfs.insert(handle);
|
||||
}
|
||||
}
|
||||
@ -26,10 +26,14 @@ impl GltfLoadingTracker {
|
||||
pub fn track_new_gltf(
|
||||
mut tracker: ResMut<GltfLoadingTracker>,
|
||||
mut events: EventReader<AssetEvent<Gltf>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
for event in events.iter() {
|
||||
if let AssetEvent::Created { handle } = event {
|
||||
tracker.add_scene(handle.clone());
|
||||
for event in events.read() {
|
||||
if let AssetEvent::Added { id } = event {
|
||||
let handle = asset_server
|
||||
.get_id_handle(*id)
|
||||
.expect("this gltf should have been loaded");
|
||||
tracker.add_gltf(handle.clone());
|
||||
debug!("gltf created {:?}", handle.clone());
|
||||
}
|
||||
}
|
||||
@ -38,21 +42,23 @@ pub fn track_new_gltf(
|
||||
|
||||
pub fn process_loaded_scenes(
|
||||
mut gltfs: ResMut<Assets<Gltf>>,
|
||||
mut scenes: ResMut<Assets<Scene>>,
|
||||
mut tracker: ResMut<GltfLoadingTracker>,
|
||||
mut scenes: ResMut<Assets<Scene>>,
|
||||
app_type_registry: Res<AppTypeRegistry>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
let mut loaded_gltfs = Vec::new();
|
||||
for gltf in &tracker.loading_gltfs {
|
||||
info!(
|
||||
debug!(
|
||||
"checking for loaded gltfs {:?}",
|
||||
asset_server.get_load_state(gltf)
|
||||
);
|
||||
|
||||
if asset_server.get_load_state(gltf.clone()) == LoadState::Loaded {
|
||||
debug!("Adding scene to processing list");
|
||||
loaded_gltfs.push(gltf.clone());
|
||||
if let Some(load_state) = asset_server.get_load_state(gltf.clone()) {
|
||||
if load_state == LoadState::Loaded {
|
||||
debug!("Adding scene to processing list");
|
||||
loaded_gltfs.push(gltf.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,14 +66,7 @@ pub fn process_loaded_scenes(
|
||||
|
||||
for gltf_handle in &loaded_gltfs {
|
||||
if let Some(gltf) = gltfs.get_mut(gltf_handle) {
|
||||
// TODO this is a temporary workaround for library management
|
||||
if let Some(asset_path) = asset_server.get_handle_path(gltf_handle) {
|
||||
let gltf_name = asset_path.path().file_stem().unwrap().to_str().unwrap();
|
||||
gltf_extras_to_components(gltf, &mut scenes, &*type_registry, gltf_name);
|
||||
//gltf_extras_to_prefab_infos(gltf, &mut scenes, &*type_registry, gltf_name);
|
||||
} else {
|
||||
gltf_extras_to_components(gltf, &mut scenes, &*type_registry, "");
|
||||
}
|
||||
gltf_extras_to_components(gltf, &mut scenes, &*type_registry);
|
||||
}
|
||||
tracker.loading_gltfs.remove(gltf_handle);
|
||||
tracker.loaded_gltfs.insert(gltf_handle.clone());
|
||||
|
@ -6,7 +6,7 @@ use bevy_asset_loader::prelude::*;
|
||||
#[derive(AssetCollection, Resource)]
|
||||
pub struct GameAssets {
|
||||
#[asset(key = "world")]
|
||||
pub world: Handle<Scene>,
|
||||
pub world: Handle<Gltf>,
|
||||
|
||||
#[asset(key = "models", collection(typed, mapped))]
|
||||
pub models: HashMap<String, Handle<Gltf>>,
|
||||
|
@ -1,5 +1,4 @@
|
||||
pub mod camera;
|
||||
use bevy_rapier3d::prelude::Velocity;
|
||||
pub use camera::*;
|
||||
|
||||
pub mod lighting;
|
||||
@ -11,61 +10,12 @@ pub use relationships::*;
|
||||
pub mod physics;
|
||||
pub use physics::*;
|
||||
|
||||
// pub mod blueprints;
|
||||
// pub use blueprints::*;
|
||||
|
||||
pub mod save_load;
|
||||
pub use save_load::*;
|
||||
// pub mod save_load;
|
||||
// pub use save_load::*;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_gltf_blueprints::*;
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
fn spawn_test(
|
||||
keycode: Res<Input<KeyCode>>,
|
||||
mut commands: Commands,
|
||||
|
||||
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
|
||||
) {
|
||||
if keycode.just_pressed(KeyCode::T) {
|
||||
let world = game_world.single_mut();
|
||||
let world = world.1[0];
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let range = 5.5;
|
||||
let x: f32 = rng.gen_range(-range..range);
|
||||
let y: f32 = rng.gen_range(-range..range);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let range = 0.8;
|
||||
let vel_x: f32 = rng.gen_range(-range..range);
|
||||
let vel_y: f32 = rng.gen_range(2.0..2.5);
|
||||
let vel_z: f32 = rng.gen_range(-range..range);
|
||||
|
||||
let name_index: u64 = rng.gen();
|
||||
|
||||
let new_entity = commands
|
||||
.spawn((
|
||||
BluePrintBundle {
|
||||
blueprint: BlueprintName("Health_Pickup".to_string()),
|
||||
transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
|
||||
..Default::default()
|
||||
},
|
||||
bevy::prelude::Name::from(format!("test{}", name_index)),
|
||||
// BlueprintName("Health_Pickup".to_string()),
|
||||
// SpawnHere,
|
||||
// TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
|
||||
Velocity {
|
||||
linvel: Vec3::new(vel_x, vel_y, vel_z),
|
||||
angvel: Vec3::new(0.0, 0.0, 0.0),
|
||||
},
|
||||
))
|
||||
.id();
|
||||
commands.entity(world).add_child(new_entity);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CorePlugin;
|
||||
impl Plugin for CorePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
@ -73,12 +23,10 @@ impl Plugin for CorePlugin {
|
||||
LightingPlugin,
|
||||
CameraPlugin,
|
||||
PhysicsPlugin,
|
||||
SaveLoadPlugin,
|
||||
// SaveLoadPlugin,
|
||||
BlueprintsPlugin {
|
||||
library_folder: "advanced/models/library".into(),
|
||||
},
|
||||
))
|
||||
// just for testing
|
||||
.add_systems(Update, spawn_test);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,15 @@ use crate::{
|
||||
assets::GameAssets,
|
||||
state::{GameState, InAppRunning},
|
||||
};
|
||||
use bevy_gltf_blueprints::GameWorldTag;
|
||||
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag};
|
||||
|
||||
use bevy_rapier3d::prelude::Velocity;
|
||||
use rand::Rng;
|
||||
|
||||
pub fn setup_game(
|
||||
mut commands: Commands,
|
||||
game_assets: Res<GameAssets>,
|
||||
models: Res<Assets<bevy::gltf::Gltf>>,
|
||||
mut next_game_state: ResMut<NextState<GameState>>,
|
||||
) {
|
||||
println!("setting up all stuff");
|
||||
@ -20,7 +24,12 @@ pub fn setup_game(
|
||||
|
||||
commands.spawn((
|
||||
SceneBundle {
|
||||
scene: game_assets.world.clone(),
|
||||
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
||||
scene: models
|
||||
.get(game_assets.world.id())
|
||||
.expect("main level should have been loaded")
|
||||
.scenes[0]
|
||||
.clone(),
|
||||
..default()
|
||||
},
|
||||
bevy::prelude::Name::from("world"),
|
||||
@ -30,3 +39,47 @@ pub fn setup_game(
|
||||
|
||||
next_game_state.set(GameState::InGame)
|
||||
}
|
||||
|
||||
pub fn spawn_test(
|
||||
keycode: Res<Input<KeyCode>>,
|
||||
mut commands: Commands,
|
||||
|
||||
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
|
||||
) {
|
||||
if keycode.just_pressed(KeyCode::T) {
|
||||
let world = game_world.single_mut();
|
||||
let world = world.1[0];
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let range = 5.5;
|
||||
let x: f32 = rng.gen_range(-range..range);
|
||||
let y: f32 = rng.gen_range(-range..range);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let range = 0.8;
|
||||
let vel_x: f32 = rng.gen_range(-range..range);
|
||||
let vel_y: f32 = rng.gen_range(2.0..2.5);
|
||||
let vel_z: f32 = rng.gen_range(-range..range);
|
||||
|
||||
let name_index: u64 = rng.gen();
|
||||
|
||||
let new_entity = commands
|
||||
.spawn((
|
||||
BluePrintBundle {
|
||||
blueprint: BlueprintName("Health_Pickup".to_string()),
|
||||
transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
|
||||
..Default::default()
|
||||
},
|
||||
bevy::prelude::Name::from(format!("test{}", name_index)),
|
||||
// BlueprintName("Health_Pickup".to_string()),
|
||||
// SpawnHere,
|
||||
// TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
|
||||
Velocity {
|
||||
linvel: Vec3::new(vel_x, vel_y, vel_z),
|
||||
angvel: Vec3::new(0.0, 0.0, 0.0),
|
||||
},
|
||||
))
|
||||
.id();
|
||||
commands.entity(world).add_child(new_entity);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
core::save_load::{LoadRequest, SaveRequest},
|
||||
state::{AppState, GameState, InMainMenu},
|
||||
};
|
||||
use crate::state::{AppState, GameState, InMainMenu};
|
||||
|
||||
pub fn setup_main_menu(mut commands: Commands) {
|
||||
commands.spawn((Camera2dBundle::default(), InMainMenu));
|
||||
@ -97,8 +94,8 @@ pub fn main_menu(
|
||||
|
||||
mut next_app_state: ResMut<NextState<AppState>>,
|
||||
// mut next_game_state: ResMut<NextState<GameState>>,
|
||||
mut save_requested_events: EventWriter<SaveRequest>,
|
||||
mut load_requested_events: EventWriter<LoadRequest>,
|
||||
// mut save_requested_events: EventWriter<SaveRequest>,
|
||||
// mut load_requested_events: EventWriter<LoadRequest>,
|
||||
) {
|
||||
if keycode.just_pressed(KeyCode::Return) {
|
||||
next_app_state.set(AppState::AppLoading);
|
||||
|
@ -70,7 +70,7 @@ pub fn test_collision_events(
|
||||
mut collision_events: EventReader<CollisionEvent>,
|
||||
mut contact_force_events: EventReader<ContactForceEvent>,
|
||||
) {
|
||||
for collision_event in collision_events.iter() {
|
||||
for collision_event in collision_events.read() {
|
||||
println!("collision");
|
||||
match collision_event {
|
||||
CollisionEvent::Started(_entity1, _entity2, _) => {
|
||||
@ -82,7 +82,7 @@ pub fn test_collision_events(
|
||||
}
|
||||
}
|
||||
|
||||
for contact_force_event in contact_force_events.iter() {
|
||||
for contact_force_event in contact_force_events.read() {
|
||||
println!("Received contact force event: {:?}", contact_force_event);
|
||||
}
|
||||
}
|
||||
@ -102,7 +102,8 @@ impl Plugin for GamePlugin {
|
||||
(
|
||||
// insert_dependant_component::<Player, ShouldBeWithPlayer>,
|
||||
player_move_demo, //.run_if(in_state(AppState::Running)),
|
||||
// test_collision_events
|
||||
// test_collision_events
|
||||
spawn_test,
|
||||
)
|
||||
.run_if(in_state(GameState::InGame)),
|
||||
)
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::Player;
|
||||
use bevy::prelude::*;
|
||||
use bevy_gltf_blueprints::GltfBlueprintsSet;
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
@ -27,11 +28,7 @@ pub fn picking(
|
||||
pub struct PickingPlugin;
|
||||
impl Plugin for PickingPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.register_type::<Pickable>().add_systems(
|
||||
Update,
|
||||
(
|
||||
picking, //.run_if(in_state(AppState::Running)),
|
||||
),
|
||||
);
|
||||
app.register_type::<Pickable>()
|
||||
.add_systems(Update, (picking.after(GltfBlueprintsSet::AfterSpawn),));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use bevy::{asset::ChangeWatcher, prelude::*};
|
||||
use bevy::prelude::*;
|
||||
use bevy_editor_pls::prelude::*;
|
||||
|
||||
mod core;
|
||||
@ -19,13 +19,7 @@ use test_components::*;
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(AssetPlugin {
|
||||
// This tells the AssetServer to watch for changes to assets.
|
||||
// It enables our scenes to automatically reload in game when we modify their files.
|
||||
// practical in our case to be able to edit the shaders without needing to recompile
|
||||
// watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(50)), : FIXME: breaks scene save/loading
|
||||
..default()
|
||||
}),
|
||||
DefaultPlugins.set(AssetPlugin::default()),
|
||||
// editor
|
||||
EditorPlugin::default(),
|
||||
// our custom plugins
|
||||
|
@ -6,7 +6,7 @@ use bevy_asset_loader::prelude::*;
|
||||
#[derive(AssetCollection, Resource)]
|
||||
pub struct GameAssets {
|
||||
#[asset(key = "world")]
|
||||
pub world: Handle<Scene>,
|
||||
pub world: Handle<Gltf>,
|
||||
|
||||
#[asset(key = "models", collection(typed, mapped))]
|
||||
pub models: HashMap<String, Handle<Gltf>>,
|
||||
|
@ -1,5 +1,4 @@
|
||||
pub mod camera;
|
||||
use bevy_rapier3d::prelude::Velocity;
|
||||
pub use camera::*;
|
||||
|
||||
pub mod lighting;
|
||||
|
@ -17,9 +17,10 @@ use super::{Fox, Player, Robot};
|
||||
pub fn setup_game(
|
||||
mut commands: Commands,
|
||||
game_assets: Res<GameAssets>,
|
||||
models: Res<Assets<bevy::gltf::Gltf>>,
|
||||
|
||||
mut next_game_state: ResMut<NextState<GameState>>,
|
||||
) {
|
||||
println!("setting up all stuff");
|
||||
commands.insert_resource(AmbientLight {
|
||||
color: Color::WHITE,
|
||||
brightness: 0.2,
|
||||
@ -28,7 +29,12 @@ pub fn setup_game(
|
||||
|
||||
commands.spawn((
|
||||
SceneBundle {
|
||||
scene: game_assets.world.clone(),
|
||||
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
||||
scene: models
|
||||
.get(game_assets.world.id())
|
||||
.expect("main level should have been loaded")
|
||||
.scenes[0]
|
||||
.clone(),
|
||||
..default()
|
||||
},
|
||||
bevy::prelude::Name::from("world"),
|
||||
|
@ -1,4 +1,4 @@
|
||||
use bevy::{asset::ChangeWatcher, prelude::*};
|
||||
use bevy::prelude::*;
|
||||
use bevy_editor_pls::prelude::*;
|
||||
|
||||
mod core;
|
||||
@ -19,13 +19,7 @@ use test_components::*;
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(AssetPlugin {
|
||||
// This tells the AssetServer to watch for changes to assets.
|
||||
// It enables our scenes to automatically reload in game when we modify their files.
|
||||
// practical in our case to be able to edit the shaders without needing to recompile
|
||||
// watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(50)), : FIXME: breaks scene save/loading
|
||||
..default()
|
||||
}),
|
||||
DefaultPlugins.set(AssetPlugin::default()),
|
||||
// editor
|
||||
EditorPlugin::default(),
|
||||
// our custom plugins
|
||||
|
@ -63,7 +63,7 @@ pub fn test_collision_events(
|
||||
mut collision_events: EventReader<CollisionEvent>,
|
||||
mut contact_force_events: EventReader<ContactForceEvent>,
|
||||
) {
|
||||
for collision_event in collision_events.iter() {
|
||||
for collision_event in collision_events.read() {
|
||||
println!("collision");
|
||||
match collision_event {
|
||||
CollisionEvent::Started(_entity1, _entity2, _) => {
|
||||
@ -75,7 +75,7 @@ pub fn test_collision_events(
|
||||
}
|
||||
}
|
||||
|
||||
for contact_force_event in contact_force_events.iter() {
|
||||
for contact_force_event in contact_force_events.read() {
|
||||
println!("Received contact force event: {:?}", contact_force_event);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
use bevy::{asset::ChangeWatcher, gltf::Gltf, prelude::*};
|
||||
use bevy::{gltf::Gltf, prelude::*};
|
||||
use bevy_editor_pls::prelude::*;
|
||||
use bevy_gltf_components::ComponentsFromGltfPlugin;
|
||||
use bevy_rapier3d::prelude::*;
|
||||
use std::time::Duration;
|
||||
|
||||
mod core;
|
||||
use crate::core::*;
|
||||
@ -28,13 +27,7 @@ enum AppState {
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(AssetPlugin {
|
||||
// This tells the AssetServer to watch for changes to assets.
|
||||
// It enables our scenes to automatically reload in game when we modify their files.
|
||||
// practical in our case to be able to edit the shaders without needing to recompile
|
||||
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(50)),
|
||||
..default()
|
||||
}),
|
||||
DefaultPlugins.set(AssetPlugin::default()),
|
||||
// editor
|
||||
EditorPlugin::default(),
|
||||
// physics
|
||||
@ -53,33 +46,33 @@ fn main() {
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct AssetLoadHelper(Handle<Scene>);
|
||||
pub struct MyGltf(pub Handle<Gltf>);
|
||||
|
||||
// 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
|
||||
// MyGltf 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("basic/models/level1.glb#Scene0");
|
||||
commands.insert_resource(AssetLoadHelper(tmp));
|
||||
commands.insert_resource(MyGltf(asset_server.load("basic/models/level1.glb")));
|
||||
}
|
||||
|
||||
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>>,
|
||||
models: Res<Assets<bevy::gltf::Gltf>>,
|
||||
) {
|
||||
if let Some(asset_event) = asset_event_reader.iter().next() {
|
||||
if let Some(asset_event) = asset_event_reader.read().next() {
|
||||
match asset_event {
|
||||
AssetEvent::Created { handle: _ } => {
|
||||
info!("GLTF loaded");
|
||||
AssetEvent::Added { id } => {
|
||||
info!("GLTF loaded/ added {:?}", asset_event);
|
||||
let my_gltf = models.get(*id).unwrap();
|
||||
if scene_markers.is_empty() {
|
||||
info!("spawning scene");
|
||||
commands.spawn((
|
||||
SceneBundle {
|
||||
scene: preloaded_scene.0.clone(),
|
||||
scene: my_gltf.scenes[0].clone(),
|
||||
..default()
|
||||
},
|
||||
LoadedMarker,
|
||||
|
Loading…
Reference in New Issue
Block a user