feat(Blenvy): big overhaul, cleanup & fixed to the assets system

* now the blender side exports .meta.ron files in addition to the gltf files
 * these ron files contain the list of assets that are then preloaded on the Bevy side
 * removed the double loading of gltf files on the Bevy side, replaced with use of the new metadata/asset files
 * added bevy_common_assets/ ron as dependency for the file loading
 * big cleanup & partial restructure of the spawning steps
 * fixed premature removal of the BlueprintAssetsLoadState component that was leading to missing material gltf files in
setups withouth hot reload
 * added OriginalVisibility component & logic to correctly reset the visibility of entities to what they where before
the blueprint spawning
 * fixed a few not so visible issues with some components staying around after the blueprint instance has become "ready"
 * moved a number of component insertions to the new "get the assets list from the meta file"
 * also, loading speed feels faster ! (thanks to now loading the gltf files only once)
 * minor various tweaks & cleanups
This commit is contained in:
kaosat.dev 2024-07-29 22:32:23 +02:00
parent 0b038de584
commit ee873b06f1
13 changed files with 295 additions and 180 deletions

View File

@ -339,5 +339,6 @@ Bevy Side:
- [ ] solving problems with scene renames
- [ ] the ability to map external TEXT files to data in BLender (git-able, hand editable)
- [x] make aabbs calculation non configurable, getting rid of the last setting (for now)
- [ ] add information & screenshots about adding assets to the Blender add-on docs
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

