Compare commits

...

4 Commits

Author SHA1 Message Date
kaosat.dev
8b4bb473dc feat(Blenvy): overhauled pieces of export to make use of Bevy 0.14 upgrade
* aka we have scene_gltf_extras, no more need for cumbersome xxx_components objects !
 * still wip
2024-06-18 22:32:17 +02:00
kaosat.dev
00bf600ccf feat(Blenvy): updated (most) of the crates' code to bevy 0.14 (rc)
* tweaked & changed code where relevant
 * also added support for the new gltf_xxx_extras in bevy_gltf_components
 * animation support needs an overhaul given the extensive changes in v0.14 (wip)
 * still a giant mess, but works
 * examples not yet updated, will get overhauled
 * testing project is now independant from the "common" example code: ie no debug ui, no physics,
no bevy_asset_loader
 * testing project WORKS , even without any of the above, so asset loading (even if rough), is functional !
 * added VERY rough hierarchy/ components debug to testing project to visualize things
without bevy_editor_pls egui & co
 * related tweaks & changes
2024-06-18 22:30:59 +02:00
kaosat.dev
1686aca655 chore(testing): removed dependency on rapier etc for testing project 2024-06-17 11:55:09 +02:00
kaosat.dev
2abdb7f64e feat(Blenvy): fixed & overhauled components processing & hashing
* now correctly using nested long names + attribute name for structs
 * so no more hashing collisions for all test cases !
 * restructured internals of property group generation & registration
 * dynamically generated property group classes are now unregistered correctly
 * lots of minor related tweaks & improvements
2024-06-17 11:44:08 +02:00
36 changed files with 951 additions and 150 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_gltf_blueprints" name = "bevy_gltf_blueprints"
version = "0.10.0" version = "0.11.0"
authors = ["Mark 'kaosat-dev' Moissette"] authors = ["Mark 'kaosat-dev' Moissette"]
description = "Adds the ability to define Blueprints/Prefabs for Bevy inside gltf files and spawn them in Bevy." description = "Adds the ability to define Blueprints/Prefabs for Bevy 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"
@ -14,8 +14,8 @@ license = "MIT OR Apache-2.0"
workspace = true workspace = true
[dependencies] [dependencies]
bevy_gltf_components = { version = "0.5", path = "../bevy_gltf_components" } bevy_gltf_components = { version = "0.6", path = "../bevy_gltf_components" }
bevy = { version = "0.13", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "bevy_animation", "animation"] } bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "bevy_animation", "animation"] }
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.13", default-features = false, features = ["dynamic_linking"] } bevy = { version = "0.14.0-rc.3", default-features = false, features = ["dynamic_linking"] }

View File

@ -73,6 +73,7 @@ pub struct AnimationMarkerReached {
///////////////////// /////////////////////
/*
/// triggers events when a given animation marker is reached for INSTANCE animations /// triggers events when a given animation marker is reached for INSTANCE animations
pub fn trigger_instance_animation_markers_events( pub fn trigger_instance_animation_markers_events(
animation_infos: Query<( animation_infos: Query<(
@ -82,13 +83,15 @@ pub fn trigger_instance_animation_markers_events(
&SceneAnimations, &SceneAnimations,
&AnimationInfos, &AnimationInfos,
)>, )>,
animation_players: Query<&AnimationPlayer>, animation_players: Query<(&AnimationPlayer)>,
animation_clips: Res<Assets<AnimationClip>>, animation_clips: Res<Assets<AnimationClip>>,
animation_graphs: Res<Assets<AnimationGraph>>,
mut animation_marker_events: EventWriter<AnimationMarkerReached>, mut animation_marker_events: EventWriter<AnimationMarkerReached>,
) { ) {
for (entity, markers, link, animations, animation_infos) in animation_infos.iter() { for (entity, markers, link, animations, animation_infos) in animation_infos.iter() {
let animation_player = animation_players.get(link.0).unwrap(); let animation_player = animation_players.get(link.0).unwrap();
let animation_clip = animation_clips.get(animation_player.animation_clip()); let animation_clip = animation_clips.get(animation_player.animation_clip());
// animation_player.play(animation)
if animation_clip.is_some() { if animation_clip.is_some() {
// println!("Entity {:?} markers {:?}", entity, markers); // println!("Entity {:?} markers {:?}", entity, markers);
@ -215,3 +218,4 @@ pub fn trigger_blueprint_animation_markers_events(
} }
} }
} }
*/

View File

@ -1,4 +1,4 @@
use bevy::ecs::system::Command; use bevy::ecs::world::Command;
use bevy::prelude::*; use bevy::prelude::*;
use std::any::TypeId; use std::any::TypeId;

View File

@ -176,12 +176,13 @@ impl Plugin for BlueprintsPlugin {
.chain() .chain()
.in_set(GltfBlueprintsSet::AfterSpawn), .in_set(GltfBlueprintsSet::AfterSpawn),
) )
.add_systems( /* .add_systems(
Update, Update,
( (
trigger_instance_animation_markers_events, trigger_instance_animation_markers_events,
trigger_blueprint_animation_markers_events, trigger_blueprint_animation_markers_events,
), ),
); )*/
;
} }
} }

View File

