From 1ceb5050f292397112ef79c0441070531b3e4b90 Mon Sep 17 00:00:00 2001 From: Mark Moissette Date: Mon, 19 Feb 2024 22:43:27 +0100 Subject: [PATCH] feat(bevy_gltf_components): testing a different approach using only gltf extras (#134) * not using / tracking gltf files anymore (should avoid issues with scenes vs gltf files) * restructured accordingly * closes #102 * closes #111 --- README.md | 1 + crates/bevy_gltf_blueprints/Cargo.toml | 2 +- crates/bevy_gltf_components/Cargo.toml | 2 +- crates/bevy_gltf_components/src/lib.rs | 20 +-- .../bevy_gltf_components/src/process_gltfs.rs | 160 ++++++++---------- ...s.rs => ronstring_to_reflect_component.rs} | 139 +-------------- 6 files changed, 94 insertions(+), 230 deletions(-) rename crates/bevy_gltf_components/src/{gltf_to_components.rs => ronstring_to_reflect_component.rs} (50%) diff --git a/README.md b/README.md index 4703885..c044000 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ Thanks to all the contributors helping out with this project ! Big kudos to you, - [BSDGuyShawn](https://github.com/BSDGuyShawn) - [yukkop](https://github.com/yukkop) - [killercup](https://github.com/killercup) +- [janhohenheim ](https://github.com/janhohenheim) ## License diff --git a/crates/bevy_gltf_blueprints/Cargo.toml b/crates/bevy_gltf_blueprints/Cargo.toml index 40d60c9..dc328a0 100644 --- a/crates/bevy_gltf_blueprints/Cargo.toml +++ b/crates/bevy_gltf_blueprints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bevy_gltf_blueprints" -version = "0.7.1" +version = "0.7.3" 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" diff --git a/crates/bevy_gltf_components/Cargo.toml b/crates/bevy_gltf_components/Cargo.toml index d2f3fad..9897ec7 100644 --- a/crates/bevy_gltf_components/Cargo.toml +++ b/crates/bevy_gltf_components/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bevy_gltf_components" -version = "0.3.1" +version = "0.3.2" 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" diff --git a/crates/bevy_gltf_components/src/lib.rs b/crates/bevy_gltf_components/src/lib.rs index 3c20a5a..74417a4 100644 --- a/crates/bevy_gltf_components/src/lib.rs +++ b/crates/bevy_gltf_components/src/lib.rs @@ -1,8 +1,8 @@ pub mod utils; pub use utils::*; -pub mod gltf_to_components; -pub use gltf_to_components::*; +pub mod ronstring_to_reflect_component; +pub use ronstring_to_reflect_component::*; pub mod process_gltfs; pub use process_gltfs::*; @@ -68,14 +68,12 @@ impl Default for ComponentsFromGltfPlugin { impl Plugin for ComponentsFromGltfPlugin { fn build(&self, app: &mut App) { - app.insert_resource(GltfLoadingTracker::new()) - .insert_resource(GltfComponentsConfig { - legacy_mode: self.legacy_mode, - }) - .add_systems(Update, (track_new_gltf, process_loaded_scenes)) - .add_systems( - Update, - (process_loaded_scenes).in_set(GltfComponentsSet::Injection), - ); + app.insert_resource(GltfComponentsConfig { + legacy_mode: self.legacy_mode, + }) + .add_systems( + Update, + (add_components_from_gltf_extras).in_set(GltfComponentsSet::Injection), + ); } } diff --git a/crates/bevy_gltf_components/src/process_gltfs.rs b/crates/bevy_gltf_components/src/process_gltfs.rs index d04bd97..bce34bc 100644 --- a/crates/bevy_gltf_components/src/process_gltfs.rs +++ b/crates/bevy_gltf_components/src/process_gltfs.rs @@ -1,101 +1,87 @@ -use bevy::asset::AssetPath; -use bevy::gltf::Gltf; -use bevy::utils::HashSet; -use bevy::{asset::LoadState, prelude::*}; -use std::path::Path; +use bevy::{ + core::Name, + ecs::{ + entity::Entity, + query::Added, + reflect::{AppTypeRegistry, ReflectComponent}, + world::World, + }, + gltf::GltfExtras, + hierarchy::Parent, + log::debug, + reflect::{Reflect, TypeRegistration}, + utils::HashMap, +}; -use crate::{gltf_extras_to_components, GltfComponentsConfig}; +use crate::{ronstring_to_reflect_component, GltfComponentsConfig}; -#[derive(Resource)] -/// component to keep track of gltfs' loading state -pub struct GltfLoadingTracker { - pub loading_gltfs: HashSet>, - pub processed_gltfs: HashSet, -} -impl Default for GltfLoadingTracker { - fn default() -> Self { - Self::new() - } -} +/// main function: injects components into each entity in gltf files that have `gltf_extras`, using reflection +pub fn add_components_from_gltf_extras(world: &mut World) { + let mut extras = + world.query_filtered::<(Entity, &Name, &GltfExtras, &Parent), Added>(); + let mut entity_components: HashMap, TypeRegistration)>> = + HashMap::new(); -impl GltfLoadingTracker { - pub fn new() -> GltfLoadingTracker { - GltfLoadingTracker { - loading_gltfs: HashSet::new(), - processed_gltfs: HashSet::new(), - } - } - pub fn add_gltf(&mut self, handle: Handle) { - self.loading_gltfs.insert(handle); - } -} + let gltf_components_config = world.resource::(); -pub fn track_new_gltf( - mut tracker: ResMut, - mut events: EventReader>, - asset_server: Res, -) { - for event in events.read() { - if let AssetEvent::Added { id } = event { - let handle = asset_server.get_id_handle(*id); - if let Some(handle) = handle { - tracker.add_gltf(handle.clone()); - debug!("gltf created {:?}", handle); - } else { - let asset_path = asset_server - .get_path(*id) - .unwrap_or(AssetPath::from_path(Path::new("n/a"))); // will unfortunatly not work, will do a PR/ discussion at the Bevy level, leaving for reference, would be very practical - warn!( - "a gltf file ({:?}) has no handle available, cannot inject components", - asset_path - ); - } - } - } - events.clear(); -} - -pub fn process_loaded_scenes( - mut gltfs: ResMut>, - mut tracker: ResMut, - mut scenes: ResMut>, - app_type_registry: Res, - asset_server: Res, - gltf_components_config: Res, -) { - let mut loaded_gltfs = Vec::new(); - for gltf in &tracker.loading_gltfs { + for (entity, name, extra, parent) in extras.iter(world) { debug!( - "checking for loaded gltfs {:?}", - asset_server.get_load_state(gltf) + "Name: {}, entity {:?}, parent: {:?}, extras {:?}", + name, entity, parent, extra ); - 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()); + let type_registry: &AppTypeRegistry = world.resource(); + let type_registry = type_registry.read(); + let reflect_components = ronstring_to_reflect_component( + &extra.value, + &type_registry, + gltf_components_config.legacy_mode, + ); + + // we assign the components specified /xxx_components objects to their parent node + let mut target_entity = entity; + // if the node contains "components" or ends with "_pa" (ie add to parent), the components will not be added to the entity itself but to its parent + // this is mostly used for Blender collections + if name.as_str().contains("components") || name.as_str().ends_with("_pa") { + debug!("adding components to parent"); + target_entity = parent.get(); + } + debug!("adding to {:?}", target_entity); + + // if there where already components set to be added to this entity (for example when entity_data was refering to a parent), update the vec of entity_components accordingly + // this allows for example blender collection to provide basic ecs data & the instances to override/ define their own values + if entity_components.contains_key(&target_entity) { + let mut updated_components: Vec<(Box, TypeRegistration)> = Vec::new(); + let current_components = &entity_components[&target_entity]; + // first inject the current components + for (component, type_registration) in current_components { + updated_components.push((component.clone_value(), type_registration.clone())); } + // then inject the new components: this also enables overwrite components set in the collection + for (component, type_registration) in reflect_components { + updated_components.push((component.clone_value(), type_registration)); + } + entity_components.insert(target_entity, updated_components); + } else { + entity_components.insert(target_entity, reflect_components); } } - let type_registry = app_type_registry.read(); - - for gltf_handle in &loaded_gltfs { - if let Some(gltf) = gltfs.get_mut(gltf_handle) { - gltf_extras_to_components( - gltf, - &mut scenes, - &*type_registry, - gltf_components_config.legacy_mode, - ); - - if let Some(path) = gltf_handle.path() { - tracker.processed_gltfs.insert(path.to_string()); - } - } else { - warn!("could not find gltf asset, cannot process it"); + for (entity, components) in entity_components { + if !components.is_empty() { + debug!("--entity {:?}, components {}", entity, components.len()); + } + for (component, type_registration) in components { + let mut entity_mut = world.entity_mut(entity); + debug!( + "------adding {} {:?}", + component.get_represented_type_info().unwrap().type_path(), + component + ); + type_registration + .data::() + .unwrap() + .insert(&mut entity_mut, &*component); // TODO: how can we insert any additional components "by hand" here ? } - tracker.loading_gltfs.remove(gltf_handle); - debug!("Done loading gltf file"); } } diff --git a/crates/bevy_gltf_components/src/gltf_to_components.rs b/crates/bevy_gltf_components/src/ronstring_to_reflect_component.rs similarity index 50% rename from crates/bevy_gltf_components/src/gltf_to_components.rs rename to crates/bevy_gltf_components/src/ronstring_to_reflect_component.rs index 11d1826..4bae651 100644 --- a/crates/bevy_gltf_components/src/gltf_to_components.rs +++ b/crates/bevy_gltf_components/src/ronstring_to_reflect_component.rs @@ -1,37 +1,19 @@ -use bevy::ecs::component::Component; -use bevy::render::color::Color; -use core::ops::Deref; +use bevy::log::{debug, warn}; +use bevy::reflect::serde::UntypedReflectDeserializer; +use bevy::reflect::{Reflect, TypeInfo, TypeRegistration, TypeRegistry}; +use bevy::utils::HashMap; use ron::Value; use serde::de::DeserializeSeed; -use bevy::ecs::{entity::Entity, reflect::ReflectComponent}; -use bevy::gltf::{Gltf, GltfExtras}; -use bevy::reflect::serde::UntypedReflectDeserializer; -use bevy::reflect::{Reflect, TypeInfo, TypeRegistry}; -use bevy::scene::Scene; -use bevy::utils::HashMap; -use bevy::{ - log::{debug, warn}, - prelude::{Assets, Name, Parent, ResMut}, -}; - use super::capitalize_first_letter; -#[derive(Component, Reflect, Default, Debug)] -#[reflect(Component)] -struct TuppleTestColor(Color); - -#[derive(Component, Reflect, Default, Debug)] -#[reflect(Component)] -pub struct VecOfColors(Vec); - pub fn ronstring_to_reflect_component( ron_string: &str, type_registry: &TypeRegistry, simplified_types: bool, -) -> Vec> { +) -> Vec<(Box, TypeRegistration)> { let lookup: HashMap = ron::from_str(ron_string).unwrap(); - let mut components: Vec> = Vec::new(); + let mut components: Vec<(Box, TypeRegistration)> = 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()); @@ -111,7 +93,8 @@ pub fn ronstring_to_reflect_component( } } - // println!("parsed value {}",parsed_value); + // FIXME: waaait this should be part of the legacy mode as it modifies the ron data ??? + // so it means the values generated by the Blende add-on are incomplete (trivial to fix, but still) if parsed_value.is_empty() { parsed_value = "()".to_string(); } @@ -144,7 +127,7 @@ pub fn ronstring_to_reflect_component( debug!("component {:?}", component); debug!("real type {:?}", component.get_represented_type_info()); - components.push(component); + components.push((component, type_registration.clone())); debug!("found type registration for {}", capitalized_type_name); } else { warn!("no type registration for {}", capitalized_type_name); @@ -152,107 +135,3 @@ pub fn ronstring_to_reflect_component( } 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>, - type_registry: impl Deref, - legacy_mode: bool, -) { - let mut added_components = 0; - let simplified_types = legacy_mode; - if simplified_types { - warn!("using simplified component definitions is deprecated since 0.3, prefer defining components with real ron values (use the bevy_components tool for Blender for simplicity) "); - } - for (_name, scene) in &gltf.named_scenes { - debug!("gltf: scene name {:?}", _name); - - let scene = scenes.get_mut(scene).unwrap(); - - let mut query = scene.world.query::<(Entity, &Name, &GltfExtras, &Parent)>(); - let mut entity_components: HashMap>> = HashMap::new(); - for (entity, name, extras, parent) in query.iter(&scene.world) { - debug!("Name: {}, entity {:?}, parent: {:?}", name, entity, parent); - let reflect_components = - ronstring_to_reflect_component(&extras.value, &type_registry, simplified_types); - added_components = reflect_components.len(); - debug!("Found components {}", added_components); - - // we assign the components specified /xxx_components objects to their parent node - let mut target_entity = entity; - // if the node contains "components" or ends with "_pa" (ie add to parent), the components will not be added to the entity itself but to its parent - // this is mostly used for Blender collections - if name.as_str().contains("components") || name.as_str().ends_with("_pa") { - debug!("adding components to parent"); - target_entity = parent.get(); - } - - debug!("adding to {:?}", target_entity); - - // if there where already components set to be added to this entity (for example when entity_data was refering to a parent), update the vec of entity_components accordingly - // this allows for example blender collection to provide basic ecs data & the instances to override/ define their own values - if entity_components.contains_key(&target_entity) { - let mut updated_components: Vec> = Vec::new(); - let current_components = &entity_components[&target_entity]; - // first inject the current components - for component in current_components { - updated_components.push(component.clone_value()); - } - // then inject the new components: this also enables overwrite components set in the collection - for component in reflect_components { - updated_components.push(component.clone_value()); - } - entity_components.insert(target_entity, updated_components); - } else { - entity_components.insert(target_entity, reflect_components); - } - // shorthand, did not manage to get it working - /* entity_components.insert( - target_entity, - if entity_components.contains_key(&target_entity) { - entity_components[&target_entity].push(reflect_components) } else { reflect_components } - );*/ - - debug!("-----value {:?}", &extras.value); - } - - // GltfNode - // find a way to link this name to the current entity ? => WOULD BE VERY USEFULL for animations & co !! - debug!("done pre-processing components, now adding them to entities"); - for (entity, components) in entity_components { - if !components.is_empty() { - debug!("--entity {:?}, components {}", entity, components.len()); - } - for component in components { - let mut entity_mut = scene.world.entity_mut(entity); - 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_type_path(component_type_path) - .unwrap() // Component was successfully deserialized, it has to be in the registry - .data::() - .unwrap() // Hopefully, the component deserializer ensures those are components - .insert(&mut entity_mut, &*component); - - // debug!("all components {:?}", scene.world.entity(entity).archetype().components()); - // scene.world.components(). - // TODO: how can we insert any additional components "by hand" here ? - } - // let entity_mut = scene.world.entity_mut(entity); - // let archetype = entity_mut.archetype().clone(); - // let _all_components = archetype.components(); - - if added_components > 0 { - debug!("------done adding {} components", added_components); - } - } - } - debug!("done injecting components from gltf_extras"); -}