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
This commit is contained in:
parent
e0e3a620f7
commit
1ceb5050f2
|
@ -113,6 +113,7 @@ Thanks to all the contributors helping out with this project ! Big kudos to you,
|
||||||
- [BSDGuyShawn](https://github.com/BSDGuyShawn)
|
- [BSDGuyShawn](https://github.com/BSDGuyShawn)
|
||||||
- [yukkop](https://github.com/yukkop)
|
- [yukkop](https://github.com/yukkop)
|
||||||
- [killercup](https://github.com/killercup)
|
- [killercup](https://github.com/killercup)
|
||||||
|
- [janhohenheim ](https://github.com/janhohenheim)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bevy_gltf_blueprints"
|
name = "bevy_gltf_blueprints"
|
||||||
version = "0.7.1"
|
version = "0.7.3"
|
||||||
authors = ["Mark 'kaosat-dev' Moissette"]
|
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."
|
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"
|
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bevy_gltf_components"
|
name = "bevy_gltf_components"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
authors = ["Mark 'kaosat-dev' Moissette"]
|
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."
|
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"
|
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub use utils::*;
|
pub use utils::*;
|
||||||
|
|
||||||
pub mod gltf_to_components;
|
pub mod ronstring_to_reflect_component;
|
||||||
pub use gltf_to_components::*;
|
pub use ronstring_to_reflect_component::*;
|
||||||
|
|
||||||
pub mod process_gltfs;
|
pub mod process_gltfs;
|
||||||
pub use process_gltfs::*;
|
pub use process_gltfs::*;
|
||||||
|
@ -68,14 +68,12 @@ impl Default for ComponentsFromGltfPlugin {
|
||||||
|
|
||||||
impl Plugin for ComponentsFromGltfPlugin {
|
impl Plugin for ComponentsFromGltfPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.insert_resource(GltfLoadingTracker::new())
|
app.insert_resource(GltfComponentsConfig {
|
||||||
.insert_resource(GltfComponentsConfig {
|
legacy_mode: self.legacy_mode,
|
||||||
legacy_mode: self.legacy_mode,
|
})
|
||||||
})
|
.add_systems(
|
||||||
.add_systems(Update, (track_new_gltf, process_loaded_scenes))
|
Update,
|
||||||
.add_systems(
|
(add_components_from_gltf_extras).in_set(GltfComponentsSet::Injection),
|
||||||
Update,
|
);
|
||||||
(process_loaded_scenes).in_set(GltfComponentsSet::Injection),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,101 +1,87 @@
|
||||||
use bevy::asset::AssetPath;
|
use bevy::{
|
||||||
use bevy::gltf::Gltf;
|
core::Name,
|
||||||
use bevy::utils::HashSet;
|
ecs::{
|
||||||
use bevy::{asset::LoadState, prelude::*};
|
entity::Entity,
|
||||||
use std::path::Path;
|
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)]
|
/// main function: injects components into each entity in gltf files that have `gltf_extras`, using reflection
|
||||||
/// component to keep track of gltfs' loading state
|
pub fn add_components_from_gltf_extras(world: &mut World) {
|
||||||
pub struct GltfLoadingTracker {
|
let mut extras =
|
||||||
pub loading_gltfs: HashSet<Handle<Gltf>>,
|
world.query_filtered::<(Entity, &Name, &GltfExtras, &Parent), Added<GltfExtras>>();
|
||||||
pub processed_gltfs: HashSet<String>,
|
let mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>> =
|
||||||
}
|
HashMap::new();
|
||||||
impl Default for GltfLoadingTracker {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GltfLoadingTracker {
|
let gltf_components_config = world.resource::<GltfComponentsConfig>();
|
||||||
pub fn new() -> GltfLoadingTracker {
|
|
||||||
GltfLoadingTracker {
|
|
||||||
loading_gltfs: HashSet::new(),
|
|
||||||
processed_gltfs: HashSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn add_gltf(&mut self, handle: Handle<Gltf>) {
|
|
||||||
self.loading_gltfs.insert(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn track_new_gltf(
|
for (entity, name, extra, parent) in extras.iter(world) {
|
||||||
mut tracker: ResMut<GltfLoadingTracker>,
|
|
||||||
mut events: EventReader<AssetEvent<Gltf>>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
) {
|
|
||||||
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<Assets<Gltf>>,
|
|
||||||
mut tracker: ResMut<GltfLoadingTracker>,
|
|
||||||
mut scenes: ResMut<Assets<Scene>>,
|
|
||||||
app_type_registry: Res<AppTypeRegistry>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
gltf_components_config: Res<GltfComponentsConfig>,
|
|
||||||
) {
|
|
||||||
let mut loaded_gltfs = Vec::new();
|
|
||||||
for gltf in &tracker.loading_gltfs {
|
|
||||||
debug!(
|
debug!(
|
||||||
"checking for loaded gltfs {:?}",
|
"Name: {}, entity {:?}, parent: {:?}, extras {:?}",
|
||||||
asset_server.get_load_state(gltf)
|
name, entity, parent, extra
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(load_state) = asset_server.get_load_state(gltf.clone()) {
|
let type_registry: &AppTypeRegistry = world.resource();
|
||||||
if load_state == LoadState::Loaded {
|
let type_registry = type_registry.read();
|
||||||
debug!("Adding scene to processing list");
|
let reflect_components = ronstring_to_reflect_component(
|
||||||
loaded_gltfs.push(gltf.clone());
|
&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<dyn Reflect>, 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 (entity, components) in entity_components {
|
||||||
|
if !components.is_empty() {
|
||||||
for gltf_handle in &loaded_gltfs {
|
debug!("--entity {:?}, components {}", entity, components.len());
|
||||||
if let Some(gltf) = gltfs.get_mut(gltf_handle) {
|
}
|
||||||
gltf_extras_to_components(
|
for (component, type_registration) in components {
|
||||||
gltf,
|
let mut entity_mut = world.entity_mut(entity);
|
||||||
&mut scenes,
|
debug!(
|
||||||
&*type_registry,
|
"------adding {} {:?}",
|
||||||
gltf_components_config.legacy_mode,
|
component.get_represented_type_info().unwrap().type_path(),
|
||||||
);
|
component
|
||||||
|
);
|
||||||
if let Some(path) = gltf_handle.path() {
|
type_registration
|
||||||
tracker.processed_gltfs.insert(path.to_string());
|
.data::<ReflectComponent>()
|
||||||
}
|
.unwrap()
|
||||||
} else {
|
.insert(&mut entity_mut, &*component); // TODO: how can we insert any additional components "by hand" here ?
|
||||||
warn!("could not find gltf asset, cannot process it");
|
|
||||||
}
|
}
|
||||||
tracker.loading_gltfs.remove(gltf_handle);
|
|
||||||
debug!("Done loading gltf file");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,19 @@
|
||||||
use bevy::ecs::component::Component;
|
use bevy::log::{debug, warn};
|
||||||
use bevy::render::color::Color;
|
use bevy::reflect::serde::UntypedReflectDeserializer;
|
||||||
use core::ops::Deref;
|
use bevy::reflect::{Reflect, TypeInfo, TypeRegistration, TypeRegistry};
|
||||||
|
use bevy::utils::HashMap;
|
||||||
use ron::Value;
|
use ron::Value;
|
||||||
use serde::de::DeserializeSeed;
|
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;
|
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<Color>);
|
|
||||||
|
|
||||||
pub fn ronstring_to_reflect_component(
|
pub fn ronstring_to_reflect_component(
|
||||||
ron_string: &str,
|
ron_string: &str,
|
||||||
type_registry: &TypeRegistry,
|
type_registry: &TypeRegistry,
|
||||||
simplified_types: bool,
|
simplified_types: bool,
|
||||||
) -> Vec<Box<dyn Reflect>> {
|
) -> Vec<(Box<dyn Reflect>, TypeRegistration)> {
|
||||||
let lookup: HashMap<String, Value> = ron::from_str(ron_string).unwrap();
|
let lookup: HashMap<String, Value> = ron::from_str(ron_string).unwrap();
|
||||||
let mut components: Vec<Box<dyn Reflect>> = Vec::new();
|
let mut components: Vec<(Box<dyn Reflect>, TypeRegistration)> = Vec::new();
|
||||||
for (key, value) in lookup.into_iter() {
|
for (key, value) in lookup.into_iter() {
|
||||||
let type_string = key.replace("component: ", "").trim().to_string();
|
let type_string = key.replace("component: ", "").trim().to_string();
|
||||||
let capitalized_type_name = capitalize_first_letter(type_string.as_str());
|
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() {
|
if parsed_value.is_empty() {
|
||||||
parsed_value = "()".to_string();
|
parsed_value = "()".to_string();
|
||||||
}
|
}
|
||||||
|
@ -144,7 +127,7 @@ pub fn ronstring_to_reflect_component(
|
||||||
debug!("component {:?}", component);
|
debug!("component {:?}", component);
|
||||||
debug!("real type {:?}", component.get_represented_type_info());
|
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);
|
debug!("found type registration for {}", capitalized_type_name);
|
||||||
} else {
|
} else {
|
||||||
warn!("no type registration for {}", capitalized_type_name);
|
warn!("no type registration for {}", capitalized_type_name);
|
||||||
|
@ -152,107 +135,3 @@ pub fn ronstring_to_reflect_component(
|
||||||
}
|
}
|
||||||
components
|
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 = TypeRegistry>,
|
|
||||||
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<Entity, Vec<Box<dyn Reflect>>> = 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<Box<dyn Reflect>> = 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::<ReflectComponent>()
|
|
||||||
.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");
|
|
||||||
}
|
|
Loading…
Reference in New Issue