@ -160,10 +160,10 @@ pub(crate) fn materials_inject2(
let mat_gltf = assets_gltf let mat_gltf = assets_gltf
.get(model_handle.id()) .get(model_handle.id())
.expect("material should have been preloaded"); .expect("material should have been preloaded");
if mat_gltf.named_materials.contains_key(&material_info.name) { if mat_gltf.named_materials.contains_key(&material_info.name as &str) {
let material = mat_gltf let material = mat_gltf
.named_materials .named_materials
.get(&material_info.name) .get(&material_info.name as &str)
.expect("this material should have been loaded"); .expect("this material should have been loaded");
blueprints_config blueprints_config
.material_library_cache .material_library_cache

View File

@ -1,6 +1,6 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use bevy::{gltf::Gltf, prelude::*, utils::HashMap}; use bevy::{gltf::Gltf, prelude::*, utils::hashbrown::HashMap};
use crate::{AllAssets, AssetsToLoad, AssetLoadTracker, BluePrintsConfig, BlueprintAnimations, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded}; use crate::{AllAssets, AssetsToLoad, AssetLoadTracker, BluePrintsConfig, BlueprintAnimations, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded};
@ -28,7 +28,7 @@ pub struct SpawnHere;
pub struct Spawned; pub struct Spawned;
#[derive(Component)] #[derive(Component, Debug)]
/// flag component added when a Blueprint instance ist Ready : ie : /// flag component added when a Blueprint instance ist Ready : ie :
/// - its assets have loaded /// - its assets have loaded
/// - it has finished spawning /// - it has finished spawning
@ -194,8 +194,13 @@ pub(crate) fn check_for_loaded2(
println!("loading {}: // load state: {:?}", tracker.name, asset_server.load_state(asset_id)); println!("loading {}: // load state: {:?}", tracker.name, asset_server.load_state(asset_id));
// FIXME: hack for now // FIXME: hack for now
let failed = asset_server.load_state(asset_id) == bevy::asset::LoadState::Failed; let mut failed = false;// asset_server.load_state(asset_id) == bevy::asset::LoadState::Failed(_error);
match asset_server.load_state(asset_id) {
bevy::asset::LoadState::Failed(_) => {
failed = true
},
_ => {}
}
tracker.loaded = loaded || failed; tracker.loaded = loaded || failed;
if loaded || failed { if loaded || failed {
loaded_amount += 1; loaded_amount += 1;
@ -295,6 +300,12 @@ pub(crate) fn spawn_from_blueprints2(
original_children.push(*child); original_children.push(*child);
} }
} }
let mut named_animations:HashMap<String, Handle<AnimationClip>> = HashMap::new() ;
for (key, value) in gltf.named_animations.iter() {
named_animations.insert(key.to_string(), value.clone());
}
commands.entity(entity).insert(( commands.entity(entity).insert((
SceneBundle { SceneBundle {
scene: scene.clone(), scene: scene.clone(),
@ -306,7 +317,7 @@ pub(crate) fn spawn_from_blueprints2(
OriginalChildren(original_children), OriginalChildren(original_children),
BlueprintAnimations { BlueprintAnimations {
// these are animations specific to the inside of the blueprint // these are animations specific to the inside of the blueprint
named_animations: gltf.named_animations.clone(), named_animations: named_animations//gltf.named_animations.clone(),
}, },
)); ));

View File

@ -38,7 +38,7 @@ pub(crate) fn spawned_blueprint_post_process(
for (original, children, original_children, animations, no_inblueprint, name) in for (original, children, original_children, animations, no_inblueprint, name) in
unprocessed_entities.iter() unprocessed_entities.iter()
{ {
debug!("post processing blueprint for entity {:?}", name); info!("post processing blueprint for entity {:?}", name);
if children.len() == 0 { if children.len() == 0 {
warn!("timing issue ! no children found, please restart your bevy app (bug being investigated)"); warn!("timing issue ! no children found, please restart your bevy app (bug being investigated)");
@ -94,9 +94,10 @@ pub(crate) fn spawned_blueprint_post_process(
commands.entity(original).remove::<SpawnHere>(); commands.entity(original).remove::<SpawnHere>();
commands.entity(original).remove::<Spawned>(); commands.entity(original).remove::<Spawned>();
commands.entity(original).remove::<Handle<Scene>>(); // commands.entity(original).remove::<Handle<Scene>>(); // FIXME: if we delete the handle to the scene, things get despawned ! not what we want
//commands.entity(original).remove::<AssetsToLoad>(); // also clear the sub assets tracker to free up handles, perhaps just freeing up the handles and leave the rest would be better ? //commands.entity(original).remove::<AssetsToLoad>(); // also clear the sub assets tracker to free up handles, perhaps just freeing up the handles and leave the rest would be better ?
//commands.entity(original).remove::<BlueprintAssetsLoaded>(); //commands.entity(original).remove::<BlueprintAssetsLoaded>();
commands.entity(root_entity).despawn_recursive(); commands.entity(root_entity).despawn_recursive();
info!("DONE WITH POST PROCESS");
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_gltf_components" name = "bevy_gltf_components"
version = "0.5.1" version = "0.6.0"
authors = ["Mark 'kaosat-dev' Moissette"] authors = ["Mark 'kaosat-dev' Moissette"]
description = "Allows you to define Bevy components direclty inside gltf files and instanciate the components on the Bevy side." description = "Allows you to define Bevy 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"
@ -14,9 +14,9 @@ license = "MIT OR Apache-2.0"
workspace = true workspace = true
[dependencies] [dependencies]
bevy = { version = "0.13", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] } bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
serde = "1.0.188" serde = "1.0.188"
ron = "0.8.1" ron = "0.8.1"
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.13", default-features = false, features = ["dynamic_linking"] } bevy = { version = "0.14.0-rc.3", default-features = false, features = ["dynamic_linking"] }

View File

@ -6,7 +6,7 @@ use bevy::{
reflect::{AppTypeRegistry, ReflectComponent}, reflect::{AppTypeRegistry, ReflectComponent},
world::World, world::World,
}, },
gltf::GltfExtras, gltf::{GltfExtras, GltfMaterialExtras, GltfMeshExtras, GltfSceneExtras},
hierarchy::Parent, hierarchy::Parent,
log::debug, log::debug,
reflect::{Reflect, TypeRegistration}, reflect::{Reflect, TypeRegistration},
@ -15,25 +15,8 @@ use bevy::{
use crate::{ronstring_to_reflect_component, GltfProcessed}; use crate::{ronstring_to_reflect_component, GltfProcessed};
/// main function: injects components into each entity in gltf files that have `gltf_extras`, using reflection // , mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>>
pub fn add_components_from_gltf_extras(world: &mut World) { fn find_entity_components(entity: Entity, name: &Name, parent: &Parent, reflect_components: Vec<(Box<dyn Reflect>, TypeRegistration)>, entity_components: &HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>>) -> (Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>){
let mut extras =
world.query_filtered::<(Entity, &Name, &GltfExtras, &Parent), (Added<GltfExtras>, Without<GltfProcessed>)>();
let mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>> =
HashMap::new();
// let gltf_components_config = world.resource::<GltfComponentsConfig>();
for (entity, name, extra, parent) in extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
// we assign the components specified /xxx_components objects to their parent node // we assign the components specified /xxx_components objects to their parent node
let mut target_entity = entity; 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 // 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
@ -57,12 +40,86 @@ pub fn add_components_from_gltf_extras(world: &mut World) {
for (component, type_registration) in reflect_components { for (component, type_registration) in reflect_components {
updated_components.push((component.clone_value(), type_registration)); updated_components.push((component.clone_value(), type_registration));
} }
entity_components.insert(target_entity, updated_components); return (target_entity, updated_components)
//entity_components.insert(target_entity, updated_components);
} else { } else {
entity_components.insert(target_entity, reflect_components); return (target_entity, reflect_components);
// entity_components.insert(target_entity, reflect_components);
} }
}
/// 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>, Without<GltfProcessed>)>();
let mut scene_extras = world.query_filtered::<(Entity, &Name, &GltfSceneExtras, &Parent), (Added<GltfSceneExtras>, Without<GltfProcessed>)>();
let mut mesh_extras = world.query_filtered::<(Entity, &Name, &GltfMeshExtras, &Parent), (Added<GltfMeshExtras>, Without<GltfProcessed>)>();
let mut material_extras = world.query_filtered::<(Entity, &Name, &GltfMaterialExtras, &Parent), (Added<GltfMaterialExtras>, Without<GltfProcessed>)>();
let mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>> =
HashMap::new();
// let gltf_components_config = world.resource::<GltfComponentsConfig>();
for (entity, name, extra, parent) in extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
let (target_entity, updated_components) = find_entity_components(entity, name, parent, reflect_components, &entity_components);
entity_components.insert(target_entity, updated_components);
} }
for (entity, name, extra, parent) in scene_extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, scene_extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
let (target_entity, updated_components) = find_entity_components(entity, name, parent, reflect_components, &entity_components);
entity_components.insert(target_entity, updated_components);
}
for (entity, name, extra, parent) in mesh_extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, mesh_extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
let (target_entity, updated_components) = find_entity_components(entity, name, parent, reflect_components, &entity_components);
entity_components.insert(target_entity, updated_components);
}
for (entity, name, extra, parent) in material_extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, material_extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
let (target_entity, updated_components) = find_entity_components(entity, name, parent, reflect_components, &entity_components);
entity_components.insert(target_entity, updated_components);
}
for (entity, components) in entity_components { for (entity, components) in entity_components {
let type_registry: &AppTypeRegistry = world.resource(); let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.clone(); let type_registry = type_registry.clone();

View File

@ -1,5 +1,5 @@
use bevy::log::{debug, warn}; use bevy::log::{debug, warn};
use bevy::reflect::serde::UntypedReflectDeserializer; use bevy::reflect::serde::ReflectDeserializer;
use bevy::reflect::{Reflect, TypeRegistration, TypeRegistry}; use bevy::reflect::{Reflect, TypeRegistration, TypeRegistry};
use bevy::utils::HashMap; use bevy::utils::HashMap;
use ron::Value; use ron::Value;
@ -68,7 +68,7 @@ fn components_string_to_components(
debug!("component data ron string {}", ron_string); debug!("component data ron string {}", ron_string);
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str()) let mut deserializer = ron::Deserializer::from_str(ron_string.as_str())
.expect("deserialzer should have been generated from string"); .expect("deserialzer should have been generated from string");
let reflect_deserializer = UntypedReflectDeserializer::new(type_registry); let reflect_deserializer = ReflectDeserializer::new(type_registry);
let component = reflect_deserializer let component = reflect_deserializer
.deserialize(&mut deserializer) .deserialize(&mut deserializer)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
@ -113,7 +113,7 @@ fn bevy_components_string_to_components(
debug!("component data ron string {}", ron_string); debug!("component data ron string {}", ron_string);
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str()) let mut deserializer = ron::Deserializer::from_str(ron_string.as_str())
.expect("deserialzer should have been generated from string"); .expect("deserialzer should have been generated from string");
let reflect_deserializer = UntypedReflectDeserializer::new(type_registry); let reflect_deserializer = ReflectDeserializer::new(type_registry);
let component = reflect_deserializer let component = reflect_deserializer
.deserialize(&mut deserializer) .deserialize(&mut deserializer)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {

View File

@ -14,8 +14,8 @@ license = "MIT OR Apache-2.0"
workspace = true workspace = true
[dependencies] [dependencies]
bevy = { version = "0.13", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] } bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
bevy_gltf_blueprints = { version = "0.10", path = "../bevy_gltf_blueprints" } bevy_gltf_blueprints = { version = "0.11", path = "../bevy_gltf_blueprints" }
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.13", default-features = false, features = ["dynamic_linking"] } bevy = { version = "0.14.0-rc.3", default-features = false, features = ["dynamic_linking"] }

View File

@ -147,7 +147,7 @@ pub(crate) fn save_game(world: &mut World) {
// dyn_scene.resources.append(&mut dyn_scene_root.resources); // dyn_scene.resources.append(&mut dyn_scene_root.resources);
let serialized_scene = dyn_scene let serialized_scene = dyn_scene
.serialize_ron(world.resource::<AppTypeRegistry>()) .serialize(&world.resource::<AppTypeRegistry>().read())
.unwrap(); .unwrap();
let save_path = Path::new("assets") let save_path = Path::new("assets")

View File

@ -11,11 +11,11 @@ edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
bevy = { version = "0.13", default-features = false, features = ["bevy_scene"] } bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_scene"] }
bevy_reflect = { version = "0.13", default-features = false } bevy_reflect = { version = "0.14.0-rc.3", default-features = false }
bevy_app = { version = "0.13", default-features = false, features = ["bevy_reflect"] } bevy_app = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_reflect"] }
bevy_ecs = { version = "0.13", default-features = false, features = ["bevy_reflect"] } bevy_ecs = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_reflect"] }
serde_json = "1.0.108" serde_json = "1.0.108"
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.13", default-features = false, features = ["dynamic_linking"] } bevy = { version = "0.14.0-rc.3", default-features = false, features = ["dynamic_linking"] }

22
crates/blenvy/Cargo.toml Normal file
View File

@ -0,0 +1,22 @@
[package]
name = "blenvy"
version = "0.0.1"
authors = ["Mark 'kaosat-dev' Moissette"]
description = "Allows you to define Bevy components direclty inside gltf files and instanciate the components on the Bevy side."
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
keywords = ["gamedev", "bevy", "assets", "gltf", "components"]
categories = ["game-development"]
edition = "2021"
license = "MIT OR Apache-2.0"
[lints]
workspace = true
[dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
serde = "1.0.188"
ron = "0.8.1"
[dev-dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["dynamic_linking"] }

View File

@ -0,0 +1,8 @@
use bevy::prelude::*;
mod lighting;
pub use lighting::*;
pub(crate) fn plugin(app: &mut App) {
app.add_plugins(lighting::plugin);
}

View File

@ -0,0 +1,97 @@
use bevy::pbr::DirectionalLightShadowMap;
use bevy::prelude::*;
use crate::GltfComponentsSet;
pub(crate) fn plugin(app: &mut App) {
app.register_type::<BlenderBackgroundShader>()
.register_type::<BlenderShadowSettings>()
.register_type::<BlenderLightShadows>()
.add_systems(
Update,
(process_lights, process_shadowmap, process_background_shader)
.after(GltfComponentsSet::Injection),
);
}
#[derive(Component, Reflect, Default, Debug, PartialEq, Clone)]
#[reflect(Component)]
#[non_exhaustive]
/// The properties of a light's shadow , to enable controlling per light shadows from Blender
pub struct BlenderLightShadows {
pub enabled: bool,
pub buffer_bias: f32,
}
/// The background color as described by Blender's [background shader](https://docs.blender.org/manual/en/latest/render/shader_nodes/shader/background.html).
#[derive(Component, Reflect, Default, Debug, PartialEq, Clone)]
#[reflect(Component)]
#[non_exhaustive]
pub struct BlenderBackgroundShader {
pub color: Color,
pub strength: f32,
}
/// The settings used by EEVEE's [shadow rendering](https://docs.blender.org/manual/en/latest/render/eevee/render_settings/shadows.html).
#[derive(Component, Reflect, Default, Debug, PartialEq, Clone)]
#[reflect(Component)]
#[non_exhaustive]
pub struct BlenderShadowSettings {
pub cascade_size: usize,
}
fn process_lights(
mut directional_lights: Query<
(&mut DirectionalLight, Option<&BlenderLightShadows>),
Added<DirectionalLight>,
>,
mut spot_lights: Query<(&mut SpotLight, Option<&BlenderLightShadows>), Added<SpotLight>>,
mut point_lights: Query<(&mut PointLight, Option<&BlenderLightShadows>), Added<PointLight>>,
) {
for (mut light, blender_light_shadows) in directional_lights.iter_mut() {
if let Some(blender_light_shadows) = blender_light_shadows {
light.shadows_enabled = blender_light_shadows.enabled;
} else {
light.shadows_enabled = true;
}
}
for (mut light, blender_light_shadows) in spot_lights.iter_mut() {
if let Some(blender_light_shadows) = blender_light_shadows {
light.shadows_enabled = blender_light_shadows.enabled;
} else {
light.shadows_enabled = true;
}
}
for (mut light, blender_light_shadows) in point_lights.iter_mut() {
if let Some(blender_light_shadows) = blender_light_shadows {
light.shadows_enabled = blender_light_shadows.enabled;
} else {
light.shadows_enabled = true;
}
}
}
fn process_shadowmap(
shadowmaps: Query<&BlenderShadowSettings, Added<BlenderShadowSettings>>,
mut commands: Commands,
) {
for shadowmap in shadowmaps.iter() {
commands.insert_resource(DirectionalLightShadowMap {
size: shadowmap.cascade_size,
});
}
}
fn process_background_shader(
background_shaders: Query<&BlenderBackgroundShader, Added<BlenderBackgroundShader>>,
mut commands: Commands,
) {
for background_shader in background_shaders.iter() {
commands.insert_resource(AmbientLight {
color: background_shader.color,
// Just a guess, see <https://github.com/bevyengine/bevy/issues/12280>
brightness: background_shader.strength * 400.0,
});
}
}

78
crates/blenvy/src/lib.rs Normal file
View File

@ -0,0 +1,78 @@
pub mod utils;
pub use utils::*;
pub mod ronstring_to_reflect_component;
pub use ronstring_to_reflect_component::*;
pub mod process_gltfs;
pub use process_gltfs::*;
pub mod blender_settings;
use bevy::{
ecs::{component::Component, reflect::ReflectComponent, system::Resource},
prelude::{App, IntoSystemConfigs, Plugin, SystemSet, Update},
reflect::Reflect,
};
/// A Bevy plugin for extracting components from gltf files and automatically adding them to the relevant entities
/// It will automatically run every time you load a gltf file
/// Add this plugin to your Bevy app to get access to this feature
/// ```
/// # use bevy::prelude::*;
/// # use bevy::gltf::*;
/// # use bevy_gltf_components::ComponentsFromGltfPlugin;
///
/// //too barebones of an example to be meaningfull, please see https://github.com/kaosat-dev/Blender_bevy_components_workflow/examples/basic for a real example
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_plugin(ComponentsFromGltfPlugin)
/// .add_system(spawn_level)
/// .run();
/// }
///
/// fn spawn_level(
/// asset_server: Res<AssetServer>,
/// mut commands: bevy::prelude::Commands,
/// keycode: Res<Input<KeyCode>>,
/// ){
/// if keycode.just_pressed(KeyCode::Return) {
/// commands.spawn(SceneBundle {
/// scene: asset_server.load("basic/models/level1.glb"),
/// transform: Transform::from_xyz(2.0, 0.0, -5.0),
/// ..Default::default()
/// });
/// }
///}
/// ```
/// this is a flag component to tag a processed gltf, to avoid processing things multiple times
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct GltfProcessed;
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
/// systemset to order your systems after the component injection when needed
pub enum GltfComponentsSet {
Injection,
}
#[derive(Clone, Resource)]
pub struct GltfComponentsConfig {}
#[derive(Default)]
pub struct ComponentsFromGltfPlugin {}
impl Plugin for ComponentsFromGltfPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(blender_settings::plugin)
.register_type::<GltfProcessed>()
.insert_resource(GltfComponentsConfig {})
.add_systems(
Update,
(add_components_from_gltf_extras).in_set(GltfComponentsSet::Injection),
);
}
}

View File

@ -0,0 +1,130 @@
use bevy::{
core::Name,
ecs::{
entity::Entity,
query::{Added, Without},
reflect::{AppTypeRegistry, ReflectComponent},
world::World,
},
gltf::{GltfExtras, GltfMaterialExtras, GltfMeshExtras, GltfSceneExtras},
hierarchy::Parent,
log::debug,
reflect::{Reflect, TypeRegistration},
utils::HashMap,
};
use crate::{ronstring_to_reflect_component, GltfProcessed};
fn bla_balb(entity: Entity, name: &Name, parent: &Parent, reflect_components: Vec<(Box<dyn Reflect>, TypeRegistration)>, mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>>){
// 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);
}
}
/// 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>, Without<GltfProcessed>)>();
let mut scene_extras = world.query_filtered::<(Entity, &Name, &GltfSceneExtras, &Parent), (Added<GltfSceneExtras>, Without<GltfProcessed>)>();
let mut mesh_extras = world.query_filtered::<(Entity, &Name, &GltfMeshExtras, &Parent), (Added<GltfMeshExtras>, Without<GltfProcessed>)>();
let mut material_extras = world.query_filtered::<(Entity, &Name, &GltfMaterialExtras, &Parent), (Added<GltfMaterialExtras>, Without<GltfProcessed>)>();
let mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>> =
HashMap::new();
// let gltf_components_config = world.resource::<GltfComponentsConfig>();
for (entity, name, extra, parent) in extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
bla_balb(entity, name, parent, reflect_components, entity_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>, 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);
} */
}
for (entity, components) in entity_components {
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.clone();
let type_registry = type_registry.read();
if !components.is_empty() {
debug!("--entity {:?}, components {}", entity, components.len());
}
for (component, type_registration) in components {
debug!(
"------adding {} {:?}",
component.get_represented_type_info().unwrap().type_path(),
component
);
{
let mut entity_mut = world.entity_mut(entity);
type_registration
.data::<ReflectComponent>()
.expect("Unable to reflect component")
.insert(&mut entity_mut, &*component, &type_registry);
entity_mut.insert(GltfProcessed); // this is how can we insert any additional components
}
}
}
}

View File

@ -0,0 +1,134 @@
use bevy::log::{debug, warn};
use bevy::reflect::serde::ReflectDeserializer;
use bevy::reflect::{Reflect, TypeRegistration, TypeRegistry};
use bevy::utils::HashMap;
use ron::Value;
use serde::de::DeserializeSeed;
use super::capitalize_first_letter;
pub fn ronstring_to_reflect_component(
ron_string: &str,
type_registry: &TypeRegistry,
) -> Vec<(Box<dyn Reflect>, TypeRegistration)> {
let lookup: HashMap<String, Value> = ron::from_str(ron_string).unwrap();
let mut components: Vec<(Box<dyn Reflect>, TypeRegistration)> = Vec::new();
// println!("ron_string {:?}", ron_string);
for (name, value) in lookup.into_iter() {
let parsed_value: String = match value.clone() {
Value::String(str) => {
str
}
_ => ron::to_string(&value).unwrap().to_string(),
};
if name.as_str() == "bevy_components" {
bevy_components_string_to_components(parsed_value, type_registry, &mut components);
} else {
components_string_to_components(
name,
value,
parsed_value,
type_registry,
&mut components,
);
}
}
components
}
fn components_string_to_components(
name: String,
value: Value,
parsed_value: String,
type_registry: &TypeRegistry,
components: &mut Vec<(Box<dyn Reflect>, TypeRegistration)>,
) {
let type_string = name.replace("component: ", "").trim().to_string();
let capitalized_type_name = capitalize_first_letter(type_string.as_str());
if let Some(type_registration) =
type_registry.get_with_short_type_path(capitalized_type_name.as_str())
{
debug!("TYPE INFO {:?}", type_registration.type_info());
let ron_string = format!(
"{{ \"{}\":{} }}",
type_registration.type_info().type_path(),
parsed_value
);
// usefull to determine what an entity looks like Serialized
/*let test_struct = CameraRenderGraph::new("name");
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())
.expect("deserialzer should have been generated from string");
let reflect_deserializer = ReflectDeserializer::new(type_registry);
let component = reflect_deserializer
.deserialize(&mut deserializer)
.unwrap_or_else(|_| {
panic!(
"failed to deserialize component {} with value: {:?}",
name, value
)
});
debug!("component {:?}", component);
debug!("real type {:?}", component.get_represented_type_info());
components.push((component, type_registration.clone()));
debug!("found type registration for {}", capitalized_type_name);
} else {
warn!("no type registration for {}", capitalized_type_name);
}
}
fn bevy_components_string_to_components(
parsed_value: String,
type_registry: &TypeRegistry,
components: &mut Vec<(Box<dyn Reflect>, TypeRegistration)>,
) {
let lookup: HashMap<String, Value> = ron::from_str(&parsed_value).unwrap();
for (key, value) in lookup.into_iter() {
let parsed_value: String = match value.clone() {
Value::String(str) => {
str
}
_ => ron::to_string(&value).unwrap().to_string(),
};
if let Some(type_registration) = type_registry.get_with_type_path(key.as_str()) {
debug!("TYPE INFO {:?}", type_registration.type_info());
let ron_string = format!(
"{{ \"{}\":{} }}",
type_registration.type_info().type_path(),
parsed_value
);
debug!("component data ron string {}", ron_string);
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str())
.expect("deserialzer should have been generated from string");
let reflect_deserializer = ReflectDeserializer::new(type_registry);
let component = reflect_deserializer
.deserialize(&mut deserializer)
.unwrap_or_else(|_| {
panic!(
"failed to deserialize component {} with value: {:?}",
key, value
)
});
debug!("component {:?}", component);
debug!("real type {:?}", component.get_represented_type_info());
components.push((component, type_registration.clone()));
debug!("found type registration for {}", key);
} else {
warn!("no type registration for {}", key);
}
}
}

View File

@ -0,0 +1,3 @@
pub fn capitalize_first_letter(s: &str) -> String {
s[0..1].to_uppercase() + &s[1..]
}

View File

@ -5,13 +5,14 @@ edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
bevy = { version = "0.13", features = ["dynamic_linking"] } bevy = { version = "0.14.0-rc.3", features = ["dynamic_linking"] }
bevy_gltf_blueprints = { path = "../../crates/bevy_gltf_blueprints" } bevy_gltf_blueprints = { path = "../../crates/bevy_gltf_blueprints" }
bevy_registry_export = { path = "../../crates/bevy_registry_export" } bevy_registry_export = { path = "../../crates/bevy_registry_export" }
bevy_gltf_worlflow_examples_common_rapier = { path = "../../examples/common_rapier" } # bevy_gltf_worlflow_examples_common_rapier = { path = "../../examples/common_rapier" }
#bevy_gltf_worlflow_examples_common = { path = "../../examples/common" }
bevy_rapier3d = { version = "0.25.0", features = ["serde-serialize", "debug-render-3d", "enhanced-determinism"] } #evy_rapier3d = { version = "0.25.0", features = ["serde-serialize", "debug-render-3d", "enhanced-determinism"] }
bevy_asset_loader = { version = "0.20", features = ["standard_dynamic_assets"] } #bevy_asset_loader = { version = "0.20", features = ["standard_dynamic_assets"] }
bevy_editor_pls = { version = "0.8" } #bevy_editor_pls = { version = "0.8" }
rand = "0.8.5" rand = "0.8.5"
json-writer ="0.3" json-writer ="0.3"

View File

@ -34,6 +34,7 @@ pub fn setup_main_scene_animations(asset_server: Res<AssetServer>, mut commands:
commands.insert_resource(AnimTest(asset_server.load("levels/World.glb"))); commands.insert_resource(AnimTest(asset_server.load("levels/World.glb")));
} }
/*
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn animations( pub fn animations(
added_animation_players: Query<(Entity, &Name, &AnimationPlayer)>, added_animation_players: Query<(Entity, &Name, &AnimationPlayer)>,
@ -225,7 +226,7 @@ pub fn play_animations(
} }
} }
} }
*/
pub fn react_to_animation_markers( pub fn react_to_animation_markers(
mut animation_marker_events: EventReader<AnimationMarkerReached>, mut animation_marker_events: EventReader<AnimationMarkerReached>,
) { ) {

View File

@ -1,8 +1,8 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, BlueprintPath, GameWorldTag}; use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, BlueprintPath, GameWorldTag};
use bevy_gltf_worlflow_examples_common_rapier::{GameState, InAppRunning}; use crate::{GameState, InAppRunning};
use bevy_rapier3d::prelude::Velocity; //use bevy_rapier3d::prelude::Velocity;
use rand::Rng; use rand::Rng;
pub fn setup_game( pub fn setup_game(
@ -69,10 +69,10 @@ pub fn spawn_test(
// BlueprintName("Health_Pickup".to_string()), // BlueprintName("Health_Pickup".to_string()),
// SpawnHere, // SpawnHere,
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
Velocity { /*Velocity {
linvel: Vec3::new(vel_x, vel_y, vel_z), linvel: Vec3::new(vel_x, vel_y, vel_z),
angvel: Vec3::new(0.0, 0.0, 0.0), angvel: Vec3::new(0.0, 0.0, 0.0),
}, },*/
)) ))
.id(); .id();
commands.entity(world).add_child(new_entity); commands.entity(world).add_child(new_entity);

View File

@ -1,8 +1,9 @@
pub mod animation;
pub mod in_game; pub mod in_game;
pub use animation::*;
pub use in_game::*; pub use in_game::*;
pub mod animation;
pub use animation::*;
use std::{collections::HashMap, fs, time::Duration}; use std::{collections::HashMap, fs, time::Duration};
use bevy_gltf_blueprints::{ use bevy_gltf_blueprints::{
@ -13,12 +14,14 @@ use bevy::{
prelude::*, render::view::screenshot::ScreenshotManager, time::common_conditions::on_timer, prelude::*, render::view::screenshot::ScreenshotManager, time::common_conditions::on_timer,
window::PrimaryWindow, window::PrimaryWindow,
}; };
use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState}; use crate::{AppState, GameState};
use json_writer::to_json_string; use json_writer::to_json_string;
fn start_game(mut next_app_state: ResMut<NextState<AppState>>) { fn start_game(mut next_app_state: ResMut<NextState<AppState>>) {
next_app_state.set(AppState::AppLoading); println!("START GAME");
//next_app_state.set(AppState::AppLoading);
next_app_state.set(AppState::AppRunning);
} }
// if the export from Blender worked correctly, we should have animations (simplified here by using AnimationPlayerLink) // if the export from Blender worked correctly, we should have animations (simplified here by using AnimationPlayerLink)
@ -121,7 +124,7 @@ fn generate_screenshot(
} }
fn exit_game(mut app_exit_events: ResMut<Events<bevy::app::AppExit>>) { fn exit_game(mut app_exit_events: ResMut<Events<bevy::app::AppExit>>) {
app_exit_events.send(bevy::app::AppExit); app_exit_events.send(bevy::app::AppExit::Success);
} }
pub struct GamePlugin; pub struct GamePlugin;
@ -134,16 +137,18 @@ impl Plugin for GamePlugin {
.add_systems(Update, (spawn_test).run_if(in_state(GameState::InGame))) .add_systems(Update, (spawn_test).run_if(in_state(GameState::InGame)))
.add_systems(Update, validate_export) .add_systems(Update, validate_export)
//.add_systems(OnEnter(AppState::CoreLoading), start_game)
.add_systems(OnEnter(AppState::MenuRunning), start_game) .add_systems(OnEnter(AppState::MenuRunning), start_game)
.add_systems(OnEnter(AppState::AppRunning), setup_game) .add_systems(OnEnter(AppState::AppRunning), setup_game)
.add_systems(OnEnter(AppState::MenuRunning), setup_main_scene_animations) .add_systems(OnEnter(AppState::MenuRunning), setup_main_scene_animations)
.add_systems(Update, (animations) /* .add_systems(Update, (animations)
.run_if(in_state(AppState::AppRunning)) .run_if(in_state(AppState::AppRunning))
.after(GltfBlueprintsSet::AfterSpawn) .after(GltfBlueprintsSet::AfterSpawn)
) )
.add_systems(Update, play_animations) .add_systems(Update, play_animations)
.add_systems(Update, react_to_animation_markers) .add_systems(Update, react_to_animation_markers)*/
/*.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once /*.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once
.add_systems( .add_systems(

View File

@ -0,0 +1,146 @@
use bevy::{gltf::{GltfMaterialExtras, GltfMeshExtras, GltfSceneExtras}, prelude::*};
use bevy_gltf_blueprints::{AllAssets, BlueprintInstanceReady};
use crate::BasicTest;
#[derive(Component)]
pub struct HiearchyDebugTag;
pub fn setup_hierarchy_debug(mut commands: Commands, asset_server: Res<AssetServer>){
// a place to display the extras on screen
commands.spawn((
TextBundle::from_section(
"",
TextStyle {
color: LinearRgba { red: 1.0, green:0.0, blue: 0.0, alpha: 1.0}.into(),
font_size: 10.,
..default()
},
)
.with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(12.0),
left: Val::Px(12.0),
..default()
}),
HiearchyDebugTag,
));
}
pub fn get_descendants(
all_children: &Query<&Children>,
all_names:&Query<&Name>, root: &Entity,
nesting: usize,
to_check: &Query<&BasicTest>//&Query<(&BlueprintInstanceReady, &AllAssets)>,
)
-> String
{
let mut hierarchy_display: Vec<String> = vec![];
let root_name = all_names.get(*root);
let name;
if root_name.is_ok() {
name = root_name.unwrap().to_string();
}else {
name = "no_name".to_string()
}
let components_to_check = to_check.get(*root);
hierarchy_display.push( format!("{}{} ({:?})", " ".repeat(nesting), name, components_to_check) ); //
if let Ok(children) = all_children.get(*root) {
for child in children.iter() {
let child_descendants_display = get_descendants(&all_children, &all_names, &child, nesting + 4, &to_check);
hierarchy_display.push(child_descendants_display);
}
}
return hierarchy_display.join("\n");
}
pub fn draw_hierarchy_debug(
root: Query<(Entity, Option<&Name>, &Children), (Without<Parent>)>,
all_children: Query<&Children>,
all_names:Query<&Name>,
to_check: Query<&BasicTest>,//Query<(&BlueprintInstanceReady, &AllAssets)>,
mut display: Query<&mut Text, With<HiearchyDebugTag>>,
){
let mut hierarchy_display: Vec<String> = vec![];
for (root_entity, name, children) in root.iter() {
// hierarchy_display.push( format!("Hierarchy root{:?}", name) );
hierarchy_display.push(get_descendants(&all_children, &all_names, &root_entity, 0, &to_check));
// let mut children = all_children.get(root_entity);
/*for child in children.iter() {
// hierarchy_display
let name = all_names.get(*child); //.unwrap_or(&Name::new("no name"));
hierarchy_display.push(format!(" {:?}", name))
}*/
//
}
let mut display = display.single_mut();
display.sections[0].value = hierarchy_display.join("\n");
}
////////:just some testing for gltf extras
fn check_for_gltf_extras(
gltf_extras_per_entity: Query<(
Entity,
Option<&Name>,
Option<&GltfSceneExtras>,
Option<&GltfExtras>,
Option<&GltfMeshExtras>,
Option<&GltfMaterialExtras>,
)>,
mut display: Query<&mut Text, With<HiearchyDebugTag>>,
) {
let mut gltf_extra_infos_lines: Vec<String> = vec![];
for (id, name, scene_extras, extras, mesh_extras, material_extras) in
gltf_extras_per_entity.iter()
{
if scene_extras.is_some()
//|| extras.is_some()
|| mesh_extras.is_some()
|| material_extras.is_some()
{
let formatted_extras = format!(
"Extras per entity {} ('Name: {}'):
- scene extras: {:?}
- mesh extras: {:?}
- material extras: {:?}
",
id,
name.unwrap_or(&Name::default()),
scene_extras,
//extras,
mesh_extras,
material_extras
);
gltf_extra_infos_lines.push(formatted_extras);
}
let mut display = display.single_mut();
display.sections[0].value = gltf_extra_infos_lines.join("\n");
}
}
pub struct HiearchyDebugPlugin;
impl Plugin for HiearchyDebugPlugin {
fn build(&self, app: &mut App) {
app
.add_systems(Startup, setup_hierarchy_debug)
.add_systems(Update, draw_hierarchy_debug)
//.add_systems(Update, check_for_gltf_extras)
;
}
}

View File

@ -1,5 +1,7 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_gltf_worlflow_examples_common_rapier::CommonPlugin; // use bevy_gltf_worlflow_examples_common::CommonPlugin;
mod state;
use state::*;
mod core; mod core;
use crate::core::*; use crate::core::*;
@ -11,12 +13,18 @@ mod dupe_components;
mod test_components; mod test_components;
use test_components::*; use test_components::*;
mod hierarchy_debug;
use hierarchy_debug::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins(( .add_plugins((
DefaultPlugins.set(AssetPlugin::default()), DefaultPlugins.set(AssetPlugin::default()),
HiearchyDebugPlugin,
// our custom plugins // our custom plugins
CommonPlugin, // CommonPlugin,
StatePlugin,
CorePlugin, // reusable plugins CorePlugin, // reusable plugins
GamePlugin, // specific to our game GamePlugin, // specific to our game
ComponentsTestPlugin, // Showcases different type of components /structs ComponentsTestPlugin, // Showcases different type of components /structs

View File

@ -0,0 +1,59 @@
use bevy::prelude::*;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
pub enum AppState {
CoreLoading,
#[default]
MenuRunning,
AppLoading,
AppRunning,
AppEnding,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
pub enum GameState {
#[default]
None,
InMenu,
InGame,
InGameOver,
InSaving,
InLoading,
}
// tag components for all entities within a certain state (for despawning them if needed) , FIXME: seems kinda hack-ish
#[derive(Component)]
pub struct InCoreLoading;
#[derive(Component, Default)]
pub struct InMenuRunning;
#[derive(Component)]
pub struct InAppRunning;
// components for tagging in game vs in game menu stuff
#[derive(Component, Default)]
pub struct InMainMenu;
#[derive(Component, Default)]
pub struct InMenu;
#[derive(Component, Default)]
pub struct InGame;
#[derive(Component, Default)]
pub struct InGameSaving;
#[derive(Component, Default)]
pub struct InGameLoading;
pub struct StatePlugin;
impl Plugin for StatePlugin {
fn build(&self, app: &mut App) {
app
.init_state::<AppState>()
.init_state::<GameState>();
}
}

View File

@ -117,6 +117,10 @@ pub struct VecOfVec3s2(Vec<TupleVec3>);
#[reflect(Component)] #[reflect(Component)]
pub struct VecOfColors(Vec<Color>); pub struct VecOfColors(Vec<Color>);
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct VecOfUints(Vec<u32>);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
pub struct AAAAddedCOMPONENT; pub struct AAAAddedCOMPONENT;
@ -196,6 +200,18 @@ pub struct ComponentAToFilterOut;
#[reflect(Component)] #[reflect(Component)]
pub struct ComponentBToFilterOut; pub struct ComponentBToFilterOut;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct ComponentWithFieldsOfIdenticalType{
pub first: f32,
pub second: f32,
pub third: Vec<f32>,
pub fourth: Vec<f32>,
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct ComponentWithFieldsOfIdenticalType2(f32, f32, f32);
pub struct ComponentsTestPlugin; pub struct ComponentsTestPlugin;
impl Plugin for ComponentsTestPlugin { impl Plugin for ComponentsTestPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
@ -227,6 +243,9 @@ impl Plugin for ComponentsTestPlugin {
.register_type::<Range<f32>>() .register_type::<Range<f32>>()
.register_type::<VecOfF32s>() .register_type::<VecOfF32s>()
.register_type::<Vec<f32>>() .register_type::<Vec<f32>>()
.register_type::<u32>()
.register_type::<Vec<u32>>()
.register_type::<VecOfUints>()
// .register_type::<AAAAddedCOMPONENT>() // .register_type::<AAAAddedCOMPONENT>()
.register_type::<AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut>() .register_type::<AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut>()
.register_type::<HashMap<String, String>>() .register_type::<HashMap<String, String>>()
@ -242,6 +261,9 @@ impl Plugin for ComponentsTestPlugin {
.register_type::<HashmapTestStringColorFlat>() .register_type::<HashmapTestStringColorFlat>()
.register_type::<ComponentAToFilterOut>() .register_type::<ComponentAToFilterOut>()
.register_type::<ComponentBToFilterOut>() .register_type::<ComponentBToFilterOut>()
.register_type::<ComponentWithFieldsOfIdenticalType>()
.register_type::<ComponentWithFieldsOfIdenticalType2>()
.add_plugins(MaterialPlugin::< .add_plugins(MaterialPlugin::<
ExtendedMaterial<StandardMaterial, MyExtension>, ExtendedMaterial<StandardMaterial, MyExtension>,
>::default()); >::default());

View File

@ -95,7 +95,7 @@ Components:
-> VERY likely due to the int-offset computation for hashes of components -> VERY likely due to the int-offset computation for hashes of components
- now switched to tiger_hash - now switched to tiger_hash
- [x] add warning about hash colision (not much we can/ could do if it is the case ?) - [x] add warning about hash colision (not much we can/ could do if it is the case ?)
- [ ] double check weird collisions AND/OR reuse existing if applicable - [x] double check weird collisions AND/OR reuse existing if applicable
- [x] annoying default path for registry, should be relative to the assets path - [x] annoying default path for registry, should be relative to the assets path
@ -156,15 +156,20 @@ General issues:
- [ ] rename project to Blenvy - [ ] rename project to Blenvy
- [ ] replace all references to the old 2 add-ons with those to Blenvy - [ ] replace all references to the old 2 add-ons with those to Blenvy
- [ ] rename repo to "Blenvy" - [ ] rename repo to "Blenvy"
- [x] switch to bevy 0.14 rc2
- [ ] find a solution for the new color handling
- [ ] add back lighting_components
- [ ] check if scene components are being deleted through our scene re-orgs in the spawn post process
- [ ] simplify testing example: - [ ] simplify testing example:
- [ ] remove use of rapier physics (or even the whole common boilerplate ?) - [x] remove use of rapier physics (or even the whole common boilerplate ?)
- [ ] remove/replace bevy editor pls with some native ui to display hierarchies - [ ] remove/replace bevy editor pls with some native ui to display hierarchies
- [ ] switch to bevy rc2
- [ ] simplify examples:
- [ ] a full fledged demo (including physics & co)
- [ ] other examples without interactions or physics
- [x] overall cleanup - [x] overall cleanup

View File

@ -20,23 +20,13 @@ def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_out
temp_root_collection = temp_scene.collection temp_root_collection = temp_scene.collection
print("additional_dataAAAAAAAAAAAAAAAH", additional_data) print("additional_dataAAAAAAAAAAAAAAAH", additional_data)
properties_black_list = ['user_assets', 'components_meta']
if additional_data is not None: # FIXME not a fan of having this here if additional_data is not None: # FIXME not a fan of having this here
for entry in dict(additional_data): for entry in dict(additional_data):
print("entry in additional data", entry) # we copy everything over except those on the black list
if entry == "local_assets": if entry not in properties_black_list:
temp_scene["local_assets"] = additional_data[entry] # this is for bevy 0.14 print("entry in additional data", entry, "value", additional_data[entry], "in", additional_data.name)
temp_root_collection["local_assets"] = additional_data[entry] # for previous bevy versions, remove when migration done temp_scene[entry] = additional_data[entry]
bla = "[(name: \"test_asset\", path: \"audio/fake.mp3\")]"
local_assets = additional_data.get(entry, [])
local_assets = [entry for entry in local_assets]
add_scene_property(temp_scene, 'assets_components', {"LocalAssets": f"LocalAssets({local_assets})".replace("'", '')})
if entry == entry == "AllAssets":
temp_scene["AllAssets"] = additional_data[entry]
temp_root_collection["AllAssets"] = additional_data[entry] # for previous bevy versions, remove when migration done
all_assets = additional_data.get(entry, [])
all_assets = [entry for entry in all_assets]
add_scene_property(temp_scene, 'assets_components', {"AllAssets": f"AllAssets({all_assets})".replace("'", '')})
# save active scene # save active scene
active_scene = bpy.context.window.scene active_scene = bpy.context.window.scene
@ -121,11 +111,11 @@ def clear_hollow_scene(temp_scene, original_root_collection):
def restore_original_names(collection): def restore_original_names(collection):
if collection.name.endswith("____bak"): if collection.name.endswith("____bak"):
collection.name = collection.name.replace("____bak", "") collection.name = collection.name.replace("____bak", "")
for object in collection.objects: for object in collection.all_objects:
if object.instance_type == 'COLLECTION': """if object.instance_type == 'COLLECTION':
if object.name.endswith("____bak"): if object.name.endswith("____bak"):
object.name = object.name.replace("____bak", "") object.name = object.name.replace("____bak", "")
else: else: """
if object.name.endswith("____bak"): if object.name.endswith("____bak"):
object.name = object.name.replace("____bak", "") object.name = object.name.replace("____bak", "")
for child_collection in collection.children: for child_collection in collection.children:

View File

@ -5,32 +5,33 @@ from blenvy.core.object_makers import make_empty
# TODO: replace this with placing scene level custom properties once support for that has been added to bevy_gltf # TODO: replace this with placing scene level custom properties once support for that has been added to bevy_gltf
def upsert_scene_components(main_scenes): def upsert_scene_components(main_scenes):
for scene in main_scenes: for scene in main_scenes:
lighting_components_name = f"lighting_components_{scene.name}" """lighting_components_name = f"lighting_components_{scene.name}"
lighting_components = bpy.data.objects.get(lighting_components_name, None) lighting_components = bpy.data.objects.get(lighting_components_name, None)
if not lighting_components: if not lighting_components:
root_collection = scene.collection root_collection = scene.collection
lighting_components = make_empty('lighting_components_'+scene.name, [0,0,0], [0,0,0], [0,0,0], root_collection) lighting_components = make_empty('lighting_components_'+scene.name, [0,0,0], [0,0,0], [0,0,0], root_collection)"""
if scene.world is not None: """if scene.world is not None:
lighting_components['BlenderBackgroundShader'] = ambient_color_to_component(scene.world) lighting_components['BlenderBackgroundShader'] = ambient_color_to_component(scene.world)""" # FIXME add back
lighting_components['BlenderShadowSettings'] = scene_shadows_to_component(scene) scene['BlenderShadowSettings'] = scene_shadows_to_component(scene)
if scene.eevee.use_bloom: if scene.eevee.use_bloom:
lighting_components['BloomSettings'] = scene_bloom_to_component(scene) scene['BloomSettings'] = scene_bloom_to_component(scene)
elif 'BloomSettings' in lighting_components: elif 'BloomSettings' in scene:
del lighting_components['BloomSettings'] del scene['BloomSettings']
if scene.eevee.use_gtao: if scene.eevee.use_gtao:
lighting_components['SSAOSettings'] = scene_ao_to_component(scene) scene['SSAOSettings'] = scene_ao_to_component(scene)
elif 'SSAOSettings' in lighting_components: elif 'SSAOSettings' in scene:
del lighting_components['SSAOSettings'] del scene['SSAOSettings']
def remove_scene_components(main_scenes): def remove_scene_components(main_scenes):
for scene in main_scenes: pass
"""for scene in main_scenes:
lighting_components_name = f"lighting_components_{scene.name}" lighting_components_name = f"lighting_components_{scene.name}"
lighting_components = bpy.data.objects.get(lighting_components_name, None) lighting_components = bpy.data.objects.get(lighting_components_name, None)
if lighting_components: if lighting_components:
bpy.data.objects.remove(lighting_components, do_unlink=True) bpy.data.objects.remove(lighting_components, do_unlink=True)"""
def ambient_color_to_component(world): def ambient_color_to_component(world):
@ -44,7 +45,7 @@ def ambient_color_to_component(world):
if color is not None and strength is not None: if color is not None and strength is not None:
colorRgba = f"Rgba(red: {color[0]}, green: {color[1]}, blue: {color[2]}, alpha: {color[3]})" colorRgba = f"LinearRgba(red: {color[0]}, green: {color[1]}, blue: {color[2]}, alpha: {color[3]})"
component = f"( color: {colorRgba}, strength: {strength})" component = f"( color: {colorRgba}, strength: {strength})"
return component return component
return None return None

View File

@ -220,7 +220,7 @@ def upsert_component_in_item(item, long_name, registry):
if property_group_name in registry.component_propertyGroups: if property_group_name in registry.component_propertyGroups:
# we have found a matching property_group, so try to inject it # we have found a matching property_group, so try to inject it
# now inject property group # now inject property group
setattr(ComponentMetadata, property_group_name, registry.component_propertyGroups[property_group_name]) # FIXME: not ideal as all ComponentMetadata get the propGroup, but have not found a way to assign it per instance setattr(ComponentMetadata, property_group_name, registry.component_propertyGroups[property_group_name]) # FIXME: not ideal as ALL instances of ComponentMetadata get the propGroup, but have not found a way to assign it per instance
propertyGroup = getattr(component_meta, property_group_name, None) propertyGroup = getattr(component_meta, property_group_name, None)
# now deal with property groups details # now deal with property groups details

View File

@ -30,13 +30,14 @@ def process_component(registry, definition, update, extras=None, nesting_long_na
with_list = False with_list = False
with_map = False with_map = False
padding = " " * (len(nesting_long_names) + 1) #padding = " " * (len(nesting_long_names) + 1)
#print(f"{padding}process component", long_name, "nesting_long_names",nesting_long_names, "foo", has_properties, has_prefixItems, is_enum, is_list, is_map) #print(f"{padding}process component", long_name, "nesting_long_names",nesting_long_names, "foo", has_properties, has_prefixItems, is_enum, is_list, is_map)
if has_properties: if has_properties:
__annotations__ = __annotations__ | process_structs.process_structs(registry, definition, properties, update, nesting_long_names) __annotations__ = __annotations__ | process_structs.process_structs(registry, definition, properties, update, nesting_long_names)
with_properties = True with_properties = True
tupple_or_struct = "struct" tupple_or_struct = "struct"
#print(f"{padding}struct")
if has_prefixItems: if has_prefixItems:
__annotations__ = __annotations__ | process_tupples.process_tupples(registry, definition, prefixItems, update, nesting_long_names) __annotations__ = __annotations__ | process_tupples.process_tupples(registry, definition, prefixItems, update, nesting_long_names)
@ -61,7 +62,6 @@ def process_component(registry, definition, update, extras=None, nesting_long_na
for a in __annotations__: for a in __annotations__:
field_names.append(a) field_names.append(a)
extras = extras if extras is not None else { extras = extras if extras is not None else {
"long_name": long_name "long_name": long_name
} }
@ -78,25 +78,16 @@ def process_component(registry, definition, update, extras=None, nesting_long_na
**dict(with_properties = with_properties, with_items= with_items, with_enum= with_enum, with_list= with_list, with_map = with_map, short_name= short_name, long_name=long_name), **dict(with_properties = with_properties, with_items= with_items, with_enum= with_enum, with_list= with_list, with_map = with_map, short_name= short_name, long_name=long_name),
'root_component': root_component 'root_component': root_component
} }
#FIXME: YIKES, but have not found another way:
""" Withouth this ; the following does not work # we need to pass the full hierarchy to disambiguate between components
# Withouth this ; the following does not work
"""
-BasicTest -BasicTest
- NestingTestLevel2 - NestingTestLevel2
-BasicTest => the registration & update callback of this one overwrites the first "basicTest" -BasicTest => the registration & update callback of this one overwrites the first "basicTest"
have not found a cleaner workaround so far
""" """
property_group_name = registry.generate_propGroup_name(nesting_long_names)
(property_group_pointer, property_group_class) = property_group_from_infos(property_group_name, property_group_params)
# add our component propertyGroup to the registry # add our component propertyGroup to the registry
registry.register_component_propertyGroup(property_group_name, property_group_pointer) (property_group_pointer, property_group_class) = registry.register_component_propertyGroup(nesting_long_names, property_group_params)
return (property_group_pointer, property_group_class) return (property_group_pointer, property_group_class)
def property_group_from_infos(property_group_name, property_group_parameters):
# print("creating property group", property_group_name)
property_group_class = type(property_group_name, (PropertyGroup,), property_group_parameters)
bpy.utils.register_class(property_group_class)
property_group_pointer = PointerProperty(type=property_group_class)
return (property_group_pointer, property_group_class)

View File

@ -33,7 +33,7 @@ def process_structs(registry, definition, properties, update, nesting_long_names
__annotations__[property_name] = blender_property __annotations__[property_name] = blender_property
else: else:
original_long_name = original["long_name"] original_long_name = original["long_name"]
(sub_component_group, _) = process_component.process_component(registry, original, update, {"nested": True, "long_name": original_long_name}, nesting_long_names) (sub_component_group, _) = process_component.process_component(registry, original, update, {"nested": True, "long_name": original_long_name}, nesting_long_names+[property_name])
__annotations__[property_name] = sub_component_group __annotations__[property_name] = sub_component_group
# if there are sub fields, add an attribute "sub_fields" possibly a pointer property ? or add a standard field to the type , that is stored under "attributes" and not __annotations (better) # if there are sub fields, add an attribute "sub_fields" possibly a pointer property ? or add a standard field to the type , that is stored under "attributes" and not __annotations (better)
else: else:

View File

@ -34,12 +34,10 @@ def generate_propertyGroups_for_components():
type_infos = registry.type_infos type_infos = registry.type_infos
for component_name in type_infos: for root_type_name in type_infos:
definition = type_infos[component_name] definition = type_infos[root_type_name]
is_component = definition['isComponent'] if "isComponent" in definition else False #print("root property", component_name,f"({is_component})")
root_property_name = component_name# if is_component else None process_component(registry, definition, update_calback_helper(definition, update_component, root_type_name), extras=None, nesting_long_names=[])
print("root property", component_name,f"({is_component})")
process_component(registry, definition, update_calback_helper(definition, update_component, root_property_name), extras=None, nesting_long_names=[])
# if we had to add any wrapper types on the fly, process them now # if we had to add any wrapper types on the fly, process them now
registry.process_custom_types() registry.process_custom_types()

View File

@ -7,6 +7,8 @@ from bpy_types import (PropertyGroup)
from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty) from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty)
from ..components.metadata import ComponentMetadata from ..components.metadata import ComponentMetadata
from .hashing.tiger import hash as tiger_hash from .hashing.tiger import hash as tiger_hash
# helper class to store missing bevy types information # helper class to store missing bevy types information
class MissingBevyType(bpy.types.PropertyGroup): class MissingBevyType(bpy.types.PropertyGroup):
long_name: bpy.props.StringProperty( long_name: bpy.props.StringProperty(
@ -14,6 +16,15 @@ class MissingBevyType(bpy.types.PropertyGroup):
) # type: ignore ) # type: ignore
def property_group_from_infos(property_group_name, property_group_parameters):
# print("creating property group", property_group_name)
property_group_class = type(property_group_name, (PropertyGroup,), property_group_parameters)
bpy.utils.register_class(property_group_class)
property_group_pointer = PointerProperty(type=property_group_class)
return (property_group_pointer, property_group_class)
# this is where we store the information for all available components # this is where we store the information for all available components
class ComponentsRegistry(PropertyGroup): class ComponentsRegistry(PropertyGroup):
registry: bpy.props. StringProperty( registry: bpy.props. StringProperty(
@ -140,6 +151,8 @@ class ComponentsRegistry(PropertyGroup):
type_infos = {} type_infos = {}
type_infos_missing = [] type_infos_missing = []
component_propertyGroups = {} component_propertyGroups = {}
component_property_group_classes = []
custom_types_to_add = {} custom_types_to_add = {}
invalid_components = [] invalid_components = []
@ -152,8 +165,18 @@ class ComponentsRegistry(PropertyGroup):
for propgroup_name in cls.component_propertyGroups.keys(): for propgroup_name in cls.component_propertyGroups.keys():
try: try:
delattr(ComponentMetadata, propgroup_name) delattr(ComponentMetadata, propgroup_name)
#print("sucess REMOVAL from Metadata")
except Exception as error: except Exception as error:
pass pass
#print("failed to unregister")
for propgroup_class in cls.component_property_group_classes:
try:
bpy.utils.unregister_class(propgroup_class)
#print("sucess UNREGISTER")
except Exception as error:
pass
#print("NEW failed to unregister")
del bpy.types.WindowManager.components_registry del bpy.types.WindowManager.components_registry
@ -167,7 +190,10 @@ class ComponentsRegistry(PropertyGroup):
self.missing_types_list.clear() self.missing_types_list.clear()
self.type_infos.clear() self.type_infos.clear()
self.type_infos_missing.clear() self.type_infos_missing.clear()
self.component_propertyGroups.clear() self.component_propertyGroups.clear()
self.component_property_group_classes.clear()
self.custom_types_to_add.clear() self.custom_types_to_add.clear()
self.invalid_components.clear() self.invalid_components.clear()
# now prepare paths to load data # now prepare paths to load data
@ -188,10 +214,6 @@ class ComponentsRegistry(PropertyGroup):
def has_type_infos(self): def has_type_infos(self):
return len(self.type_infos.keys()) != 0 return len(self.type_infos.keys()) != 0
# we keep a list of component propertyGroup around
def register_component_propertyGroup(self, name, propertyGroup):
self.component_propertyGroups[name] = propertyGroup
# to be able to give the user more feedback on any missin/unregistered types in their schema file # to be able to give the user more feedback on any missin/unregistered types in their schema file
def add_missing_typeInfo(self, long_name): def add_missing_typeInfo(self, long_name):
if not long_name in self.type_infos_missing: if not long_name in self.type_infos_missing:
@ -217,18 +239,25 @@ class ComponentsRegistry(PropertyGroup):
long_names_to_propgroup_names = {} long_names_to_propgroup_names = {}
# we keep a list of component propertyGroup around
def register_component_propertyGroup(self, nesting, property_group_params):
property_group_name = self.generate_propGroup_name(nesting)
(property_group_pointer, property_group_class) = property_group_from_infos(property_group_name, property_group_params)
self.component_propertyGroups[property_group_name] = property_group_pointer
self.component_property_group_classes.append(property_group_class)
return (property_group_pointer, property_group_class)
# generate propGroup name from nesting level: each longName + nesting is unique # generate propGroup name from nesting level: each longName + nesting is unique
def generate_propGroup_name(self, nesting): def generate_propGroup_name(self, nesting):
#print("gen propGroup name for", shortName, nesting)
key = str(nesting) key = str(nesting)
propGroupHash = tiger_hash(key) propGroupHash = tiger_hash(key)
propGroupName = propGroupHash + "_ui" propGroupName = propGroupHash + "_ui"
# check for collision # check for collision
padding = " " * (len(nesting) + 1) #padding = " " * (len(nesting) + 1)
#print(f"{padding}--computing hash for", nesting)
print(f"{padding}--computing hash for", nesting)
if propGroupName in self.long_names_to_propgroup_names.values(): if propGroupName in self.long_names_to_propgroup_names.values():
print(" WARNING !! you have a collision between the hash of multiple component names: collision for", nesting) print(" WARNING !! you have a collision between the hash of multiple component names: collision for", nesting)
@ -239,7 +268,6 @@ class ComponentsRegistry(PropertyGroup):
def get_propertyGroupName_from_longName(self, longName): def get_propertyGroupName_from_longName(self, longName):
return self.long_names_to_propgroup_names.get(str([longName]), None) return self.long_names_to_propgroup_names.get(str([longName]), None)
########### ###########
""" """