mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-26 13:32:32 +00:00
Compare commits
2 Commits
6cbb144746
...
6dde9823ed
Author | SHA1 | Date | |
---|---|---|---|
|
6dde9823ed | ||
|
07d58467c4 |
@ -1,10 +1,10 @@
|
|||||||
use bevy::{math::Vec3A, prelude::*, render::primitives::Aabb};
|
use bevy::{math::Vec3A, prelude::*, render::primitives::Aabb};
|
||||||
|
|
||||||
use crate::{BlenvyConfig, Spawned};
|
use crate::{BlenvyConfig, BlueprintReadyForFinalizing, BlueprintReadyForPostProcess};
|
||||||
|
|
||||||
/// helper system that computes the compound aabbs of the scenes/blueprints
|
/// helper system that computes the compound aabbs of the scenes/blueprints
|
||||||
pub fn compute_scene_aabbs(
|
pub fn compute_scene_aabbs(
|
||||||
root_entities: Query<(Entity, &Name), (With<Spawned>, Without<Aabb>)>,
|
root_entities: Query<(Entity, &Name), (With<BlueprintReadyForPostProcess>, Without<Aabb>)>,
|
||||||
children: Query<&Children>,
|
children: Query<&Children>,
|
||||||
existing_aabbs: Query<&Aabb>,
|
existing_aabbs: Query<&Aabb>,
|
||||||
|
|
||||||
@ -21,10 +21,10 @@ pub fn compute_scene_aabbs(
|
|||||||
.aabb_cache
|
.aabb_cache
|
||||||
.get(&name.to_string())
|
.get(&name.to_string())
|
||||||
.expect("we should have the aabb available");
|
.expect("we should have the aabb available");
|
||||||
commands.entity(root_entity).insert(*aabb);
|
commands.entity(root_entity).insert(*aabb).insert(BlueprintReadyForFinalizing);
|
||||||
} else {
|
} else {
|
||||||
let aabb = compute_descendant_aabb(root_entity, &children, &existing_aabbs);
|
let aabb = compute_descendant_aabb(root_entity, &children, &existing_aabbs);
|
||||||
commands.entity(root_entity).insert(aabb);
|
commands.entity(root_entity).insert(aabb).insert(BlueprintReadyForFinalizing);
|
||||||
blenvy_config.aabb_cache.insert(name.to_string(), aabb);
|
blenvy_config.aabb_cache.insert(name.to_string(), aabb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,35 +107,36 @@ impl Plugin for BlueprintsPlugin {
|
|||||||
(
|
(
|
||||||
blueprints_prepare_spawn,
|
blueprints_prepare_spawn,
|
||||||
blueprints_check_assets_loading,
|
blueprints_check_assets_loading,
|
||||||
blueprints_spawn,
|
blueprints_assets_ready,
|
||||||
|
blueprints_scenes_spawned,
|
||||||
|
blueprints_transfer_components,
|
||||||
|
|
||||||
|
|
||||||
|
(compute_scene_aabbs, apply_deferred)
|
||||||
|
.chain(),
|
||||||
|
// .run_if(aabbs_enabled),
|
||||||
|
apply_deferred,
|
||||||
|
|
||||||
|
blueprints_finalize_instances,
|
||||||
|
|
||||||
|
|
||||||
/*(
|
/*(
|
||||||
prepare_blueprints,
|
|
||||||
blueprints_check_assets_loading,
|
|
||||||
blueprints_spawn,
|
|
||||||
apply_deferred,
|
|
||||||
)
|
|
||||||
.chain(),*/
|
|
||||||
(compute_scene_aabbs, apply_deferred)
|
|
||||||
.chain()
|
|
||||||
.run_if(aabbs_enabled),
|
|
||||||
apply_deferred,
|
|
||||||
(
|
|
||||||
materials_inject,
|
materials_inject,
|
||||||
check_for_material_loaded,
|
check_for_material_loaded,
|
||||||
materials_inject2,
|
materials_inject2,
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()*/
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(GltfBlueprintsSet::Spawn),
|
.in_set(GltfBlueprintsSet::Spawn),
|
||||||
)
|
)
|
||||||
.add_systems(
|
/* .add_systems(
|
||||||
Update,
|
Update,
|
||||||
(spawned_blueprint_post_process, apply_deferred)
|
(spawned_blueprint_post_process, apply_deferred)
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(GltfBlueprintsSet::AfterSpawn),
|
.in_set(GltfBlueprintsSet::AfterSpawn),
|
||||||
)
|
)*/
|
||||||
|
|
||||||
/* .add_systems(
|
/* .add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
@ -145,6 +146,7 @@ impl Plugin for BlueprintsPlugin {
|
|||||||
)*/
|
)*/
|
||||||
|
|
||||||
.add_systems(Update, react_to_asset_changes)
|
.add_systems(Update, react_to_asset_changes)
|
||||||
|
// .add_systems(Update, track_sub_blueprints)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use bevy::{asset::LoadedUntypedAsset, gltf::Gltf, prelude::*, render::view::visibility, scene::SceneInstance, transform::commands, utils::hashbrown::HashMap};
|
use bevy::{asset::LoadedUntypedAsset, ecs::entity, gltf::Gltf, prelude::*, render::view::visibility, scene::SceneInstance, transform::commands, utils::hashbrown::HashMap};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{BlueprintAssets, BlueprintAssetsLoadState, AssetLoadTracker, BlenvyConfig, BlueprintAnimations, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded};
|
use crate::{BlueprintAssets, BlueprintAssetsLoadState, AssetLoadTracker, BlenvyConfig, BlueprintAnimations, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded};
|
||||||
@ -65,6 +65,7 @@ pub enum BlueprintEvent {
|
|||||||
|
|
||||||
/// event fired when a blueprint has finished loading its assets & before it attempts spawning
|
/// event fired when a blueprint has finished loading its assets & before it attempts spawning
|
||||||
AssetsLoaded {
|
AssetsLoaded {
|
||||||
|
entity: Entity,
|
||||||
blueprint_name: String,
|
blueprint_name: String,
|
||||||
blueprint_path: String,
|
blueprint_path: String,
|
||||||
// TODO: add assets list ?
|
// TODO: add assets list ?
|
||||||
@ -73,18 +74,40 @@ pub enum BlueprintEvent {
|
|||||||
/// - all its assets have been loaded
|
/// - all its assets have been loaded
|
||||||
/// - the spawning attempt has been sucessfull
|
/// - the spawning attempt has been sucessfull
|
||||||
Spawned {
|
Spawned {
|
||||||
|
entity: Entity,
|
||||||
blueprint_name: String,
|
blueprint_name: String,
|
||||||
blueprint_path: String,
|
blueprint_path: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
///
|
///
|
||||||
Ready {
|
Ready {
|
||||||
|
entity: Entity,
|
||||||
blueprint_path: String,
|
blueprint_path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: move this somewhere else ?
|
||||||
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// component used to mark any entity as Dynamic: aka add this to make sure your entity is going to be saved
|
||||||
|
pub struct DynamicBlueprintInstance;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: move these somewhere else ?
|
||||||
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// component gets added when a blueprint starts spawning, removed when spawning is done
|
||||||
|
pub struct BlueprintSpawning;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// component gets added when a blueprint spawning is done
|
||||||
|
pub struct BlueprintSpawned;
|
||||||
|
|
||||||
|
|
||||||
use gltf::Gltf as RawGltf;
|
use gltf::Gltf as RawGltf;
|
||||||
|
|
||||||
pub(crate) fn blueprints_prepare_spawn(
|
pub(crate) fn blueprints_prepare_spawn(
|
||||||
@ -101,8 +124,8 @@ asset_server: Res<AssetServer>,
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
for (entity, blueprint_info, parent, all_assets) in blueprint_instances_to_spawn.iter() {
|
for (entity, blueprint_info, parent, all_assets) in blueprint_instances_to_spawn.iter() {
|
||||||
println!("Detected blueprint to spawn: {:?} path:{:?}", blueprint_info.name, blueprint_info.path);
|
info!("BLUEPRINT: to spawn detected: {:?} path:{:?}", blueprint_info.name, blueprint_info.path);
|
||||||
println!("all assets {:?}", all_assets);
|
//println!("all assets {:?}", all_assets);
|
||||||
//////////////
|
//////////////
|
||||||
|
|
||||||
// we add the asset of the blueprint itself
|
// we add the asset of the blueprint itself
|
||||||
@ -124,7 +147,7 @@ asset_server: Res<AssetServer>,
|
|||||||
|
|
||||||
// and we also add all its assets
|
// and we also add all its assets
|
||||||
/* prefetch attempt */
|
/* prefetch attempt */
|
||||||
let gltf = RawGltf::open(format!("assets/{}", blueprint_info.path)).unwrap();// RawGltf::open("examples/Box.gltf")?;
|
let gltf = RawGltf::open(format!("assets/{}", blueprint_info.path)).unwrap();
|
||||||
for scene in gltf.scenes() {
|
for scene in gltf.scenes() {
|
||||||
let foo_extras = scene.extras().clone().unwrap();
|
let foo_extras = scene.extras().clone().unwrap();
|
||||||
|
|
||||||
@ -137,7 +160,7 @@ asset_server: Res<AssetServer>,
|
|||||||
let assets_raw = &lookup["BlueprintAssets"];
|
let assets_raw = &lookup["BlueprintAssets"];
|
||||||
//println!("ASSETS RAW {}", assets_raw);
|
//println!("ASSETS RAW {}", assets_raw);
|
||||||
let all_assets: BlueprintAssets = ron::from_str(&assets_raw.as_str().unwrap()).unwrap();
|
let all_assets: BlueprintAssets = ron::from_str(&assets_raw.as_str().unwrap()).unwrap();
|
||||||
println!("all_assets {:?}", all_assets);
|
// println!("all_assets {:?}", all_assets);
|
||||||
|
|
||||||
for asset in all_assets.assets.iter() {
|
for asset in all_assets.assets.iter() {
|
||||||
let untyped_handle = asset_server.load_untyped(&asset.path);
|
let untyped_handle = asset_server.load_untyped(&asset.path);
|
||||||
@ -174,6 +197,9 @@ asset_server: Res<AssetServer>,
|
|||||||
} else {
|
} else {
|
||||||
commands.entity(entity).insert(BlueprintAssetsLoaded);
|
commands.entity(entity).insert(BlueprintAssetsLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
commands.entity(entity).insert(BlueprintSpawning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,8 +242,8 @@ pub(crate) fn blueprints_check_assets_loading(
|
|||||||
|
|
||||||
if all_loaded {
|
if all_loaded {
|
||||||
assets_to_load.all_loaded = true;
|
assets_to_load.all_loaded = true;
|
||||||
println!("LOADING: in progress for ALL assets of {:?} (instance of {}), preparing for spawn", entity_name, blueprint_info.path);
|
// println!("LOADING: DONE for ALL assets of {:?} (instance of {}), preparing for spawn", entity_name, blueprint_info.path);
|
||||||
blueprint_events.send(BlueprintEvent::AssetsLoaded {blueprint_name:"".into(), blueprint_path: blueprint_info.path.clone() });
|
// blueprint_events.send(BlueprintEvent::AssetsLoaded {blueprint_name:"".into(), blueprint_path: blueprint_info.path.clone() });
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
@ -226,84 +252,13 @@ pub(crate) fn blueprints_check_assets_loading(
|
|||||||
//.remove::<BlueprintAssetsLoadState>() //REMOVE it in release mode/ when hot reload is off, keep it for dev/hot reload
|
//.remove::<BlueprintAssetsLoadState>() //REMOVE it in release mode/ when hot reload is off, keep it for dev/hot reload
|
||||||
;
|
;
|
||||||
}else {
|
}else {
|
||||||
println!("LOADING: done for ALL assets of {:?} (instance of {}): {} ",entity_name, blueprint_info.path, progress * 100.0);
|
// println!("LOADING: in progress for ALL assets of {:?} (instance of {}): {} ",entity_name, blueprint_info.path, progress * 100.0);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub(crate) fn hot_reload_asset_check(
|
|
||||||
mut blueprint_assets: Query<
|
|
||||||
(Entity, Option<&Name>, &BlueprintInfo, &mut BlueprintAssetsLoadState)>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
mut commands: Commands,
|
|
||||||
){
|
|
||||||
for (entity, entity_name, blueprint_info, mut assets_to_load) in blueprint_assets.iter_mut() {
|
|
||||||
for tracker in assets_to_load.asset_infos.iter_mut() {
|
|
||||||
let asset_id = tracker.id;
|
|
||||||
asset_server.is_changed()
|
|
||||||
if asset_server.load_state(asset_id) == bevy::asset::LoadState::
|
|
||||||
match asset_server.load_state(asset_id) {
|
|
||||||
bevy::asset::LoadState::Failed(_) => {
|
|
||||||
failed = true
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//AssetEvent::Modified`
|
|
||||||
}
|
|
||||||
|
|
||||||
}*/
|
|
||||||
|
|
||||||
use bevy::asset::AssetEvent;
|
|
||||||
|
|
||||||
pub(crate) fn react_to_asset_changes(
|
|
||||||
mut gltf_events: EventReader<AssetEvent<Gltf>>,
|
|
||||||
mut untyped_events: EventReader<AssetEvent<LoadedUntypedAsset>>,
|
|
||||||
mut blueprint_assets: Query<(Entity, Option<&Name>, &BlueprintInfo, &mut BlueprintAssetsLoadState, Option<&Children>)>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
mut commands: Commands,
|
|
||||||
|
|
||||||
) {
|
|
||||||
|
|
||||||
for event in gltf_events.read() {
|
|
||||||
// LoadedUntypedAsset
|
|
||||||
match event {
|
|
||||||
AssetEvent::Modified { id } => {
|
|
||||||
// React to the image being modified
|
|
||||||
// println!("Modified gltf {:?}", asset_server.get_path(*id));
|
|
||||||
for (entity, entity_name, blueprint_info, mut assets_to_load, children) in blueprint_assets.iter_mut() {
|
|
||||||
for tracker in assets_to_load.asset_infos.iter_mut() {
|
|
||||||
if asset_server.get_path(*id).is_some() {
|
|
||||||
if tracker.path == asset_server.get_path(*id).unwrap().to_string() {
|
|
||||||
println!("HOLY MOLY IT DETECTS !!, now respawn {:?}", entity_name);
|
|
||||||
if children.is_some() {
|
|
||||||
for child in children.unwrap().iter(){
|
|
||||||
commands.entity(*child).despawn_recursive();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commands.entity(entity)
|
|
||||||
.remove::<BlueprintAssetsLoaded>()
|
|
||||||
.remove::<SceneInstance>()
|
|
||||||
.remove::<BlueprintAssetsLoadState>()
|
|
||||||
.insert(SpawnHere);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn blueprints_spawn(
|
pub(crate) fn blueprints_assets_ready(spawn_placeholders: Query<
|
||||||
spawn_placeholders: Query<
|
|
||||||
(
|
(
|
||||||
Entity,
|
Entity,
|
||||||
&BlueprintInfo,
|
&BlueprintInfo,
|
||||||
@ -324,8 +279,8 @@ pub(crate) fn blueprints_spawn(
|
|||||||
|
|
||||||
assets_gltf: Res<Assets<Gltf>>,
|
assets_gltf: Res<Assets<Gltf>>,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
children: Query<&Children>,
|
children: Query<&Children>,)
|
||||||
) {
|
{
|
||||||
for (
|
for (
|
||||||
entity,
|
entity,
|
||||||
blueprint_info,
|
blueprint_info,
|
||||||
@ -335,9 +290,14 @@ pub(crate) fn blueprints_spawn(
|
|||||||
name,
|
name,
|
||||||
) in spawn_placeholders.iter()
|
) in spawn_placeholders.iter()
|
||||||
{
|
{
|
||||||
info!(
|
/*info!(
|
||||||
"all assets loaded, attempting to spawn blueprint {:?} for entity {:?}, id: {:?}, parent:{:?}",
|
"BLUEPRINT: all assets loaded, attempting to spawn blueprint SCENE {:?} for entity {:?}, id: {:}, parent:{:?}",
|
||||||
blueprint_info.name, name, entity, original_parent
|
blueprint_info.name, name, entity, original_parent
|
||||||
|
);*/
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"BLUEPRINT: all assets loaded, attempting to spawn blueprint SCENE {:?} for entity {:?}, id: {}",
|
||||||
|
blueprint_info.name, name, entity
|
||||||
);
|
);
|
||||||
|
|
||||||
// info!("attempting to spawn {:?}", model_path);
|
// info!("attempting to spawn {:?}", model_path);
|
||||||
@ -385,25 +345,278 @@ pub(crate) fn blueprints_spawn(
|
|||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Spawned,
|
|
||||||
BlueprintInstanceReady, // FIXME: not sure if this is should be added here or in the post process
|
|
||||||
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: named_animations//gltf.named_animations.clone(),
|
named_animations: named_animations//gltf.named_animations.clone(),
|
||||||
},
|
},
|
||||||
|
|
||||||
));
|
));
|
||||||
|
|
||||||
if add_to_world.is_some() {
|
/* if add_to_world.is_some() {
|
||||||
let world = game_world
|
let world = game_world
|
||||||
.get_single_mut()
|
.get_single_mut()
|
||||||
.expect("there should be a game world present");
|
.expect("there should be a game world present");
|
||||||
commands.entity(world).add_child(entity);
|
commands.entity(world).add_child(entity);
|
||||||
|
} */
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct SubBlueprintsSpawnTracker{
|
||||||
|
pub sub_blueprint_instances: HashMap<Entity, bool>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct SpawnTrackRoot(pub Entity);
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct BlueprintSceneSpawned;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct BlueprintChildrenReady;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct BlueprintReadyForPostProcess;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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>>,
|
||||||
|
all_children: Query<&Children>,
|
||||||
|
|
||||||
|
mut sub_blueprint_trackers: Query<(Entity, &mut SubBlueprintsSpawnTracker, &BlueprintInfo)>,
|
||||||
|
|
||||||
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
|
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);
|
||||||
|
let mut sub_blueprint_instances: Vec<Entity> = vec![];
|
||||||
|
let mut tracker_data: HashMap<Entity, bool> = HashMap::new();
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.entity(child).insert(SpawnTrackRoot(entity));// Injecting to know which entity is the root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("sub blueprint instances {:?}", sub_blueprint_instances);
|
||||||
|
|
||||||
|
// TODO: how about when no sub blueprints are present
|
||||||
|
if tracker_data.keys().len() > 0 {
|
||||||
|
commands.entity(entity)
|
||||||
|
.insert(SubBlueprintsSpawnTracker{sub_blueprint_instances: tracker_data.clone()});
|
||||||
|
}else {
|
||||||
|
commands.entity(entity).insert(BlueprintChildrenReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// could be done differently, by notifying each parent of a spawning blueprint that this child is done spawning ?
|
||||||
|
// perhaps using component hooks or observers (ie , if a ComponentSpawning + Parent)
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
CopyComponents,
|
||||||
|
};
|
||||||
|
use std::any::TypeId;
|
||||||
|
|
||||||
|
|
||||||
|
/// this system is in charge of doing component transfers & co
|
||||||
|
/// - it removes one level of useless nesting
|
||||||
|
/// - it copies the blueprint's root components to the entity it was spawned on (original entity)
|
||||||
|
/// - it copies the children of the blueprint scene into the original entity
|
||||||
|
/// - it add `AnimationLink` components so that animations can be controlled from the original entity
|
||||||
|
pub(crate) fn blueprints_transfer_components(
|
||||||
|
foo: Query<(
|
||||||
|
Entity,
|
||||||
|
&Children,
|
||||||
|
&OriginalChildren,
|
||||||
|
Option<&Name>,
|
||||||
|
Option<&SpawnTrackRoot>),
|
||||||
|
Added<BlueprintChildrenReady>
|
||||||
|
>,
|
||||||
|
mut sub_blueprint_trackers: Query<(Entity, &mut SubBlueprintsSpawnTracker, &BlueprintInfo)>,
|
||||||
|
all_children: Query<&Children>,
|
||||||
|
|
||||||
|
mut commands: Commands,
|
||||||
|
|
||||||
|
all_names: Query<&Name>
|
||||||
|
) {
|
||||||
|
|
||||||
|
for (original, children, original_children, name, track_root) in foo.iter() {
|
||||||
|
println!("YOOO ready !! {:?}", name);
|
||||||
|
|
||||||
|
if children.len() == 0 {
|
||||||
|
warn!("timing issue ! no children found, please restart your bevy app (bug being investigated)");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// the root node is the first & normally only child inside a scene, it is the one that has all relevant components
|
||||||
|
let mut blueprint_root_entity = Entity::PLACEHOLDER; //FIXME: and what about childless ones ?? => should not be possible normally
|
||||||
|
// let diff = HashSet::from_iter(original_children.0).difference(HashSet::from_iter(children));
|
||||||
|
// we find the first child that was not in the entity before (aka added during the scene spawning)
|
||||||
|
for child in children.iter() {
|
||||||
|
if !original_children.0.contains(child) {
|
||||||
|
blueprint_root_entity = *child;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy components into from blueprint instance's blueprint_root_entity to original entity
|
||||||
|
commands.add(CopyComponents {
|
||||||
|
source: blueprint_root_entity,
|
||||||
|
destination: original,
|
||||||
|
exclude: vec![TypeId::of::<Parent>(), TypeId::of::<Children>()],
|
||||||
|
stringent: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// we move all of children of the blueprint instance one level to the original entity to avoid having an additional, useless nesting level
|
||||||
|
if let Ok(root_entity_children) = all_children.get(blueprint_root_entity) {
|
||||||
|
for child in root_entity_children.iter() {
|
||||||
|
// info!("copying child {:?} upward from {:?} to {:?}", names.get(*child), blueprint_root_entity, original);
|
||||||
|
commands.entity(original).add_child(*child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
commands.entity(original)
|
||||||
|
.insert(BlueprintReadyForPostProcess); // Tag the entity so any systems dealing with post processing can now 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>();
|
||||||
|
commands.entity(blueprint_root_entity).despawn_recursive(); // Remove the root entity that comes from the spawned-in scene
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
if let Some(track_root) = track_root {
|
||||||
|
// println!("got some root {:?}", root_name);
|
||||||
|
if let Ok((s_entity, mut tracker, bp_info)) = sub_blueprint_trackers.get_mut(track_root.0) {
|
||||||
|
tracker.sub_blueprint_instances.entry(original).or_insert(true);
|
||||||
|
tracker.sub_blueprint_instances.insert(original, true);
|
||||||
|
|
||||||
|
// TODO: ugh, my limited rust knowledge, this is bad code
|
||||||
|
let mut all_spawned = true;
|
||||||
|
for val in tracker.sub_blueprint_instances.values() {
|
||||||
|
if !val {
|
||||||
|
all_spawned = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if all_spawned {
|
||||||
|
// println!("ALLLLL SPAAAAWNED for {} named {:?}", track_root.0, root_name);
|
||||||
|
commands.entity(track_root.0).insert(BlueprintChildrenReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct BlueprintReadyForFinalizing;
|
||||||
|
|
||||||
|
pub(crate) fn blueprints_finalize_instances(
|
||||||
|
blueprint_instances: Query<(Entity, Option<&Name>), (With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>)>,
|
||||||
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
|
for (entity, name) in blueprint_instances.iter() {
|
||||||
|
info!("Finalizing blueprint instance {:?}", name);
|
||||||
|
commands.entity(entity)
|
||||||
|
.remove::<SpawnHere>()
|
||||||
|
.remove::<BlueprintSpawning>()
|
||||||
|
.insert(BlueprintSpawned)
|
||||||
|
.insert(Visibility::Visible)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
BlueprintSpawning
|
||||||
|
- Blueprint Load Assets
|
||||||
|
- Blueprint Assets Ready: spawn Blueprint's scene
|
||||||
|
- Blueprint Scene Ready:
|
||||||
|
- get list of sub Blueprints if any, inject blueprints spawn tracker
|
||||||
|
=> annoying issue with the "nested" useless root node created by blender
|
||||||
|
=> distinguish between blueprint instances inside blueprint instances vs blueprint instances inside blueprints ??
|
||||||
|
- Blueprint sub_blueprints Ready
|
||||||
|
if all children are ready
|
||||||
|
|
||||||
|
- Blueprints post process
|
||||||
|
- generate aabb (need full hierarchy in its final form)
|
||||||
|
- materials ?
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// HOT RELOAD
|
||||||
|
|
||||||
|
|
||||||
|
use bevy::asset::AssetEvent;
|
||||||
|
|
||||||
|
pub(crate) fn react_to_asset_changes(
|
||||||
|
mut gltf_events: EventReader<AssetEvent<Gltf>>,
|
||||||
|
mut untyped_events: EventReader<AssetEvent<LoadedUntypedAsset>>,
|
||||||
|
mut blueprint_assets: Query<(Entity, Option<&Name>, &BlueprintInfo, &mut BlueprintAssetsLoadState, Option<&Children>)>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut commands: Commands,
|
||||||
|
|
||||||
|
) {
|
||||||
|
|
||||||
|
for event in gltf_events.read() {
|
||||||
|
// LoadedUntypedAsset
|
||||||
|
match event {
|
||||||
|
AssetEvent::Modified { id } => {
|
||||||
|
// React to the image being modified
|
||||||
|
// println!("Modified gltf {:?}", asset_server.get_path(*id));
|
||||||
|
for (entity, entity_name, blueprint_info, mut assets_to_load, children) in blueprint_assets.iter_mut() {
|
||||||
|
for tracker in assets_to_load.asset_infos.iter_mut() {
|
||||||
|
if asset_server.get_path(*id).is_some() {
|
||||||
|
if tracker.path == asset_server.get_path(*id).unwrap().to_string() {
|
||||||
|
println!("HOLY MOLY IT DETECTS !!, now respawn {:?}", entity_name);
|
||||||
|
if children.is_some() {
|
||||||
|
for child in children.unwrap().iter(){
|
||||||
|
commands.entity(*child).despawn_recursive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commands.entity(entity)
|
||||||
|
.remove::<BlueprintAssetsLoaded>()
|
||||||
|
.remove::<SceneInstance>()
|
||||||
|
.remove::<BlueprintAssetsLoadState>()
|
||||||
|
.insert(SpawnHere);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ use bevy::prelude::*;
|
|||||||
use bevy::scene::SceneInstance;
|
use bevy::scene::SceneInstance;
|
||||||
// use bevy::utils::hashbrown::HashSet;
|
// use bevy::utils::hashbrown::HashSet;
|
||||||
|
|
||||||
use crate::{BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintInfo};
|
use crate::{BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintInfo, BlueprintReadyForPostProcess, BlueprintSpawned, BlueprintSpawning, SpawnTrackRoot, SubBlueprintsSpawnTracker};
|
||||||
use crate::{SpawnHere, Spawned};
|
use crate::{SpawnHere, Spawned};
|
||||||
use crate::{
|
use crate::{
|
||||||
BlueprintEvent, CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren
|
BlueprintEvent, CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren
|
||||||
@ -13,13 +13,14 @@ use crate::{
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// this system is in charge of doing any necessary post processing after a blueprint scene has been spawned
|
/// this system is in charge of doing any necessary post processing after a blueprint scene has been spawned
|
||||||
/// - it removes one level of useless nesting
|
/// - it removes one level of useless nesting
|
||||||
/// - it copies the blueprint's root components to the entity it was spawned on (original entity)
|
/// - it copies the blueprint's root components to the entity it was spawned on (original entity)
|
||||||
/// - it copies the children of the blueprint scene into the original entity
|
/// - it copies the children of the blueprint scene into the original entity
|
||||||
/// - it add `AnimationLink` components so that animations can be controlled from the original entity
|
/// - it add `AnimationLink` components so that animations can be controlled from the original entity
|
||||||
/// - it cleans up/ removes a few , by then uneeded components
|
/// - it cleans up/ removes a few , by then uneeded components
|
||||||
pub(crate) fn spawned_blueprint_post_process(
|
pub(crate) fn spawned_blueprint_post_process( // rename to '
|
||||||
unprocessed_entities: Query<
|
unprocessed_entities: Query<
|
||||||
(
|
(
|
||||||
Entity,
|
Entity,
|
||||||
@ -28,18 +29,24 @@ pub(crate) fn spawned_blueprint_post_process(
|
|||||||
&BlueprintAnimations,
|
&BlueprintAnimations,
|
||||||
Option<&NoInBlueprint>,
|
Option<&NoInBlueprint>,
|
||||||
Option<&Name>,
|
Option<&Name>,
|
||||||
&BlueprintInfo
|
&BlueprintInfo,
|
||||||
|
|
||||||
|
// sub blueprint instances tracker
|
||||||
|
Option<&SpawnTrackRoot>
|
||||||
),
|
),
|
||||||
(With<SpawnHere>, With<SceneInstance>, With<Spawned>),
|
(With<SpawnHere>, With<SceneInstance>, Added<BlueprintReadyForPostProcess>),
|
||||||
>,
|
>,
|
||||||
added_animation_players: Query<(Entity, &Parent), Added<AnimationPlayer>>,
|
added_animation_players: Query<(Entity, &Parent), Added<AnimationPlayer>>,
|
||||||
all_children: Query<&Children>,
|
all_children: Query<&Children>,
|
||||||
|
|
||||||
|
mut trackers: Query<(Entity, &mut SubBlueprintsSpawnTracker, &BlueprintInfo)>,
|
||||||
|
|
||||||
|
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut blueprint_events: EventWriter<BlueprintEvent>,
|
mut blueprint_events: EventWriter<BlueprintEvent>,
|
||||||
|
|
||||||
) {
|
) {
|
||||||
for (original, children, original_children, animations, no_inblueprint, name, blueprint_info) in
|
for (original, children, original_children, animations, no_inblueprint, name, blueprint_info, track_root) in
|
||||||
unprocessed_entities.iter()
|
unprocessed_entities.iter()
|
||||||
{
|
{
|
||||||
info!("post processing blueprint for entity {:?}", name);
|
info!("post processing blueprint for entity {:?}", name);
|
||||||
@ -63,7 +70,7 @@ pub(crate) fn spawned_blueprint_post_process(
|
|||||||
// can be usefull to filter out anything that came from blueprints vs normal children
|
// can be usefull to filter out anything that came from blueprints vs normal children
|
||||||
if no_inblueprint.is_none() {
|
if no_inblueprint.is_none() {
|
||||||
for child in all_children.iter_descendants(root_entity) {
|
for child in all_children.iter_descendants(root_entity) {
|
||||||
commands.entity(child).insert(InBlueprint);
|
commands.entity(child).insert(InBlueprint); // we do this here in order to avoid doing it to normal children
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,11 +108,58 @@ pub(crate) fn spawned_blueprint_post_process(
|
|||||||
// 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::<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::<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>();
|
//commands.entity(original).remove::<BlueprintAssetsLoaded>();
|
||||||
commands.entity(root_entity).despawn_recursive();
|
commands.entity(root_entity).despawn_recursive(); // Remove the root entity that comes from the spawned-in scene
|
||||||
commands.entity(original).insert( Visibility::Visible
|
commands.entity(original).insert( Visibility::Visible
|
||||||
);
|
);
|
||||||
blueprint_events.send(BlueprintEvent::Spawned {blueprint_name: blueprint_info.name.clone(), blueprint_path: blueprint_info.path.clone() });
|
|
||||||
|
commands.entity(original)
|
||||||
|
.insert(BlueprintSpawned)
|
||||||
|
.remove::<BlueprintSpawning>()
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
.remove::<BlueprintReadyForPostProcess>()
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
if let Some(track_root) = track_root {
|
||||||
|
//println!("got some root");
|
||||||
|
if let Ok((s_entity, mut tracker, bp_info)) = trackers.get_mut(track_root.0) {
|
||||||
|
// println!("found the tracker, setting loaded for {}", entity);
|
||||||
|
tracker.sub_blueprint_instances.entry(original).or_insert(true);
|
||||||
|
tracker.sub_blueprint_instances.insert(original, true);
|
||||||
|
|
||||||
|
// TODO: ugh, my limited rust knowledge, this is bad code
|
||||||
|
let mut all_spawned = true;
|
||||||
|
|
||||||
|
for key in tracker.sub_blueprint_instances.keys() {
|
||||||
|
let val = tracker.sub_blueprint_instances[key];
|
||||||
|
println!("Key: {key}, Spawned {}", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
for val in tracker.sub_blueprint_instances.values() {
|
||||||
|
println!("spawned {}", val);
|
||||||
|
if !val {
|
||||||
|
all_spawned = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if all_spawned { // TODO: move this to an other system, or "notify" the tracked root entity of the fact that all its sub blueprints have been loaded
|
||||||
|
println!("ALLLLL SPAAAAWNED for {}", track_root.0);
|
||||||
|
// commands.entity(track_root.0).insert(bundle)
|
||||||
|
blueprint_events.send(BlueprintEvent::Spawned {entity: track_root.0, blueprint_name: bp_info.name.clone(), blueprint_path: bp_info.path.clone()});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if trackers.get(original).is_err() {
|
||||||
|
// if it has no sub blueprint instances
|
||||||
|
blueprint_events.send(BlueprintEvent::Spawned {entity: original, blueprint_name: blueprint_info.name.clone(), blueprint_path: blueprint_info.path.clone()});
|
||||||
|
}
|
||||||
|
|
||||||
debug!("DONE WITH POST PROCESS");
|
debug!("DONE WITH POST PROCESS");
|
||||||
|
info!("done instanciating blueprint for entity {:?}", name);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,15 +128,15 @@ fn process_tonemapping(
|
|||||||
for (scene_id, tone_mapping) in tonemappings.iter(){
|
for (scene_id, tone_mapping) in tonemappings.iter(){
|
||||||
match tone_mapping {
|
match tone_mapping {
|
||||||
BlenderToneMapping::None => {
|
BlenderToneMapping::None => {
|
||||||
println!("TONEMAPPING NONE");
|
//println!("TONEMAPPING NONE");
|
||||||
commands.entity(entity).remove::<Tonemapping>();
|
commands.entity(entity).remove::<Tonemapping>();
|
||||||
}
|
}
|
||||||
BlenderToneMapping::AgX => {
|
BlenderToneMapping::AgX => {
|
||||||
println!("TONEMAPPING Agx");
|
//println!("TONEMAPPING Agx");
|
||||||
commands.entity(entity).insert(Tonemapping::AgX);
|
commands.entity(entity).insert(Tonemapping::AgX);
|
||||||
}
|
}
|
||||||
BlenderToneMapping::Filmic => {
|
BlenderToneMapping::Filmic => {
|
||||||
println!("TONEMAPPING Filmic");
|
//println!("TONEMAPPING Filmic");
|
||||||
commands.entity(entity).insert(Tonemapping::BlenderFilmic);
|
commands.entity(entity).insert(Tonemapping::BlenderFilmic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
testing/bevy_example/art/testing_simple.blend
Normal file
BIN
testing/bevy_example/art/testing_simple.blend
Normal file
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use blenvy::{BluePrintBundle, BlueprintInfo, GameWorldTag, SpawnHere};
|
use blenvy::{BluePrintBundle, BlueprintInfo, DynamicBlueprintInstance, GameWorldTag, SpawnHere};
|
||||||
use crate::{GameState, InAppRunning};
|
use crate::{GameState, InAppRunning};
|
||||||
|
|
||||||
//use bevy_rapier3d::prelude::Velocity;
|
//use bevy_rapier3d::prelude::Velocity;
|
||||||
@ -65,6 +65,7 @@ pub fn spawn_test(
|
|||||||
blueprint: BlueprintInfo{name: "Blueprint1".into() , path:"blueprints/Blueprint1.glb".into()}, // FIXME
|
blueprint: BlueprintInfo{name: "Blueprint1".into() , path:"blueprints/Blueprint1.glb".into()}, // FIXME
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
DynamicBlueprintInstance,
|
||||||
bevy::prelude::Name::from(format!("test{}", name_index)),
|
bevy::prelude::Name::from(format!("test{}", name_index)),
|
||||||
// SpawnHere,
|
// SpawnHere,
|
||||||
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
|
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
|
||||||
|
@ -13,7 +13,7 @@ pub fn setup_hierarchy_debug(mut commands: Commands, asset_server: Res<AssetServ
|
|||||||
"",
|
"",
|
||||||
TextStyle {
|
TextStyle {
|
||||||
color: LinearRgba { red: 1.0, green:0.0, blue: 0.0, alpha: 1.0}.into(),
|
color: LinearRgba { red: 1.0, green:0.0, blue: 0.0, alpha: 1.0}.into(),
|
||||||
font_size: 20.,
|
font_size: 15.,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -156,8 +156,8 @@ impl Plugin for HiearchyDebugPlugin {
|
|||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app
|
app
|
||||||
.add_systems(Startup, setup_hierarchy_debug)
|
.add_systems(Startup, setup_hierarchy_debug)
|
||||||
.add_systems(Update, check_for_component)
|
// .add_systems(Update, check_for_component)
|
||||||
//.add_systems(Update, draw_hierarchy_debug)
|
.add_systems(Update, draw_hierarchy_debug)
|
||||||
//.add_systems(Update, check_for_gltf_extras)
|
//.add_systems(Update, check_for_gltf_extras)
|
||||||
|
|
||||||
;
|
;
|
||||||
|
@ -216,6 +216,12 @@ Bevy Side:
|
|||||||
- [x] deprecate BlueprintName & BlueprintPath & use BlueprintInfo instead
|
- [x] deprecate BlueprintName & BlueprintPath & use BlueprintInfo instead
|
||||||
- [ ] make blueprint instances invisible until spawning is done to avoid "spawn flash"?
|
- [ ] make blueprint instances invisible until spawning is done to avoid "spawn flash"?
|
||||||
- [ ] should "blueprint spawned" only be triggered after all its sub blueprints have spawned ?
|
- [ ] should "blueprint spawned" only be triggered after all its sub blueprints have spawned ?
|
||||||
|
- [ ] "blueprintInstance ready"/finished
|
||||||
|
BlueprintAssetsLoaded
|
||||||
|
BlueprintSceneSpawned
|
||||||
|
BlueprintChildrenReady
|
||||||
|
BlueprintReadyForPostProcess
|
||||||
|
|
||||||
- [ ] simplify testing example:
|
- [ ] simplify testing example:
|
||||||
- [x] 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
|
||||||
|
@ -386,7 +386,7 @@ class BLENVY_PT_component_tools_panel(bpy.types.Panel):
|
|||||||
self.draw_invalid_or_unregistered(layout, status, custom_property, item, item_type)
|
self.draw_invalid_or_unregistered(layout, status, custom_property, item, item_type)
|
||||||
|
|
||||||
def gather_invalid_item_data(self, item, invalid_component_names, items_with_invalid_components, items_with_original_components, original_name, item_type):
|
def gather_invalid_item_data(self, item, invalid_component_names, items_with_invalid_components, items_with_original_components, original_name, item_type):
|
||||||
blenvy_custom_properties = ['components_meta', 'bevy_components', 'user_assets', 'generated_assets' ] # some of our own hard coded custom properties that should be ignored
|
blenvy_custom_properties = ['components_meta', 'bevy_components', 'user_assets', 'generated_assets', 'BlueprintAssets', 'export_path' ] # some of our own hard coded custom properties that should be ignored
|
||||||
upgreadable_entries = []
|
upgreadable_entries = []
|
||||||
|
|
||||||
if "components_meta" in item or hasattr(item, "components_meta"): # FIXME; wrong way of determining
|
if "components_meta" in item or hasattr(item, "components_meta"): # FIXME; wrong way of determining
|
||||||
|
184
tools/blenvy/tests/test_bevy_integration_simple.py
Normal file
184
tools/blenvy/tests/test_bevy_integration_simple.py
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import bpy
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import pytest
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
import filecmp
|
||||||
|
from PIL import Image
|
||||||
|
from pixelmatch.contrib.PIL import pixelmatch
|
||||||
|
|
||||||
|
from blenvy.add_ons.auto_export.common.prepare_and_export import prepare_and_export
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def setup_data(request):
|
||||||
|
print("\nSetting up resources...")
|
||||||
|
|
||||||
|
root_path = "../../testing/bevy_example"
|
||||||
|
assets_root_path = os.path.join(root_path, "assets")
|
||||||
|
blueprints_path = os.path.join(assets_root_path, "blueprints")
|
||||||
|
levels_path = os.path.join(assets_root_path, "levels")
|
||||||
|
|
||||||
|
models_path = os.path.join(assets_root_path, "models")
|
||||||
|
materials_path = os.path.join(assets_root_path, "materials")
|
||||||
|
yield {
|
||||||
|
"root_path": root_path,
|
||||||
|
"models_path": models_path,
|
||||||
|
"blueprints_path": blueprints_path,
|
||||||
|
"levels_path": levels_path,
|
||||||
|
"materials_path":materials_path
|
||||||
|
}
|
||||||
|
|
||||||
|
def finalizer():
|
||||||
|
|
||||||
|
#other_materials_path = os.path.join("../../testing", "other_materials")
|
||||||
|
|
||||||
|
print("\nPerforming teardown...")
|
||||||
|
if os.path.exists(blueprints_path):
|
||||||
|
shutil.rmtree(blueprints_path)
|
||||||
|
|
||||||
|
if os.path.exists(levels_path):
|
||||||
|
shutil.rmtree(levels_path)
|
||||||
|
|
||||||
|
if os.path.exists(models_path):
|
||||||
|
shutil.rmtree(models_path)
|
||||||
|
|
||||||
|
if os.path.exists(materials_path):
|
||||||
|
shutil.rmtree(materials_path)
|
||||||
|
|
||||||
|
diagnostics_file_path = os.path.join(root_path, "bevy_diagnostics.json")
|
||||||
|
if os.path.exists(diagnostics_file_path):
|
||||||
|
os.remove(diagnostics_file_path)
|
||||||
|
|
||||||
|
hierarchy_file_path = os.path.join(root_path, "bevy_hierarchy.json")
|
||||||
|
if os.path.exists(hierarchy_file_path):
|
||||||
|
os.remove(hierarchy_file_path)
|
||||||
|
|
||||||
|
screenshot_observed_path = os.path.join(root_path, "screenshot.png")
|
||||||
|
if os.path.exists(screenshot_observed_path):
|
||||||
|
os.remove(screenshot_observed_path)
|
||||||
|
|
||||||
|
#request.addfinalizer(finalizer)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
- calls exporter on the testing scene
|
||||||
|
- launches bevy app & checks for output
|
||||||
|
- checks screenshot, hierarchy & diagnostics files generated on the bevy side against reference files
|
||||||
|
- if all worked => test is a-ok
|
||||||
|
- removes generated files
|
||||||
|
"""
|
||||||
|
def test_export_complex(setup_data):
|
||||||
|
root_path = setup_data["root_path"]
|
||||||
|
|
||||||
|
# with change detection
|
||||||
|
# first, configure things
|
||||||
|
# we use the global settings for that
|
||||||
|
export_props = {
|
||||||
|
}
|
||||||
|
gltf_settings = {
|
||||||
|
"export_animations": True,
|
||||||
|
"export_optimize_animation_size": False,
|
||||||
|
"export_apply":True
|
||||||
|
}
|
||||||
|
|
||||||
|
# store settings for the auto_export part
|
||||||
|
stored_auto_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")
|
||||||
|
stored_auto_settings.clear()
|
||||||
|
stored_auto_settings.write(json.dumps(export_props))
|
||||||
|
|
||||||
|
# and store settings for the gltf part
|
||||||
|
stored_gltf_settings = bpy.data.texts[".blenvy_gltf_settings"] if ".blenvy_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".blenvy_gltf_settings")
|
||||||
|
stored_gltf_settings.clear()
|
||||||
|
stored_gltf_settings.write(json.dumps(gltf_settings))
|
||||||
|
|
||||||
|
# move the main cube
|
||||||
|
# bpy.data.objects["Cube"].location = [1, 0, 0]
|
||||||
|
# move the cube in the library
|
||||||
|
# TODO: add back bpy.data.objects["Blueprint1_mesh"].location = [1, 2, 1]
|
||||||
|
|
||||||
|
blenvy = bpy.context.window_manager.blenvy
|
||||||
|
#blenvy.project_root_path =
|
||||||
|
#blenvy.blueprints_path
|
||||||
|
blenvy.auto_export.auto_export = True
|
||||||
|
blenvy.auto_export.export_scene_settings = True
|
||||||
|
blenvy.auto_export.export_blueprints = True
|
||||||
|
#blenvy.auto_export.export_materials_library = True
|
||||||
|
|
||||||
|
bpy.data.scenes['World'].blenvy_scene_type = 'Level' # set scene as main/level scene
|
||||||
|
bpy.data.scenes['Library'].blenvy_scene_type = 'Library' # set scene as Library scene
|
||||||
|
|
||||||
|
# scene asset
|
||||||
|
user_asset = bpy.data.scenes['World'].user_assets.add()
|
||||||
|
'''user_asset.name = "test_asset"
|
||||||
|
user_asset.path = "audio/fake.mp3"'''
|
||||||
|
|
||||||
|
# blueprint asset
|
||||||
|
#user_asset = bpy.data.collections['Blueprint4_nested'].user_assets.add()
|
||||||
|
'''user_asset.name = "yoho_audio"
|
||||||
|
user_asset.path = "audio/fake.mp3"'''
|
||||||
|
|
||||||
|
# we have to cheat, since we cannot rely on the data injected when saving the library file (since we are not saving it as part of the tests)
|
||||||
|
'''bpy.data.collections["External_blueprint"]["export_path"] = "blueprints/External_blueprint.glb"
|
||||||
|
bpy.data.collections["External_blueprint2"]["export_path"] = "blueprints/External_blueprint2.glb"
|
||||||
|
bpy.data.collections["External_blueprint3"]["export_path"] = "blueprints/External_blueprint3.glb"'''
|
||||||
|
|
||||||
|
# do the actual exporting
|
||||||
|
prepare_and_export()
|
||||||
|
|
||||||
|
# blueprint1 => has an instance, got changed, should export
|
||||||
|
# blueprint2 => has NO instance, but marked as asset, should export
|
||||||
|
# blueprint3 => has NO instance, not marked as asset, used inside blueprint 4: should export
|
||||||
|
# blueprint4 => has an instance, with nested blueprint3, should export
|
||||||
|
# blueprint5 => has NO instance, not marked as asset, should NOT export
|
||||||
|
|
||||||
|
'''assert os.path.exists(os.path.join(setup_data["levels_path"], "World.glb")) == True
|
||||||
|
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint1.glb")) == True
|
||||||
|
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint2.glb")) == True
|
||||||
|
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint3.glb")) == True
|
||||||
|
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint4_nested.glb")) == True
|
||||||
|
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint5.glb")) == False
|
||||||
|
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint6_animated.glb")) == True
|
||||||
|
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint7_hierarchy.glb")) == True'''
|
||||||
|
|
||||||
|
# 'assets_list_'+scene.name+"_components" should have been removed after the export
|
||||||
|
assets_list_object_name = "assets_list_"+"World"+"_components"
|
||||||
|
assets_list_object_present = assets_list_object_name in bpy.data.objects
|
||||||
|
assert assets_list_object_present == False
|
||||||
|
|
||||||
|
# now run bevy
|
||||||
|
command = "cargo run --features bevy/dynamic_linking"
|
||||||
|
FNULL = open(os.devnull, 'w') #use this if you want to suppress output to stdout from the subprocess
|
||||||
|
return_code = subprocess.call(["cargo", "run", "--features", "bevy/dynamic_linking"], cwd=root_path)
|
||||||
|
print("RETURN CODE OF BEVY APP", return_code)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
with open(os.path.join(root_path, "bevy_diagnostics.json")) as diagnostics_file:
|
||||||
|
diagnostics = json.load(diagnostics_file)
|
||||||
|
print("diagnostics", diagnostics)
|
||||||
|
assert diagnostics["animations"] == True
|
||||||
|
assert diagnostics["empty_found"] == True
|
||||||
|
assert diagnostics["blueprints_list_found"] == True
|
||||||
|
assert diagnostics["exported_names_correct"] == True
|
||||||
|
|
||||||
|
with open(os.path.join(root_path, "bevy_hierarchy.json")) as hierarchy_file:
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), "expected_bevy_hierarchy.json")) as expexted_hierarchy_file:
|
||||||
|
hierarchy = json.load(hierarchy_file)
|
||||||
|
expected = json.load(expexted_hierarchy_file)
|
||||||
|
assert sorted(hierarchy.items()) == sorted(expected.items())
|
||||||
|
|
||||||
|
# last but not least, do a visual compare
|
||||||
|
screenshot_expected_path = os.path.join(os.path.dirname(__file__), "expected_screenshot.png")
|
||||||
|
screenshot_observed_path = os.path.join(root_path, "screenshot.png")
|
||||||
|
img_a = Image.open(screenshot_expected_path)
|
||||||
|
img_b = Image.open(screenshot_observed_path)
|
||||||
|
img_diff = Image.new("RGBA", img_a.size)
|
||||||
|
mismatch = pixelmatch(img_a, img_b, img_diff, includeAA=True)
|
||||||
|
print("image mismatch", mismatch)
|
||||||
|
assert mismatch < 50
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user