mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-12-22 15:44:11 +00:00
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:
parent
0b038de584
commit
ee873b06f1
1
TODO.md
1
TODO.md
@ -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
|
@ -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]
|
||||
|
@ -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)
|
||||
|
@ -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;
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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,67 +275,57 @@ 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
|
||||
if watching_for_changes.0 {
|
||||
@ -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 {
|
||||
|
@ -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()
|
||||
},));
|
||||
}
|
||||
|
@ -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>(),
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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(")")
|
Loading…
Reference in New Issue
Block a user