feat(Blenvy): basic working loading of levels & assets

* removed/ obsoleted blueprintsList
 * now using AllAssets (meh naming)
 * modified example & internals to enable loading levels as Blueprints as well
 * internals quite messy for now, needs a big cleanup still
 * disabled materials library for now, needs to be overhauled to make use of asset logic as well
 * added more mock assets for testing
 * related changes to blender & bevy side
This commit is contained in:
kaosat.dev 2024-06-09 23:14:49 +02:00
parent 2a74dedcb8
commit 1fdb45bab6
17 changed files with 418 additions and 124 deletions

View File

@ -1,6 +1,6 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use bevy::{gltf::Gltf, prelude::*, utils::HashMap}; use bevy::{asset::LoadedUntypedAsset, gltf::Gltf, prelude::*, utils::HashMap};
use crate::{BluePrintsConfig, BlueprintAnimations}; use crate::{BluePrintsConfig, BlueprintAnimations};
@ -24,30 +24,35 @@ pub struct AllAssets(pub Vec<MyAsset>);
/// helper component, is used to store the list of sub blueprints to enable automatic loading of dependend blueprints ////////////////////////
#[derive(Component, Reflect, Default, Debug)] ///
#[reflect(Component)] /// flag component, usually added when a blueprint is loaded
pub struct BlueprintsList(pub HashMap<String, Vec<String>>); #[derive(Component)]
pub(crate) struct BlueprintAssetsLoaded;
/// flag component
#[derive(Component)]
pub(crate) struct BlueprintAssetsNotLoaded;
/// helper component, for tracking loaded assets's loading state, id , handle etc /// helper component, for tracking loaded assets's loading state, id , handle etc
#[derive(Default, Debug)] #[derive(Debug)]
pub(crate) struct AssetLoadTracker<T: bevy::prelude::Asset> { pub(crate) struct AssetLoadTracker {
#[allow(dead_code)] #[allow(dead_code)]
pub name: String, pub name: String,
pub id: AssetId<T>, pub id: AssetId<LoadedUntypedAsset>,
pub loaded: bool, pub loaded: bool,
#[allow(dead_code)] #[allow(dead_code)]
pub handle: Handle<T>, pub handle: Handle<LoadedUntypedAsset>,
} }
/// helper component, for tracking loaded assets /// helper component, for tracking loaded assets
#[derive(Component, Debug)] #[derive(Component, Debug)]
pub(crate) struct AssetsToLoad<T: bevy::prelude::Asset> { pub(crate) struct AssetsToLoad {
pub all_loaded: bool, pub all_loaded: bool,
pub asset_infos: Vec<AssetLoadTracker<T>>, pub asset_infos: Vec<AssetLoadTracker>,
pub progress: f32, pub progress: f32,
} }
impl<T: bevy::prelude::Asset> Default for AssetsToLoad<T> { impl Default for AssetsToLoad {
fn default() -> Self { fn default() -> Self {
Self { Self {
all_loaded: Default::default(), all_loaded: Default::default(),
@ -56,10 +61,3 @@ impl<T: bevy::prelude::Asset> Default for AssetsToLoad<T> {
} }
} }
} }
/// flag component, usually added when a blueprint is loaded
#[derive(Component)]
pub(crate) struct BlueprintAssetsLoaded;
/// flag component
#[derive(Component)]
pub(crate) struct BlueprintAssetsNotLoaded;

View File

@ -129,13 +129,13 @@ impl Plugin for BlueprintsPlugin {
.register_type::<HashMap<u32, Vec<String>>>() .register_type::<HashMap<u32, Vec<String>>>()
.register_type::<HashMap<String, HashMap<u32, Vec<String>>>>() .register_type::<HashMap<String, HashMap<u32, Vec<String>>>>()
.add_event::<AnimationMarkerReached>() .add_event::<AnimationMarkerReached>()
.register_type::<BlueprintsList>()
.register_type::<MyAsset>() .register_type::<MyAsset>()
.register_type::<Vec<MyAsset>>() .register_type::<Vec<MyAsset>>()
.register_type::<Vec<String>>() .register_type::<Vec<String>>()
.register_type::<LocalAssets>() .register_type::<LocalAssets>()
.register_type::<AllAssets>() .register_type::<AllAssets>()
.register_type::<HashMap<String, Vec<String>>>() .register_type::<HashMap<String, Vec<String>>>()
.insert_resource(BluePrintsConfig { .insert_resource(BluePrintsConfig {
format: self.format, format: self.format,
@ -158,20 +158,23 @@ impl Plugin for BlueprintsPlugin {
Update, Update,
( (
test_thingy, test_thingy,
( check_for_loaded2,
spawn_from_blueprints2,
/*(
prepare_blueprints, prepare_blueprints,
check_for_loaded, check_for_loaded,
spawn_from_blueprints, spawn_from_blueprints,
apply_deferred, apply_deferred,
) )
.chain(), .chain(),*/
(compute_scene_aabbs, apply_deferred) (compute_scene_aabbs, apply_deferred)
.chain() .chain()
.run_if(aabbs_enabled), .run_if(aabbs_enabled),
apply_deferred, apply_deferred,
( (
materials_inject, materials_inject,
check_for_material_loaded, // check_for_material_loaded,
materials_inject2, materials_inject2,
) )
.chain() .chain()

View File

@ -66,7 +66,9 @@ pub(crate) fn materials_inject(
} else { } else {
let material_file_handle: Handle<Gltf> = asset_server.load(materials_path.clone()); let material_file_handle: Handle<Gltf> = asset_server.load(materials_path.clone());
let material_file_id = material_file_handle.id(); let material_file_id = material_file_handle.id();
let asset_infos: Vec<AssetLoadTracker<Gltf>> = vec![AssetLoadTracker {
// FIXME: fix this stuff
/*let asset_infos: Vec<AssetLoadTracker> = vec![AssetLoadTracker {
name: material_full_path, name: material_full_path,
id: material_file_id, id: material_file_id,
loaded: false, loaded: false,
@ -81,12 +83,14 @@ pub(crate) fn materials_inject(
..Default::default() ..Default::default()
}) })
.insert(BlueprintMaterialAssetsNotLoaded); .insert(BlueprintMaterialAssetsNotLoaded);
/**/ */
} }
} }
} }
// TODO, merge with check_for_loaded, make generic ? // TODO, merge with check_for_loaded, make generic ?
// FIXME: fix this:
/*
pub(crate) fn check_for_material_loaded( pub(crate) fn check_for_material_loaded(
mut blueprint_assets_to_load: Query< mut blueprint_assets_to_load: Query<
(Entity, &mut AssetsToLoad<Gltf>), (Entity, &mut AssetsToLoad<Gltf>),
@ -121,7 +125,7 @@ pub(crate) fn check_for_material_loaded(
} }
} }
} }
*/
/// system that injects / replaces materials from material library /// system that injects / replaces materials from material library
pub(crate) fn materials_inject2( pub(crate) fn materials_inject2(
mut blueprints_config: ResMut<BluePrintsConfig>, mut blueprints_config: ResMut<BluePrintsConfig>,

View File

@ -0,0 +1,248 @@
/// helper component, for tracking loaded assets's loading state, id , handle etc
#[derive(Default, Debug)]
pub(crate) struct AssetLoadTracker<T: bevy::prelude::Asset> {
#[allow(dead_code)]
pub name: String,
pub id: AssetId<T>,
pub loaded: bool,
#[allow(dead_code)]
pub handle: Handle<T>,
}
/// helper component, for tracking loaded assets
#[derive(Component, Debug)]
pub(crate) struct AssetsToLoad<T: bevy::prelude::Asset> {
pub all_loaded: bool,
pub asset_infos: Vec<AssetLoadTracker<T>>,
pub progress: f32,
}
impl<T: bevy::prelude::Asset> Default for AssetsToLoad<T> {
fn default() -> Self {
Self {
all_loaded: Default::default(),
asset_infos: Default::default(),
progress: Default::default(),
}
}
}
/// flag component, usually added when a blueprint is loaded
#[derive(Component)]
pub(crate) struct BlueprintAssetsLoaded;
/// flag component
#[derive(Component)]
pub(crate) struct BlueprintAssetsNotLoaded;
/// spawning prepare function,
/// * also takes into account the already exisiting "override" components, ie "override components" > components from blueprint
pub(crate) fn prepare_blueprints(
spawn_placeholders: Query<
(
Entity,
&BlueprintName,
Option<&Parent>,
Option<&Library>,
Option<&Name>,
Option<&BlueprintsList>,
),
(Added<BlueprintName>, Added<SpawnHere>, Without<Spawned>),
>,
mut commands: Commands,
asset_server: Res<AssetServer>,
blueprints_config: Res<BluePrintsConfig>,
) {
for (entity, blupeprint_name, original_parent, library_override, name, blueprints_list) in
spawn_placeholders.iter()
{
debug!(
"requesting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
blupeprint_name.0, name, entity, original_parent
);
// println!("main model path {:?}", model_path);
if blueprints_list.is_some() {
let blueprints_list = blueprints_list.unwrap();
// println!("blueprints list {:?}", blueprints_list.0.keys());
let mut asset_infos: Vec<AssetLoadTracker<Gltf>> = vec![];
let library_path =
library_override.map_or_else(|| &blueprints_config.library_folder, |l| &l.0);
for (blueprint_name, _) in blueprints_list.0.iter() {
let model_file_name = format!("{}.{}", &blueprint_name, &blueprints_config.format);
let model_path = Path::new(&library_path).join(Path::new(model_file_name.as_str()));
let model_handle: Handle<Gltf> = asset_server.load(model_path.clone());
let model_id = model_handle.id();
let loaded = asset_server.is_loaded_with_dependencies(model_id);
if !loaded {
asset_infos.push(AssetLoadTracker {
name: model_path.to_string_lossy().into(),
id: model_id,
loaded: false,
handle: model_handle.clone(),
});
}
}
// if not all assets are already loaded, inject a component to signal that we need them to be loaded
if !asset_infos.is_empty() {
commands
.entity(entity)
.insert(AssetsToLoad {
all_loaded: false,
asset_infos,
..Default::default()
})
.insert(BlueprintAssetsNotLoaded);
} else {
commands.entity(entity).insert(BlueprintAssetsLoaded);
}
} else {
// in case there are no blueprintsList, we revert back to the old behaviour
commands.entity(entity).insert(BlueprintAssetsLoaded);
}
}
}
pub(crate) fn check_for_loaded(
mut blueprint_assets_to_load: Query<
(Entity, &mut AssetsToLoad<Gltf>),
With<BlueprintAssetsNotLoaded>,
>,
asset_server: Res<AssetServer>,
mut commands: Commands,
) {
for (entity, mut assets_to_load) in blueprint_assets_to_load.iter_mut() {
let mut all_loaded = true;
let mut loaded_amount = 0;
let total = assets_to_load.asset_infos.len();
for tracker in assets_to_load.asset_infos.iter_mut() {
let asset_id = tracker.id;
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
tracker.loaded = loaded;
if loaded {
loaded_amount += 1;
} else {
all_loaded = false;
}
}
let progress: f32 = loaded_amount as f32 / total as f32;
// println!("progress: {}",progress);
assets_to_load.progress = progress;
if all_loaded {
assets_to_load.all_loaded = true;
commands
.entity(entity)
.insert(BlueprintAssetsLoaded)
.remove::<BlueprintAssetsNotLoaded>();
}
}
}
pub(crate) fn spawn_from_blueprints(
spawn_placeholders: Query<
(
Entity,
&BlueprintName,
Option<&Transform>,
Option<&Parent>,
Option<&Library>,
Option<&AddToGameWorld>,
Option<&Name>,
),
(
With<BlueprintAssetsLoaded>,
Added<BlueprintAssetsLoaded>,
Without<BlueprintAssetsNotLoaded>,
),
>,
mut commands: Commands,
mut game_world: Query<Entity, With<GameWorldTag>>,
assets_gltf: Res<Assets<Gltf>>,
asset_server: Res<AssetServer>,
blueprints_config: Res<BluePrintsConfig>,
children: Query<&Children>,
) {
for (
entity,
blupeprint_name,
transform,
original_parent,
library_override,
add_to_world,
name,
) in spawn_placeholders.iter()
{
debug!(
"attempting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
blupeprint_name.0, name, entity, original_parent
);
let what = &blupeprint_name.0;
let model_file_name = format!("{}.{}", &what, &blueprints_config.format);
// library path is either defined at the plugin level or overriden by optional Library components
let library_path =
library_override.map_or_else(|| &blueprints_config.library_folder, |l| &l.0);
let model_path = Path::new(&library_path).join(Path::new(model_file_name.as_str()));
// info!("attempting to spawn {:?}", model_path);
let model_handle: Handle<Gltf> = asset_server.load(model_path.clone()); // FIXME: kinda weird now
let gltf = assets_gltf.get(&model_handle).unwrap_or_else(|| {
panic!(
"gltf file {:?} should have been loaded",
model_path.to_str()
)
});
// WARNING we work under the assumtion that there is ONLY ONE named scene, and that the first one is the right one
let main_scene_name = gltf
.named_scenes
.keys()
.next()
.expect("there should be at least one named scene in the gltf file to spawn");
let scene = &gltf.named_scenes[main_scene_name];
// transforms are optional, but still deal with them correctly
let mut transforms: Transform = Transform::default();
if transform.is_some() {
transforms = *transform.unwrap();
}
let mut original_children: Vec<Entity> = vec![];
if let Ok(c) = children.get(entity) {
for child in c.iter() {
original_children.push(*child);
}
}
commands.entity(entity).insert((
SceneBundle {
scene: scene.clone(),
transform: transforms,
..Default::default()
},
Spawned,
OriginalChildren(original_children),
BlueprintAnimations {
// these are animations specific to the inside of the blueprint
named_animations: gltf.named_animations.clone(),
},
));
if add_to_world.is_some() {
let world = game_world
.get_single_mut()
.expect("there should be a game world present");
commands.entity(world).add_child(entity);
}
}
}

View File

@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};
use bevy::{gltf::Gltf, prelude::*, utils::HashMap}; use bevy::{gltf::Gltf, prelude::*, utils::HashMap};
use crate::{BluePrintsConfig, BlueprintAnimations, BlueprintsList, AssetLoadTracker, AssetsToLoad, BlueprintAssetsNotLoaded, BlueprintAssetsLoaded}; use crate::{AllAssets, AssetsToLoad, AssetLoadTracker, BluePrintsConfig, BlueprintAnimations, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded};
/// this is a flag component for our levels/game world /// this is a flag component for our levels/game world
#[derive(Component)] #[derive(Component)]
@ -59,78 +59,61 @@ pub(crate) fn test_thingy(
Entity, Entity,
&BlueprintPath, &BlueprintPath,
), ),
(Added<BlueprintPath>, Without<Spawned>, Without<SpawnHere>), (Added<BlueprintPath>, Without<Spawned>, Without<SpawnHere>)>,
>,
// before 0.14 we have to use a seperate query, after migrating we can query at the root level
entities_with_assets: Query<
(
Entity,
/*&BlueprintName,
&BlueprintPath,
Option<&Parent>,*/
Option<&Name>,
Option<&AllAssets>,
),
(Added<AllAssets>), // Added<AllAssets>
>,
bla_bla : Query<
(Entity,
&BlueprintName,
&BlueprintPath,
Option<&Parent>,),(Added<BlueprintPath>)
>,
mut commands: Commands, mut commands: Commands,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
) { ) {
for (entity, blueprint_path) in spawn_placeholders.iter() { for (entity, blueprint_path) in spawn_placeholders.iter() {
println!("added blueprint_path {:?}", blueprint_path); //println!("added blueprint_path {:?}", blueprint_path);
commands.entity(entity).insert( /*commands.entity(entity).insert(
SceneBundle { SceneBundle {
scene: asset_server.load(format!("{}#Scene0", &blueprint_path.0)), // "levels/World.glb#Scene0"), scene: asset_server.load(format!("{}#Scene0", &blueprint_path.0)), // "levels/World.glb#Scene0"),
..default() ..default()
}, },
); );*/
// let model_handle: Handle<Gltf> = asset_server.load(model_path.clone()); // let model_handle: Handle<Gltf> = asset_server.load(model_path.clone());
} }
} for (entity, blueprint_name, blueprint_path, parent) in bla_bla.iter() {
println!("added blueprint to spawn {:?} {:?}", blueprint_name, blueprint_path);
let untyped_handle = asset_server.load_untyped(&blueprint_path.0);
let asset_id = untyped_handle.id();
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
/// spawning prepare function, let mut asset_infos: Vec<AssetLoadTracker> = vec![];
/// * also takes into account the already exisiting "override" components, ie "override components" > components from blueprint
pub(crate) fn prepare_blueprints(
spawn_placeholders: Query<
(
Entity,
&BlueprintName,
Option<&Parent>,
Option<&Library>,
Option<&Name>,
Option<&BlueprintsList>,
),
(Added<BlueprintName>, Added<SpawnHere>, Without<Spawned>),
>,
mut commands: Commands,
asset_server: Res<AssetServer>,
blueprints_config: Res<BluePrintsConfig>,
) {
for (entity, blupeprint_name, original_parent, library_override, name, blueprints_list) in
spawn_placeholders.iter()
{
debug!(
"requesting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
blupeprint_name.0, name, entity, original_parent
);
// println!("main model path {:?}", model_path);
if blueprints_list.is_some() {
let blueprints_list = blueprints_list.unwrap();
// println!("blueprints list {:?}", blueprints_list.0.keys());
let mut asset_infos: Vec<AssetLoadTracker<Gltf>> = vec![];
let library_path =
library_override.map_or_else(|| &blueprints_config.library_folder, |l| &l.0);
for (blueprint_name, _) in blueprints_list.0.iter() {
let model_file_name = format!("{}.{}", &blueprint_name, &blueprints_config.format);
let model_path = Path::new(&library_path).join(Path::new(model_file_name.as_str()));
let model_handle: Handle<Gltf> = asset_server.load(model_path.clone());
let model_id = model_handle.id();
let loaded = asset_server.is_loaded_with_dependencies(model_id);
if !loaded { if !loaded {
asset_infos.push(AssetLoadTracker { asset_infos.push(AssetLoadTracker {
name: model_path.to_string_lossy().into(), name: blueprint_name.0.clone(),
id: model_id, id: asset_id,
loaded: false, loaded: false,
handle: model_handle.clone(), handle: untyped_handle.clone(),
}); });
} }
}
// if not all assets are already loaded, inject a component to signal that we need them to be loaded // now insert load tracker
if !asset_infos.is_empty() { if !asset_infos.is_empty() {
commands commands
.entity(entity) .entity(entity)
@ -143,57 +126,103 @@ pub(crate) fn prepare_blueprints(
} else { } else {
commands.entity(entity).insert(BlueprintAssetsLoaded); commands.entity(entity).insert(BlueprintAssetsLoaded);
} }
}
for (child_entity, child_entity_name, all_assets) in entities_with_assets.iter(){
println!("added assets {:?} to {:?}", all_assets, child_entity_name);
if all_assets.is_some() {
let mut asset_infos: Vec<AssetLoadTracker> = vec![];
for asset in all_assets.unwrap().0.iter() {
let untyped_handle = asset_server.load_untyped(&asset.path);
//println!("untyped handle {:?}", untyped_handle);
//asset_server.load(asset.path);
let asset_id = untyped_handle.id();
//println!("ID {:?}", asset_id);
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
//println!("Loaded ? {:?}", loaded);
if !loaded {
asset_infos.push(AssetLoadTracker {
name: asset.name.clone(),
id: asset_id,
loaded: false,
handle: untyped_handle.clone(),
});
}
}
// now insert load tracker
if !asset_infos.is_empty() {
commands
.entity(child_entity)
.insert(AssetsToLoad {
all_loaded: false,
asset_infos,
..Default::default()
})
.insert(BlueprintAssetsNotLoaded);
} else { } else {
// in case there are no blueprintsList, we revert back to the old behaviour commands.entity(child_entity).insert(BlueprintAssetsLoaded);
commands.entity(entity).insert(BlueprintAssetsLoaded); }
} }
} }
} }
pub(crate) fn check_for_loaded( pub(crate) fn check_for_loaded2(
mut blueprint_assets_to_load: Query< mut blueprint_assets_to_load: Query<
(Entity, &mut AssetsToLoad<Gltf>), (Entity, Option<&Name>, &mut AssetsToLoad),
With<BlueprintAssetsNotLoaded>, With<BlueprintAssetsNotLoaded>,
>, >,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
mut commands: Commands, mut commands: Commands,
) { ) {
for (entity, mut assets_to_load) in blueprint_assets_to_load.iter_mut() { for (entity,entity_name, mut assets_to_load) in blueprint_assets_to_load.iter_mut() {
let mut all_loaded = true; let mut all_loaded = true;
let mut loaded_amount = 0; let mut loaded_amount = 0;
let total = assets_to_load.asset_infos.len(); let total = assets_to_load.asset_infos.len();
for tracker in assets_to_load.asset_infos.iter_mut() { for tracker in assets_to_load.asset_infos.iter_mut() {
let asset_id = tracker.id; let asset_id = tracker.id;
let loaded = asset_server.is_loaded_with_dependencies(asset_id); let loaded = asset_server.is_loaded_with_dependencies(asset_id);
tracker.loaded = loaded; println!("loading {}: // load state: {:?}", tracker.name, asset_server.load_state(asset_id));
if loaded {
// FIXME: hack for now
let failed = asset_server.load_state(asset_id) == bevy::asset::LoadState::Failed;
tracker.loaded = loaded || failed;
if loaded || failed {
loaded_amount += 1; loaded_amount += 1;
} else { } else {
all_loaded = false; all_loaded = false;
} }
} }
let progress: f32 = loaded_amount as f32 / total as f32; let progress: f32 = loaded_amount as f32 / total as f32;
// println!("progress: {}",progress); println!("progress: {}",progress);
assets_to_load.progress = progress; assets_to_load.progress = progress;
if all_loaded { if all_loaded {
assets_to_load.all_loaded = true; assets_to_load.all_loaded = true;
println!("done with loading {:?}, inserting components", entity_name);
commands commands
.entity(entity) .entity(entity)
.insert(BlueprintAssetsLoaded) .insert(BlueprintAssetsLoaded)
.remove::<BlueprintAssetsNotLoaded>(); .remove::<BlueprintAssetsNotLoaded>()
.remove::<AssetsToLoad>()
;
} }
} }
} }
pub(crate) fn spawn_from_blueprints(
pub(crate) fn spawn_from_blueprints2(
spawn_placeholders: Query< spawn_placeholders: Query<
( (
Entity, Entity,
&BlueprintName, &BlueprintName,
&BlueprintPath,
Option<&Transform>, Option<&Transform>,
Option<&Parent>, Option<&Parent>,
Option<&Library>,
Option<&AddToGameWorld>, Option<&AddToGameWorld>,
Option<&Name>, Option<&Name>,
), ),
@ -216,33 +245,28 @@ pub(crate) fn spawn_from_blueprints(
for ( for (
entity, entity,
blupeprint_name, blupeprint_name,
blueprint_path,
transform, transform,
original_parent, original_parent,
library_override,
add_to_world, add_to_world,
name, name,
) in spawn_placeholders.iter() ) in spawn_placeholders.iter()
{ {
debug!( info!(
"attempting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}", "attempting to spawn blueprint {:?} for entity {:?}, id: {:?}, parent:{:?}",
blupeprint_name.0, name, entity, original_parent blupeprint_name.0, name, entity, original_parent
); );
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);
// library path is either defined at the plugin level or overriden by optional Library components
let library_path =
library_override.map_or_else(|| &blueprints_config.library_folder, |l| &l.0);
let model_path = Path::new(&library_path).join(Path::new(model_file_name.as_str()));
// info!("attempting to spawn {:?}", model_path); // info!("attempting to spawn {:?}", model_path);
let model_handle: Handle<Gltf> = asset_server.load(model_path.clone()); // FIXME: kinda weird now let model_handle: Handle<Gltf> = asset_server.load(blueprint_path.0.clone()); // FIXME: kinda weird now
let gltf = assets_gltf.get(&model_handle).unwrap_or_else(|| { let gltf = assets_gltf.get(&model_handle).unwrap_or_else(|| {
panic!( panic!(
"gltf file {:?} should have been loaded", "gltf file {:?} should have been loaded",
model_path.to_str() &blueprint_path.0
) )
}); });
@ -289,3 +313,8 @@ pub(crate) fn spawn_from_blueprints(
} }
} }
} }

View File

@ -95,8 +95,8 @@ pub(crate) fn spawned_blueprint_post_process(
commands.entity(original).remove::<SpawnHere>(); commands.entity(original).remove::<SpawnHere>();
commands.entity(original).remove::<Spawned>(); commands.entity(original).remove::<Spawned>();
commands.entity(original).remove::<Handle<Scene>>(); commands.entity(original).remove::<Handle<Scene>>();
commands.entity(original).remove::<AssetsToLoad<Gltf>>(); // 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::<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(original).remove::<BlueprintAssetsLoaded>();
commands.entity(root_entity).despawn_recursive(); commands.entity(root_entity).despawn_recursive();
} }
} }

View File

@ -0,0 +1 @@
some text

View File

@ -1,5 +1,5 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag}; use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, BlueprintPath, GameWorldTag};
use bevy_gltf_worlflow_examples_common_rapier::{GameState, InAppRunning}; use bevy_gltf_worlflow_examples_common_rapier::{GameState, InAppRunning};
use bevy_rapier3d::prelude::Velocity; use bevy_rapier3d::prelude::Velocity;
@ -11,7 +11,7 @@ pub fn setup_game(
mut next_game_state: ResMut<NextState<GameState>>, mut next_game_state: ResMut<NextState<GameState>>,
) { ) {
// here we actually spawn our game world/level // here we actually spawn our game world/level
commands.spawn(( /*commands.spawn((
SceneBundle { SceneBundle {
scene: asset_server.load("levels/World.glb#Scene0"), scene: asset_server.load("levels/World.glb#Scene0"),
..default() ..default()
@ -19,7 +19,16 @@ pub fn setup_game(
bevy::prelude::Name::from("world"), bevy::prelude::Name::from("world"),
GameWorldTag, GameWorldTag,
InAppRunning, InAppRunning,
));*/
commands.spawn((
BlueprintName("World".into()),
BlueprintPath("levels/World.glb".into()),
bevy::prelude::Name::from("world"),
GameWorldTag,
InAppRunning,
)); ));
next_game_state.set(GameState::InGame) next_game_state.set(GameState::InGame)
} }

