Skip to content

Commit

Permalink
feat(Blenvy:Bevy):
Browse files Browse the repository at this point in the history
 * bumped up Bevy version to v0.14 !
 * fixed (albeit in a clunky way) issues with sub blueprint detection
 * improved error message for missing material library
 * added HideUntilReady component & logic, to hide 'in-spawning' blueprint instances until they are ready
 * "add-to-world" is now only trigerred for blueprint instances that have no parents (avoid footgun)
 * minor cleanups & tweaks
 * added test component with Vec3 to double check for issues
 * updated test blend file to include the component above + added a light to the test spawnable blueprint
to check for "light flashes" (aka indirectly testing "HideUntilReady")
  • Loading branch information
kaosat-dev committed Jul 7, 2024
1 parent 478be88 commit bef709a
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 57 deletions.
4 changes: 2 additions & 2 deletions crates/blenvy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0"
workspace = true

[dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "file_watcher"] }
bevy = { version = "0.14", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "file_watcher"] }
serde = "1.0.188"
ron = "0.8.1"
serde_json = "1.0.108"
Expand All @@ -34,4 +34,4 @@ gltf = { version = "1.4.0", default-features = false, features = [


[dev-dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["dynamic_linking"] }
bevy = { version = "0.14", default-features = false, features = ["dynamic_linking"] }
2 changes: 1 addition & 1 deletion crates/blenvy/src/blueprints/materials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub(crate) fn inject_materials(
let model_handle: Handle<Gltf> = asset_server.load(material_info.path.clone()); // FIXME: kinda weird now
let mat_gltf = assets_gltf
.get(model_handle.id())
.expect("material should have been preloaded");
.expect(&format!("materials file {} should have been preloaded", material_info.path));
if mat_gltf.named_materials.contains_key(&material_info.name as &str) {
let material = mat_gltf
.named_materials
Expand Down
97 changes: 67 additions & 30 deletions crates/blenvy/src/blueprints/spawn_from_blueprints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ pub struct AddToGameWorld;
pub(crate) struct OriginalChildren(pub Vec<Entity>);


#[derive(Component)]
/// You can add this component to a blueprint instance, and the instance will be hidden until it is ready
/// You usually want to use this for worlds/level spawning , or dynamic spawning at runtime, but not when you are adding blueprint instances to an existing entity
/// as it would first become invisible before re-appearing again
pub struct HideUntilReady;

#[derive(Event, Debug)]
pub enum BlueprintEvent {

Expand Down Expand Up @@ -261,6 +267,7 @@ pub(crate) fn blueprints_assets_ready(spawn_placeholders: Query<
Option<&Parent>,
Option<&AddToGameWorld>,
Option<&Name>,
Option<&HideUntilReady>
),
(
With<BlueprintAssetsLoaded>,
Expand All @@ -283,6 +290,7 @@ pub(crate) fn blueprints_assets_ready(spawn_placeholders: Query<
original_parent,
add_to_world,
name,
hide_until_ready,
) in spawn_placeholders.iter()
{
/*info!(
Expand Down Expand Up @@ -336,24 +344,26 @@ pub(crate) fn blueprints_assets_ready(spawn_placeholders: Query<
SceneBundle {
scene: scene.clone(),
transform: transforms,
visibility: Visibility::Hidden,

..Default::default()
},
OriginalChildren(original_children),
BlueprintAnimations {
// these are animations specific to the inside of the blueprint
named_animations: named_animations//gltf.named_animations.clone(),
},

));

/* if add_to_world.is_some() {
if hide_until_ready.is_some() {
commands.entity(entity).insert(Visibility::Hidden); // visibility:
}

// only allow automatically adding a newly spawned blueprint instance to the "world", if the entity does not have a parent
if add_to_world.is_some() && original_parent.is_some() {
let world = game_world
.get_single_mut()
.expect("there should be a game world present");
commands.entity(world).add_child(entity);
} */
}

}
}
Expand Down Expand Up @@ -382,8 +392,6 @@ pub struct BlueprintChildrenReady;
#[reflect(Component)]
pub struct BlueprintReadyForPostProcess;


// TODO: disregard blueprints that have been spawned WAIT , we already have BlueprintSpawning
pub(crate) fn blueprints_scenes_spawned(
spawned_blueprint_scene_instances: Query<(Entity, Option<&Name>, Option<&Children>, Option<&SpawnTrackRoot>), (With<BlueprintSpawning>, Added<SceneInstance>)>,
with_blueprint_infos : Query<(Entity, Option<&Name>), With<BlueprintInfo>>,
Expand All @@ -396,7 +404,6 @@ pub(crate) fn blueprints_scenes_spawned(
mut commands: Commands,

all_names: Query<&Name>

) {
for (entity, name, children, track_root) in spawned_blueprint_scene_instances.iter(){
info!("Done spawning blueprint scene for entity named {:?} (track root: {:?})", name, track_root);
Expand All @@ -405,34 +412,59 @@ pub(crate) fn blueprints_scenes_spawned(

let mut tracker_data: HashMap<Entity, bool> = HashMap::new();

for parent in all_parents.iter_ancestors(entity) {
if with_blueprint_infos.get(parent).is_ok() {
if track_root.is_none() {
for parent in all_parents.iter_ancestors(entity) {
if with_blueprint_infos.get(parent).is_ok() {

println!("found a parent with blueprint_info {:?} for {:?}", all_names.get(parent), all_names.get(entity));
commands.entity(entity).insert(SpawnTrackRoot(parent));// Injecting to know which entity is the root

println!("found a parent with blueprint_info {:?} for {:?}", all_names.get(parent), all_names.get(entity));
break;
break;
}
}
}


if children.is_some() {
for child in all_children.iter_descendants(entity) {
if with_blueprint_infos.get(child).is_ok() {
sub_blueprint_instances.push(child);
if let Ok(nname) = all_names.get(child) {
sub_blueprint_instance_names.push(nname.clone());
}
// println!("Parent blueprint instance of {:?} is {:?}", all_names.get(child), all_names.get(entity));


tracker_data.insert(child, false);


if track_root.is_some() {
let prev_root = track_root.unwrap().0;
// if we already had a track root, and it is different from the current entity , change the previous track root's list of children
if prev_root != entity {
let mut tracker = sub_blueprint_trackers.get_mut(prev_root).expect("should have a tracker");
tracker.1.sub_blueprint_instances.remove(&child);
for parent in all_parents.iter_ancestors(child) {
if with_blueprint_infos.get(parent).is_ok() {

if parent == entity {
//println!("yohoho");
println!("Parent blueprint instance of {:?} is {:?}", all_names.get(child), all_names.get(parent));

commands.entity(child).insert(SpawnTrackRoot(entity));// Injecting to know which entity is the root

tracker_data.insert(child, false);

sub_blueprint_instances.push(child);
if let Ok(nname) = all_names.get(child) {
sub_blueprint_instance_names.push(nname.clone());
}

/*if track_root.is_some() {
let prev_root = track_root.unwrap().0;
// if we already had a track root, and it is different from the current entity , change the previous track root's list of children
if prev_root != entity {
let mut tracker = sub_blueprint_trackers.get_mut(prev_root).expect("should have a tracker");
tracker.1.sub_blueprint_instances.remove(&child);
}
}*/

}
break;
}
}

commands.entity(child).insert(SpawnTrackRoot(entity));// Injecting to know which entity is the root



}
}
}
Expand Down Expand Up @@ -481,7 +513,7 @@ pub(crate) fn blueprints_transfer_components(
) {

for (original, children, original_children, name, track_root) in foo.iter() {
println!("YOOO ready !! {:?}", name);
info!("YOOO ready !! removing empty nodes {:?}", name);

if children.len() == 0 {
warn!("timing issue ! no children found, please restart your bevy app (bug being investigated)");
Expand Down Expand Up @@ -515,7 +547,7 @@ pub(crate) fn blueprints_transfer_components(
}

commands.entity(original)
.insert(BlueprintReadyForPostProcess); // Tag the entity so any systems dealing with post processing can now it is now their "turn"
.insert(BlueprintReadyForPostProcess); // Tag the entity so any systems dealing with post processing can know it is now their "turn"
// 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::<BlueprintAssetsLoadState>(); // 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>();
Expand All @@ -524,6 +556,7 @@ pub(crate) fn blueprints_transfer_components(

// now check if the current entity is a child blueprint instance of another entity
// this should always be done last, as children should be finished before the parent can be processed correctly
// TODO: perhaps use observers for these
if let Some(track_root) = track_root {
let root_name = all_names.get(track_root.0);
println!("got some root {:?}", root_name);
Expand Down Expand Up @@ -555,19 +588,23 @@ pub(crate) fn blueprints_transfer_components(
pub struct BlueprintReadyForFinalizing;

pub(crate) fn blueprints_finalize_instances(
blueprint_instances: Query<(Entity, Option<&Name>, &BlueprintInfo), (With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>)>,
blueprint_instances: Query<(Entity, Option<&Name>, &BlueprintInfo, Option<&HideUntilReady>), (With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>)>,
mut blueprint_events: EventWriter<BlueprintEvent>,
mut commands: Commands,
) {
for (entity, name, blueprint_info) in blueprint_instances.iter() {
for (entity, name, blueprint_info, hide_until_ready) in blueprint_instances.iter() {
info!("Finalizing blueprint instance {:?}", name);
commands.entity(entity)
.remove::<SpawnHere>()
.remove::<BlueprintSpawning>()
.remove::<BlueprintReadyForPostProcess>()
.insert(BlueprintInstanceReady)
.insert(Visibility::Visible)
;
if hide_until_ready.is_some() {
println!("REVEAAAL");
commands.entity(entity).insert(Visibility::Visible);
}


blueprint_events.send(BlueprintEvent::InstanceReady {entity: entity, blueprint_name: blueprint_info.name.clone(), blueprint_path: blueprint_info.path.clone()});
}
Expand Down
2 changes: 1 addition & 1 deletion testing/bevy_example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
license = "MIT OR Apache-2.0"

[dependencies]
bevy = { version = "0.14.0-rc.3", features = ["dynamic_linking"] }
bevy = { version = "0.14", features = ["dynamic_linking"] }
blenvy = { path = "../../crates/blenvy" }
# bevy_gltf_blueprints = { path = "../../crates/bevy_gltf_blueprints" }
# bevy_registry_export = { path = "../../crates/bevy_registry_export" }
Expand Down
Binary file modified testing/bevy_example/art/testing.blend
Binary file not shown.
35 changes: 24 additions & 11 deletions testing/bevy_example/assets/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -4182,6 +4182,30 @@
"type": "object",
"typeInfo": "Struct"
},
"bevy_example::test_components::RedirectPropHitImpulse": {
"isComponent": true,
"isResource": false,
"long_name": "bevy_example::test_components::RedirectPropHitImpulse",
"oneOf": [
{
"items": false,
"long_name": "Local",
"prefixItems": [
{
"type": {
"$ref": "#/$defs/glam::Vec3"
}
}
],
"short_name": "Local",
"type": "array",
"typeInfo": "Tuple"
}
],
"short_name": "RedirectPropHitImpulse",
"type": "object",
"typeInfo": "Enum"
},
"bevy_example::test_components::TupleTest2": {
"isComponent": true,
"isResource": false,
Expand Down Expand Up @@ -8145,11 +8169,6 @@
"$ref": "#/$defs/bevy_render::alpha::AlphaMode"
}
},
"anisotropy_channel": {
"type": {
"$ref": "#/$defs/bevy_pbr::pbr_material::UvChannel"
}
},
"anisotropy_rotation": {
"type": {
"$ref": "#/$defs/f32"
Expand All @@ -8160,11 +8179,6 @@
"$ref": "#/$defs/f32"
}
},
"anisotropy_texture": {
"type": {
"$ref": "#/$defs/core::option::Option<bevy_asset::handle::Handle<bevy_render::texture::image::Image>>"
}
},
"attenuation_color": {
"type": {
"$ref": "#/$defs/bevy_color::color::Color"
Expand Down Expand Up @@ -8374,7 +8388,6 @@
"clearcoat_perceptual_roughness",
"anisotropy_strength",
"anisotropy_rotation",
"anisotropy_channel",
"double_sided",
"unlit",
"fog_enabled",
Expand Down
4 changes: 3 additions & 1 deletion testing/bevy_example/src/game/in_game.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bevy::prelude::*;
use blenvy::{BluePrintBundle, BlueprintInfo, DynamicBlueprintInstance, GameWorldTag, SpawnHere};
use blenvy::{BluePrintBundle, BlueprintInfo, DynamicBlueprintInstance, GameWorldTag, HideUntilReady, SpawnHere};
use crate::{GameState, InAppRunning};

//use bevy_rapier3d::prelude::Velocity;
Expand All @@ -23,6 +23,7 @@ pub fn setup_game(

commands.spawn((
BlueprintInfo{name: "World".into(), path: "levels/World.glb".into()},
HideUntilReady,
bevy::prelude::Name::from("world"), //FIXME: not really needed ? could be infered from blueprint's name/ path
SpawnHere,
GameWorldTag,
Expand Down Expand Up @@ -67,6 +68,7 @@ pub fn spawn_test(
},
DynamicBlueprintInstance,
bevy::prelude::Name::from(format!("test{}", name_index)),
HideUntilReady,
// SpawnHere,
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
/*Velocity {
Expand Down
11 changes: 10 additions & 1 deletion testing/bevy_example/src/game/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,19 @@ fn exit_game(mut app_exit_events: ResMut<Events<bevy::app::AppExit>>) {

fn check_for_gltf_events(
mut blueprint_events: EventReader<BlueprintEvent>,
all_names: Query<&Name>,
)
{
for event in blueprint_events.read() {
info!("BLUEPRINT EVENT: {:?}", event);
match event {
BlueprintEvent::InstanceReady{entity, blueprint_name, blueprint_path} => {
info!("BLUEPRINT EVENT: {:?} for {:?}", event, all_names.get(*entity));

}
_=> {
info!("BLUEPRINT EVENT: {:?}", event);
}
}
}
}

Expand Down
Loading

0 comments on commit bef709a

Please sign in to comment.