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
This commit is contained in:
kaosat.dev 2024-06-18 22:26:18 +02:00
parent 1686aca655
commit 00bf600ccf
27 changed files with 840 additions and 75 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_gltf_blueprints"
version = "0.10.0"
version = "0.11.0"
authors = ["Mark 'kaosat-dev' Moissette"]
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"
@ -14,8 +14,8 @@ license = "MIT OR Apache-2.0"
workspace = true
[dependencies]
bevy_gltf_components = { version = "0.5", path = "../bevy_gltf_components" }
bevy = { version = "0.13", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "bevy_animation", "animation"] }
bevy_gltf_components = { version = "0.6", path = "../bevy_gltf_components" }
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "bevy_animation", "animation"] }
[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
pub fn trigger_instance_animation_markers_events(
animation_infos: Query<(
@ -82,13 +83,15 @@ pub fn trigger_instance_animation_markers_events(
&SceneAnimations,
&AnimationInfos,
)>,
animation_players: Query<&AnimationPlayer>,
animation_players: Query<(&AnimationPlayer)>,
animation_clips: Res<Assets<AnimationClip>>,
animation_graphs: Res<Assets<AnimationGraph>>,
mut animation_marker_events: EventWriter<AnimationMarkerReached>,
) {
for (entity, markers, link, animations, animation_infos) in animation_infos.iter() {
let animation_player = animation_players.get(link.0).unwrap();
let animation_clip = animation_clips.get(animation_player.animation_clip());
// animation_player.play(animation)
if animation_clip.is_some() {
// 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 std::any::TypeId;

View File

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

View File

@ -160,10 +160,10 @@ pub(crate) fn materials_inject2(
let mat_gltf = assets_gltf
.get(model_handle.id())
.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
.named_materials
.get(&material_info.name)
.get(&material_info.name as &str)
.expect("this material should have been loaded");
blueprints_config
.material_library_cache

View File

@ -1,6 +1,6 @@
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};
@ -28,7 +28,7 @@ pub struct SpawnHere;
pub struct Spawned;
#[derive(Component)]
#[derive(Component, Debug)]
/// flag component added when a Blueprint instance ist Ready : ie :
/// - its assets have loaded
/// - 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));
// 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;
if loaded || failed {
loaded_amount += 1;
@ -295,6 +300,12 @@ pub(crate) fn spawn_from_blueprints2(
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((
SceneBundle {
scene: scene.clone(),
@ -306,7 +317,7 @@ pub(crate) fn spawn_from_blueprints2(
OriginalChildren(original_children),
BlueprintAnimations {
// 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
unprocessed_entities.iter()
{
debug!("post processing blueprint for entity {:?}", name);
info!("post processing blueprint for entity {:?}", name);
if children.len() == 0 {
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::<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::<BlueprintAssetsLoaded>();
commands.entity(root_entity).despawn_recursive();
info!("DONE WITH POST PROCESS");
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_gltf_components"
version = "0.5.1"
version = "0.6.0"
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"
@ -14,9 +14,9 @@ license = "MIT OR Apache-2.0"
workspace = true
[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"
ron = "0.8.1"
[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},
world::World,
},
gltf::GltfExtras,
gltf::{GltfExtras, GltfMaterialExtras, GltfMeshExtras, GltfSceneExtras},
hierarchy::Parent,
log::debug,
reflect::{Reflect, TypeRegistration},
@ -15,25 +15,8 @@ use bevy::{
use crate::{ronstring_to_reflect_component, GltfProcessed};
/// 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 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);
// , mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>>
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)>){
// 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
@ -57,12 +40,86 @@ pub fn add_components_from_gltf_extras(world: &mut World) {
for (component, type_registration) in reflect_components {
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 {
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 {
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.clone();

View File

@ -1,5 +1,5 @@
use bevy::log::{debug, warn};
use bevy::reflect::serde::UntypedReflectDeserializer;
use bevy::reflect::serde::ReflectDeserializer;
use bevy::reflect::{Reflect, TypeRegistration, TypeRegistry};
use bevy::utils::HashMap;
use ron::Value;
@ -68,7 +68,7 @@ fn components_string_to_components(
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 = UntypedReflectDeserializer::new(type_registry);
let reflect_deserializer = ReflectDeserializer::new(type_registry);
let component = reflect_deserializer
.deserialize(&mut deserializer)
.unwrap_or_else(|_| {
@ -113,7 +113,7 @@ fn bevy_components_string_to_components(
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 = UntypedReflectDeserializer::new(type_registry);
let reflect_deserializer = ReflectDeserializer::new(type_registry);
let component = reflect_deserializer
.deserialize(&mut deserializer)
.unwrap_or_else(|_| {

View File

@ -14,8 +14,8 @@ license = "MIT OR Apache-2.0"
workspace = true
[dependencies]
bevy = { version = "0.13", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
bevy_gltf_blueprints = { version = "0.10", path = "../bevy_gltf_blueprints" }
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
bevy_gltf_blueprints = { version = "0.11", path = "../bevy_gltf_blueprints" }
[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);
let serialized_scene = dyn_scene
.serialize_ron(world.resource::<AppTypeRegistry>())
.serialize(&world.resource::<AppTypeRegistry>().read())
.unwrap();
let save_path = Path::new("assets")

View File

@ -11,11 +11,11 @@ edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
bevy = { version = "0.13", default-features = false, features = ["bevy_scene"] }
bevy_reflect = { version = "0.13", default-features = false }
bevy_app = { version = "0.13", default-features = false, features = ["bevy_reflect"] }
bevy_ecs = { version = "0.13", default-features = false, features = ["bevy_reflect"] }
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_scene"] }
bevy_reflect = { version = "0.14.0-rc.3", default-features = false }
bevy_app = { version = "0.14.0-rc.3", 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"
[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,14 +5,14 @@ edition = "2021"
license = "MIT OR Apache-2.0"
[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_registry_export = { path = "../../crates/bevy_registry_export" }
# bevy_gltf_worlflow_examples_common_rapier = { path = "../../examples/common_rapier" }
bevy_gltf_worlflow_examples_common = { path = "../../examples/common" }
#bevy_gltf_worlflow_examples_common = { path = "../../examples/common" }
#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_editor_pls = { version = "0.8" }
#bevy_asset_loader = { version = "0.20", features = ["standard_dynamic_assets"] }
#bevy_editor_pls = { version = "0.8" }
rand = "0.8.5"
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")));
}
/*
#[allow(clippy::type_complexity)]
pub fn animations(
added_animation_players: Query<(Entity, &Name, &AnimationPlayer)>,
@ -225,7 +226,7 @@ pub fn play_animations(
}
}
}
*/
pub fn react_to_animation_markers(
mut animation_marker_events: EventReader<AnimationMarkerReached>,
) {

View File

@ -1,6 +1,6 @@
use bevy::prelude::*;
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, BlueprintPath, GameWorldTag};
use bevy_gltf_worlflow_examples_common::{GameState, InAppRunning};
use crate::{GameState, InAppRunning};
//use bevy_rapier3d::prelude::Velocity;
use rand::Rng;

View File

@ -1,8 +1,9 @@
pub mod animation;
pub mod in_game;
pub use animation::*;
pub use in_game::*;
pub mod animation;
pub use animation::*;
use std::{collections::HashMap, fs, time::Duration};
use bevy_gltf_blueprints::{
@ -13,12 +14,14 @@ use bevy::{
prelude::*, render::view::screenshot::ScreenshotManager, time::common_conditions::on_timer,
window::PrimaryWindow,
};
use bevy_gltf_worlflow_examples_common::{AppState, GameState};
use crate::{AppState, GameState};
use json_writer::to_json_string;
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)
@ -121,7 +124,7 @@ fn generate_screenshot(
}
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;
@ -134,16 +137,18 @@ impl Plugin for GamePlugin {
.add_systems(Update, (spawn_test).run_if(in_state(GameState::InGame)))
.add_systems(Update, validate_export)
//.add_systems(OnEnter(AppState::CoreLoading), start_game)
.add_systems(OnEnter(AppState::MenuRunning), start_game)
.add_systems(OnEnter(AppState::AppRunning), setup_game)
.add_systems(OnEnter(AppState::MenuRunning), setup_main_scene_animations)
.add_systems(Update, (animations)
/* .add_systems(Update, (animations)
.run_if(in_state(AppState::AppRunning))
.after(GltfBlueprintsSet::AfterSpawn)
)
.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(

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_gltf_worlflow_examples_common::CommonPlugin;
// use bevy_gltf_worlflow_examples_common::CommonPlugin;
mod state;
use state::*;
mod core;
use crate::core::*;
@ -11,12 +13,18 @@ mod dupe_components;
mod test_components;
use test_components::*;
mod hierarchy_debug;
use hierarchy_debug::*;
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(AssetPlugin::default()),
HiearchyDebugPlugin,
// our custom plugins
CommonPlugin,
// CommonPlugin,
StatePlugin,
CorePlugin, // reusable plugins
GamePlugin, // specific to our game
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>();
}
}