View File

@ -6,7 +6,7 @@ pub use in_game::*;
use std::{collections::HashMap, fs, time::Duration}; use std::{collections::HashMap, fs, time::Duration};
use bevy_gltf_blueprints::{ use bevy_gltf_blueprints::{
BlueprintAnimationPlayerLink, BlueprintName, BlueprintsList, GltfBlueprintsSet, SceneAnimations, AllAssets, BlueprintAnimationPlayerLink, BlueprintName, GltfBlueprintsSet, SceneAnimations
}; };
use bevy::{ use bevy::{
@ -23,7 +23,7 @@ fn start_game(mut next_app_state: ResMut<NextState<AppState>>) {
// if the export from Blender worked correctly, we should have animations (simplified here by using AnimationPlayerLink) // if the export from Blender worked correctly, we should have animations (simplified here by using AnimationPlayerLink)
// if the export from Blender worked correctly, we should have an Entity called "Blueprint4_nested" that has a child called "Blueprint3" that has a "BlueprintName" component with value Blueprint3 // if the export from Blender worked correctly, we should have an Entity called "Blueprint4_nested" that has a child called "Blueprint3" that has a "BlueprintName" component with value Blueprint3
// if the export from Blender worked correctly, we should have a blueprints_list // if the export from Blender worked correctly, we should have an assets_list
// if the export from Blender worked correctly, we should have the correct tree of entities // if the export from Blender worked correctly, we should have the correct tree of entities
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
@ -36,7 +36,7 @@ fn validate_export(
scene_animations: Query<(Entity, &SceneAnimations)>, scene_animations: Query<(Entity, &SceneAnimations)>,
empties_candidates: Query<(Entity, &Name, &GlobalTransform)>, empties_candidates: Query<(Entity, &Name, &GlobalTransform)>,
blueprints_list: Query<(Entity, &BlueprintsList)>, assets_list: Query<(Entity, &AllAssets)>,
root: Query<(Entity, &Name, &Children), (Without<Parent>, With<Children>)>, root: Query<(Entity, &Name, &Children), (Without<Parent>, With<Children>)>,
) { ) {
let animations_found = let animations_found =
@ -66,8 +66,8 @@ fn validate_export(
break; break;
} }
} }
// check if there are blueprints_list components // check if there are assets_list components
let blueprints_list_found = !blueprints_list.is_empty(); let assets_list_found = !assets_list.is_empty();
// there should be no entity named xxx____bak as it means an error in the Blender side export process // there should be no entity named xxx____bak as it means an error in the Blender side export process
let mut exported_names_correct = true; let mut exported_names_correct = true;
@ -104,8 +104,8 @@ fn validate_export(
fs::write( fs::write(
"bevy_diagnostics.json", "bevy_diagnostics.json",
format!( format!(
"{{ \"animations\": {}, \"nested_blueprint_found\": {}, \"empty_found\": {}, \"blueprints_list_found\": {}, \"exported_names_correct\": {} }}", "{{ \"animations\": {}, \"nested_blueprint_found\": {}, \"empty_found\": {}, \"assets_list_found\": {}, \"exported_names_correct\": {} }}",
animations_found, nested_blueprint_found, empty_found, blueprints_list_found, exported_names_correct animations_found, nested_blueprint_found, empty_found, assets_list_found, exported_names_correct
), ),
) )
.expect("Unable to write file"); .expect("Unable to write file");

View File

@ -116,6 +116,8 @@ General issues:
- [ ] remove 'export_marked_assets' it should be a default setting - [ ] remove 'export_marked_assets' it should be a default setting
- [x] disable/ hide asset editing ui for external assets - [x] disable/ hide asset editing ui for external assets
- [ ] inject_export_path_into_internal_blueprints should be called on every asset/blueprint scan !! Not just on export - [ ] inject_export_path_into_internal_blueprints should be called on every asset/blueprint scan !! Not just on export
- [ ] fix level asets UI - [x] fix level asets UI
- [x] remove BlueprintsList & replace is with assets list
clear && pytest -svv --blender-template ../../testing/bevy_example/art/testing_library.blend --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration_prepare.py && pytest -svv --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration.py clear && pytest -svv --blender-template ../../testing/bevy_example/art/testing_library.blend --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration_prepare.py && pytest -svv --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration.py

View File

@ -109,13 +109,6 @@ def duplicate_object(object, parent, combine_mode, destination_collection, bluep
empty_obj["BlueprintPath"] = f'("{blueprint_path}")' empty_obj["BlueprintPath"] = f'("{blueprint_path}")'
empty_obj['SpawnHere'] = '()' empty_obj['SpawnHere'] = '()'
# we also inject a list of all sub blueprints, so that the bevy side can preload them
children_per_blueprint = {}
blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if blueprint:
children_per_blueprint[blueprint_name] = blueprint.nested_blueprints
empty_obj["BlueprintsList"] = f"({json.dumps(dict(children_per_blueprint))})"
# we copy custom properties over from our original object to our empty # we copy custom properties over from our original object to our empty
for component_name, component_value in object.items(): for component_name, component_value in object.items():
if component_name not in custom_properties_to_filter_out and is_component_valid_and_enabled(object, component_name): #copy only valid properties if component_name not in custom_properties_to_filter_out and is_component_valid_and_enabled(object, component_name): #copy only valid properties

View File

@ -8,6 +8,12 @@ from blenvy.materials.materials_helpers import get_all_materials
from .generate_temporary_scene_and_export import generate_temporary_scene_and_export from .generate_temporary_scene_and_export import generate_temporary_scene_and_export
from .export_gltf import (generate_gltf_export_settings) from .export_gltf import (generate_gltf_export_settings)
# material library logic
# To avoid redundant materials (can be very costly, mostly when using high res textures)
# - we explore a gltf file containing all materials from a blend file
# - we add materialInfo component to each object that uses one of the materials, so that "what material is used by which object" is preserved
#
def clear_material_info(collection_names, library_scenes): def clear_material_info(collection_names, library_scenes):
for scene in library_scenes: for scene in library_scenes:
root_collection = scene.collection root_collection = scene.collection

View File

@ -9,7 +9,7 @@ def draw_assets(layout, name, title, asset_registry, target_type, target_name, e
number_of_generated_assets = len(generated_assets) number_of_generated_assets = len(generated_assets)
header, panel = layout.panel(f"assets{name}", default_closed=True) header, panel = layout.panel(f"assets{name}", default_closed=True)
header.label(text=title + f"({number_of_user_assets + number_of_generated_assets})", icon="ASSET_MANAGER") header.label(text=title + f"({number_of_user_assets})", icon="ASSET_MANAGER")
blueprint_assets = target_type == 'BLUEPRINT' blueprint_assets = target_type == 'BLUEPRINT'
@ -42,8 +42,9 @@ def draw_assets(layout, name, title, asset_registry, target_type, target_name, e
if editable: if editable:
row = panel.row() row = panel.row()
#panel.separator() #panel.separator()
print("here", user_assets)
for asset in user_assets: for asset in user_assets:
print("asset", asset)
row = panel.row() row = panel.row()
split = row.split(factor=nesting_indent) split = row.split(factor=nesting_indent)
col = split.column() col = split.column()

View File

@ -59,7 +59,7 @@ def setup_data(request):
if os.path.exists(screenshot_observed_path): if os.path.exists(screenshot_observed_path):
os.remove(screenshot_observed_path) os.remove(screenshot_observed_path)
request.addfinalizer(finalizer) #request.addfinalizer(finalizer)
return None return None
@ -105,7 +105,7 @@ def test_export_complex(setup_data):
blenvy.auto_export.auto_export = True blenvy.auto_export.auto_export = True
blenvy.auto_export.export_scene_settings = True blenvy.auto_export.export_scene_settings = True
blenvy.auto_export.export_blueprints = True blenvy.auto_export.export_blueprints = True
blenvy.auto_export.export_materials_library = True blenvy.auto_export.export_materials_library = False # TODO: switch back
bpy.data.scenes['World'].blenvy_scene_type = 'Level' # set scene as main/level scene 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 bpy.data.scenes['Library'].blenvy_scene_type = 'Library' # set scene as Library scene

View File

@ -53,7 +53,7 @@ def test_export_external_blueprints(setup_data):
blenvy.auto_export.auto_export = True blenvy.auto_export.auto_export = True
blenvy.auto_export.export_scene_settings = True blenvy.auto_export.export_scene_settings = True
blenvy.auto_export.export_blueprints = True blenvy.auto_export.export_blueprints = True
blenvy.auto_export.export_materials_library = True blenvy.auto_export.export_materials_library = False # TODO switch back
print("SCENES", bpy.data.scenes) print("SCENES", bpy.data.scenes)
for scene in bpy.data.scenes: for scene in bpy.data.scenes: