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]]
name = "bevy_gltf_blueprints"
version = "0.5.0"
version = "0.5.1"
dependencies = [
"bevy",
"bevy_gltf_components 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

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

View File

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

View File

@ -49,12 +49,10 @@ pub(crate) fn spawn_from_blueprints(
assets_gltf: Res<Assets<Gltf>>,
asset_server: Res<AssetServer>,
blueprints_config: Res<BluePrintsConfig>,
) {
for (entity, name, blupeprint_name, transform, original_parent) in spawn_placeholders.iter() {
debug!("need to spawn {:?}, id: {:?}", blupeprint_name.0, entity);
let what = &blupeprint_name.0;
let model_file_name = format!("{}.{}", &what, &blueprints_config.format);
let model_path =
@ -63,8 +61,6 @@ pub(crate) fn spawn_from_blueprints(
debug!("attempting to spawn {:?}", model_path);
let model_handle: Handle<Gltf> = asset_server.load(model_path);
let gltf = assets_gltf
.get(&model_handle)
.expect("this gltf should have been loaded");
@ -97,12 +93,12 @@ pub(crate) fn spawn_from_blueprints(
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
// 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 {
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)
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct UnregisteredComponent;
pub fn spawn_test(
keycode: Res<Input<KeyCode>>,
mut commands: Commands,
@ -83,3 +87,48 @@ pub fn spawn_test(
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)),
// test_collision_events
spawn_test,
spawn_test_failing,
)
.run_if(in_state(GameState::InGame)),
)

View File

@ -6,16 +6,23 @@ use bevy::prelude::*;
use super::CameraTrackingOffset;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct SSAOSettings;
pub fn camera_replace_proxies(
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
) {
for (entity, mut camera, bloom_settings, ssao_setting) in added_cameras.iter_mut() {
@ -24,27 +31,25 @@ pub fn camera_replace_proxies(
commands
.entity(entity)
.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
if bloom_settings.is_none() {
for bloom_settings in added_bloom_settings.iter(){
commands
.entity(entity)
.insert(BloomSettings {
intensity: bloom_settings.intensity,
composite_mode: BloomCompositeMode::Additive,
..default()
});
for bloom_settings in added_bloom_settings.iter() {
commands.entity(entity).insert(BloomSettings {
intensity: bloom_settings.intensity,
composite_mode: BloomCompositeMode::Additive,
..default()
});
}
}
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.entity(entity)
commands
.entity(entity)
.insert(ScreenSpaceAmbientOcclusionBundle::default())
.insert(TemporalAntiAliasBundle::default());
}

View File

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

View File

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

View File

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