@ -18,19 +18,7 @@ bevy = { version = "0.14", default-features = false, features = ["bevy_asset", "
serde = "1.0.188"
ron = "0.8.1"
serde_json = "1.0.108"
gltf = { version = "1.4.0", default-features = false, features = [
"KHR_lights_punctual",
"KHR_materials_transmission",
"KHR_materials_ior",
"KHR_materials_volume",
"KHR_materials_unlit",
"KHR_materials_emissive_strength",
"KHR_texture_transform",
"extras",
"extensions",
"names",
"utils",
] }
bevy_common_assets = {version = "0.11", features = ["ron"]}
[dev-dependencies]

View File

@ -29,7 +29,7 @@ pub fn compute_scene_aabbs(
} else {
let aabb = compute_descendant_aabb(root_entity, &children, &existing_aabbs);
blenvy_config.aabb_cache.insert(name.to_string(), aabb);
info!("generating aabb for {:?}", name);
info!("Step 7: generating aabb for {:?}", name);
commands
.entity(root_entity)
.insert(aabb)

View File

@ -74,3 +74,26 @@ impl Default for BlueprintAssetsLoadState {
}
}
}
// for preloading asset files
#[derive(serde::Deserialize, bevy::asset::Asset, bevy::reflect::TypePath, Debug)]
pub(crate) struct File{
pub(crate) path: String,
}
#[derive(serde::Deserialize, bevy::asset::Asset, bevy::reflect::TypePath, Debug)]
pub(crate) struct BlueprintPreloadAssets{
pub(crate) assets: Vec<(String, File)>
}
#[derive(Component)]
pub(crate) struct BlueprintMetaHandle(pub Handle<BlueprintPreloadAssets>);
/// flag component, usually added when a blueprint is loaded
#[derive(Component)]
pub(crate) struct BlueprintMetaLoaded;
#[derive(Component)]
pub(crate) struct BlueprintMetaLoading;

View File

@ -57,12 +57,17 @@ pub(crate) fn inject_materials(
material_found = Some(material);
} else {
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()).unwrap_or_else(|| {
let Some(mat_gltf) = assets_gltf.get(model_handle.id()) else {
warn!("materials file {} should have been preloaded skipping",material_info.path);
continue;
};
/*let mat_gltf = assets_gltf.get(model_handle.id()).unwrap_or_else(|| {
panic!(
"materials file {} should have been preloaded",
material_info.path
)
});
});*/
if mat_gltf
.named_materials
.contains_key(&material_info.name as &str)
@ -79,6 +84,7 @@ pub(crate) fn inject_materials(
}
if let Some(material) = material_found {
info!("Step 6: injecting/replacing materials");
for (child_index, child) in children.iter().enumerate() {
if child_index == material_index {
if with_materials_and_meshes.contains(*child) {

View File

@ -1,4 +1,5 @@
pub mod spawn_from_blueprints;
use bevy_common_assets::ron::RonAssetPlugin;
pub use spawn_from_blueprints::*;
pub mod animation;
@ -110,8 +111,10 @@ impl Plugin for BlueprintsPlugin {
.register_type::<Vec<String>>()
.register_type::<BlueprintAssets>()
.register_type::<HashMap<String, Vec<String>>>()
.init_asset::<RawGltfAsset>()
.init_asset_loader::<RawGltfAssetLoader>()
//.init_asset::<RawGltfAsset>()
//.init_asset_loader::<RawGltfAssetLoader>()
.add_plugins(RonAssetPlugin::<BlueprintPreloadAssets>::new(&["meta.ron"]),)
.configure_sets(
Update,
(GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)
@ -121,7 +124,8 @@ impl Plugin for BlueprintsPlugin {
.add_systems(
Update,
(
load_raw_gltf,
blueprints_prepare_metadata_file_for_spawn,
blueprints_check_assets_metadata_files_loading,
blueprints_prepare_spawn,
blueprints_check_assets_loading,
blueprints_assets_loaded,

View File

@ -1,21 +1,17 @@
use std::path::Path;
use bevy::{
asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext},
gltf::Gltf,
prelude::*,
scene::SceneInstance,
utils::hashbrown::HashMap,
utils::{hashbrown::HashMap, warn},
};
use serde_json::Value;
use crate::{
AnimationInfos, AssetLoadTracker, AssetToBlueprintInstancesMapper, BlueprintAnimationInfosLink,
BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState,
BlueprintAssetsLoaded, BlueprintAssetsNotLoaded, InstanceAnimationInfosLink,
InstanceAnimationPlayerLink, InstanceAnimations, WatchingForChanges,
AnimationInfos, AssetLoadTracker, AssetToBlueprintInstancesMapper, BlueprintAnimationInfosLink, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintMetaLoading, BlueprintAssetsNotLoaded, BlueprintPreloadAssets, InstanceAnimationInfosLink, InstanceAnimationPlayerLink, InstanceAnimations, WatchingForChanges
};
/// this is a flag component for our levels/game world
#[derive(Component)]
pub struct GameWorldTag;
@ -72,6 +68,13 @@ pub(crate) struct OriginalChildren(pub Vec<Entity>);
/// as it would first become invisible before re-appearing again
pub struct HideUntilReady;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Companion to the `HideUntilReady` component: this stores the visibility of the entity before the blueprint was inserted into it
pub(crate) struct OriginalVisibility(Visibility);
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// marker component, gets added to all children of a currently spawning blueprint instance, can be usefull to avoid manipulating still in progress entities
@ -103,8 +106,6 @@ pub enum BlueprintEvent {
/// component gets added when a blueprint starts spawning, removed when spawning is completely done
pub struct BlueprintSpawning;
use gltf::Gltf as RawGltf;
/*
Overview of the Blueprint Spawning process
- Blueprint Load Assets
@ -121,62 +122,139 @@ Overview of the Blueprint Spawning process
=> distinguish between blueprint instances inside blueprint instances vs blueprint instances inside blueprints ??
*/
#[derive(Asset, TypePath, Debug)]
pub struct RawGltfAsset(pub RawGltf);
pub(super) fn blueprints_prepare_metadata_file_for_spawn(
blueprint_instances_to_spawn: Query<(
Entity,
&BlueprintInfo,
Option<&Name>,
Option<&Parent>,
Option<&HideUntilReady>,
Option<&Visibility>,
Option<&AddToGameWorld>,
#[derive(Default)]
pub(super) struct RawGltfAssetLoader;
impl AssetLoader for RawGltfAssetLoader {
type Asset = RawGltfAsset;
type Settings = ();
type Error = gltf::Error;
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
_settings: &'a (),
_load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
let gltf = RawGltf::from_slice_without_validation(&bytes)?;
Ok(RawGltfAsset(gltf))
}
}
#[derive(Debug, Component, Deref, DerefMut)]
#[component(storage = "SparseSet")]
pub(super) struct AssociatedRawGltfHandle(Handle<RawGltfAsset>);
pub(super) fn load_raw_gltf(
blueprint_instances_to_spawn: Query<(Entity, &BlueprintInfo), Added<SpawnBlueprint>>,
), (Without<BlueprintMetaLoading>, Without<BlueprintSpawning>, Without<BlueprintInstanceReady>)>,
mut game_world: Query<Entity, With<GameWorldTag>>,
asset_server: Res<AssetServer>,
mut commands: Commands,
) {
for (entity, blueprint_info) in blueprint_instances_to_spawn.iter() {
let gltf_handle: Handle<RawGltfAsset> = asset_server.load(&blueprint_info.path);
commands
.entity(entity)
.insert(AssociatedRawGltfHandle(gltf_handle));
for (entity, blueprint_info, entity_name, original_parent, hide_until_ready, original_visibility, add_to_world) in blueprint_instances_to_spawn.iter() {
// get path to assets / metadata file
info!("Step 1: spawn request detected: loading metadata file for {:?}", blueprint_info);
let blueprint_path = blueprint_info.path.clone();
let metadata_path = blueprint_path.replace(".glb", ".meta.ron").replace(".gltf", ".meta.ron"); // FIXME: horrible
let mut asset_infos: Vec<AssetLoadTracker> = vec![];
//let foo_handle:Handle<BlueprintPreloadAssets> = asset_server.load(metadata_path);
let untyped_handle = asset_server.load_untyped(metadata_path.clone());
let asset_id = untyped_handle.id();
asset_infos.push(AssetLoadTracker {
name: metadata_path.clone(),
path: metadata_path.clone(),
id: asset_id,
loaded: false,
handle: untyped_handle.clone(),
});
// add the blueprint spawning marker & co
commands.entity(entity).insert((
BlueprintAssetsLoadState {
all_loaded: false,
asset_infos,
..Default::default()
},
BlueprintMetaLoading,
BlueprintSpawning
));
// if the entity has no name, add one based on the blueprint's
if entity_name.is_none(){
commands
.entity(entity)
.insert(bevy::prelude::Name::from(blueprint_info.name.clone()));
}
if original_parent.is_none() {
// only allow hiding until ready when the entity does not have a parent (?)
if hide_until_ready.is_some() {
// if there is already a set visibility, save it for later
if let Some(original_visibility) = original_visibility {
commands.entity(entity).insert(OriginalVisibility(*original_visibility));
}
// & now hide the instance until it is ready
commands.entity(entity)
.insert(Visibility::Hidden);
}
// 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() {
let world = game_world
.get_single_mut()
.expect("there should be a game world present");
commands.entity(world).add_child(entity);
}
}
}
}
// TODO: merge with other asset loading checker ?
pub(crate) fn blueprints_check_assets_metadata_files_loading(
mut blueprint_assets_to_load: Query<
(Entity, &BlueprintInfo, &mut BlueprintAssetsLoadState),
With<BlueprintMetaLoading>,
>,
asset_server: Res<AssetServer>,
mut commands: Commands,
) {
for (entity, blueprint_info, 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);
let mut failed = false;
if let bevy::asset::LoadState::Failed(_) = asset_server.load_state(asset_id) {
failed = true
}
tracker.loaded = loaded || failed;
if loaded || failed {
loaded_amount += 1;
} else {
all_loaded = false;
}
if all_loaded {
commands.entity(entity).insert(BlueprintMetaHandle(asset_server.load(tracker.path.clone()))).remove::<BlueprintAssetsLoadState>();
break;
}
}
let progress: f32 = loaded_amount as f32 / total as f32;
assets_to_load.progress = progress;
// println!("LOADING: in progress for ALL assets of {:?} (instance of {}): {} ",entity_name, blueprint_info.path, progress * 100.0);
}
}
pub(super) fn blueprints_prepare_spawn(
blueprint_instances_to_spawn: Query<(Entity, &BlueprintInfo, &AssociatedRawGltfHandle)>,
blueprint_instances_to_spawn: Query<(Entity, &BlueprintInfo, &BlueprintMetaHandle, Option<&Name>), Added<BlueprintMetaHandle>>,
mut commands: Commands,
asset_server: Res<AssetServer>,
// for hot reload
watching_for_changes: Res<WatchingForChanges>,
mut assets_to_blueprint_instances: ResMut<AssetToBlueprintInstancesMapper>,
raw_gltf_assets: Res<Assets<RawGltfAsset>>,
// for debug
// all_names: Query<&Name>
blueprint_metas: Res<Assets<BlueprintPreloadAssets>>,
) {
for (entity, blueprint_info, raw_gltf_handle) in blueprint_instances_to_spawn.iter() {
for (entity, blueprint_info, blueprint_meta_handle, entity_name) in blueprint_instances_to_spawn.iter() {
info!(
"BLUEPRINT: to spawn detected: {:?} path:{:?}",
blueprint_info.name, blueprint_info.path
"Step 2: metadata loaded: loading assets for {:?}",
blueprint_info,
);
// we add the asset of the blueprint itself
// TODO: add detection of already loaded data
@ -197,66 +275,56 @@ pub(super) fn blueprints_prepare_spawn(
// and we also add all its assets
/* prefetch attempt */
let Some(RawGltfAsset(gltf)) = raw_gltf_assets.get(&raw_gltf_handle.0) else {
continue;
};
for scene in gltf.scenes() {
if let Some(scene_extras) = scene.extras().clone() {
let lookup: HashMap<String, Value> =
serde_json::from_str(scene_extras.get()).unwrap();
if lookup.contains_key("BlueprintAssets") {
let assets_raw = &lookup["BlueprintAssets"];
//println!("ASSETS RAW {}", assets_raw);
let all_assets: BlueprintAssets =
ron::from_str(assets_raw.as_str().unwrap()).unwrap();
// println!("all_assets {:?}", all_assets);
if let Some(blenvy_metadata) = blueprint_metas.get(&blueprint_meta_handle.0) {
for asset in blenvy_metadata.assets.iter() {
let asset_path = asset.1.path.clone();
let asset_name = asset.0.clone();
for asset in all_assets.assets.iter() {
let untyped_handle = asset_server.load_untyped(&asset.path);
let asset_id = untyped_handle.id();
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
if !loaded {
asset_infos.push(AssetLoadTracker {
name: asset.name.clone(),
path: asset.path.clone(),
id: asset_id,
loaded: false,
handle: untyped_handle.clone(),
});
}
let untyped_handle = asset_server.load_untyped(&asset_path);
let asset_id = untyped_handle.id();
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
if !loaded {
asset_infos.push(AssetLoadTracker {
name: asset_name.clone(),
path: asset_path.clone(),
id: asset_id,
loaded: false,
handle: untyped_handle.clone(),
});
}
// FIXME: dang, too early, asset server has not yet started loading yet
// let path_id = asset_server.get_path_id(&asset.path).expect("we should have alread checked for this asset");
let path_id = asset.path.clone();
// FIXME: dang, too early, asset server has not yet started loading yet
// let path_id = asset_server.get_path_id(&asset.path).expect("we should have alread checked for this asset");
let path_id = asset_path.clone();
// Only do this if hot reload is enabled
if watching_for_changes.0 {
if !assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.contains_key(&path_id)
{
assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.insert(path_id.clone(), vec![]);
}
// Only do this if hot reload is enabled
if watching_for_changes.0 {
if !assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.contains_key(&path_id)
{
assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.insert(path_id.clone(), vec![]);
}
// only insert if not already present in mapping
if !assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids
[&path_id]
.contains(&entity)
{
// println!("adding mapping between {} and entity {:?}", path_id, all_names.get(entity));
assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.get_mut(&path_id)
.unwrap()
.push(entity);
}
}
}
// only insert if not already present in mapping
if !assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids
[&path_id]
.contains(&entity)
{
// println!("adding mapping between {} and entity {:?}", path_id, all_names.get(entity));
assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.get_mut(&path_id)
.unwrap()
.push(entity);
}
}
}
}else {
warn!("no asset metadata found for {}, please make sure to generate them using the Blender add-on, or preload your assets manually", blueprint_info.path);
}
// Only do this if hot reload is enabled
// TODO: should this be added to the list of "all assets" on the blender side instead
@ -299,40 +367,35 @@ pub(super) fn blueprints_prepare_spawn(
commands.entity(entity).insert(BlueprintAssetsLoaded);
}
// if the entity has no name, add one based on the blueprint's
commands
.entity(entity)
.insert(bevy::prelude::Name::from(blueprint_info.name.clone()));
// add the blueprint spawning marker
commands
.entity(entity)
.insert(BlueprintSpawning)
.remove::<AssociatedRawGltfHandle>();
commands.entity(entity)
.remove::<BlueprintMetaLoading>()
.remove::<BlueprintMetaHandle>();
}
}
/// This system tracks & updates the loading state of all blueprints assets
pub(crate) fn blueprints_check_assets_loading(
mut blueprint_assets_to_load: Query<
(Entity, &BlueprintInfo, &mut BlueprintAssetsLoadState),
(Entity, &BlueprintInfo, &mut BlueprintAssetsLoadState, Option<&Name>),
With<BlueprintAssetsNotLoaded>,
>,
asset_server: Res<AssetServer>,
mut commands: Commands,
mut blueprint_events: EventWriter<BlueprintEvent>,
// for hot reload
watching_for_changes: Res<WatchingForChanges>,
) {
for (entity, blueprint_info, mut assets_to_load) in blueprint_assets_to_load.iter_mut() {
for (entity, blueprint_info, mut assets_to_load, entity_name) 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);
if loaded {
debug!("LOADED {}", tracker.path.clone());
}
let mut failed = false;
if let bevy::asset::LoadState::Failed(_) = asset_server.load_state(asset_id) {
warn!("FAILED TO LOAD {}", tracker.path.clone());
failed = true
}
tracker.loaded = loaded || failed;
@ -344,7 +407,7 @@ pub(crate) fn blueprints_check_assets_loading(
}
let progress: f32 = loaded_amount as f32 / total as f32;
assets_to_load.progress = progress;
// println!("LOADING: in progress 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);
if all_loaded {
assets_to_load.all_loaded = true;
@ -359,10 +422,6 @@ pub(crate) fn blueprints_check_assets_loading(
.entity(entity)
.insert(BlueprintAssetsLoaded)
.remove::<BlueprintAssetsNotLoaded>();
if !watching_for_changes.0 {
commands.entity(entity).remove::<BlueprintAssetsLoadState>(); //we REMOVE this component when in hot reload is OFF, as we
}
}
}
}
@ -373,20 +432,15 @@ pub(crate) fn blueprints_assets_loaded(
Entity,
&BlueprintInfo,
Option<&Transform>,
Option<&Parent>,
Option<&AddToGameWorld>,
Option<&Name>,
Option<&HideUntilReady>,
Option<&AnimationInfos>,
),
(
With<BlueprintAssetsLoaded>,
Added<BlueprintAssetsLoaded>,
Without<BlueprintAssetsNotLoaded>,
),
>,
all_children: Query<&Children>,
mut game_world: Query<Entity, With<GameWorldTag>>,
assets_gltf: Res<Assets<Gltf>>,
asset_server: Res<AssetServer>,
@ -398,10 +452,7 @@ pub(crate) fn blueprints_assets_loaded(
entity,
blueprint_info,
transform,
original_parent,
add_to_world,
name,
hide_until_ready,
animation_infos,
) in spawn_placeholders.iter()
{
@ -411,8 +462,8 @@ pub(crate) fn blueprints_assets_loaded(
);*/
info!(
"BLUEPRINT: all assets loaded, attempting to spawn blueprint SCENE {:?} for entity {:?}, id: {}",
blueprint_info.name, name, entity
"Step 3: all assets loaded, attempting to spawn blueprint scene {:?} for entity {:?}, id: {}",
blueprint_info, name, entity
);
// info!("attempting to spawn {:?}", model_path);
@ -479,20 +530,7 @@ pub(crate) fn blueprints_assets_loaded(
},
));
if original_parent.is_none() {
// only allow hiding until ready when the entity does not have a parent (?)
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() {
let world = game_world
.get_single_mut()
.expect("there should be a game world present");
commands.entity(world).add_child(entity);
}
}
}
}
@ -536,7 +574,7 @@ pub(crate) fn blueprints_scenes_spawned(
) {
for (entity, name, children, track_root) in spawned_blueprint_scene_instances.iter() {
info!(
"Done spawning blueprint scene for entity named {:?} (track root: {:?})",
"Step 4: Done spawning blueprint scene for entity named {:?} (track root: {:?})",
name, track_root
);
let mut sub_blueprint_instances: Vec<Entity> = vec![];
@ -614,6 +652,8 @@ pub(crate) fn blueprints_scenes_spawned(
use crate::CopyComponents;
use std::any::TypeId;
use super::{BlueprintMetaHandle, BlueprintMetaLoaded};
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
pub struct BlueprintReadyForPostProcess;
@ -646,7 +686,7 @@ pub(crate) fn blueprints_cleanup_spawned_scene(
all_names: Query<&Name>,
) {
for (original, children, original_children, name, animations) in blueprint_scenes.iter() {
info!("Cleaning up spawned scene {:?}", name);
info!("Step 5: Cleaning up spawned scene {:?}", name);
if children.len() == 0 {
// TODO: investigate, Honestly not sure if this issue from Bevy 0.12 is still present at all anymore
@ -783,6 +823,7 @@ pub(crate) fn blueprints_finalize_instances(
&BlueprintInfo,
Option<&SubBlueprintSpawnRoot>,
Option<&HideUntilReady>,
Option<&OriginalVisibility>,
),
(With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>),
>,
@ -793,10 +834,10 @@ pub(crate) fn blueprints_finalize_instances(
mut commands: Commands,
// all_names: Query<&Name>
) {
for (entity, name, blueprint_info, parent_blueprint, hide_until_ready) in
for (entity, name, blueprint_info, parent_blueprint, hide_until_ready, original_visibility) in
blueprint_instances.iter()
{
info!("Finalizing blueprint instance {:?}", name);
info!("Step 8: Finalizing blueprint instance {:?}", name);
commands
.entity(entity)
.remove::<BlueprintReadyForFinalizing>()
@ -804,8 +845,9 @@ pub(crate) fn blueprints_finalize_instances(
.remove::<BlueprintSpawning>()
.remove::<SpawnBlueprint>()
//.remove::<Handle<Scene>>(); // FIXME: if we delete the handle to the scene, things get despawned ! not what we want
//.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 ?
//.remove::<BlueprintAssetsLoaded>();
.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 ?
.remove::<BlueprintAssetsLoaded>()
.remove::<OriginalChildren>() // we do not need to keep the original children information
.insert(BlueprintInstanceReady);
// Deal with sub blueprints
@ -839,12 +881,18 @@ pub(crate) fn blueprints_finalize_instances(
}
}
commands.entity(entity).remove::<BlueprintInstanceDisabled>();
for child in all_children.iter_descendants(entity) {
commands.entity(child).remove::<BlueprintInstanceDisabled>();
}
if hide_until_ready.is_some() {
commands.entity(entity).insert(Visibility::Inherited);
if let Some(original_visibility) = original_visibility {
commands.entity(entity).insert(original_visibility.0);
}else {
commands.entity(entity).insert(Visibility::Inherited);
}
}
blueprint_events.send(BlueprintEvent::InstanceReady {

View File

@ -4,8 +4,7 @@ use blenvy::*;
pub struct CorePlugin;
impl Plugin for CorePlugin {
fn build(&self, app: &mut App) {
app.add_plugins((BlueprintsPlugin {
material_library: true,
app.add_plugins((BlenvyPlugin {
..Default::default()
},));
}

View File

@ -13,7 +13,6 @@ impl Plugin for CorePlugin {
fn build(&self, app: &mut App) {
app.add_plugins(
BlenvyPlugin {
aabbs: true,
registry_component_filter: SceneFilter::Denylist(HashSet::from([
// this is using Bevy's build in SceneFilter, you can compose what components you want to allow/deny
TypeId::of::<ComponentAToFilterOut>(),

View File

@ -1,12 +1,10 @@
import os
import bpy
from blenvy.assets.assets_scan import get_blueprint_asset_tree
from blenvy.assets.generate_asset_file import write_ron_assets_file
from ....materials.materials_helpers import add_material_info_to_objects, get_blueprint_materials
from ..constants import TEMPSCENE_PREFIX
from ..common.generate_temporary_scene_and_export import generate_temporary_scene_and_export, copy_hollowed_collection_into, clear_hollow_scene
from ..common.export_gltf import generate_gltf_export_settings
from ..utils import upsert_blueprint_assets
from ..utils import upsert_blueprint_assets, write_blueprint_metadata_file
def export_blueprints(blueprints, settings, blueprints_data):
blueprints_path_full = getattr(settings, "blueprints_path_full")
@ -33,6 +31,9 @@ def export_blueprints(blueprints, settings, blueprints_data):
(_, materials_per_object) = get_blueprint_materials(blueprint)
add_material_info_to_objects(materials_per_object, settings)
write_blueprint_metadata_file(blueprint=blueprint, blueprints_data=blueprints_data, settings=settings)
# do the actual export
generate_temporary_scene_and_export(
settings,

View File

@ -65,8 +65,9 @@ def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_out
except Exception as error:
print("failed to export gltf !", error)
raise error
# restore everything
tempScene_cleaner(temp_scene, scene_filler_data)
finally:
# restore everything
tempScene_cleaner(temp_scene, scene_filler_data)
# reset active scene
bpy.context.window.scene = active_scene

View File

@ -1,9 +1,10 @@
import os
from ..constants import TEMPSCENE_PREFIX
from ..common.generate_temporary_scene_and_export import generate_temporary_scene_and_export, copy_hollowed_collection_into, clear_hollow_scene
from ..common.export_gltf import (generate_gltf_export_settings, export_gltf)
from .is_object_dynamic import is_object_dynamic, is_object_static
from ..utils import upsert_scene_assets
from ..utils import upsert_scene_assets, write_level_metadata_file
def export_level_scene(scene, settings, blueprints_data):
@ -28,6 +29,7 @@ def export_level_scene(scene, settings, blueprints_data):
# we inject assets into the scene before it gets exported
# TODO: this should be done in the temporary scene !
upsert_scene_assets(scene, blueprints_data=blueprints_data, settings=settings)
write_level_metadata_file(scene=scene, blueprints_data=blueprints_data, settings=settings)
if export_separate_dynamic_and_static_objects:
#print("SPLIT STATIC AND DYNAMIC")

View File

@ -4,6 +4,7 @@ from pathlib import Path
from blenvy.assets.assets_scan import get_blueprint_asset_tree, get_level_scene_assets_tree2
from blenvy.add_ons.bevy_components.utils import is_component_valid_and_enabled
from .constants import custom_properties_to_filter_out
from ...assets.assets_scan import get_level_scene_assets_tree2
def remove_unwanted_custom_properties(object):
to_remove = []
@ -42,3 +43,45 @@ def upsert_blueprint_assets(blueprint, blueprints_data, settings):
print("all_assets_raw", all_assets_raw)
print("local assets", local_assets)
blueprint.collection["BlueprintAssets"] = assets_to_fake_ron(local_assets)
import os
def write_level_metadata_file(scene, blueprints_data, settings):
levels_path_full = getattr(settings,"levels_path_full")
all_assets_raw = get_level_scene_assets_tree2(level_scene=scene, blueprints_data=blueprints_data, settings=settings)
formated_assets = []
for asset in all_assets_raw:
#if asset["internal"] :
formated_asset = f'\n ("{asset["name"]}", File ( path: "{asset["path"]}" )),'
formated_assets.append(formated_asset)
metadata_file_path_full = os.path.join(levels_path_full, scene.name+".meta.ron")
os.makedirs(os.path.dirname(metadata_file_path_full), exist_ok=True)
with open(metadata_file_path_full, "w") as assets_file:
assets_file.write("(\n ")
assets_file.write(" assets:\n [ ")
assets_file.writelines(formated_assets)
assets_file.write("\n ]\n")
assets_file.write(")")
def write_blueprint_metadata_file(blueprint, blueprints_data, settings):
blueprints_path_full = getattr(settings,"blueprints_path_full")
all_assets_raw = get_blueprint_asset_tree(blueprint=blueprint, blueprints_data=blueprints_data, settings=settings)
formated_assets = []
for asset in all_assets_raw:
#if asset["internal"] :
formated_asset = f'\n ("{asset["name"]}", File ( path: "{asset["path"]}" )),'
formated_assets.append(formated_asset)
metadata_file_path_full = os.path.join(blueprints_path_full, blueprint.name+".meta.ron")
os.makedirs(os.path.dirname(metadata_file_path_full), exist_ok=True)
with open(metadata_file_path_full, "w") as assets_file:
assets_file.write("(\n ")
assets_file.write(" assets:\n [ ")
assets_file.writelines(formated_assets)
assets_file.write("\n ]\n")
assets_file.write(")")