feat(bevy_gltf_blueprints): improved error handling for clone_entity (#82)

* feat(bevy_gltf_blueprints): improved error handling for clone_entity
 * you will now get component name for components that have not been registered & thus cannot be cloned
* added a small example (in examples/bevy_gltf_blueprints/basic), just press 'U' at runtime to try to spawn an entity with unregistered components
* closes #81
This commit is contained in:
Mark Moissette 2023-12-27 13:14:13 +01:00 committed by GitHub
parent 6047959b3f
commit 20b34dde20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 114 additions and 64 deletions

2
Cargo.lock generated
View File

@ -779,7 +779,7 @@ dependencies = [
[[package]] [[package]]
name = "bevy_gltf_blueprints" name = "bevy_gltf_blueprints"
version = "0.5.0" version = "0.5.1"
dependencies = [ dependencies = [
"bevy", "bevy",
"bevy_gltf_components 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "bevy_gltf_components 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_gltf_blueprints" name = "bevy_gltf_blueprints"
version = "0.5.0" version = "0.5.1"
authors = ["Mark 'kaosat-dev' Moissette"] authors = ["Mark 'kaosat-dev' Moissette"]
description = "Adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy." description = "Adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy."
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"

View File

@ -18,29 +18,31 @@ impl CloneEntity {
// - the source or destination entity do not exist // - the source or destination entity do not exist
fn clone_entity(self, world: &mut World) { fn clone_entity(self, world: &mut World) {
let components = { let components = {
let registry = world.get_resource::<AppTypeRegistry>().unwrap().read(); let registry = world
.get_resource::<AppTypeRegistry>()
.expect("the world should have a type registry")
.read();
world world
.get_entity(self.source) .get_entity(self.source)
.unwrap() .expect("source entity should exist")
.archetype() .archetype()
.components() .components()
.map(|component_id| { .map(|component_id| {
world let component_info = world
.components() .components()
.get_info(component_id) .get_info(component_id)
.unwrap() .expect("component info should be available");
.type_id()
.unwrap() let type_id = component_info.type_id().unwrap();
}) let type_id = registry.get(type_id).expect(
.map(|type_id| { format!(
// println!("type_id {:?}", type_id); "cannot clone entity: component: {:?} is not registered",
registry component_info.name()
.get(type_id) )
.unwrap() .as_str(),
.data::<ReflectComponent>() );
.unwrap() return type_id.data::<ReflectComponent>().unwrap().clone();
.clone()
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
}; };
@ -51,7 +53,9 @@ impl CloneEntity {
.unwrap() .unwrap()
.clone_value(); .clone_value();
let mut destination = world.get_entity_mut(self.destination).unwrap(); let mut destination = world
.get_entity_mut(self.destination)
.expect("destination entity should exist");
component.apply_or_insert(&mut destination, &*source); component.apply_or_insert(&mut destination, &*source);
} }

View File

@ -49,12 +49,10 @@ pub(crate) fn spawn_from_blueprints(
assets_gltf: Res<Assets<Gltf>>, assets_gltf: Res<Assets<Gltf>>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
blueprints_config: Res<BluePrintsConfig>, blueprints_config: Res<BluePrintsConfig>,
) { ) {
for (entity, name, blupeprint_name, transform, original_parent) in spawn_placeholders.iter() { for (entity, name, blupeprint_name, transform, original_parent) in spawn_placeholders.iter() {
debug!("need to spawn {:?}, id: {:?}", blupeprint_name.0, entity); debug!("need to spawn {:?}, id: {:?}", blupeprint_name.0, entity);
let what = &blupeprint_name.0; let what = &blupeprint_name.0;
let model_file_name = format!("{}.{}", &what, &blueprints_config.format); let model_file_name = format!("{}.{}", &what, &blueprints_config.format);
let model_path = let model_path =
@ -63,8 +61,6 @@ pub(crate) fn spawn_from_blueprints(
debug!("attempting to spawn {:?}", model_path); debug!("attempting to spawn {:?}", model_path);
let model_handle: Handle<Gltf> = asset_server.load(model_path); let model_handle: Handle<Gltf> = asset_server.load(model_path);
let gltf = assets_gltf let gltf = assets_gltf
.get(&model_handle) .get(&model_handle)
.expect("this gltf should have been loaded"); .expect("this gltf should have been loaded");
@ -97,12 +93,12 @@ pub(crate) fn spawn_from_blueprints(
let world = game_world.single_mut(); let world = game_world.single_mut();
let mut parent = world.1[0]; // FIXME: dangerous hack because our gltf data have a single child like this, but might not always be the case let mut parent = world.1[0]; // FIXME: dangerous hack because our gltf data have a single child like this, but might not always be the case
// ideally, insert the newly created entity as a child of the original parent, if any, the world otherwise // ideally, insert the newly created entity as a child of the original parent, if any, the world otherwise
if let Some(original_parent) = original_parent { if let Some(original_parent) = original_parent {
parent = original_parent.get(); parent = original_parent.get();
} }
commands.entity(parent).add_child(child_scene); commands.entity(parent).add_child(child_scene);
} }
} }

View File

@ -40,6 +40,10 @@ pub fn setup_game(
next_game_state.set(GameState::InGame) next_game_state.set(GameState::InGame)
} }
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct UnregisteredComponent;
pub fn spawn_test( pub fn spawn_test(
keycode: Res<Input<KeyCode>>, keycode: Res<Input<KeyCode>>,
mut commands: Commands, mut commands: Commands,
@ -83,3 +87,48 @@ pub fn spawn_test(
commands.entity(world).add_child(new_entity); commands.entity(world).add_child(new_entity);
} }
} }
pub fn spawn_test_failing(
keycode: Res<Input<KeyCode>>,
mut commands: Commands,
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
) {
if keycode.just_pressed(KeyCode::U) {
let world = game_world.single_mut();
let world = world.1[0];
let mut rng = rand::thread_rng();
let range = 5.5;
let x: f32 = rng.gen_range(-range..range);
let y: f32 = rng.gen_range(-range..range);
let mut rng = rand::thread_rng();
let range = 0.8;
let vel_x: f32 = rng.gen_range(-range..range);
let vel_y: f32 = rng.gen_range(2.0..2.5);
let vel_z: f32 = rng.gen_range(-range..range);
let name_index: u64 = rng.gen();
let new_entity = commands
.spawn((
BluePrintBundle {
blueprint: BlueprintName("Health_Pickup".to_string()),
transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
..Default::default()
},
bevy::prelude::Name::from(format!("test{}", name_index)),
// BlueprintName("Health_Pickup".to_string()),
// SpawnHere,
// TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
Velocity {
linvel: Vec3::new(vel_x, vel_y, vel_z),
angvel: Vec3::new(0.0, 0.0, 0.0),
},
UnregisteredComponent,
))
.id();
commands.entity(world).add_child(new_entity);
}
}

View File

@ -104,6 +104,7 @@ impl Plugin for GamePlugin {
player_move_demo, //.run_if(in_state(AppState::Running)), player_move_demo, //.run_if(in_state(AppState::Running)),
// test_collision_events // test_collision_events
spawn_test, spawn_test,
spawn_test_failing,
) )
.run_if(in_state(GameState::InGame)), .run_if(in_state(GameState::InGame)),
) )

View File

@ -6,16 +6,23 @@ use bevy::prelude::*;
use super::CameraTrackingOffset; use super::CameraTrackingOffset;
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
pub struct SSAOSettings; pub struct SSAOSettings;
pub fn camera_replace_proxies( pub fn camera_replace_proxies(
mut commands: Commands, mut commands: Commands,
mut added_cameras: Query<(Entity, &mut Camera, Option<&BloomSettings>, Option<&SSAOSettings>), (Added<Camera>, With<CameraTrackingOffset>)>, mut added_cameras: Query<
(
Entity,
&mut Camera,
Option<&BloomSettings>,
Option<&SSAOSettings>,
),
(Added<Camera>, With<CameraTrackingOffset>),
>,
added_bloom_settings : Query<&BloomSettings, Added<BloomSettings>>, added_bloom_settings: Query<&BloomSettings, Added<BloomSettings>>,
added_ssao_settings: Query<&SSAOSettings, Added<SSAOSettings>>, // Move to camera added_ssao_settings: Query<&SSAOSettings, Added<SSAOSettings>>, // Move to camera
) { ) {
for (entity, mut camera, bloom_settings, ssao_setting) in added_cameras.iter_mut() { for (entity, mut camera, bloom_settings, ssao_setting) in added_cameras.iter_mut() {
@ -24,27 +31,25 @@ pub fn camera_replace_proxies(
commands commands
.entity(entity) .entity(entity)
.insert(DebandDither::Enabled) .insert(DebandDither::Enabled)
.insert(Tonemapping::BlenderFilmic) .insert(Tonemapping::BlenderFilmic);
;
// we only inject the scene_level bloom settings if there are no settings already on the Camera // we only inject the scene_level bloom settings if there are no settings already on the Camera
if bloom_settings.is_none() { if bloom_settings.is_none() {
for bloom_settings in added_bloom_settings.iter(){ for bloom_settings in added_bloom_settings.iter() {
commands commands.entity(entity).insert(BloomSettings {
.entity(entity) intensity: bloom_settings.intensity,
.insert(BloomSettings { composite_mode: BloomCompositeMode::Additive,
intensity: bloom_settings.intensity, ..default()
composite_mode: BloomCompositeMode::Additive, });
..default()
});
} }
} }
if ssao_setting.is_none() { if ssao_setting.is_none() {
for _ in added_ssao_settings.iter(){ for _ in added_ssao_settings.iter() {
commands.insert_resource(Msaa::Off); // when using SSAO, you cannot use Msaa commands.insert_resource(Msaa::Off); // when using SSAO, you cannot use Msaa
commands.entity(entity) commands
.entity(entity)
.insert(ScreenSpaceAmbientOcclusionBundle::default()) .insert(ScreenSpaceAmbientOcclusionBundle::default())
.insert(TemporalAntiAliasBundle::default()); .insert(TemporalAntiAliasBundle::default());
} }

View File

@ -2,23 +2,26 @@ use bevy::prelude::*;
use bevy::pbr::{CascadeShadowConfig, CascadeShadowConfigBuilder, DirectionalLightShadowMap}; use bevy::pbr::{CascadeShadowConfig, CascadeShadowConfigBuilder, DirectionalLightShadowMap};
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct AmbientLightSettings {
pub color: Color,
pub brightness: f32,
}
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
pub struct AmbientLightSettings {pub color: Color, pub brightness: f32} pub struct ShadowmapSettings {
pub size: usize,
#[derive(Component, Reflect, Default, Debug)] }
#[reflect(Component)]
pub struct ShadowmapSettings {pub size: usize}
pub fn lighting_replace_proxies( pub fn lighting_replace_proxies(
mut added_dirights: Query<(Entity, &mut DirectionalLight), Added<DirectionalLight>>, mut added_dirights: Query<(Entity, &mut DirectionalLight), Added<DirectionalLight>>,
mut added_spotlights: Query<&mut SpotLight, Added<SpotLight>>, mut added_spotlights: Query<&mut SpotLight, Added<SpotLight>>,
mut added_pointlights: Query<&mut PointLight, Added<PointLight>>, mut added_pointlights: Query<&mut PointLight, Added<PointLight>>,
added_ambient_proxies : Query<&AmbientLightSettings, Added<AmbientLightSettings>>, added_ambient_proxies: Query<&AmbientLightSettings, Added<AmbientLightSettings>>,
added_shadowmap_settings : Query<&ShadowmapSettings, Added<ShadowmapSettings>>, added_shadowmap_settings: Query<&ShadowmapSettings, Added<ShadowmapSettings>>,
mut commands: Commands, mut commands: Commands,
) { ) {
@ -42,12 +45,11 @@ pub fn lighting_replace_proxies(
light.shadows_enabled = true; light.shadows_enabled = true;
} }
for setting in added_shadowmap_settings.iter() { for setting in added_shadowmap_settings.iter() {
commands.insert_resource(DirectionalLightShadowMap { size: setting.size }); commands.insert_resource(DirectionalLightShadowMap { size: setting.size });
} }
for ambient in added_ambient_proxies.iter(){ for ambient in added_ambient_proxies.iter() {
commands.insert_resource(AmbientLight { commands.insert_resource(AmbientLight {
color: ambient.color, color: ambient.color,
brightness: ambient.brightness, brightness: ambient.brightness,
@ -55,5 +57,4 @@ pub fn lighting_replace_proxies(
// FIXME: does this belong here ? // FIXME: does this belong here ?
commands.insert_resource(ClearColor(ambient.color * ambient.brightness)); commands.insert_resource(ClearColor(ambient.color * ambient.brightness));
} }
} }

View File

@ -7,13 +7,10 @@ use bevy::prelude::*;
pub struct LightingPlugin; pub struct LightingPlugin;
impl Plugin for LightingPlugin { impl Plugin for LightingPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app app.register_type::<AmbientLightSettings>()
.register_type::<AmbientLightSettings>() .register_type::<ShadowmapSettings>()
.register_type::<ShadowmapSettings>() // FIXME: adding these since they are missing
// FIXME: adding these since they are missing .register_type::<NotShadowCaster>()
.register_type::<NotShadowCaster>() .add_systems(PreUpdate, lighting_replace_proxies);
.add_systems(PreUpdate, lighting_replace_proxies)
;
} }
} }

View File

@ -4,7 +4,6 @@ use bevy::prelude::*;
#[reflect(Component)] #[reflect(Component)]
struct Marker; struct Marker;
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct Enemy; struct Enemy;
@ -13,7 +12,6 @@ struct Enemy;
#[reflect(Component)] #[reflect(Component)]
struct NestingTest; struct NestingTest;
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)] #[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTestF32(f32); struct TuppleTestF32(f32);
@ -77,7 +75,6 @@ impl Plugin for ComponentsTestPlugin {
.register_type::<Marker>() .register_type::<Marker>()
.register_type::<Enemy>() .register_type::<Enemy>()
.register_type::<NestingTest>() .register_type::<NestingTest>()
.register_type::<TuppleTestF32>() .register_type::<TuppleTestF32>()
.register_type::<TuppleTestU64>() .register_type::<TuppleTestU64>()
.register_type::<TuppleTestStr>() .register_type::<TuppleTestStr>()