Compare commits

...

7 Commits

Author SHA1 Message Date
kaosat.dev
ed0c85b66e feat(Blenvy): more work done on materials handling overhaul (wip)
* split out materials scan from injection of materialInfo into objects
 * added material Asset injection into list of assets at scene level
 * related tweaks & cleanups
 * continued overhaul on the bevy side
2024-06-10 00:31:23 +02:00
kaosat.dev
1fdb45bab6 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
2024-06-09 23:14:49 +02:00
kaosat.dev
2a74dedcb8 feat(Blenvy & crates):
* added injection of assets lists to blueprints at export time
 * assets & co related tweaks & experiments
 * tweaks on the bevy side
 * all wip !
2024-06-09 10:54:10 +02:00
kaosat.dev
37f4514b31 feat(bevy_gltf_blueprints): added new experimental asset structs
* also started moving code to a new "assets" mod in preparartion for bigger changes
2024-06-08 11:55:01 +02:00
kaosat.dev
ba25c3cb20 feat(Blenvy): experimenting with injection of assets data into exported scenes/gltf files
* added extraction of local/all (wip) assets into auto export
 * added injection of LocalAssets & AllAssets (unsure)
 * related tweaks & experiments
 * also cleaned up asset ui for external assets
 * started updating the bevy integration tests
2024-06-08 11:54:40 +02:00
kaosat.dev
6f6b813474 feat(Blenvy): total overhaul of main/library scene handling
* main/library/nothing is now an enumProperty on scenes themselves
 * main/library scene listing is a dynamic property of the Blenvy core class instead of CollectionProperties
 * this means that removing / renaming scenes using the normal blender workflow automatically changes things for Blenvy as well !
 * overhauled & simplified main/library scenes ui accordingly
 * same for all the code that needed access to the main/library scenes
 * added main/library scene NAMES computed property for convenience
2024-06-07 14:08:28 +02:00
kaosat.dev
29c8eba49c feat(Blenvy): ui tweaks 2024-06-06 20:16:32 +02:00
40 changed files with 830 additions and 440 deletions

View File

@ -0,0 +1,63 @@
use std::path::{Path, PathBuf};
use bevy::{asset::LoadedUntypedAsset, gltf::Gltf, prelude::*, utils::HashMap};
use crate::{BluePrintsConfig, BlueprintAnimations};
/// 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)]
pub struct MyAsset{
pub name: String,
pub path: String
}
/// 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)]
pub struct LocalAssets(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)]
pub struct AllAssets(pub Vec<MyAsset>);
////////////////////////
///
/// flag component, usually added when a blueprint is loaded
#[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
#[derive(Debug)]
pub(crate) struct AssetLoadTracker {
#[allow(dead_code)]
pub name: String,
pub id: AssetId<LoadedUntypedAsset>,
pub loaded: bool,
#[allow(dead_code)]
pub handle: Handle<LoadedUntypedAsset>,
}
/// helper component, for tracking loaded assets
#[derive(Component, Debug)]
pub(crate) struct AssetsToLoad {
pub all_loaded: bool,
pub asset_infos: Vec<AssetLoadTracker>,
pub progress: f32,
}
impl Default for AssetsToLoad {
fn default() -> Self {
Self {
all_loaded: Default::default(),
asset_infos: Default::default(),
progress: Default::default(),
}
}
}

View File

