mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-12-21 23:24:10 +00:00
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)
|
||||
- [yukkop](https://github.com/yukkop)
|
||||
- [killercup](https://github.com/killercup)
|
||||
- [janhohenheim ](https://github.com/janhohenheim)
|
||||
|
||||
## License
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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<Handle<Gltf>>,
|
||||
pub processed_gltfs: HashSet<String>,
|
||||
}
|
||||
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<GltfExtras>>();
|
||||
let mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, 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<Gltf>) {
|
||||
self.loading_gltfs.insert(handle);
|
||||
}
|
||||
}
|
||||
let gltf_components_config = world.resource::<GltfComponentsConfig>();
|
||||
|
||||
pub fn track_new_gltf(
|
||||
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 {
|
||||
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<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 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::<ReflectComponent>()
|
||||
.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");
|
||||
}
|
||||
}
|
||||
|
@ -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<Color>);
|
||||
|
||||
pub fn ronstring_to_reflect_component(
|
||||
ron_string: &str,
|
||||
type_registry: &TypeRegistry,
|
||||
simplified_types: bool,
|
||||
) -> Vec<Box<dyn Reflect>> {
|
||||
) -> Vec<(Box<dyn Reflect>, TypeRegistration)> {
|
||||
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() {
|
||||
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<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
Block a user