@ -10,6 +10,9 @@ pub use animation::*;
pub mod aabb; pub mod aabb;
pub use aabb::*; pub use aabb::*;
pub mod assets;
pub use assets::*;
pub mod materials; pub mod materials;
pub use materials::*; pub use materials::*;
@ -32,12 +35,14 @@ pub enum GltfBlueprintsSet {
#[derive(Bundle)] #[derive(Bundle)]
pub struct BluePrintBundle { pub struct BluePrintBundle {
pub blueprint: BlueprintName, pub blueprint: BlueprintName,
pub blueprint_path: BlueprintPath,
pub spawn_here: SpawnHere, pub spawn_here: SpawnHere,
} }
impl Default for BluePrintBundle { impl Default for BluePrintBundle {
fn default() -> Self { fn default() -> Self {
BluePrintBundle { BluePrintBundle {
blueprint: BlueprintName("default".into()), blueprint: BlueprintName("default".into()),
blueprint_path: BlueprintPath("".into()),
spawn_here: SpawnHere, spawn_here: SpawnHere,
} }
} }
@ -112,6 +117,7 @@ impl Plugin for BlueprintsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(ComponentsFromGltfPlugin {}) app.add_plugins(ComponentsFromGltfPlugin {})
.register_type::<BlueprintName>() .register_type::<BlueprintName>()
.register_type::<BlueprintPath>()
.register_type::<MaterialInfo>() .register_type::<MaterialInfo>()
.register_type::<SpawnHere>() .register_type::<SpawnHere>()
.register_type::<BlueprintAnimations>() .register_type::<BlueprintAnimations>()
@ -123,7 +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::<Vec<MyAsset>>()
.register_type::<Vec<String>>()
.register_type::<LocalAssets>()
.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,
@ -145,20 +157,24 @@ impl Plugin for BlueprintsPlugin {
.add_systems( .add_systems(
Update, Update,
( (
( 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

@ -17,14 +17,14 @@ use bevy::{
render::mesh::Mesh, render::mesh::Mesh,
}; };
use crate::{AssetLoadTracker, AssetsToLoad, BluePrintsConfig}; use crate::{AssetLoadTracker, AssetsToLoad, BluePrintsConfig, BlueprintInstanceReady};
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
/// struct containing the name & source of the material to apply /// struct containing the name & path of the material to apply
pub struct MaterialInfo { pub struct MaterialInfo {
pub name: String, pub name: String,
pub source: String, pub path: String,
} }
/// flag component /// flag component
@ -37,21 +37,21 @@ pub(crate) struct BlueprintMaterialAssetsNotLoaded;
/// system that injects / replaces materials from material library /// system that injects / replaces materials from material library
pub(crate) fn materials_inject( pub(crate) fn materials_inject(
blueprints_config: ResMut<BluePrintsConfig>, blueprints_config: ResMut<BluePrintsConfig>,
material_infos: Query<(Entity, &MaterialInfo), Added<MaterialInfo>>,
ready_blueprints: Query<(Entity, &Children), (With<BlueprintInstanceReady>)>,
material_infos: Query<(Entity, &MaterialInfo, &Parent), Added<MaterialInfo>>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
mut commands: Commands, mut commands: Commands,
) { ) {
for (entity, material_info) in material_infos.iter() {
let model_file_name = format!(
"{}_materials_library.{}",
&material_info.source, &blueprints_config.format
);
let materials_path = Path::new(&blueprints_config.material_library_folder)
.join(Path::new(model_file_name.as_str()));
let material_name = &material_info.name;
let material_full_path = materials_path.to_str().unwrap().to_string() + "#" + material_name; // TODO: yikes, cleanup
if blueprints_config /*for(entity, children) in ready_blueprints.iter() {
println!("Blueprint ready !");
} */
for (entity, material_info, parent) in material_infos.iter() {
println!("Entity with material info {:?} {:?}", entity, material_info);
let parent_blueprint = ready_blueprints.get(parent.get());
println!("Parent blueprint {:?}", parent_blueprint)
/*if blueprints_config
.material_library_cache .material_library_cache
.contains_key(&material_full_path) .contains_key(&material_full_path)
{ {
@ -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,15 +83,17 @@ 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),
With<BlueprintMaterialAssetsNotLoaded>, With<BlueprintMaterialAssetsNotLoaded>,
>, >,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
@ -148,7 +152,7 @@ pub(crate) fn materials_inject2(
for (material_info, children) in material_infos.iter() { for (material_info, children) in material_infos.iter() {
let model_file_name = format!( let model_file_name = format!(
"{}_materials_library.{}", "{}_materials_library.{}",
&material_info.source, &blueprints_config.format &material_info.path, &blueprints_config.format
); );
let materials_path = Path::new(&blueprints_config.material_library_folder) let materials_path = Path::new(&blueprints_config.material_library_folder)
.join(Path::new(model_file_name.as_str())); .join(Path::new(model_file_name.as_str()));

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}; 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)]
@ -13,6 +13,11 @@ pub struct GameWorldTag;
#[reflect(Component)] #[reflect(Component)]
pub struct BlueprintName(pub String); pub struct BlueprintName(pub String);
/// path component for the blueprints
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct BlueprintPath(pub String);
/// flag component needed to signify the intent to spawn a Blueprint /// flag component needed to signify the intent to spawn a Blueprint
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
@ -22,6 +27,13 @@ pub struct SpawnHere;
/// flag component for dynamically spawned scenes /// flag component for dynamically spawned scenes
pub struct Spawned; pub struct Spawned;
#[derive(Component)]
/// flag component added when a Blueprint instance ist Ready : ie :
/// - its assets have loaded
/// - it has finished spawning
pub struct BlueprintInstanceReady;
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
/// flag component marking any spwaned child of blueprints ..unless the original entity was marked with the `NoInBlueprint` marker component /// flag component marking any spwaned child of blueprints ..unless the original entity was marked with the `NoInBlueprint` marker component
@ -46,97 +58,69 @@ pub struct AddToGameWorld;
/// helper component, just to transfer child data /// helper component, just to transfer child data
pub(crate) struct OriginalChildren(pub Vec<Entity>); pub(crate) struct OriginalChildren(pub Vec<Entity>);
/// 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)]
pub struct BlueprintsList(pub HashMap<String, Vec<String>>);
/// 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 pub(crate) fn test_thingy(
#[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< spawn_placeholders: Query<
( (
Entity, Entity,
&BlueprintName, &BlueprintPath,
Option<&Parent>,
Option<&Library>,
Option<&Name>,
Option<&BlueprintsList>,
), ),
(Added<BlueprintName>, Added<SpawnHere>, Without<Spawned>), (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>
>, >,
mut commands: Commands,
asset_server: Res<AssetServer>, bla_bla : Query<
blueprints_config: Res<BluePrintsConfig>, (Entity,
&BlueprintName,
&BlueprintPath,
Option<&Parent>,),(Added<BlueprintPath>)
>,
mut commands: Commands,
asset_server: Res<AssetServer>,
) { ) {
for (entity, blupeprint_name, original_parent, library_override, name, blueprints_list) in for (entity, blueprint_path) in spawn_placeholders.iter() {
spawn_placeholders.iter() //println!("added blueprint_path {:?}", blueprint_path);
{ /*commands.entity(entity).insert(
debug!( SceneBundle {
"requesting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}", scene: asset_server.load(format!("{}#Scene0", &blueprint_path.0)), // "levels/World.glb#Scene0"),
blupeprint_name.0, name, entity, original_parent ..default()
); },
);*/
// let model_handle: Handle<Gltf> = asset_server.load(model_path.clone());
}
// println!("main model path {:?}", model_path); for (entity, blueprint_name, blueprint_path, parent) in bla_bla.iter() {
if blueprints_list.is_some() { println!("added blueprint to spawn {:?} {:?}", blueprint_name, blueprint_path);
let blueprints_list = blueprints_list.unwrap(); let untyped_handle = asset_server.load_untyped(&blueprint_path.0);
// println!("blueprints list {:?}", blueprints_list.0.keys()); let asset_id = untyped_handle.id();
let mut asset_infos: Vec<AssetLoadTracker<Gltf>> = vec![]; let loaded = asset_server.is_loaded_with_dependencies(asset_id);
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 mut asset_infos: Vec<AssetLoadTracker> = vec![];
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)
@ -149,57 +133,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>,
), ),
@ -222,33 +252,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
) )
}); });
@ -280,6 +305,7 @@ pub(crate) fn spawn_from_blueprints(
..Default::default() ..Default::default()
}, },
Spawned, Spawned,
BlueprintInstanceReady, // FIXME: not sure if this is should be added here or in the post process
OriginalChildren(original_children), OriginalChildren(original_children),
BlueprintAnimations { BlueprintAnimations {
// these are animations specific to the inside of the blueprint // these are animations specific to the inside of the blueprint
@ -295,3 +321,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

@ -15,7 +15,7 @@ Auto export
- [x] main/ library scene names - [x] main/ library scene names
- [x] paths - [x] paths
Data storage for custom properties: - [ ] Data storage for custom properties:
- for scenes (main scenes) - for scenes (main scenes)
- at scene level - at scene level
- for blueprints - for blueprints
@ -106,6 +106,22 @@ General issues:
- [x] add ability to FORCE export specific blueprints & levels - [x] add ability to FORCE export specific blueprints & levels
- [ ] undo after a save removes any saved "serialized scene" data ? DIG into this - [ ] undo after a save removes any saved "serialized scene" data ? DIG into this
- [ ] handle scene renames between saves (breaks diffing) - [ ] handle scene renames between saves (breaks diffing)
- [ ] change scene selector to work on actual scenes aka to deal with renamed scenes - [x] change scene selector to work on actual scenes aka to deal with renamed scenes
- [x] remove get_main_and_library_scenes as it should not be needed anymore
- [x] fix asset file selection - [x] fix asset file selection
- [x] change "assets" tab to "levels"/worlds tab & modify UI accordingly - [x] change "assets" tab to "levels"/worlds tab & modify UI accordingly
- [ ] add option to 'split out' meshes from blueprints ?
- [ ] ie considering meshletts etc , it would make sense to keep blueprints seperate from purely mesh gltfs
- [x] remove 'export_marked_assets' it should be a default setting
- [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
- [x] fix level asets UI
- [ ] persist exported materials path in blueprints so that it can be read from library file users
- [ ] just like "export_path" write it into each blueprint's collection
- [ ] scan for used materials per blueprint !
- [ ] for scenes, scan for used materials of all non instance objects (TODO: what about overrides ?)
- [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

View File

@ -50,9 +50,8 @@ from .blueprints.operators import OT_select_blueprint
# blenvy core # blenvy core
from .core.blenvy_manager import BlenvyManager from .core.blenvy_manager import BlenvyManager
from .core.operators import OT_switch_bevy_tooling from .core.operators import OT_switch_bevy_tooling
from .core.scene_helpers import (SceneSelector)
from .core.ui.ui import (BLENVY_PT_SidePanel) from .core.ui.ui import (BLENVY_PT_SidePanel)
from .core.ui.scenes_list import SCENES_LIST_OT_actions, SCENE_UL_Blenvy from .core.ui.scenes_list import SCENES_LIST_OT_actions
from .core.ui.assets_folder_browser import OT_OpenAssetsFolderBrowser from .core.ui.assets_folder_browser import OT_OpenAssetsFolderBrowser
@ -63,8 +62,6 @@ def glTF2_post_export_callback(data):
classes = [ classes = [
# common/core # common/core
SceneSelector,
SCENE_UL_Blenvy,
SCENES_LIST_OT_actions, SCENES_LIST_OT_actions,
OT_OpenAssetsFolderBrowser, OT_OpenAssetsFolderBrowser,

View File

@ -4,6 +4,12 @@ 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.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 ..common.export_gltf import generate_gltf_export_settings
def assets_to_fake_ron(list_like):
result = []
for item in list_like:
result.append(f"(name: \"{item['name']}\", path: \"{item['path']}\")")
return result#.join(", ")
def export_blueprints(blueprints, settings, blueprints_data): def export_blueprints(blueprints, settings, blueprints_data):
blueprints_path_full = getattr(settings, "blueprints_path_full") blueprints_path_full = getattr(settings, "blueprints_path_full")
gltf_export_settings = generate_gltf_export_settings(settings) gltf_export_settings = generate_gltf_export_settings(settings)
@ -15,7 +21,7 @@ def export_blueprints(blueprints, settings, blueprints_data):
for blueprint in blueprints: for blueprint in blueprints:
print("exporting collection", blueprint.name) print("exporting collection", blueprint.name)
gltf_output_path = os.path.join(blueprints_path_full, blueprint.name) gltf_output_path = os.path.join(blueprints_path_full, blueprint.name) # TODO: reuse the export_path custom property ?
gltf_export_settings = { **gltf_export_settings, 'use_active_scene': True, 'use_active_collection': True, 'use_active_collection_with_nested':True} gltf_export_settings = { **gltf_export_settings, 'use_active_scene': True, 'use_active_collection': True, 'use_active_collection_with_nested':True}
# if we are using the material library option, do not export materials, use placeholder instead # if we are using the material library option, do not export materials, use placeholder instead
@ -23,10 +29,23 @@ def export_blueprints(blueprints, settings, blueprints_data):
gltf_export_settings['export_materials'] = 'PLACEHOLDER' gltf_export_settings['export_materials'] = 'PLACEHOLDER'
collection = bpy.data.collections[blueprint.name] collection = bpy.data.collections[blueprint.name]
print("BLUEPRINT", blueprint.name)
for asset in collection.user_assets:
print(" user asset", asset.name, asset.path)
all_assets = []
auto_assets = []
collection["local_assets"] = assets_to_fake_ron([{"name": asset.name, "path": asset.path} for asset in collection.user_assets] + auto_assets)
collection["AllAssets"] = assets_to_fake_ron([{"name": asset.name, "path": asset.path} for asset in collection.user_assets]) #all_assets + [{"name": asset.name, "path": asset.path} for asset in collection.user_assets] + auto_assets)
# do the actual export # do the actual export
generate_temporary_scene_and_export( generate_temporary_scene_and_export(
settings, settings,
temp_scene_name=TEMPSCENE_PREFIX+collection.name, temp_scene_name=TEMPSCENE_PREFIX+collection.name,
additional_data = collection,
gltf_export_settings=gltf_export_settings, gltf_export_settings=gltf_export_settings,
gltf_output_path=gltf_output_path, gltf_output_path=gltf_output_path,
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(collection, temp_collection, blueprints_data=blueprints_data, settings=settings), tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(collection, temp_collection, blueprints_data=blueprints_data, settings=settings),

View File

@ -1,5 +1,4 @@
from blenvy.core.scene_helpers import get_main_and_library_scenes
from blenvy.blueprints.blueprint_helpers import find_blueprints_not_on_disk from blenvy.blueprints.blueprint_helpers import find_blueprints_not_on_disk
@ -13,7 +12,6 @@ def get_blueprints_to_export(changes_per_scene, changed_export_parameters, bluep
change_detection = getattr(settings.auto_export, "change_detection") change_detection = getattr(settings.auto_export, "change_detection")
collection_instances_combine_mode = getattr(settings.auto_export, "collection_instances_combine_mode") collection_instances_combine_mode = getattr(settings.auto_export, "collection_instances_combine_mode")
[main_scene_names, level_scenes, library_scene_names, library_scenes] = get_main_and_library_scenes(settings)
internal_blueprints = blueprints_data.internal_blueprints internal_blueprints = blueprints_data.internal_blueprints
blueprints_to_export = internal_blueprints # just for clarity blueprints_to_export = internal_blueprints # just for clarity
@ -28,7 +26,7 @@ def get_blueprints_to_export(changes_per_scene, changed_export_parameters, bluep
# in your current Blender session for example) # in your current Blender session for example)
blueprints_not_on_disk = find_blueprints_not_on_disk(internal_blueprints, blueprints_path_full, export_gltf_extension) blueprints_not_on_disk = find_blueprints_not_on_disk(internal_blueprints, blueprints_path_full, export_gltf_extension)
for scene in library_scenes: for scene in settings.library_scenes:
if scene.name in changes_per_scene: if scene.name in changes_per_scene:
changed_objects = list(changes_per_scene[scene.name].keys()) changed_objects = list(changes_per_scene[scene.name].keys())
changed_blueprints = [blueprints_data.blueprints_from_objects[changed] for changed in changed_objects if changed in blueprints_data.blueprints_from_objects] changed_blueprints = [blueprints_data.blueprints_from_objects[changed] for changed in changed_objects if changed in blueprints_data.blueprints_from_objects]

View File

@ -3,7 +3,6 @@ import os
import bpy import bpy
import traceback import traceback
from blenvy.core.scene_helpers import get_main_and_library_scenes
from blenvy.blueprints.blueprints_scan import blueprints_scan from blenvy.blueprints.blueprints_scan import blueprints_scan
from blenvy.blueprints.blueprint_helpers import inject_export_path_into_internal_blueprints from blenvy.blueprints.blueprint_helpers import inject_export_path_into_internal_blueprints
@ -39,8 +38,6 @@ def auto_export(changes_per_scene, changed_export_parameters, settings):
gltf_extension = '.glb' if gltf_extension == 'GLB' else '.gltf' gltf_extension = '.glb' if gltf_extension == 'GLB' else '.gltf'
settings.export_gltf_extension = gltf_extension settings.export_gltf_extension = gltf_extension
[main_scene_names, level_scenes, library_scene_names, library_scenes] = get_main_and_library_scenes(settings)
blueprints_data = bpy.context.window_manager.blueprints_registry.refresh_blueprints() blueprints_data = bpy.context.window_manager.blueprints_registry.refresh_blueprints()
#blueprints_data = bpy.context.window_manager.blueprints_registry.blueprints_data #blueprints_data = bpy.context.window_manager.blueprints_registry.blueprints_data
#print("blueprints_data", blueprints_data) #print("blueprints_data", blueprints_data)
@ -58,7 +55,7 @@ def auto_export(changes_per_scene, changed_export_parameters, settings):
if export_scene_settings: if export_scene_settings:
# inject/ update scene components # inject/ update scene components
upsert_scene_components(level_scenes) upsert_scene_components(settings.main_scenes)
#inject/ update light shadow information #inject/ update light shadow information
for light in bpy.data.lights: for light in bpy.data.lights:
enabled = 'true' if light.use_shadow else 'false' enabled = 'true' if light.use_shadow else 'false'
@ -76,7 +73,7 @@ def auto_export(changes_per_scene, changed_export_parameters, settings):
# since materials export adds components we need to call this before blueprints are exported # since materials export adds components we need to call this before blueprints are exported
# export materials & inject materials components into relevant objects # export materials & inject materials components into relevant objects
if export_materials_library: if export_materials_library:
export_materials(blueprints_data.blueprint_names, library_scenes, settings) export_materials(blueprints_data.blueprint_names, settings.library_scenes, settings)
# update the list of tracked exports # update the list of tracked exports
exports_total = len(blueprints_to_export) + len(main_scenes_to_export) + (1 if export_materials_library else 0) exports_total = len(blueprints_to_export) + len(main_scenes_to_export) + (1 if export_materials_library else 0)
@ -108,7 +105,7 @@ def auto_export(changes_per_scene, changed_export_parameters, settings):
print("export MAIN scenes") print("export MAIN scenes")
for scene_name in main_scenes_to_export: for scene_name in main_scenes_to_export:
print(" exporting scene:", scene_name) print(" exporting scene:", scene_name)
export_main_scene(bpy.data.scenes[scene_name], blend_file_path, settings, blueprints_data) export_main_scene(bpy.data.scenes[scene_name], settings, blueprints_data)
# now deal with blueprints/collections # now deal with blueprints/collections
do_export_library_scene = not change_detection or changed_export_parameters or len(blueprints_to_export) > 0 do_export_library_scene = not change_detection or changed_export_parameters or len(blueprints_to_export) > 0
@ -123,11 +120,11 @@ def auto_export(changes_per_scene, changed_export_parameters, settings):
for obj in old_selections: for obj in old_selections:
obj.select_set(True) obj.select_set(True)
if export_materials_library: if export_materials_library:
cleanup_materials(blueprints_data.blueprint_names, library_scenes) cleanup_materials(blueprints_data.blueprint_names, settings.library_scenes)
else: else:
for scene_name in main_scene_names: for scene in settings.main_scenes:
export_main_scene(bpy.data.scenes[scene_name], blend_file_path, settings, []) export_main_scene(scene, settings, [])
@ -141,9 +138,7 @@ def auto_export(changes_per_scene, changed_export_parameters, settings):
finally: finally:
# FIXME: error handling ? also redundant # FIXME: error handling ? also redundant
[main_scene_names, main_scenes, library_scene_names, library_scenes] = get_main_and_library_scenes(settings)
if export_scene_settings: if export_scene_settings:
# inject/ update scene components # inject/ update scene components
remove_scene_components(main_scenes) remove_scene_components(settings.main_scenes)

View File

@ -96,6 +96,8 @@ def duplicate_object(object, parent, combine_mode, destination_collection, bluep
original_collection = object.instance_collection original_collection = object.instance_collection
original_name = object.name original_name = object.name
blueprint_name = original_collection.name blueprint_name = original_collection.name
# FIXME: blueprint path is WRONG !
print("BLUEPRINT PATH", original_collection.get('export_path', None))
blueprint_path = original_collection['export_path'] if 'export_path' in original_collection else f'./{blueprint_name}' # TODO: the default requires the currently used extension !! blueprint_path = original_collection['export_path'] if 'export_path' in original_collection else f'./{blueprint_name}' # TODO: the default requires the currently used extension !!
@ -107,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

@ -4,10 +4,16 @@ from pathlib import Path
from blenvy.core.helpers_collections import (traverse_tree) from blenvy.core.helpers_collections import (traverse_tree)
from blenvy.core.object_makers import make_cube from blenvy.core.object_makers import make_cube
from blenvy.materials.materials_helpers import get_all_materials from blenvy.materials.materials_helpers import add_material_info_to_objects, 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
@ -63,8 +69,8 @@ def export_materials(collections, library_scenes, settings):
gltf_export_settings = generate_gltf_export_settings(settings) gltf_export_settings = generate_gltf_export_settings(settings)
materials_path_full = getattr(settings,"materials_path_full") materials_path_full = getattr(settings,"materials_path_full")
used_material_names = get_all_materials(collections, library_scenes) (used_material_names, materials_per_object) = get_all_materials(collections, library_scenes)
current_project_name = Path(bpy.context.blend_data.filepath).stem add_material_info_to_objects(materials_per_object, settings)
gltf_export_settings = { **gltf_export_settings, gltf_export_settings = { **gltf_export_settings,
'use_active_scene': True, 'use_active_scene': True,
@ -75,7 +81,8 @@ def export_materials(collections, library_scenes, settings):
'export_apply':True 'export_apply':True
} }
gltf_output_path = os.path.join(materials_path_full, current_project_name + "_materials_library") current_project_name = Path(bpy.context.blend_data.filepath).stem
gltf_output_path = os.path.join(materials_path_full, current_project_name + "_materials")
print(" exporting Materials to", gltf_output_path, ".gltf/glb") print(" exporting Materials to", gltf_output_path, ".gltf/glb")

View File

@ -1,8 +1,10 @@
import json
import bpy import bpy
from blenvy.core.helpers_collections import (set_active_collection) from blenvy.core.helpers_collections import (set_active_collection)
from blenvy.core.object_makers import (make_empty) from blenvy.core.object_makers import (make_empty)
from .duplicate_object import duplicate_object from .duplicate_object import duplicate_object
from .export_gltf import export_gltf from .export_gltf import export_gltf
from blenvy.core.scene_helpers import add_scene_property
""" """
generates a temporary scene, fills it with data, cleans up after itself generates a temporary scene, fills it with data, cleans up after itself
@ -12,11 +14,30 @@ generates a temporary scene, fills it with data, cleans up after itself
* cleaned up using tempScene_cleaner * cleaned up using tempScene_cleaner
""" """
def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_output_path, temp_scene_name="__temp_scene", tempScene_filler=None, tempScene_cleaner=None): def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_output_path, temp_scene_name="__temp_scene", tempScene_filler=None, tempScene_cleaner=None, additional_data=None):
temp_scene = bpy.data.scenes.new(name=temp_scene_name) temp_scene = bpy.data.scenes.new(name=temp_scene_name)
temp_root_collection = temp_scene.collection temp_root_collection = temp_scene.collection
print("additional_dataAAAAAAAAAAAAAAAH", additional_data)
if additional_data is not None: # FIXME not a fan of having this here
for entry in dict(additional_data):
print("entry in additional data", entry)
if entry == "local_assets":
temp_scene["local_assets"] = additional_data[entry] # this is for bevy 0.14
temp_root_collection["local_assets"] = additional_data[entry] # for previous bevy versions, remove when migration done
bla = "[(name: \"test_asset\", path: \"audio/fake.mp3\")]"
local_assets = additional_data.get(entry, [])
local_assets = [entry for entry in local_assets]
add_scene_property(temp_scene, 'assets_components', {"LocalAssets": f"LocalAssets({local_assets})".replace("'", '')})
if entry == entry == "AllAssets":
temp_scene["AllAssets"] = additional_data[entry]
temp_root_collection["AllAssets"] = additional_data[entry] # for previous bevy versions, remove when migration done
all_assets = additional_data.get(entry, [])
all_assets = [entry for entry in all_assets]
add_scene_property(temp_scene, 'assets_components', {"AllAssets": f"AllAssets({all_assets})".replace("'", '')})
# save active scene # save active scene
original_scene = bpy.context.window.scene original_scene = bpy.context.window.scene
# and selected collection # and selected collection
@ -43,6 +64,7 @@ def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_out
scene_filler_data = tempScene_filler(temp_root_collection) scene_filler_data = tempScene_filler(temp_root_collection)
# export the temporary scene # export the temporary scene
try: try:
print("dry_run MODE", settings.auto_export.dry_run)
if settings.auto_export.dry_run == "DISABLED": if settings.auto_export.dry_run == "DISABLED":
export_gltf(gltf_output_path, gltf_export_settings) export_gltf(gltf_output_path, gltf_export_settings)
except Exception as error: except Exception as error:

View File

@ -15,9 +15,10 @@ def prepare_and_export():
# determine changed objects # determine changed objects
per_scene_changes = get_changes_per_scene(settings=blenvy) per_scene_changes = get_changes_per_scene(settings=blenvy)
# determine changed parameters # determine changed parameters
setting_changes = False # get_setting_changes() setting_changes = get_setting_changes()
print("setting_changes", setting_changes)
# do the actual export # do the actual export
blenvy.auto_export.dry_run = 'NO_EXPORT'#'DISABLED'# # blenvy.auto_export.dry_run = 'NO_EXPORT'#'DISABLED'#
auto_export(per_scene_changes, setting_changes, blenvy) auto_export(per_scene_changes, setting_changes, blenvy)
# cleanup # cleanup

View File

@ -21,7 +21,6 @@ parameter_names_whitelist_auto_export = [
'export_separate_dynamic_and_static_objects', 'export_separate_dynamic_and_static_objects',
'export_materials_library', 'export_materials_library',
'collection_instances_combine_mode', 'collection_instances_combine_mode',
'export_marked_assets'
] ]
def get_setting_changes(): def get_setting_changes():
@ -37,6 +36,7 @@ def get_setting_changes():
previous_gltf_settings = load_settings(".blenvy_gltf_settings_previous") previous_gltf_settings = load_settings(".blenvy_gltf_settings_previous")
current_gltf_settings = load_settings(".blenvy_gltf_settings") current_gltf_settings = load_settings(".blenvy_gltf_settings")
print("previous_gltf_settings", previous_gltf_settings, "current_gltf_settings", current_gltf_settings)
gltf_settings_changed = not are_settings_identical(previous_gltf_settings, current_gltf_settings) gltf_settings_changed = not are_settings_identical(previous_gltf_settings, current_gltf_settings)
# write the new settings to the old settings # write the new settings to the old settings

View File

@ -83,7 +83,7 @@ class AutoExportTracker(PropertyGroup):
print("last operators", ops) print("last operators", ops)
for op in ops: for op in ops:
print("operator", op)""" print("operator", op)"""
active_operator = getattr(bpy.context.active_operator, 'active_operator' , None) active_operator = getattr(bpy.context, 'active_operator' , None)
if active_operator is not None: if active_operator is not None:
#print("Operator", active_operator.bl_label, active_operator.bl_idname) #print("Operator", active_operator.bl_label, active_operator.bl_idname)
if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf" and active_operator.gltf_export_id == "gltf_auto_export": if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf" and active_operator.gltf_export_id == "gltf_auto_export":

View File

@ -1,11 +1,23 @@
import json
import os import os
from pathlib import Path
from types import SimpleNamespace
import bpy
from blenvy.blueprints.blueprint_helpers import inject_blueprints_list_into_main_scene, remove_blueprints_list_from_main_scene from blenvy.blueprints.blueprint_helpers import inject_blueprints_list_into_main_scene, remove_blueprints_list_from_main_scene
from ..constants import TEMPSCENE_PREFIX 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.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 ..common.export_gltf import (generate_gltf_export_settings, export_gltf)
from .is_object_dynamic import is_object_dynamic, is_object_static from .is_object_dynamic import is_object_dynamic, is_object_static
def export_main_scene(scene, blend_file_path, settings, blueprints_data): def assets_to_fake_ron(list_like):
result = []
for item in list_like:
result.append(f"(name: \"{item['name']}\", path: \"{item['path']}\")")
return result#.join(", ")
def export_main_scene(scene, settings, blueprints_data):
gltf_export_settings = generate_gltf_export_settings(settings) gltf_export_settings = generate_gltf_export_settings(settings)
assets_path_full = getattr(settings,"assets_path_full") assets_path_full = getattr(settings,"assets_path_full")
levels_path_full = getattr(settings,"levels_path_full") levels_path_full = getattr(settings,"levels_path_full")
@ -25,7 +37,52 @@ def export_main_scene(scene, blend_file_path, settings, blueprints_data):
if export_blueprints : if export_blueprints :
gltf_output_path = os.path.join(levels_path_full, scene.name) gltf_output_path = os.path.join(levels_path_full, scene.name)
#inject_blueprints_list_into_main_scene(scene, blueprints_data, settings) inject_blueprints_list_into_main_scene(scene, blueprints_data, settings)
print("main scene", scene)
for asset in scene.user_assets:
print(" user asset", asset.name, asset.path)
for asset in scene.generated_assets:
print(" generated asset", asset)
"""for blueprint in blueprints_data.blueprints_per_scenes[scene.name]:
print("BLUEPRINT", blueprint)"""
blueprint_instances_in_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene.name, {}).keys()
blueprints_in_scene = [blueprints_data.blueprints_per_name[blueprint_name] for blueprint_name in blueprint_instances_in_scene]
#yala = [blueprint.collection.user_assets for blueprint in blueprints_in_scene]
#print("dsfsdf", yala)
auto_assets = []
all_assets = []
export_gltf_extension = getattr(settings.auto_export, "export_gltf_extension", ".glb")
blueprints_path = getattr(settings, "blueprints_path")
for blueprint in blueprints_in_scene:
if blueprint.local:
blueprint_exported_path = os.path.join(blueprints_path, f"{blueprint.name}{export_gltf_extension}")
else:
# get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['export_path'] if 'export_path' in blueprint.collection else None
if blueprint_exported_path is not None: # and not does_asset_exist(assets_list, blueprint_exported_path):
auto_assets.append({"name": blueprint.name, "path": blueprint_exported_path})#, "generated": True, "internal":blueprint.local, "parent": None})
# now also add the assets of the blueprints # TODO: wait no , these should not be a part of the (scene) local assets
for asset in blueprint.collection.user_assets:
print("adding assets of blueprint", asset.name)
all_assets.append({"name": asset.name, "path": asset.path})
"""for asset in auto_assets:
print(" generated asset", asset.name, asset.path)"""
materials_path = getattr(settings, "materials_path")
current_project_name = Path(bpy.context.blend_data.filepath).stem
materials_library_name = f"{current_project_name}_materials"
materials_exported_path = os.path.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
material_assets = [{"name": materials_library_name, "path": materials_exported_path}] # we also add the material library as an asset
scene["local_assets"] = assets_to_fake_ron([{"name": asset.name, "path": asset.path} for asset in scene.user_assets] + auto_assets + material_assets)
scene["AllAssets"] = assets_to_fake_ron(all_assets + [{"name": asset.name, "path": asset.path} for asset in scene.user_assets] + auto_assets + material_assets)
if export_separate_dynamic_and_static_objects: if export_separate_dynamic_and_static_objects:
#print("SPLIT STATIC AND DYNAMIC") #print("SPLIT STATIC AND DYNAMIC")
# first export static objects # first export static objects
@ -54,19 +111,21 @@ def export_main_scene(scene, blend_file_path, settings, blueprints_data):
generate_temporary_scene_and_export( generate_temporary_scene_and_export(
settings, settings,
temp_scene_name=TEMPSCENE_PREFIX, temp_scene_name=TEMPSCENE_PREFIX,
additional_data = scene,
gltf_export_settings=gltf_export_settings, gltf_export_settings=gltf_export_settings,
gltf_output_path=gltf_output_path, gltf_output_path=gltf_output_path,
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(scene.collection, temp_collection, blueprints_data=blueprints_data, settings=settings), tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(scene.collection, temp_collection, blueprints_data=blueprints_data, settings=settings),
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params) tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params)
) )
remove_blueprints_list_from_main_scene(scene)
else: else:
gltf_output_path = os.path.join(assets_path_full, scene.name) gltf_output_path = os.path.join(assets_path_full, scene.name)
print(" exporting gltf to", gltf_output_path, ".gltf/glb") print(" exporting gltf to", gltf_output_path, ".gltf/glb")
if settings.auto_export.dry_run == "DISABLED": if settings.auto_export.dry_run == "DISABLED":
export_gltf(gltf_output_path, gltf_export_settings) export_gltf(gltf_output_path, gltf_export_settings)
remove_blueprints_list_from_main_scene(scene)

View File

@ -1,5 +1,4 @@
import bpy import bpy
from blenvy.core.scene_helpers import get_main_and_library_scenes
from blenvy.blueprints.blueprint_helpers import check_if_blueprint_on_disk from blenvy.blueprints.blueprint_helpers import check_if_blueprint_on_disk
# IF collection_instances_combine_mode is not 'split' check for each scene if any object in changes_per_scene has an instance in the scene # IF collection_instances_combine_mode is not 'split' check for each scene if any object in changes_per_scene has an instance in the scene
@ -57,9 +56,8 @@ def should_level_be_exported(scene_name, changed_export_parameters, changes_per_
# this also takes the split/embed mode into account: if a collection instance changes AND embed is active, its container level/world should also be exported # this also takes the split/embed mode into account: if a collection instance changes AND embed is active, its container level/world should also be exported
def get_levels_to_export(changes_per_scene, changed_export_parameters, blueprints_data, settings): def get_levels_to_export(changes_per_scene, changed_export_parameters, blueprints_data, settings):
[main_scene_names, level_scenes, library_scene_names, library_scenes] = get_main_and_library_scenes(settings)
# determine list of main scenes to export # determine list of main scenes to export
# we have more relaxed rules to determine if the main scenes have changed : any change is ok, (allows easier handling of changes, render settings etc) # we have more relaxed rules to determine if the main scenes have changed : any change is ok, (allows easier handling of changes, render settings etc)
main_scenes_to_export = [scene_name for scene_name in main_scene_names if should_level_be_exported(scene_name, changed_export_parameters, changes_per_scene, blueprints_data, settings)] main_scenes_to_export = [scene_name for scene_name in settings.main_scenes_names if should_level_be_exported(scene_name, changed_export_parameters, changes_per_scene, blueprints_data, settings)]
return (main_scenes_to_export) return (main_scenes_to_export)

View File

@ -101,13 +101,6 @@ class AutoExportSettings(PropertyGroup):
update=save_settings update=save_settings
) # type: ignore ) # type: ignore
export_marked_assets: BoolProperty(
name='Auto export marked assets',
description='Collections that have been marked as assets will be systematically exported, even if not in use in another scene',
default=True,
update=save_settings
) # type: ignore
dry_run: EnumProperty( dry_run: EnumProperty(
name="dry run", name="dry run",
description="debug/ develop helper to enable everything but the actual exporting of files", description="debug/ develop helper to enable everything but the actual exporting of files",

View File

@ -51,7 +51,6 @@ def draw_settings_ui(layout, auto_export_settings):
# collections/blueprints # collections/blueprints
section.prop(auto_export_settings, "collection_instances_combine_mode") section.prop(auto_export_settings, "collection_instances_combine_mode")
section.prop(auto_export_settings, "export_marked_assets")
section.separator() section.separator()
section.prop(auto_export_settings, "export_separate_dynamic_and_static_objects") section.prop(auto_export_settings, "export_separate_dynamic_and_static_objects")

View File

@ -4,11 +4,12 @@ from .assets_scan import get_main_scene_assets_tree
from .asset_helpers import get_user_assets, does_asset_exist from .asset_helpers import get_user_assets, does_asset_exist
def draw_assets(layout, name, title, asset_registry, target_type, target_name, editable=True, user_assets= [], generated_assets = []): def draw_assets(layout, name, title, asset_registry, target_type, target_name, editable=True, user_assets= [], generated_assets = []):
nesting_indent = 0.05
number_of_user_assets = len(user_assets) number_of_user_assets = len(user_assets)
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'
@ -19,8 +20,7 @@ def draw_assets(layout, name, title, asset_registry, target_type, target_name, e
add_possible = does_asset_exist(target, {"path": asset_registry.asset_path_selector}) #"name": asset_registry.asset_name_selector, add_possible = does_asset_exist(target, {"path": asset_registry.asset_path_selector}) #"name": asset_registry.asset_name_selector,
if header: if header and editable:
row = header.row() row = header.row()
row.alert = add_possible row.alert = add_possible
@ -38,39 +38,21 @@ def draw_assets(layout, name, title, asset_registry, target_type, target_name, e
add_asset.asset_type = asset_registry.asset_type_selector add_asset.asset_type = asset_registry.asset_type_selector
add_asset.asset_path = asset_registry.asset_path_selector add_asset.asset_path = asset_registry.asset_path_selector
if panel: if panel:
if editable: if editable:
row = panel.row() row = panel.row()
#panel.separator()
print("here", user_assets)
"""row.alert = add_possible
row.prop(asset_registry, "asset_name_selector", text="")
row.label(text=asset_registry.asset_path_selector)
asset_selector = row.operator(operator="asset.open_filebrowser", text="", icon="FILE_FOLDER")
add_asset_layout = row.column()
add_asset_layout.enabled = not add_possible
add_asset = add_asset_layout.operator(operator="bevyassets.add", text="", icon="ADD")
add_asset.target_type = target_type
add_asset.target_name = target_name
add_asset.asset_name = asset_registry.asset_name_selector
add_asset.asset_type = asset_registry.asset_type_selector
add_asset.asset_path = asset_registry.asset_path_selector"""
panel.separator()
for asset in user_assets: for asset in user_assets:
print("asset", asset)
row = panel.row() row = panel.row()
split = row.split(factor=0.005) split = row.split(factor=nesting_indent)
col = split.column() col = split.column()
col.label(text=" ") col.label(text=" ")
col = split.column() col = split.column()
row = col.row() row = col.row()
row.label(icon="ASSET_MANAGER")
row.prop(asset, "name", text="") row.prop(asset, "name", text="")
row.label(text=asset.path) row.label(text=asset.path)
asset_selector = row.operator(operator="asset.open_filebrowser", text="", icon="FILE_FOLDER") asset_selector = row.operator(operator="asset.open_filebrowser", text="", icon="FILE_FOLDER")
@ -79,27 +61,6 @@ def draw_assets(layout, name, title, asset_registry, target_type, target_name, e
remove_asset.target_type = target_type remove_asset.target_type = target_type
remove_asset.target_name = target_name remove_asset.target_name = target_name
remove_asset.asset_path = asset.path remove_asset.asset_path = asset.path
for asset in generated_assets:
row = panel.row()
#row.label(text=asset.name)
#row.label(text=asset.path)
split = row.split(factor=0.005)
col = split.column()
col.label(text=" ")
col = split.column()
sub_header, sub_panel = col.panel(f"assets_sub{asset.name}", default_closed=False)
sub_header.label(text=f"{asset.name} ({asset.path})", icon="XRAY")
if sub_panel:
sub_panel.label(text=" some stuff")
"""remove_asset = row.operator(operator="bevyassets.remove", text="", icon="TRASH")
remove_asset.target_type = target_type
remove_asset.target_name = target_name
remove_asset.asset_path = asset.path"""
return panel return panel
class Blenvy_assets(bpy.types.Panel): class Blenvy_assets(bpy.types.Panel):
@ -133,9 +94,7 @@ class Blenvy_assets(bpy.types.Panel):
settings = SimpleNamespace(**settings) settings = SimpleNamespace(**settings)
if panel: if panel:
#print("dfs") for scene in blenvy.main_scenes:
for scene_selector in blenvy.main_scenes:
scene = bpy.data.scenes[scene_selector.name]
user_assets = get_user_assets(scene) user_assets = get_user_assets(scene)
row = panel.row() row = panel.row()

View File

@ -23,6 +23,8 @@ def check_if_blueprint_on_disk(scene_name, folder_path, extension):
def inject_export_path_into_internal_blueprints(internal_blueprints, blueprints_path, gltf_extension): def inject_export_path_into_internal_blueprints(internal_blueprints, blueprints_path, gltf_extension):
for blueprint in internal_blueprints: for blueprint in internal_blueprints:
blueprint_exported_path = os.path.join(blueprints_path, f"{blueprint.name}{gltf_extension}") blueprint_exported_path = os.path.join(blueprints_path, f"{blueprint.name}{gltf_extension}")
print("injecting blueprint path", blueprint_exported_path, "for", blueprint.name)
blueprint.collection["export_path"] = blueprint_exported_path blueprint.collection["export_path"] = blueprint_exported_path
def inject_blueprints_list_into_main_scene(scene, blueprints_data, settings): def inject_blueprints_list_into_main_scene(scene, blueprints_data, settings):

View File

@ -8,7 +8,6 @@ from bpy_types import (PropertyGroup)
from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty) from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty)
from ..settings import load_settings from ..settings import load_settings
from ..core.scene_helpers import get_main_and_library_scenes
from .blueprints_scan import blueprints_scan from .blueprints_scan import blueprints_scan
@ -68,7 +67,6 @@ class BlueprintsRegistry(PropertyGroup):
#print("titi", self) #print("titi", self)
blenvy = bpy.context.window_manager.blenvy blenvy = bpy.context.window_manager.blenvy
settings = blenvy settings = blenvy
[main_scene_names, level_scenes, library_scene_names, library_scenes] = get_main_and_library_scenes(settings) blueprints_data = blueprints_scan(settings.main_scenes, settings.library_scenes, settings)
blueprints_data = blueprints_scan(level_scenes, library_scenes, settings)
self.blueprints_data = blueprints_data self.blueprints_data = blueprints_data
return blueprints_data return blueprints_data

View File

@ -8,8 +8,6 @@ from .blueprint import Blueprint
# - with the "auto_export" flag # - with the "auto_export" flag
# https://blender.stackexchange.com/questions/167878/how-to-get-all-collections-of-the-current-scene # https://blender.stackexchange.com/questions/167878/how-to-get-all-collections-of-the-current-scene
def blueprints_scan(main_scenes, library_scenes, settings): def blueprints_scan(main_scenes, library_scenes, settings):
export_marked_assets = getattr(settings.auto_export, "export_marked_assets")
blueprints = {} blueprints = {}
blueprints_from_objects = {} blueprints_from_objects = {}
blueprint_name_from_instances = {} blueprint_name_from_instances = {}
@ -84,12 +82,12 @@ def blueprints_scan(main_scenes, library_scenes, settings):
if ( if (
'AutoExport' in collection and collection['AutoExport'] == True # get marked collections 'AutoExport' in collection and collection['AutoExport'] == True # get marked collections
or export_marked_assets and collection.asset_data is not None # or if you have marked collections as assets you can auto export them too or collection.asset_data is not None # or if you have marked collections as assets you can auto export them too
or collection.name in list(internal_collection_instances.keys()) # or if the collection has an instance in one of the main scenes or collection.name in list(internal_collection_instances.keys()) # or if the collection has an instance in one of the main scenes
): ):
blueprint = Blueprint(collection.name) blueprint = Blueprint(collection.name)
blueprint.local = True blueprint.local = True
blueprint.marked = 'AutoExport' in collection and collection['AutoExport'] == True or export_marked_assets and collection.asset_data is not None blueprint.marked = 'AutoExport' in collection and collection['AutoExport'] == True or collection.asset_data is not None
blueprint.objects = [object.name for object in collection.all_objects if not object.instance_type == 'COLLECTION'] # inneficient, double loop blueprint.objects = [object.name for object in collection.all_objects if not object.instance_type == 'COLLECTION'] # inneficient, double loop
blueprint.nested_blueprints = [object.instance_collection.name for object in collection.all_objects if object.instance_type == 'COLLECTION'] # FIXME: not precise enough, aka "what is a blueprint" blueprint.nested_blueprints = [object.instance_collection.name for object in collection.all_objects if object.instance_type == 'COLLECTION'] # FIXME: not precise enough, aka "what is a blueprint"
blueprint.collection = collection blueprint.collection = collection

View File

@ -5,6 +5,27 @@ from ..assets.asset_helpers import get_user_assets, get_generated_assets
from ..assets.ui import draw_assets from ..assets.ui import draw_assets
def draw_blueprints(layout, name, title, generated_assets):
nesting_indent = 0.05
number_of_generated_assets = len(generated_assets)
header, panel = layout.panel(f"assets{name}", default_closed=True)
header.label(text=title + f"({number_of_generated_assets})", icon="XRAY")
if panel:
for asset in generated_assets:
row = panel.row()
split = row.split(factor=nesting_indent)
col = split.column()
col.label(text=" ")
col = split.column()
sub_header, sub_panel = col.panel(f"assets_sub{asset.name}", default_closed=False)
sub_header.label(text=f"{asset.name} ({asset.path})", icon="XRAY")
if sub_panel:
sub_panel.label(text=" some stuff")
class GLTF_PT_auto_export_blueprints_list(bpy.types.Panel): class GLTF_PT_auto_export_blueprints_list(bpy.types.Panel):
bl_space_type = 'VIEW_3D' bl_space_type = 'VIEW_3D'
bl_region_type = 'UI' bl_region_type = 'UI'
@ -56,4 +77,4 @@ class GLTF_PT_auto_export_blueprints_list(bpy.types.Panel):
generated_assets = get_generated_assets(blueprint.collection) generated_assets = get_generated_assets(blueprint.collection)
draw_assets(layout=col, name=blueprint.name, title="Assets", asset_registry=asset_registry, user_assets=user_assets, generated_assets=generated_assets, target_type="BLUEPRINT", target_name=blueprint.name, editable=False) draw_assets(layout=col, name=blueprint.name, title="Assets", asset_registry=asset_registry, user_assets=user_assets, generated_assets=generated_assets, target_type="BLUEPRINT", target_name=blueprint.name, editable=False)
panel.label(text="External") panel.label(text="External blueprint, assets are not editable")

View File

@ -2,12 +2,12 @@ import os
import bpy import bpy
from bpy_types import (PropertyGroup) from bpy_types import (PropertyGroup)
from bpy.props import (BoolProperty, EnumProperty, PointerProperty, StringProperty, CollectionProperty, IntProperty) from bpy.props import (BoolProperty, EnumProperty, PointerProperty, StringProperty, CollectionProperty, IntProperty)
from .scene_helpers import SceneSelector
from ..settings import upsert_settings, load_settings, generate_complete_settings_dict from ..settings import upsert_settings, load_settings, generate_complete_settings_dict
import blenvy.add_ons.auto_export.settings as auto_export_settings import blenvy.add_ons.auto_export.settings as auto_export_settings
import blenvy.add_ons.bevy_components.settings as component_settings import blenvy.add_ons.bevy_components.settings as component_settings
# list of settings we do NOT want to save # list of settings we do NOT want to save
settings_black_list = ['settings_save_enabled', 'main_scene_selector', 'main_scenes', 'main_scenes_index', 'library_scene_selector', 'library_scenes', 'library_scenes_index', settings_black_list = ['settings_save_enabled', 'main_scene_selector', 'main_scenes', 'main_scenes_index', 'library_scene_selector', 'library_scenes', 'library_scenes_index',
#'project_root_path_full', 'assets_path_full', '' #'project_root_path_full', 'assets_path_full', ''
@ -35,7 +35,10 @@ def update_mode(blenvy, context):
def is_scene_already_in_use(self, scene): def is_scene_already_in_use(self, scene):
try: try:
return scene.name not in self.main_scenes and scene.name not in self.library_scenes current_main_scene_names = list(map(lambda x: x.name, self.main_scenes))
current_library_scene_names = list(map(lambda x: x.name, self.library_scenes))
#print("scene ", scene.name, current_main_scene_names, current_library_scene_names)
return scene.name not in current_main_scene_names and scene.name not in current_library_scene_names
except: except:
return True return True
@ -117,14 +120,6 @@ class BlenvyManager(PropertyGroup):
get=lambda self: os.path.abspath(os.path.join(os.path.dirname(bpy.data.filepath), self.project_root_path, self.assets_path, self.materials_path)) get=lambda self: os.path.abspath(os.path.join(os.path.dirname(bpy.data.filepath), self.project_root_path, self.assets_path, self.materials_path))
) # type: ignore ) # type: ignore
main_scenes: CollectionProperty(name="main scenes", type=SceneSelector) # type: ignore
main_scenes_index: IntProperty(name = "Index for main scenes list", default = 0, update=update_scene_lists) # type: ignore
#main_scene_names = [] # FIXME: unsure
library_scenes: CollectionProperty(name="library scenes", type=SceneSelector) # type: ignore
library_scenes_index: IntProperty(name = "Index for library scenes list", default = 0, update=update_scene_lists) # type: ignore
#library_scene_names = [] # FIXME: unsure
# sub ones # sub ones
auto_export: PointerProperty(type=auto_export_settings.AutoExportSettings) # type: ignore auto_export: PointerProperty(type=auto_export_settings.AutoExportSettings) # type: ignore
components: PointerProperty(type=component_settings.ComponentsSettings) # type: ignore components: PointerProperty(type=component_settings.ComponentsSettings) # type: ignore
@ -132,15 +127,37 @@ class BlenvyManager(PropertyGroup):
main_scene_selector: PointerProperty(type=bpy.types.Scene, name="main scene", description="main_scene_picker", poll=is_scene_already_in_use)# type: ignore main_scene_selector: PointerProperty(type=bpy.types.Scene, name="main scene", description="main_scene_picker", poll=is_scene_already_in_use)# type: ignore
library_scene_selector: PointerProperty(type=bpy.types.Scene, name="library scene", description="library_scene_picker", poll=is_scene_already_in_use)# type: ignore library_scene_selector: PointerProperty(type=bpy.types.Scene, name="library scene", description="library_scene_picker", poll=is_scene_already_in_use)# type: ignore
@property
def main_scenes(self):
return [scene for scene in bpy.data.scenes if scene.blenvy_scene_type == 'Level']
@property
def main_scenes_names(self):
return [scene.name for scene in self.main_scenes]
@property
def library_scenes(self):
return [scene for scene in bpy.data.scenes if scene.blenvy_scene_type == 'Library']
@property
def library_scenes_names(self):
return [scene.name for scene in self.library_scenes]
@classmethod @classmethod
def register(cls): def register(cls):
bpy.types.WindowManager.blenvy = PointerProperty(type=BlenvyManager) bpy.types.WindowManager.blenvy = PointerProperty(type=BlenvyManager)
# unsure # unsure
# you can add components to both objects & collections
#bpy.types.Object.components_meta = PointerProperty(type=ComponentsMeta)
bpy.types.Collection.always_export = BoolProperty(default=False, description="always export this blueprint, regardless of changed status") # FIXME: not sure about this one bpy.types.Collection.always_export = BoolProperty(default=False, description="always export this blueprint, regardless of changed status") # FIXME: not sure about this one
bpy.types.Scene.always_export = BoolProperty(default=False, description="always export this blueprint, regardless of changed status") # FIXME: not sure about this one bpy.types.Scene.always_export = BoolProperty(default=False, description="always export this blueprint, regardless of changed status") # FIXME: not sure about this one
bpy.types.Scene.blenvy_scene_type = EnumProperty(
items= (
('None', 'None', 'No blenvy type specified'),
('Level', 'Level','Main/ Level scene'),
('Library', 'Library', 'Library scene'),
),
default='None'
)
@classmethod @classmethod
def unregister(cls): def unregister(cls):
@ -148,6 +165,7 @@ class BlenvyManager(PropertyGroup):
del bpy.types.Collection.always_export del bpy.types.Collection.always_export
del bpy.types.Scene.always_export del bpy.types.Scene.always_export
del bpy.types.Scene.blenvy_scene_type
def load_settings(self): def load_settings(self):
print("LOAD SETTINGS") print("LOAD SETTINGS")
@ -155,14 +173,6 @@ class BlenvyManager(PropertyGroup):
if settings is not None: if settings is not None:
if "mode" in settings: if "mode" in settings:
self.mode = settings["mode"] self.mode = settings["mode"]
if "main_scene_names" in settings:
for main_scene_name in settings["main_scene_names"]:
added = self.main_scenes.add()
added.name = main_scene_name
if "library_scene_names" in settings:
for main_scene_name in settings["library_scene_names"]:
added = self.library_scenes.add()
added.name = main_scene_name
asset_path_names = ['project_root_path', 'assets_path', 'blueprints_path', 'levels_path', 'materials_path'] asset_path_names = ['project_root_path', 'assets_path', 'blueprints_path', 'levels_path', 'materials_path']
for asset_path_name in asset_path_names: for asset_path_name in asset_path_names:

View File

@ -1,29 +1,7 @@
import bpy import bpy
from bpy.props import (PointerProperty)
from .object_makers import make_empty from .object_makers import make_empty
class SceneSelector(bpy.types.PropertyGroup):
name: bpy.props.StringProperty() # type: ignore
display: bpy.props.BoolProperty() # type: ignore
# convenience utility to get lists of scenes
def get_main_and_library_scenes(settings):
level_scene_names= getattr(settings, "main_scene_names", []) #list(map(lambda scene: scene.name, getattr(settings,"main_scenes")))
library_scene_names = getattr(settings, "library_scene_names", []) #list(map(lambda scene: scene.name, getattr(settings,"library_scenes")))
level_scene_names= list(map(lambda scene: scene.name, getattr(settings,"main_scenes")))
library_scene_names = list(map(lambda scene: scene.name, getattr(settings,"library_scenes")))
level_scene_names = list(filter(lambda name: name in bpy.data.scenes, level_scene_names))
library_scene_names = list(filter(lambda name: name in bpy.data.scenes, library_scene_names))
level_scenes = list(map(lambda name: bpy.data.scenes[name], level_scene_names))
library_scenes = list(map(lambda name: bpy.data.scenes[name], library_scene_names))
return [level_scene_names, level_scenes, library_scene_names, library_scenes]
def add_scene_property(scene, scene_component_name, property_data, limit_to=None): def add_scene_property(scene, scene_component_name, property_data, limit_to=None):
root_collection = scene.collection root_collection = scene.collection
scene_property = None scene_property = None

View File

@ -1,39 +1,6 @@
import bpy import bpy
from bpy.types import Operator from bpy.types import Operator
class SCENE_UL_Blenvy(bpy.types.UIList):
# The draw_item function is called for each item of the collection that is visible in the list.
# data is the RNA object containing the collection,
# item is the current drawn item of the collection,
# icon is the "computed" icon for the item (as an integer, because some objects like materials or textures
# have custom icons ID, which are not available as enum items).
# active_data is the RNA object containing the active property for the collection (i.e. integer pointing to the
# active item of the collection).
# active_propname is the name of the active property (use 'getattr(active_data, active_propname)').
# index is index of the current item in the collection.
# flt_flag is the result of the filtering process for this item.
# Note: as index and flt_flag are optional arguments, you do not have to use/declare them here if you don't
# need them.
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
ob = data
# draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
if self.layout_type in {'DEFAULT', 'COMPACT'}:
# You should always start your row layout by a label (icon + text), or a non-embossed text field,
# this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
# We use icon_value of label, as our given icon is an integer value, not an enum ID.
# Note "data" names should never be translated!
#if ma:
# layout.prop(ma, "name", text="", emboss=False, icon_value=icon)
#else:
# layout.label(text="", translate=False, icon_value=icon)
layout.label(text=item.name, icon_value=icon)
#layout.prop(item, "name", text="", emboss=False, icon_value=icon)
# 'GRID' layout type should be as compact as possible (typically a single icon!).
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon)
class SCENES_LIST_OT_actions(Operator): class SCENES_LIST_OT_actions(Operator):
"""Move items up and down, add and remove""" """Move items up and down, add and remove"""
bl_idname = "scene_list.list_action" bl_idname = "scene_list.list_action"
@ -56,64 +23,40 @@ class SCENES_LIST_OT_actions(Operator):
) )
) # type: ignore ) # type: ignore
scene_name : bpy.props.StringProperty(name="scene_name")# type: ignore
def invoke(self, context, event): def invoke(self, context, event):
source = context.window_manager.blenvy if self.action == 'REMOVE':
target_name = "library_scenes" bpy.data.scenes[self.scene_name].blenvy_scene_type = 'None'
target_index = "library_scenes_index" """info = 'Item "%s" removed from list' % (target[idx].name)
if self.scene_type == "LEVEL":
target_name = "main_scenes"
target_index = "main_scenes_index"
target = getattr(source, target_name)
idx = getattr(source, target_index)
current_index = getattr(source, target_index)
try:
item = target[idx]
except IndexError:
pass
else:
if self.action == 'DOWN' and idx < len(target) - 1:
target.move(idx, idx + 1)
setattr(source, target_index, current_index +1 )
info = 'Item "%s" moved to position %d' % (item.name, current_index + 1)
self.report({'INFO'}, info)
elif self.action == 'UP' and idx >= 1:
target.move(idx, idx - 1)
setattr(source, target_index, current_index -1 )
info = 'Item "%s" moved to position %d' % (item.name, current_index + 1)
self.report({'INFO'}, info)
elif self.action == 'REMOVE':
info = 'Item "%s" removed from list' % (target[idx].name)
target.remove(idx) target.remove(idx)
setattr(source, target_index, current_index -1 ) setattr(source, target_index, current_index -1 )
self.report({'INFO'}, info) self.report({'INFO'}, info)"""
if self.action == 'ADD': if self.action == 'ADD':
new_scene_name = None scene_to_add = None
if self.scene_type == "LEVEL": if self.scene_type == "LEVEL":
if context.window_manager.blenvy.main_scene_selector: if context.window_manager.blenvy.main_scene_selector:
new_scene_name = context.window_manager.blenvy.main_scene_selector.name scene_to_add = context.window_manager.blenvy.main_scene_selector
scene_to_add.blenvy_scene_type = "Level"
else: else:
if context.window_manager.blenvy.library_scene_selector: if context.window_manager.blenvy.library_scene_selector:
new_scene_name = context.window_manager.blenvy.library_scene_selector.name scene_to_add = context.window_manager.blenvy.library_scene_selector
if new_scene_name: scene_to_add.blenvy_scene_type = "Library"
item = target.add()
item.name = new_scene_name if scene_to_add is not None:
print("adding scene", scene_to_add)
if self.scene_type == "LEVEL": if self.scene_type == "LEVEL":
context.window_manager.blenvy.main_scene_selector = None context.window_manager.blenvy.main_scene_selector = None
else: else:
context.window_manager.blenvy.library_scene_selector = None context.window_manager.blenvy.library_scene_selector = None
setattr(source, target_index, len(target) - 1) #setattr(source, target_index, len(target) - 1)
#info = '"%s" added to list' % (item.name)
#self.report({'INFO'}, info)
info = '"%s" added to list' % (item.name)
self.report({'INFO'}, info)
return {"FINISHED"} return {"FINISHED"}

View File

@ -109,44 +109,40 @@ class BLENVY_PT_SidePanel(bpy.types.Panel):
row = section.row() row = section.row()
row.label(text="main scenes") row.label(text="main scenes")
row.prop(blenvy, "main_scene_selector", text='') row.prop(blenvy, "main_scene_selector", text='')
add_operator = row.operator("scene_list.list_action", icon='ADD', text="")
row = section.row()
row.template_list("SCENE_UL_Blenvy", "level scenes", blenvy, "main_scenes", blenvy, "main_scenes_index", rows=rows)
col = row.column(align=True)
sub_row = col.row()
add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="")
add_operator.action = 'ADD' add_operator.action = 'ADD'
add_operator.scene_type = 'LEVEL' add_operator.scene_type = 'LEVEL'
#add_operator.operator = operator #sub_row.enabled = blenvy.main_scene_selector is not None
sub_row.enabled = blenvy.main_scene_selector is not None
sub_row = col.row() row = section.row()
remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="") col = row.column()
for scene in blenvy.main_scenes:
sub_row = col.box().row()
sub_row.label(text=scene.name)
remove_operator = sub_row.operator("scene_list.list_action", icon='TRASH', text="")
remove_operator.action = 'REMOVE' remove_operator.action = 'REMOVE'
remove_operator.scene_type = 'LEVEL' remove_operator.scene_type = 'LEVEL'
remove_operator.scene_name = scene.name
col.separator() col.separator()
# library scenes # library scenes
row = section.row() row = section.row()
row.label(text="library scenes") row.label(text="library scenes")
row.prop(blenvy, "library_scene_selector", text='') row.prop(blenvy, "library_scene_selector", text='')
add_operator = row.operator("scene_list.list_action", icon='ADD', text="")
row = section.row()
row.template_list("SCENE_UL_Blenvy", "library scenes", blenvy, "library_scenes", blenvy, "library_scenes_index", rows=rows)
col = row.column(align=True)
sub_row = col.row()
add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="")
add_operator.action = 'ADD' add_operator.action = 'ADD'
add_operator.scene_type = 'LIBRARY' add_operator.scene_type = 'LIBRARY'
sub_row.enabled = blenvy.library_scene_selector is not None
row = section.row()
sub_row = col.row() col = row.column()
remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="") for scene in blenvy.library_scenes:
sub_row = col.box().row()
sub_row.label(text=scene.name)
remove_operator = sub_row.operator("scene_list.list_action", icon='TRASH', text="")
remove_operator.action = 'REMOVE' remove_operator.action = 'REMOVE'
remove_operator.scene_type = 'LIBRARY' remove_operator.scene_type = 'LEVEL'
remove_operator.scene_name = scene.name
col.separator() col.separator()
header, panel = layout.panel("components", default_closed=False) header, panel = layout.panel("components", default_closed=False)

View File

@ -2,6 +2,7 @@ from types import SimpleNamespace
import bpy import bpy
from ..assets.asset_helpers import get_generated_assets, get_user_assets from ..assets.asset_helpers import get_generated_assets, get_user_assets
from ..assets.ui import draw_assets from ..assets.ui import draw_assets
from ..blueprints.ui import draw_blueprints
class Blenvy_levels(bpy.types.Panel): class Blenvy_levels(bpy.types.Panel):
bl_space_type = 'VIEW_3D' bl_space_type = 'VIEW_3D'
@ -18,15 +19,14 @@ class Blenvy_levels(bpy.types.Panel):
layout.use_property_split = True layout.use_property_split = True
layout.use_property_decorate = False # No animation. layout.use_property_decorate = False # No animation.
blenvy = context.window_manager.blenvy blenvy = context.window_manager.blenvy
layout.operator(operator="bevyassets.test") layout.operator(operator="bevyassets.test", text="Generate")
asset_registry = context.window_manager.assets_registry asset_registry = context.window_manager.assets_registry
blueprints_registry = context.window_manager.blueprints_registry blueprints_registry = context.window_manager.blueprints_registry
#blueprints_registry.refresh_blueprints() #blueprints_registry.refresh_blueprints()
blueprints_data = blueprints_registry.blueprints_data blueprints_data = blueprints_registry.blueprints_data
for scene_selector in blenvy.main_scenes: for scene in blenvy.main_scenes:
scene = bpy.data.scenes[scene_selector.name]
header, panel = layout.box().panel(f"level_assets{scene.name}", default_closed=False) header, panel = layout.box().panel(f"level_assets{scene.name}", default_closed=False)
if header: if header:
header.label(text=scene.name)#, icon="HIDE_OFF") header.label(text=scene.name)#, icon="HIDE_OFF")
@ -54,7 +54,7 @@ class Blenvy_levels(bpy.types.Panel):
col = split.column() col = split.column()
scene_assets_panel = draw_assets(layout=col, name=f"{scene.name}_assets", title=f"Assets", asset_registry=asset_registry, user_assets=user_assets, generated_assets=generated_assets, target_type="SCENE", target_name=scene.name) scene_assets_panel = draw_assets(layout=col, name=f"{scene.name}_assets", title=f"Assets", asset_registry=asset_registry, user_assets=user_assets, generated_assets=generated_assets, target_type="SCENE", target_name=scene.name)
scene_blueprints_panel = draw_blueprints(layout=col, name=f"{scene.name}_blueprints", title=f"Blueprints", generated_assets=generated_assets, )
settings = {"blueprints_path": "blueprints", "export_gltf_extension": ".glb"} settings = {"blueprints_path": "blueprints", "export_gltf_extension": ".glb"}
settings = SimpleNamespace(**settings) settings = SimpleNamespace(**settings)

View File

@ -4,32 +4,43 @@ from pathlib import Path
from ..core.helpers_collections import (traverse_tree) from ..core.helpers_collections import (traverse_tree)
# get materials per object, and injects the materialInfo component # get materials per object, and injects the materialInfo component
def get_materials(object): def get_materials(object, materials_per_object):
material_slots = object.material_slots material_slots = object.material_slots
used_materials_names = [] used_materials_names = []
#materials_per_object = {}
current_project_name = Path(bpy.context.blend_data.filepath).stem
for m in material_slots: for m in material_slots:
material = m.material material = m.material
# print(" slot", m, "material", material) # print(" slot", m, "material", material)
used_materials_names.append(material.name) used_materials_names.append(material.name)
# TODO:, also respect slots & export multiple materials if applicable ! # TODO:, also respect slots & export multiple materials if applicable !
# TODO: do NOT modify objects like this !! do it in a different function materials_per_object[object] = material
object['MaterialInfo'] = '(name: "'+material.name+'", source: "'+current_project_name + '")'
return used_materials_names return used_materials_names
def get_all_materials(collection_names, library_scenes): def get_all_materials(collection_names, library_scenes):
used_material_names = [] used_material_names = []
materials_per_object = {}
for scene in library_scenes: for scene in library_scenes:
root_collection = scene.collection root_collection = scene.collection
for cur_collection in traverse_tree(root_collection): for cur_collection in traverse_tree(root_collection):
if cur_collection.name in collection_names: if cur_collection.name in collection_names:
for object in cur_collection.all_objects: for object in cur_collection.all_objects:
used_material_names = used_material_names + get_materials(object) used_material_names = used_material_names + get_materials(object, materials_per_object)
# we only want unique names # we only want unique names
used_material_names = list(set(used_material_names)) used_material_names = list(set(used_material_names))
return used_material_names return (used_material_names, materials_per_object)
def add_material_info_to_objects(materials_per_object, settings):
materials_path = getattr(settings, "materials_path")
export_gltf_extension = getattr(settings.auto_export, "export_gltf_extension", ".glb")
current_project_name = Path(bpy.context.blend_data.filepath).stem
materials_library_name = f"{current_project_name}_materials"
materials_exported_path = os.path.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
for object in materials_per_object.keys():
material = materials_per_object[object]
# TODO: switch to using actual components ?
materials_exported_path = os.path.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
object['MaterialInfo'] = f'(name: "{material.name}", path: "{materials_exported_path}")'

View File

@ -9,7 +9,7 @@ import filecmp
from PIL import Image from PIL import Image
from pixelmatch.contrib.PIL import pixelmatch from pixelmatch.contrib.PIL import pixelmatch
from blenvy.auto_export.export.prepare_and_export import prepare_and_export from blenvy.add_ons.auto_export.common.prepare_and_export import prepare_and_export
@pytest.fixture @pytest.fixture
def setup_data(request): def setup_data(request):
@ -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
@ -73,14 +73,11 @@ def setup_data(request):
""" """
def test_export_complex(setup_data): def test_export_complex(setup_data):
root_path = setup_data["root_path"] root_path = setup_data["root_path"]
auto_export_operator = bpy.ops.export_scenes.auto_gltf
# with change detection # with change detection
# first, configure things # first, configure things
# we use the global settings for that # we use the global settings for that
export_props = { export_props = {
"main_scene_names" : ['World'],
"library_scene_names": ['Library'],
} }
gltf_settings = { gltf_settings = {
"export_animations": True, "export_animations": True,
@ -102,26 +99,35 @@ def test_export_complex(setup_data):
# move the cube in the library # move the cube in the library
# TODO: add back bpy.data.objects["Blueprint1_mesh"].location = [1, 2, 1] # TODO: add back bpy.data.objects["Blueprint1_mesh"].location = [1, 2, 1]
registry = bpy.context.window_manager.components_registry
blenvy = bpy.context.window_manager.blenvy blenvy = bpy.context.window_manager.blenvy
main_scene = blenvy.main_scenes.add() #blenvy.project_root_path =
main_scene.name = "World" #blenvy.blueprints_path
blenvy.auto_export.auto_export = True blenvy.auto_export.auto_export = True
blenvy.auto_export.export_scene_settings = True
blenvy.auto_export.export_blueprints = True
blenvy.auto_export.export_materials_library = True
bpy.data.scenes['World'].blenvy_scene_type = 'Level' # set scene as main/level scene
bpy.data.scenes['Library'].blenvy_scene_type = 'Library' # set scene as Library scene
# scene asset
user_asset = bpy.data.scenes['World'].user_assets.add()
user_asset.name = "test_asset"
user_asset.path = "audio/fake.mp3"
# blueprint asset
user_asset = bpy.data.collections['Blueprint4_nested'].user_assets.add()
user_asset.name = "yoho_audio"
user_asset.path = "audio/fake.mp3"
# we have to cheat, since we cannot rely on the data injected when saving the library file
bpy.data.collections["External_blueprint"]["export_path"] = "blueprints/External_blueprint.glb"
bpy.data.collections["External_blueprint2"]["export_path"] = "blueprints/External_blueprint2.glb"
bpy.data.collections["External_blueprint3"]["export_path"] = "blueprints/External_blueprint3.glb"
prepare_and_export() prepare_and_export()
"""auto_export_operator(
auto_export=True,
direct_mode=True,
project_root_path = os.path.abspath(root_path),
#blueprints_path = os.path.join("assets", "models", "library"),
export_output_folder = os.path.join("assets", "models"), #"./models",
#levels_path = os.path.join("assets", "models"),
export_scene_settings=True,
export_blueprints=True,
export_materials_library=True
)"""
# blueprint1 => has an instance, got changed, should export # blueprint1 => has an instance, got changed, should export
# blueprint2 => has NO instance, but marked as asset, should export # blueprint2 => has NO instance, but marked as asset, should export
# blueprint3 => has NO instance, not marked as asset, used inside blueprint 4: should export # blueprint3 => has NO instance, not marked as asset, used inside blueprint 4: should export

View File

@ -2,6 +2,7 @@ import os
import json import json
import pytest import pytest
import bpy import bpy
from blenvy.add_ons.auto_export.common.prepare_and_export import prepare_and_export
@pytest.fixture @pytest.fixture
def setup_data(request): def setup_data(request):
@ -25,14 +26,11 @@ def setup_data(request):
# this runs the external blueprints file # this runs the external blueprints file
def test_export_external_blueprints(setup_data): def test_export_external_blueprints(setup_data):
root_path = setup_data["root_path"] root_path = setup_data["root_path"]
auto_export_operator = bpy.ops.export_scenes.auto_gltf
# with change detection # with change detection
# first, configure things # first, configure things
# we use the global settings for that # we use the global settings for that
export_props = { export_props = {
"main_scene_names" : [],
"library_scene_names": ['Library'],
} }
gltf_settings = { gltf_settings = {
"export_animations": True, "export_animations": True,
@ -49,20 +47,20 @@ def test_export_external_blueprints(setup_data):
stored_gltf_settings.clear() stored_gltf_settings.clear()
stored_gltf_settings.write(json.dumps(gltf_settings)) stored_gltf_settings.write(json.dumps(gltf_settings))
blenvy = bpy.context.window_manager.blenvy
#blenvy.project_root_path =
#blenvy.blueprints_path
blenvy.auto_export.auto_export = True
blenvy.auto_export.export_scene_settings = True
blenvy.auto_export.export_blueprints = True
blenvy.auto_export.export_materials_library = True
auto_export_operator( print("SCENES", bpy.data.scenes)
auto_export=True, for scene in bpy.data.scenes:
direct_mode=True, print("SCNE", scene)
project_root_path = os.path.abspath(root_path), bpy.data.scenes['Library'].blenvy_scene_type = 'Library' # set scene as Library scene
#blueprints_path = os.path.join("assets", "models", "library"), # do the actual export
#export_output_folder = os.path.join("assets", "models"), #"./models", prepare_and_export()
#levels_path = os.path.join("assets", "models"),
export_scene_settings=False,
export_blueprints=True,
export_materials_library=True,
export_marked_assets= True
)
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "External_blueprint.glb")) == True assert os.path.exists(os.path.join(setup_data["blueprints_path"], "External_blueprint.glb")) == True
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "External_blueprint2.glb")) == True assert os.path.exists(os.path.join(setup_data["blueprints_path"], "External_blueprint2.glb")) == True

View File

@ -223,7 +223,6 @@ def test_export_do_not_export_marked_assets(setup_data):
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
export_marked_assets = False
) )
assert os.path.exists(os.path.join(setup_data["levels_path"], "World.glb")) == True assert os.path.exists(os.path.join(setup_data["levels_path"], "World.glb")) == True
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint1.glb")) == True assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint1.glb")) == True