Compare commits
3 Commits
6ffd39803a
...
2cdac0dddf
Author | SHA1 | Date |
---|---|---|
Jan Hohenheim | 2cdac0dddf | |
Mark Moissette | 9f21df035b | |
Mark Moissette | 1353e14802 |
|
@ -1,8 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bevy_gltf_blueprints"
|
name = "bevy_gltf_blueprints"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
authors = ["Mark 'kaosat-dev' Moissette"]
|
authors = ["Mark 'kaosat-dev' Moissette"]
|
||||||
description = "Adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy."
|
description = "Adds the ability to define Blueprints/Prefabs for Bevy inside gltf files and spawn them in Bevy."
|
||||||
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||||
repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||||
keywords = ["gamedev", "bevy", "gltf", "blueprint", "prefab"]
|
keywords = ["gamedev", "bevy", "gltf", "blueprint", "prefab"]
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
# bevy_gltf_blueprints
|
# bevy_gltf_blueprints
|
||||||
|
|
||||||
Built upon [bevy_gltf_components](https://crates.io/crates/bevy_gltf_components) this crate adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy.
|
Built on [bevy_gltf_components](https://crates.io/crates/bevy_gltf_components) this crate adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy.
|
||||||
|
|
||||||
* Allows you to create lightweight levels, where all assets are different gltf files and loaded after the main level is loaded
|
* Allows you to create lightweight levels, where all assets are different gltf files and loaded after the main level is loaded
|
||||||
* Allows you to spawn different entities from gtlf files at runtime in a clean manner, including simplified animation support !
|
* Allows you to spawn different entities from gtlf files at runtime in a clean manner, including simplified animation support !
|
||||||
|
@ -15,7 +15,9 @@ A blueprint is a set of **overrideable** components + a hierarchy: ie
|
||||||
* just a Gltf file with Gltf_extras specifying components
|
* just a Gltf file with Gltf_extras specifying components
|
||||||
* a component called BlueprintName
|
* a component called BlueprintName
|
||||||
|
|
||||||
Particularly useful when using [Blender](https://www.blender.org/) as an editor for the [Bevy](https://bevyengine.org/) game engine, combined with the [Blender plugin](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/gltf_auto_export) that does a lot of the work for you
|
Particularly useful when using [Blender](https://www.blender.org/) as an editor for the [Bevy](https://bevyengine.org/) game engine, combined with the Blender add-ons that do a lot of the work for you
|
||||||
|
- [gltf_auto_export](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/gltf_auto_export)
|
||||||
|
- [bevy_components](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/bevy_components)
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -26,7 +28,7 @@ Here's a minimal usage example:
|
||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy="0.13"
|
bevy="0.13"
|
||||||
bevy_gltf_blueprints = { version = "0.9"}
|
bevy_gltf_blueprints = { version = "0.10"}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -64,7 +66,7 @@ fn spawn_blueprint(
|
||||||
Add the following to your `[dependencies]` section in `Cargo.toml`:
|
Add the following to your `[dependencies]` section in `Cargo.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
bevy_gltf_blueprints = "0.9"
|
bevy_gltf_blueprints = "0.10"
|
||||||
```
|
```
|
||||||
|
|
||||||
Or use `cargo add`:
|
Or use `cargo add`:
|
||||||
|
@ -165,13 +167,10 @@ commands.spawn((
|
||||||
|
|
||||||
### BluePrintBundle
|
### BluePrintBundle
|
||||||
|
|
||||||
There is also a bundle for convenience , which just has
|
There is also a ```BluePrintBundle``` for convenience , which just has
|
||||||
* a ```BlueprintName``` component
|
* a ```BlueprintName``` component
|
||||||
* a ```SpawnHere``` component
|
* a ```SpawnHere``` component
|
||||||
|
|
||||||
[```BluePrintBundle```](./src/lib.rs#22)
|
|
||||||
|
|
||||||
|
|
||||||
## Additional information
|
## Additional information
|
||||||
|
|
||||||
- When a blueprint is spawned, all its children entities (and nested children etc) also have an ```InBlueprint``` component that gets insert
|
- When a blueprint is spawned, all its children entities (and nested children etc) also have an ```InBlueprint``` component that gets insert
|
||||||
|
@ -219,7 +218,7 @@ the ordering of systems is very important !
|
||||||
|
|
||||||
For example to replace your proxy components (stand-in components when you cannot/ do not want to use real components in the gltf file) with actual ones, which should happen **AFTER** the Blueprint based spawning,
|
For example to replace your proxy components (stand-in components when you cannot/ do not want to use real components in the gltf file) with actual ones, which should happen **AFTER** the Blueprint based spawning,
|
||||||
|
|
||||||
so ```bevy_gltf_blueprints``` provides a **SystemSet** for that purpose:[```GltfBlueprintsSet```](./src/lib.rs#16)
|
so ```bevy_gltf_blueprints``` provides a **SystemSet** for that purpose: ```GltfBlueprintsSet```
|
||||||
|
|
||||||
Typically , the order of systems should be
|
Typically , the order of systems should be
|
||||||
|
|
||||||
|
@ -281,8 +280,7 @@ pub fn animation_change_on_proximity_foxes(
|
||||||
|
|
||||||
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation for how to set it up correctly
|
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation for how to set it up correctly
|
||||||
|
|
||||||
particularly from https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation/game/in_game.rs#86
|
particularly from https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation/game/in_game.rs
|
||||||
onward
|
|
||||||
|
|
||||||
|
|
||||||
## Materials
|
## Materials
|
||||||
|
@ -346,7 +344,7 @@ The main branch is compatible with the latest Bevy release, while the branch `be
|
||||||
Compatibility of `bevy_gltf_blueprints` versions:
|
Compatibility of `bevy_gltf_blueprints` versions:
|
||||||
| `bevy_gltf_blueprints` | `bevy` |
|
| `bevy_gltf_blueprints` | `bevy` |
|
||||||
| :-- | :-- |
|
| :-- | :-- |
|
||||||
| `0.9` | `0.13` |
|
| `0.9 - 0.10` | `0.13` |
|
||||||
| `0.3 - 0.8` | `0.12` |
|
| `0.3 - 0.8` | `0.12` |
|
||||||
| `0.1 - 0.2` | `0.11` |
|
| `0.1 - 0.2` | `0.11` |
|
||||||
| branch `main` | `0.13` |
|
| branch `main` | `0.13` |
|
||||||
|
|
|
@ -124,6 +124,9 @@ impl Plugin for BlueprintsPlugin {
|
||||||
.register_type::<MaterialInfo>()
|
.register_type::<MaterialInfo>()
|
||||||
.register_type::<SpawnHere>()
|
.register_type::<SpawnHere>()
|
||||||
.register_type::<Animations>()
|
.register_type::<Animations>()
|
||||||
|
.register_type::<BlueprintsList>()
|
||||||
|
.register_type::<Vec<String>>()
|
||||||
|
.register_type::<HashMap<String, Vec<String>>>()
|
||||||
.insert_resource(BluePrintsConfig {
|
.insert_resource(BluePrintsConfig {
|
||||||
format: self.format,
|
format: self.format,
|
||||||
library_folder: self.library_folder.clone(),
|
library_folder: self.library_folder.clone(),
|
||||||
|
@ -144,11 +147,24 @@ impl Plugin for BlueprintsPlugin {
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
spawn_from_blueprints,
|
(
|
||||||
compute_scene_aabbs.run_if(aabbs_enabled),
|
prepare_blueprints,
|
||||||
apply_deferred.run_if(aabbs_enabled),
|
check_for_loaded,
|
||||||
|
spawn_from_blueprints,
|
||||||
|
apply_deferred,
|
||||||
|
)
|
||||||
|
.chain(),
|
||||||
|
(compute_scene_aabbs, apply_deferred)
|
||||||
|
.chain()
|
||||||
|
.run_if(aabbs_enabled),
|
||||||
apply_deferred,
|
apply_deferred,
|
||||||
materials_inject.run_if(materials_library_enabled),
|
(
|
||||||
|
materials_inject,
|
||||||
|
check_for_material_loaded,
|
||||||
|
materials_inject2,
|
||||||
|
)
|
||||||
|
.chain()
|
||||||
|
.run_if(materials_library_enabled),
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(GltfBlueprintsSet::Spawn),
|
.in_set(GltfBlueprintsSet::Spawn),
|
||||||
|
|
|
@ -4,6 +4,7 @@ use bevy::{
|
||||||
asset::{AssetServer, Assets, Handle},
|
asset::{AssetServer, Assets, Handle},
|
||||||
ecs::{
|
ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
|
entity::Entity,
|
||||||
query::{Added, With},
|
query::{Added, With},
|
||||||
reflect::ReflectComponent,
|
reflect::ReflectComponent,
|
||||||
system::{Commands, Query, Res, ResMut},
|
system::{Commands, Query, Res, ResMut},
|
||||||
|
@ -16,7 +17,7 @@ use bevy::{
|
||||||
render::mesh::Mesh,
|
render::mesh::Mesh,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::BluePrintsConfig;
|
use crate::{AssetLoadTracker, AssetsToLoad, BluePrintsConfig};
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
|
@ -26,10 +27,111 @@ pub struct MaterialInfo {
|
||||||
pub source: String,
|
pub source: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// flag component
|
||||||
|
#[derive(Component)]
|
||||||
|
pub(crate) struct BlueprintMaterialAssetsLoaded;
|
||||||
|
/// flag component
|
||||||
|
#[derive(Component)]
|
||||||
|
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>,
|
||||||
|
material_infos: Query<(Entity, &MaterialInfo), Added<MaterialInfo>>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
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
|
||||||
|
.material_library_cache
|
||||||
|
.contains_key(&material_full_path)
|
||||||
|
{
|
||||||
|
debug!("material is cached, retrieving");
|
||||||
|
blueprints_config
|
||||||
|
.material_library_cache
|
||||||
|
.get(&material_full_path)
|
||||||
|
.expect("we should have the material available");
|
||||||
|
commands
|
||||||
|
.entity(entity)
|
||||||
|
.insert(BlueprintMaterialAssetsLoaded);
|
||||||
|
} else {
|
||||||
|
let material_file_handle: Handle<Gltf> = asset_server.load(materials_path.clone());
|
||||||
|
let material_file_id = material_file_handle.id();
|
||||||
|
let asset_infos: Vec<AssetLoadTracker<Gltf>> = vec![AssetLoadTracker {
|
||||||
|
name: material_full_path,
|
||||||
|
id: material_file_id,
|
||||||
|
loaded: false,
|
||||||
|
handle: material_file_handle.clone(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
commands
|
||||||
|
.entity(entity)
|
||||||
|
.insert(AssetsToLoad {
|
||||||
|
all_loaded: false,
|
||||||
|
asset_infos,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(BlueprintMaterialAssetsNotLoaded);
|
||||||
|
/**/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO, merge with check_for_loaded, make generic ?
|
||||||
|
pub(crate) fn check_for_material_loaded(
|
||||||
|
mut blueprint_assets_to_load: Query<
|
||||||
|
(Entity, &mut AssetsToLoad<Gltf>),
|
||||||
|
With<BlueprintMaterialAssetsNotLoaded>,
|
||||||
|
>,
|
||||||
|
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;
|
||||||
|
assets_to_load.progress = progress;
|
||||||
|
|
||||||
|
if all_loaded {
|
||||||
|
assets_to_load.all_loaded = true;
|
||||||
|
commands
|
||||||
|
.entity(entity)
|
||||||
|
.insert(BlueprintMaterialAssetsLoaded)
|
||||||
|
.remove::<BlueprintMaterialAssetsNotLoaded>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// system that injects / replaces materials from material library
|
||||||
|
pub(crate) fn materials_inject2(
|
||||||
mut blueprints_config: ResMut<BluePrintsConfig>,
|
mut blueprints_config: ResMut<BluePrintsConfig>,
|
||||||
material_infos: Query<(&MaterialInfo, &Children), Added<MaterialInfo>>,
|
material_infos: Query<
|
||||||
|
(&MaterialInfo, &Children),
|
||||||
|
(
|
||||||
|
Added<BlueprintMaterialAssetsLoaded>,
|
||||||
|
With<BlueprintMaterialAssetsLoaded>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
with_materials_and_meshes: Query<
|
with_materials_and_meshes: Query<
|
||||||
(),
|
(),
|
||||||
(
|
(
|
||||||
|
@ -38,9 +140,9 @@ pub(crate) fn materials_inject(
|
||||||
With<Handle<Mesh>>,
|
With<Handle<Mesh>>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
models: Res<Assets<bevy::gltf::Gltf>>,
|
assets_gltf: Res<Assets<Gltf>>,
|
||||||
|
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
|
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (material_info, children) in material_infos.iter() {
|
for (material_info, children) in material_infos.iter() {
|
||||||
|
@ -66,9 +168,9 @@ pub(crate) fn materials_inject(
|
||||||
.expect("we should have the material available");
|
.expect("we should have the material available");
|
||||||
material_found = Some(material);
|
material_found = Some(material);
|
||||||
} else {
|
} else {
|
||||||
let my_gltf: Handle<Gltf> = asset_server.load(materials_path.clone());
|
let model_handle: Handle<Gltf> = asset_server.load(materials_path.clone()); // FIXME: kinda weird now
|
||||||
let mat_gltf = models
|
let mat_gltf = assets_gltf
|
||||||
.get(my_gltf.id())
|
.get(model_handle.id())
|
||||||
.expect("material should have been preloaded");
|
.expect("material should have been preloaded");
|
||||||
if mat_gltf.named_materials.contains_key(material_name) {
|
if mat_gltf.named_materials.contains_key(material_name) {
|
||||||
let material = mat_gltf
|
let material = mat_gltf
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use bevy::{gltf::Gltf, prelude::*};
|
use bevy::{gltf::Gltf, prelude::*, utils::HashMap};
|
||||||
|
|
||||||
use crate::{Animations, BluePrintsConfig};
|
use crate::{Animations, BluePrintsConfig};
|
||||||
|
|
||||||
|
@ -46,8 +46,152 @@ 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>);
|
||||||
|
|
||||||
/// main spawning functions,
|
/// 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
|
||||||
|
#[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
|
/// * 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(
|
pub(crate) fn spawn_from_blueprints(
|
||||||
spawn_placeholders: Query<
|
spawn_placeholders: Query<
|
||||||
(
|
(
|
||||||
|
@ -59,7 +203,11 @@ pub(crate) fn spawn_from_blueprints(
|
||||||
Option<&AddToGameWorld>,
|
Option<&AddToGameWorld>,
|
||||||
Option<&Name>,
|
Option<&Name>,
|
||||||
),
|
),
|
||||||
(Added<BlueprintName>, Added<SpawnHere>, Without<Spawned>),
|
(
|
||||||
|
With<BlueprintAssetsLoaded>,
|
||||||
|
Added<BlueprintAssetsLoaded>,
|
||||||
|
Without<BlueprintAssetsNotLoaded>,
|
||||||
|
),
|
||||||
>,
|
>,
|
||||||
|
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
@ -82,17 +230,10 @@ pub(crate) fn spawn_from_blueprints(
|
||||||
) in spawn_placeholders.iter()
|
) in spawn_placeholders.iter()
|
||||||
{
|
{
|
||||||
debug!(
|
debug!(
|
||||||
"need to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
|
"attempting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
|
||||||
blupeprint_name.0, name, entity, original_parent
|
blupeprint_name.0, name, entity, original_parent
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut original_children: Vec<Entity> = vec![];
|
|
||||||
if let Ok(c) = children.get(entity) {
|
|
||||||
for child in c.iter() {
|
|
||||||
original_children.push(*child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
@ -101,12 +242,15 @@ pub(crate) fn spawn_from_blueprints(
|
||||||
library_override.map_or_else(|| &blueprints_config.library_folder, |l| &l.0);
|
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()));
|
let model_path = Path::new(&library_path).join(Path::new(model_file_name.as_str()));
|
||||||
|
|
||||||
debug!("attempting to spawn {:?}", model_path);
|
// info!("attempting to spawn {:?}", model_path);
|
||||||
let model_handle: Handle<Gltf> = asset_server.load(model_path);
|
let model_handle: Handle<Gltf> = asset_server.load(model_path.clone()); // FIXME: kinda weird now
|
||||||
|
|
||||||
let gltf = assets_gltf
|
let gltf = assets_gltf.get(&model_handle).unwrap_or_else(|| {
|
||||||
.get(&model_handle)
|
panic!(
|
||||||
.expect("this gltf should have been loaded");
|
"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
|
// 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
|
let main_scene_name = gltf
|
||||||
|
@ -123,6 +267,12 @@ pub(crate) fn spawn_from_blueprints(
|
||||||
transforms = *transform.unwrap();
|
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((
|
commands.entity(entity).insert((
|
||||||
SceneBundle {
|
SceneBundle {
|
||||||
scene: scene.clone(),
|
scene: scene.clone(),
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
|
||||||
|
use bevy::gltf::Gltf;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::scene::SceneInstance;
|
use bevy::scene::SceneInstance;
|
||||||
// use bevy::utils::hashbrown::HashSet;
|
// use bevy::utils::hashbrown::HashSet;
|
||||||
|
|
||||||
use super::{AnimationPlayerLink, Animations};
|
use super::{AnimationPlayerLink, Animations};
|
||||||
use super::{SpawnHere, Spawned};
|
use super::{SpawnHere, Spawned};
|
||||||
use crate::{CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren};
|
use crate::{
|
||||||
|
AssetsToLoad, BlueprintAssetsLoaded, CopyComponents, InBlueprint, NoInBlueprint,
|
||||||
|
OriginalChildren,
|
||||||
|
};
|
||||||
|
|
||||||
/// this system is in charge of doing any necessary post processing after a blueprint scene has been spawned
|
/// this system is in charge of doing any necessary post processing after a blueprint scene has been spawned
|
||||||
/// - it removes one level of useless nesting
|
/// - it removes one level of useless nesting
|
||||||
|
@ -89,6 +93,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::<BlueprintAssetsLoaded>();
|
||||||
commands.entity(root_entity).despawn_recursive();
|
commands.entity(root_entity).despawn_recursive();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bevy_gltf_components"
|
name = "bevy_gltf_components"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
authors = ["Mark 'kaosat-dev' Moissette"]
|
authors = ["Mark 'kaosat-dev' Moissette"]
|
||||||
description = "Allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side."
|
description = "Allows you to define Bevy components direclty inside gltf files and instanciate the components on the Bevy side."
|
||||||
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||||
repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||||
keywords = ["gamedev", "bevy", "assets", "gltf", "components"]
|
keywords = ["gamedev", "bevy", "assets", "gltf", "components"]
|
||||||
|
|
|
@ -13,9 +13,9 @@ This crate allows you to define [Bevy](https://bevyengine.org/) components direc
|
||||||
***important*** : the plugin for processing gltf files runs in ***update*** , so you cannot use the components directly if you spawn your scene from gltf in ***setup*** (the additional components will not show up)
|
***important*** : the plugin for processing gltf files runs in ***update*** , so you cannot use the components directly if you spawn your scene from gltf in ***setup*** (the additional components will not show up)
|
||||||
|
|
||||||
Please see the
|
Please see the
|
||||||
* [example](https://github.com/kaosat-dev/Blender_bevy_components_workflow/examples/basic)
|
* [example](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_components/basic)
|
||||||
* or use [```bevy_asset_loader```](https://github.com/NiklasEi/bevy_asset_loader) for a reliable workflow.
|
* or use [```bevy_asset_loader```](https://github.com/NiklasEi/bevy_asset_loader) for reliable preloading of files, as this crate does not deal with loading your assets.
|
||||||
* alternatively, use the [```bevy_gltf_blueprints```](https://github.com/kaosat-dev/Blender_bevy_components_workflow/blob/main/crates/bevy_gltf_blueprints) crate, build on this crate's features,
|
* alternatively, use the [```bevy_gltf_blueprints```](https://crates.io/crates/bevy_gltf_blueprints) crate, built on this crate's features,
|
||||||
that allows you to directly spawn entities from gltf based blueprints.
|
that allows you to directly spawn entities from gltf based blueprints.
|
||||||
|
|
||||||
Here's a minimal usage example:
|
Here's a minimal usage example:
|
||||||
|
@ -29,7 +29,7 @@ bevy_gltf_components = { version = "0.5"}
|
||||||
```
|
```
|
||||||
|
|
||||||
```rust no_run
|
```rust no_run
|
||||||
//too barebones of an example to be meaningfull, please see https://github.com/kaosat-dev/Blender_bevy_components_workflow/examples/basic for a real example
|
//too barebones of an example to be meaningfull, please see https://github.com/kaosat-dev/Blender_bevy_components_workflow/bevy_gltf_components/examples/basic for a real example
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
|
@ -84,7 +84,7 @@ Or disable the legacy mode: (enabled by default)
|
||||||
ComponentsFromGltfPlugin{legacy_mode: false}
|
ComponentsFromGltfPlugin{legacy_mode: false}
|
||||||
```
|
```
|
||||||
|
|
||||||
You **need** to disable legacy mode if you want to use the [```bevy_components```](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/tools_bevy_blueprints/tools/bevy_components) Blender addon + the [```bevy_registry_export crate```](https://crates.io/crates/bevy_registry_export) !
|
You **need** to disable legacy mode if you want to use the [```bevy_components```](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/bevy_components) Blender addon + the [```bevy_registry_export crate```](https://crates.io/crates/bevy_registry_export) !
|
||||||
As it create custom properties that are writen in real **ron** file format
|
As it create custom properties that are writen in real **ron** file format
|
||||||
instead of a simplified version (the one in the legacy mode)
|
instead of a simplified version (the one in the legacy mode)
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ For example to replace your proxy components (stand-in components when you canno
|
||||||
|
|
||||||
which should happen **AFTER** the components from the gltf files have been injected,
|
which should happen **AFTER** the components from the gltf files have been injected,
|
||||||
|
|
||||||
so ```bevy_gltf_components``` provides a **SystemSet** for that purpose:[```GltfComponentsSet```](./src/lib.rs#46)
|
so ```bevy_gltf_components``` provides a **SystemSet** for that purpose:```GltfComponentsSet```
|
||||||
|
|
||||||
Typically , the order of systems should be
|
Typically , the order of systems should be
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ Typically , the order of systems should be
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/basic
|
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_components/basic
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bevy_gltf_save_load"
|
name = "bevy_gltf_save_load"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
authors = ["Mark 'kaosat-dev' Moissette"]
|
authors = ["Mark 'kaosat-dev' Moissette"]
|
||||||
description = "Save & load your bevy games"
|
description = "Save & load your bevy games"
|
||||||
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||||
|
@ -15,8 +15,7 @@ workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = { version = "0.13", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
|
bevy = { version = "0.13", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
|
||||||
#bevy_gltf_blueprints = "0.9"
|
bevy_gltf_blueprints = { version = "0.10", path = "../bevy_gltf_blueprints" }
|
||||||
bevy_gltf_blueprints = { version = "0.9", path = "../bevy_gltf_blueprints" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bevy = { version = "0.13", default-features = false, features = ["dynamic_linking"] }
|
bevy = { version = "0.13", default-features = false, features = ["dynamic_linking"] }
|
||||||
|
|
|
@ -36,7 +36,7 @@ Here's a minimal usage example:
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy="0.13"
|
bevy="0.13"
|
||||||
bevy_gltf_save_load = "0.4"
|
bevy_gltf_save_load = "0.4"
|
||||||
bevy_gltf_blueprints = "0.9" // also needed
|
bevy_gltf_blueprints = "0.10" // also needed
|
||||||
```
|
```
|
||||||
|
|
||||||
```rust no_run
|
```rust no_run
|
||||||
|
@ -133,7 +133,7 @@ pub fn setup_game(
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
take a look at the [example]('https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs) for more clarity
|
take a look at the [example](https://github.com/kaosat-dev/Blender_bevy_components_workflow/blob/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs) for more clarity
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -142,12 +142,7 @@ Add the following to your `[dependencies]` section in `Cargo.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
bevy_gltf_save_load = "0.3"
|
bevy_gltf_save_load = "0.3"
|
||||||
<<<<<<< HEAD
|
bevy_gltf_blueprints = "0.10" // also needed, as bevy_gltf_save_load does not re-export it at this time
|
||||||
bevy_gltf_blueprints = "0.8" // also needed, as bevy_gltf_save_load does not re-export it at this time
|
|
||||||
=======
|
|
||||||
bevy_gltf_blueprints = "0.6" // also needed, as bevy_gltf_save_load does not re-export it at this time
|
|
||||||
>>>>>>> 9cb9dda5d35c635d367fa81ca1a6c752cda9bc02
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Or use `cargo add`:
|
Or use `cargo add`:
|
||||||
|
@ -268,7 +263,7 @@ pub fn request_load(
|
||||||
- ```LoadingFinished``` for loading
|
- ```LoadingFinished``` for loading
|
||||||
|
|
||||||
> Note: I **highly** recomend you change states when you start/finish saving & loading, otherwise things **will** get unpredictable
|
> Note: I **highly** recomend you change states when you start/finish saving & loading, otherwise things **will** get unpredictable
|
||||||
Please see [the example]('https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs#77') for this.
|
Please see [the example](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs) for this.
|
||||||
|
|
||||||
## Additional notes
|
## Additional notes
|
||||||
|
|
||||||
|
@ -287,8 +282,8 @@ For convenience ```bevy_gltf_save_load``` provides two **SystemSets**
|
||||||
Highly advised to get a better understanding of how things work !
|
Highly advised to get a better understanding of how things work !
|
||||||
To get started I recomend looking at
|
To get started I recomend looking at
|
||||||
|
|
||||||
- [world setup]('https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/in_game.rs#13')
|
- [world setup](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/in_game.rs)
|
||||||
- [various events & co]('https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs#77')
|
- [various events & co](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs)
|
||||||
|
|
||||||
|
|
||||||
All examples are here:
|
All examples are here:
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bevy_registry_export"
|
name = "bevy_registry_export"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
authors = ["Mark 'kaosat-dev' Moissette", "Pascal 'Killercup' Hertleif"]
|
authors = ["Mark 'kaosat-dev' Moissette", "Pascal 'Killercup' Hertleif"]
|
||||||
description = "Allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side."
|
description = "Allows you to create a Json export of all your components/ registered types of your Bevy app/game"
|
||||||
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||||
repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||||
keywords = ["gamedev", "bevy", "assets", "gltf", "components"]
|
keywords = ["gamedev", "bevy", "assets", "registry", "components"]
|
||||||
categories = ["game-development"]
|
categories = ["game-development"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
|
@ -36,7 +36,7 @@ fn main() {
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
take a look at the [example]('https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_registry_export/basic/src/core/mod.rs) for more clarity
|
take a look at the [example](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_registry_export/basic/src/core/mod.rs) for more clarity
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub fn setup_game(
|
||||||
SceneBundle {
|
SceneBundle {
|
||||||
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
||||||
scene: models
|
scene: models
|
||||||
.get(game_assets.world.id())
|
.get(game_assets.world.clone().unwrap().id())
|
||||||
.expect("main level should have been loaded")
|
.expect("main level should have been loaded")
|
||||||
.scenes[0]
|
.scenes[0]
|
||||||
.clone(),
|
.clone(),
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub fn setup_game(
|
||||||
SceneBundle {
|
SceneBundle {
|
||||||
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
||||||
scene: models
|
scene: models
|
||||||
.get(game_assets.world.id())
|
.get(game_assets.world.clone().unwrap().id())
|
||||||
.expect("main level should have been loaded")
|
.expect("main level should have been loaded")
|
||||||
.scenes[0]
|
.scenes[0]
|
||||||
.clone(),
|
.clone(),
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub fn setup_game(
|
||||||
SceneBundle {
|
SceneBundle {
|
||||||
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
||||||
scene: models
|
scene: models
|
||||||
.get(game_assets.world.id())
|
.get(game_assets.world.clone().unwrap().id())
|
||||||
.expect("main level should have been loaded")
|
.expect("main level should have been loaded")
|
||||||
.scenes[0]
|
.scenes[0]
|
||||||
.clone(),
|
.clone(),
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub fn setup_game(
|
||||||
SceneBundle {
|
SceneBundle {
|
||||||
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
||||||
scene: models
|
scene: models
|
||||||
.get(game_assets.world.id())
|
.get(game_assets.world.clone().unwrap().id())
|
||||||
.expect("main level should have been loaded")
|
.expect("main level should have been loaded")
|
||||||
.scenes[0]
|
.scenes[0]
|
||||||
.clone(),
|
.clone(),
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub fn setup_game(
|
||||||
SceneBundle {
|
SceneBundle {
|
||||||
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
||||||
scene: models
|
scene: models
|
||||||
.get(game_assets.world.id())
|
.get(game_assets.world.clone().unwrap().id())
|
||||||
.expect("main level should have been loaded")
|
.expect("main level should have been loaded")
|
||||||
.scenes[0]
|
.scenes[0]
|
||||||
.clone(),
|
.clone(),
|
||||||
|
|
|
@ -76,7 +76,7 @@ pub fn trigger_level_transition(
|
||||||
} else if target_level == "Level2" {
|
} else if target_level == "Level2" {
|
||||||
level = game_assets.level2.clone().unwrap();
|
level = game_assets.level2.clone().unwrap();
|
||||||
} else {
|
} else {
|
||||||
level = game_assets.world.clone();
|
level = game_assets.world.clone().unwrap();
|
||||||
}
|
}
|
||||||
info!("spawning new level");
|
info!("spawning new level");
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub fn setup_game(
|
||||||
SceneBundle {
|
SceneBundle {
|
||||||
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
||||||
scene: models
|
scene: models
|
||||||
.get(game_assets.world.id())
|
.get(game_assets.world.clone().unwrap().id())
|
||||||
.expect("main level should have been loaded")
|
.expect("main level should have been loaded")
|
||||||
.scenes[0]
|
.scenes[0]
|
||||||
.clone(),
|
.clone(),
|
||||||
|
|
|
@ -5,8 +5,8 @@ use bevy_asset_loader::prelude::*;
|
||||||
|
|
||||||
#[derive(AssetCollection, Resource)]
|
#[derive(AssetCollection, Resource)]
|
||||||
pub struct GameAssets {
|
pub struct GameAssets {
|
||||||
#[asset(key = "world")]
|
#[asset(key = "world", optional)]
|
||||||
pub world: Handle<Gltf>,
|
pub world: Option<Handle<Gltf>>,
|
||||||
|
|
||||||
#[asset(key = "world_dynamic", optional)]
|
#[asset(key = "world_dynamic", optional)]
|
||||||
pub world_dynamic: Option<Handle<Gltf>>,
|
pub world_dynamic: Option<Handle<Gltf>>,
|
||||||
|
@ -16,8 +16,8 @@ pub struct GameAssets {
|
||||||
#[asset(key = "level2", optional)]
|
#[asset(key = "level2", optional)]
|
||||||
pub level2: Option<Handle<Gltf>>,
|
pub level2: Option<Handle<Gltf>>,
|
||||||
|
|
||||||
#[asset(key = "models", collection(typed, mapped))]
|
#[asset(key = "models", collection(typed, mapped), optional)]
|
||||||
pub models: HashMap<String, Handle<Gltf>>,
|
pub models: Option<HashMap<String, Handle<Gltf>>>,
|
||||||
|
|
||||||
#[asset(key = "materials", collection(typed, mapped), optional)]
|
#[asset(key = "materials", collection(typed, mapped), optional)]
|
||||||
pub materials: Option<HashMap<String, Handle<Gltf>>>,
|
pub materials: Option<HashMap<String, Handle<Gltf>>>,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
({
|
({
|
||||||
"world":File (path: "models/World.glb"),
|
/*"world":File (path: "models/World.glb"),
|
||||||
"models": Folder (
|
"models": Folder (
|
||||||
path: "models/library",
|
path: "models/library",
|
||||||
),
|
),*/
|
||||||
})
|
})
|
|
@ -3576,6 +3576,22 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"typeInfo": "TupleStruct"
|
"typeInfo": "TupleStruct"
|
||||||
},
|
},
|
||||||
|
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintsList": {
|
||||||
|
"isComponent": true,
|
||||||
|
"isResource": false,
|
||||||
|
"items": false,
|
||||||
|
"prefixItems": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<alloc::string::String, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"short_name": "BlueprintsList",
|
||||||
|
"title": "bevy_gltf_blueprints::spawn_from_blueprints::BlueprintsList",
|
||||||
|
"type": "array",
|
||||||
|
"typeInfo": "TupleStruct"
|
||||||
|
},
|
||||||
"bevy_gltf_blueprints::spawn_from_blueprints::SpawnHere": {
|
"bevy_gltf_blueprints::spawn_from_blueprints::SpawnHere": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"isComponent": true,
|
"isComponent": true,
|
||||||
|
@ -10834,6 +10850,19 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Value"
|
"typeInfo": "Value"
|
||||||
},
|
},
|
||||||
|
"bevy_utils::hashbrown::HashMap<alloc::string::String, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/alloc::vec::Vec<alloc::string::String>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isComponent": false,
|
||||||
|
"isResource": false,
|
||||||
|
"short_name": "HashMap<String, Vec<String>, DefaultHashBuilder>",
|
||||||
|
"title": "bevy_utils::hashbrown::HashMap<alloc::string::String, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>",
|
||||||
|
"type": "object",
|
||||||
|
"typeInfo": "Map"
|
||||||
|
},
|
||||||
"bevy_utils::smallvec::SmallVec<[bevy_ecs::entity::Entity; 8]>": {
|
"bevy_utils::smallvec::SmallVec<[bevy_ecs::entity::Entity; 8]>": {
|
||||||
"isComponent": false,
|
"isComponent": false,
|
||||||
"isResource": false,
|
"isResource": false,
|
||||||
|
|
|
@ -11,6 +11,7 @@ impl Plugin for CorePlugin {
|
||||||
legacy_mode: false,
|
legacy_mode: false,
|
||||||
library_folder: "models/library".into(),
|
library_folder: "models/library".into(),
|
||||||
format: GltfFormat::GLB,
|
format: GltfFormat::GLB,
|
||||||
|
material_library: true,
|
||||||
aabbs: true,
|
aabbs: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,37 +1,25 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag};
|
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag};
|
||||||
use bevy_gltf_worlflow_examples_common_rapier::{assets::GameAssets, GameState, InAppRunning};
|
use bevy_gltf_worlflow_examples_common_rapier::{GameState, InAppRunning};
|
||||||
|
|
||||||
use bevy_rapier3d::prelude::Velocity;
|
use bevy_rapier3d::prelude::Velocity;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
pub fn setup_game(
|
pub fn setup_game(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
game_assets: Res<GameAssets>,
|
asset_server: Res<AssetServer>,
|
||||||
models: Res<Assets<bevy::gltf::Gltf>>,
|
|
||||||
mut next_game_state: ResMut<NextState<GameState>>,
|
mut next_game_state: ResMut<NextState<GameState>>,
|
||||||
) {
|
) {
|
||||||
commands.insert_resource(AmbientLight {
|
|
||||||
color: Color::WHITE,
|
|
||||||
brightness: 0.2,
|
|
||||||
});
|
|
||||||
// here we actually spawn our game world/level
|
// here we actually spawn our game world/level
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
SceneBundle {
|
SceneBundle {
|
||||||
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
scene: asset_server.load("models/World.glb#Scene0"),
|
||||||
scene: models
|
|
||||||
.get(game_assets.world.id())
|
|
||||||
.expect("main level should have been loaded")
|
|
||||||
.scenes[0]
|
|
||||||
.clone(),
|
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
bevy::prelude::Name::from("world"),
|
bevy::prelude::Name::from("world"),
|
||||||
GameWorldTag,
|
GameWorldTag,
|
||||||
InAppRunning,
|
InAppRunning,
|
||||||
));
|
));
|
||||||
|
|
||||||
next_game_state.set(GameState::InGame)
|
next_game_state.set(GameState::InGame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy_gltf_blueprints::{AnimationPlayerLink, BlueprintName};
|
use bevy_gltf_blueprints::{AnimationPlayerLink, BlueprintName, BlueprintsList};
|
||||||
pub use in_game::*;
|
pub use in_game::*;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
|
@ -22,7 +22,8 @@ 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 "Cylinder" that has two components: UnitTest, TupleTestF32
|
// if the export from Blender worked correctly, we should have an Entity called "Cylinder" that has two components: UnitTest, TupleTestF32
|
||||||
// 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
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn validate_export(
|
fn validate_export(
|
||||||
parents: Query<&Parent>,
|
parents: Query<&Parent>,
|
||||||
children: Query<&Children>,
|
children: Query<&Children>,
|
||||||
|
@ -31,6 +32,8 @@ fn validate_export(
|
||||||
animation_player_links: Query<(Entity, &AnimationPlayerLink)>,
|
animation_player_links: Query<(Entity, &AnimationPlayerLink)>,
|
||||||
exported_cylinder: Query<(Entity, &Name, &UnitTest, &TupleTestF32)>,
|
exported_cylinder: Query<(Entity, &Name, &UnitTest, &TupleTestF32)>,
|
||||||
empties_candidates: Query<(Entity, &Name, &GlobalTransform)>,
|
empties_candidates: Query<(Entity, &Name, &GlobalTransform)>,
|
||||||
|
|
||||||
|
blueprints_list: Query<(Entity, &BlueprintsList)>,
|
||||||
) {
|
) {
|
||||||
let animations_found = !animation_player_links.is_empty();
|
let animations_found = !animation_player_links.is_empty();
|
||||||
|
|
||||||
|
@ -69,11 +72,13 @@ fn validate_export(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let blueprints_list_found = !blueprints_list.is_empty();
|
||||||
|
|
||||||
fs::write(
|
fs::write(
|
||||||
"bevy_diagnostics.json",
|
"bevy_diagnostics.json",
|
||||||
format!(
|
format!(
|
||||||
"{{ \"animations\": {}, \"cylinder_found\": {} , \"nested_blueprint_found\": {}, \"empty_found\": {} }}",
|
"{{ \"animations\": {}, \"cylinder_found\": {} , \"nested_blueprint_found\": {}, \"empty_found\": {}, \"blueprints_list_found\": {} }}",
|
||||||
animations_found, cylinder_found, nested_blueprint_found, empty_found
|
animations_found, cylinder_found, nested_blueprint_found, empty_found, blueprints_list_found
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.expect("Unable to write file");
|
.expect("Unable to write file");
|
||||||
|
@ -97,9 +102,9 @@ impl Plugin for GamePlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Update, (spawn_test).run_if(in_state(GameState::InGame)))
|
app.add_systems(Update, (spawn_test).run_if(in_state(GameState::InGame)))
|
||||||
.add_systems(Update, validate_export)
|
.add_systems(Update, validate_export)
|
||||||
.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once
|
|
||||||
.add_systems(OnEnter(AppState::MenuRunning), start_game)
|
.add_systems(OnEnter(AppState::MenuRunning), start_game)
|
||||||
.add_systems(OnEnter(AppState::AppRunning), setup_game)
|
.add_systems(OnEnter(AppState::AppRunning), setup_game)
|
||||||
|
.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
exit_game.run_if(on_timer(Duration::from_secs_f32(0.5))),
|
exit_game.run_if(on_timer(Duration::from_secs_f32(0.5))),
|
||||||
|
|
|
@ -57,6 +57,14 @@ Before you can use the add-on you need to configure it
|
||||||
|
|
||||||
![configuration 3](./docs/configuration3.png)
|
![configuration 3](./docs/configuration3.png)
|
||||||
|
|
||||||
|
#### registry file polling
|
||||||
|
|
||||||
|
|
||||||
|
* by default, the add-on will check for changes in your registry file every second, and refresh the UI accordingly
|
||||||
|
* you can set the polling frequency or turn it off if you do not want auto-refresh
|
||||||
|
|
||||||
|
![registry file polling](./docs/registry_polling.png)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Use
|
## Use
|
||||||
|
@ -173,8 +181,8 @@ It will add the component to the select object
|
||||||
|
|
||||||
![invalid component](./docs/invalid_components.png)
|
![invalid component](./docs/invalid_components.png)
|
||||||
|
|
||||||
> important ! ```gltf_auto_export``` currently has no way of filtering out components, so you need to delete invalid components like these before exporting
|
> see [here](#invalidunregistered-type-renaming--conversion) for ways to convert invalid / unregistered components to other types.
|
||||||
this will be adress in the future
|
|
||||||
|
|
||||||
- if you are encountering this type of view: don't panic your component data is not gone ! It just means you need to reload the registry data by clicking on the relevant button
|
- if you are encountering this type of view: don't panic your component data is not gone ! It just means you need to reload the registry data by clicking on the relevant button
|
||||||
|
|
||||||
|
@ -182,18 +190,65 @@ It will add the component to the select object
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## advanced configuration
|
## Advanced Tools
|
||||||
|
|
||||||
### registry file polling
|
In this section you will find various additional more advanced tooling
|
||||||
|
|
||||||
|
### Invalid/unregistered type renaming / conversion
|
||||||
|
|
||||||
|
If you have components that are
|
||||||
|
* invalid : ie some error was diagnosed
|
||||||
|
* unregistered: a custom property is present on the object, but there is no matching type in the registry
|
||||||
|
|
||||||
|
Here you will get an overview, of ALL invalid and unregistered components in your Blender project, so you can find them, rename/convert them,
|
||||||
|
or delete them, also in bulk
|
||||||
|
|
||||||
|
![component rename overview](./docs/component_rename_overview2.png)
|
||||||
|
|
||||||
|
* you can click on the button to select the object in your outliner (this also works across scenes, so you will be taken to the scene where the
|
||||||
|
given object is located)
|
||||||
|
|
||||||
|
![update custom properties](./docs/component_rename_object_select.png)
|
||||||
|
|
||||||
|
|
||||||
* by default, the add-on will check for changes in your registry file every second, and refresh the UI accordingly
|
#### Single object component renaming/ conversion
|
||||||
* you can set the polling frequency or turn it off if you do not want auto-refresh
|
|
||||||
|
|
||||||
![registry file polling](./docs/registry_polling.png)
|
- to rename/convert a single component for a single object:
|
||||||
|
|
||||||
|
* go to the row of the object you want to convert the component of
|
||||||
|
* in the dropdown menu, choose the target component
|
||||||
|
* click on the button with the magic wand to convert the component
|
||||||
|
|
||||||
|
![single rename](./docs/component_rename_single.png)
|
||||||
|
|
||||||
|
> the tool will attempt to automatically convert the source component, including the field names/values, if the target component has the same ones
|
||||||
|
If it fails to do the conversion, you will get an error message, and you will either have to change the custom property yourself, or you can simply
|
||||||
|
change the values in the UI, which will automatically generate the custom property value
|
||||||
|
|
||||||
|
- to delete a single component for a single object:
|
||||||
|
|
||||||
|
* go to the row of the object you want to remove the component from
|
||||||
|
* click on the button with the "x" to remove the component
|
||||||
|
|
||||||
|
![single delete](./docs/component_remove_single.png)
|
||||||
|
|
||||||
|
#### Bulk component renaming/ conversion
|
||||||
|
|
||||||
|
- use this method if you want to convert ALL components of a given type of ALL objects
|
||||||
|
|
||||||
|
* click on this button to pick your source component
|
||||||
|
|
||||||
|
![bulk convert remove](./docs/component_rename_remove_bulk.png)
|
||||||
|
|
||||||
|
* for conversion: in the dropdown menu, choose the target component & click apply to convert all matching components
|
||||||
|
* for deletion: clic on the "x" to remove all matching components
|
||||||
|
|
||||||
|
![bulk convert remove](./docs/component_rename_remove_bulk2.png)
|
||||||
|
|
||||||
|
|
||||||
### regenerate custom property values
|
### For conversion between custom properties & components & vice-versa
|
||||||
|
|
||||||
|
#### regenerate custom property values
|
||||||
|
|
||||||
- "update custom properties of current object" : will go over **all components** that you have defined for the **currently selected object**, and re-generate the
|
- "update custom properties of current object" : will go over **all components** that you have defined for the **currently selected object**, and re-generate the
|
||||||
|
|
||||||
|
@ -212,7 +267,7 @@ It will add the component to the select object
|
||||||
You should also re-export your gltf files , otherwise you might run into issues
|
You should also re-export your gltf files , otherwise you might run into issues
|
||||||
|
|
||||||
|
|
||||||
### regenerate UI values
|
#### regenerate component/ UI values
|
||||||
|
|
||||||
- since v0.2, you have the option to regenerate (for the selected object or all objects, as above) to regenerate your UI values from the custom property values
|
- since v0.2, you have the option to regenerate (for the selected object or all objects, as above) to regenerate your UI values from the custom property values
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "bevy_components",
|
"name": "bevy_components",
|
||||||
"author": "kaosigh",
|
"author": "kaosigh",
|
||||||
"version": (0, 4, 0),
|
"version": (0, 4, 1),
|
||||||
"blender": (3, 4, 0),
|
"blender": (3, 4, 0),
|
||||||
"location": "VIEW_3D",
|
"location": "VIEW_3D",
|
||||||
"description": "UI to help create Bevy blueprints and components",
|
"description": "UI to help create Bevy blueprints and components",
|
||||||
|
@ -16,11 +16,11 @@ from bpy.props import (StringProperty)
|
||||||
|
|
||||||
from .helpers import load_settings
|
from .helpers import load_settings
|
||||||
from .blueprints import CreateBlueprintOperator
|
from .blueprints import CreateBlueprintOperator
|
||||||
from .components.operators import CopyComponentOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, AddComponentOperator, Toggle_ComponentVisibility
|
from .components.operators import CopyComponentOperator, Fix_Component_Operator, OT_rename_component, RemoveComponentFromAllObjectsOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, AddComponentOperator, RenameHelper, Toggle_ComponentVisibility
|
||||||
|
|
||||||
from .registry.registry import ComponentsRegistry,MissingBevyType
|
from .registry.registry import ComponentsRegistry,MissingBevyType
|
||||||
from .registry.operators import (COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT, ReloadRegistryOperator, OT_OpenFilebrowser)
|
from .registry.operators import (COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT, OT_select_component_name_to_replace, OT_select_object, ReloadRegistryOperator, OT_OpenFilebrowser)
|
||||||
from .registry.ui import (BEVY_COMPONENTS_PT_Configuration, BEVY_COMPONENTS_PT_MissingTypesPanel, MISSING_TYPES_UL_List)
|
from .registry.ui import (BEVY_COMPONENTS_PT_Configuration, BEVY_COMPONENTS_PT_AdvancedToolsPanel, BEVY_COMPONENTS_PT_MissingTypesPanel, MISSING_TYPES_UL_List)
|
||||||
|
|
||||||
from .components.metadata import (ComponentMetadata, ComponentsMeta, ensure_metadata_for_all_objects)
|
from .components.metadata import (ComponentMetadata, ComponentsMeta, ensure_metadata_for_all_objects)
|
||||||
from .propGroups.prop_groups import (generate_propertyGroups_for_components)
|
from .propGroups.prop_groups import (generate_propertyGroups_for_components)
|
||||||
|
@ -87,6 +87,10 @@ classes = [
|
||||||
CopyComponentOperator,
|
CopyComponentOperator,
|
||||||
PasteComponentOperator,
|
PasteComponentOperator,
|
||||||
RemoveComponentOperator,
|
RemoveComponentOperator,
|
||||||
|
RemoveComponentFromAllObjectsOperator,
|
||||||
|
Fix_Component_Operator,
|
||||||
|
OT_rename_component,
|
||||||
|
RenameHelper,
|
||||||
GenerateComponent_From_custom_property_Operator,
|
GenerateComponent_From_custom_property_Operator,
|
||||||
Toggle_ComponentVisibility,
|
Toggle_ComponentVisibility,
|
||||||
|
|
||||||
|
@ -105,9 +109,13 @@ classes = [
|
||||||
|
|
||||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
||||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT,
|
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT,
|
||||||
|
|
||||||
|
OT_select_object,
|
||||||
|
OT_select_component_name_to_replace,
|
||||||
|
|
||||||
BEVY_COMPONENTS_PT_MainPanel,
|
BEVY_COMPONENTS_PT_MainPanel,
|
||||||
BEVY_COMPONENTS_PT_ComponentsPanel,
|
BEVY_COMPONENTS_PT_ComponentsPanel,
|
||||||
|
BEVY_COMPONENTS_PT_AdvancedToolsPanel,
|
||||||
BEVY_COMPONENTS_PT_Configuration,
|
BEVY_COMPONENTS_PT_Configuration,
|
||||||
MISSING_TYPES_UL_List,
|
MISSING_TYPES_UL_List,
|
||||||
BEVY_COMPONENTS_PT_MissingTypesPanel,
|
BEVY_COMPONENTS_PT_MissingTypesPanel,
|
||||||
|
|
|
@ -237,6 +237,23 @@ def apply_propertyGroup_values_to_object_customProperties(object):
|
||||||
value = property_group_value_to_custom_property_value(propertyGroup, component_definition, registry, None)
|
value = property_group_value_to_custom_property_value(propertyGroup, component_definition, registry, None)
|
||||||
object[component_name] = value
|
object[component_name] = value
|
||||||
|
|
||||||
|
# apply component value(s) to custom property of a single component
|
||||||
|
def apply_propertyGroup_values_to_object_customProperties_for_component(object, component_name):
|
||||||
|
registry = bpy.context.window_manager.components_registry
|
||||||
|
print("yallah", component_name)
|
||||||
|
(_, propertyGroup) = upsert_component_in_object(object, component_name, registry)
|
||||||
|
component_definition = find_component_definition_from_short_name(component_name)
|
||||||
|
if component_definition != None:
|
||||||
|
print("merde")
|
||||||
|
value = property_group_value_to_custom_property_value(propertyGroup, component_definition, registry, None)
|
||||||
|
object[component_name] = value
|
||||||
|
|
||||||
|
components_metadata = object.components_meta.components
|
||||||
|
componentMeta = next(filter(lambda component: component["name"] == component_name, components_metadata), None)
|
||||||
|
if componentMeta:
|
||||||
|
print("here")
|
||||||
|
componentMeta.invalid = False
|
||||||
|
componentMeta.invalid_details = ""
|
||||||
|
|
||||||
|
|
||||||
def apply_customProperty_values_to_object_propertyGroups(object):
|
def apply_customProperty_values_to_object_propertyGroups(object):
|
||||||
|
@ -258,6 +275,8 @@ def apply_customProperty_values_to_object_propertyGroups(object):
|
||||||
object["__disable__update"] = True # disable update callback while we set the values of the propertyGroup "tree" (as a propertyGroup can contain other propertyGroups)
|
object["__disable__update"] = True # disable update callback while we set the values of the propertyGroup "tree" (as a propertyGroup can contain other propertyGroups)
|
||||||
property_group_value_from_custom_property_value(propertyGroup, component_definition, registry, customProperty_value)
|
property_group_value_from_custom_property_value(propertyGroup, component_definition, registry, customProperty_value)
|
||||||
del object["__disable__update"]
|
del object["__disable__update"]
|
||||||
|
source_componentMeta.invalid = False
|
||||||
|
source_componentMeta.invalid_details = ""
|
||||||
|
|
||||||
# removes the given component from the object: removes both the custom property and the matching metadata from the object
|
# removes the given component from the object: removes both the custom property and the matching metadata from the object
|
||||||
def remove_component_from_object(object, component_name):
|
def remove_component_from_object(object, component_name):
|
||||||
|
|
|
@ -3,7 +3,7 @@ import json
|
||||||
import bpy
|
import bpy
|
||||||
from bpy_types import Operator
|
from bpy_types import Operator
|
||||||
from bpy.props import (StringProperty)
|
from bpy.props import (StringProperty)
|
||||||
from .metadata import add_component_to_object, add_metadata_to_components_without_metadata, apply_customProperty_values_to_object_propertyGroups, copy_propertyGroup_values_to_another_object, find_component_definition_from_short_name, remove_component_from_object
|
from .metadata import add_component_to_object, add_metadata_to_components_without_metadata, apply_customProperty_values_to_object_propertyGroups, apply_propertyGroup_values_to_object_customProperties_for_component, copy_propertyGroup_values_to_another_object, find_component_definition_from_short_name, remove_component_from_object
|
||||||
|
|
||||||
class AddComponentOperator(Operator):
|
class AddComponentOperator(Operator):
|
||||||
"""Add component to blueprint"""
|
"""Add component to blueprint"""
|
||||||
|
@ -90,12 +90,10 @@ class PasteComponentOperator(Operator):
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveComponentOperator(Operator):
|
class RemoveComponentOperator(Operator):
|
||||||
"""Delete component from blueprint"""
|
"""Remove component from object"""
|
||||||
bl_idname = "object.remove_bevy_component"
|
bl_idname = "object.remove_bevy_component"
|
||||||
bl_label = "Delete component from blueprint Operator"
|
bl_label = "Remove component from object Operator"
|
||||||
bl_options = {"UNDO"}
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
component_name: StringProperty(
|
component_name: StringProperty(
|
||||||
|
@ -103,11 +101,18 @@ class RemoveComponentOperator(Operator):
|
||||||
description="component to delete",
|
description="component to delete",
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
|
object_name: StringProperty(
|
||||||
|
name="object name",
|
||||||
|
description="object whose component to delete",
|
||||||
|
default=""
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
object = context.object
|
if self.object_name == "":
|
||||||
|
object = context.object
|
||||||
|
else:
|
||||||
|
object = bpy.data.objects[self.object_name]
|
||||||
print("removing component ", self.component_name, "from object '"+object.name+"'")
|
print("removing component ", self.component_name, "from object '"+object.name+"'")
|
||||||
|
|
||||||
if object is not None and self.component_name in object:
|
if object is not None and self.component_name in object:
|
||||||
remove_component_from_object(object, self.component_name)
|
remove_component_from_object(object, self.component_name)
|
||||||
else:
|
else:
|
||||||
|
@ -116,6 +121,154 @@ class RemoveComponentOperator(Operator):
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveComponentFromAllObjectsOperator(Operator):
|
||||||
|
"""Remove component from all object"""
|
||||||
|
bl_idname = "object.remove_bevy_component_all"
|
||||||
|
bl_label = "Remove component from all objects Operator"
|
||||||
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
|
component_name: StringProperty(
|
||||||
|
name="component name",
|
||||||
|
description="component to delete",
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register(cls):
|
||||||
|
bpy.types.WindowManager.components_remove_progress = bpy.props.FloatProperty(default=-1.0)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unregister(cls):
|
||||||
|
del bpy.types.WindowManager.components_remove_progress
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
print("removing component ", self.component_name, "from all objects")
|
||||||
|
total = len(bpy.data.objects)
|
||||||
|
for index, object in enumerate(bpy.data.objects):
|
||||||
|
if len(object.keys()) > 0:
|
||||||
|
if object is not None and self.component_name in object:
|
||||||
|
remove_component_from_object(object, self.component_name)
|
||||||
|
|
||||||
|
progress = index / total
|
||||||
|
context.window_manager.components_remove_progress = progress
|
||||||
|
# now force refresh the ui
|
||||||
|
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||||
|
context.window_manager.components_remove_progress = -1.0
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class RenameHelper(bpy.types.PropertyGroup):
|
||||||
|
original_name: bpy.props.StringProperty(name="") # type: ignore
|
||||||
|
new_name: bpy.props.StringProperty(name="") # type: ignore
|
||||||
|
|
||||||
|
#object: bpy.props.PointerProperty(type=bpy.types.Object)
|
||||||
|
@classmethod
|
||||||
|
def register(cls):
|
||||||
|
bpy.types.WindowManager.bevy_component_rename_helper = bpy.props.PointerProperty(type=RenameHelper)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unregister(cls):
|
||||||
|
# remove handlers & co
|
||||||
|
del bpy.types.WindowManager.bevy_component_rename_helper
|
||||||
|
|
||||||
|
class OT_rename_component(Operator):
|
||||||
|
"""Rename component"""
|
||||||
|
bl_idname = "object.rename_bevy_component"
|
||||||
|
bl_label = "rename component"
|
||||||
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
|
original_name: bpy.props.StringProperty(default="") # type: ignore
|
||||||
|
new_name: StringProperty(
|
||||||
|
name="new_name",
|
||||||
|
description="new name of component",
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
target_objects: bpy.props.StringProperty() # type: ignore
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register(cls):
|
||||||
|
bpy.types.WindowManager.components_rename_progress = bpy.props.FloatProperty(default=-1.0) #bpy.props.PointerProperty(type=RenameHelper)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unregister(cls):
|
||||||
|
del bpy.types.WindowManager.components_rename_progress
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
registry = context.window_manager.components_registry
|
||||||
|
type_infos = registry.type_infos
|
||||||
|
settings = context.window_manager.bevy_component_rename_helper
|
||||||
|
original_name = settings.original_name if self.original_name == "" else self.original_name
|
||||||
|
new_name = self.new_name
|
||||||
|
|
||||||
|
|
||||||
|
print("renaming components: original name", original_name, "new_name", self.new_name, "targets", self.target_objects)
|
||||||
|
target_objects = json.loads(self.target_objects)
|
||||||
|
errors = []
|
||||||
|
total = len(target_objects)
|
||||||
|
|
||||||
|
if original_name != '' and new_name != '' and original_name != new_name and len(target_objects) > 0:
|
||||||
|
for index, object_name in enumerate(target_objects):
|
||||||
|
object = bpy.data.objects[object_name]
|
||||||
|
if object and original_name in object:
|
||||||
|
|
||||||
|
# copy data to new component, remove the old one
|
||||||
|
try:
|
||||||
|
object[new_name] = object[original_name]
|
||||||
|
remove_component_from_object(object, original_name)
|
||||||
|
except Exception as error:
|
||||||
|
if '__disable__update' in object:
|
||||||
|
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||||
|
# get metadata
|
||||||
|
components_metadata = getattr(object, "components_meta", None)
|
||||||
|
if components_metadata:
|
||||||
|
components_metadata = components_metadata.components
|
||||||
|
component_meta = next(filter(lambda component: component["name"] == new_name, components_metadata), None)
|
||||||
|
if component_meta:
|
||||||
|
component_meta.invalid = True
|
||||||
|
component_meta.invalid_details = "unknow issue when renaming/transforming component, please remove it & add it back again"
|
||||||
|
|
||||||
|
errors.append( "failed to copy old component value to new component: object: '" + object.name + "', error: " + str(error))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# attempt conversion
|
||||||
|
long_name = registry.short_names_to_long_names[new_name]
|
||||||
|
component_definition = type_infos[long_name]
|
||||||
|
add_component_to_object(object, component_definition, object[new_name])
|
||||||
|
except Exception as error:
|
||||||
|
if '__disable__update' in object:
|
||||||
|
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||||
|
components_metadata = getattr(object, "components_meta", None)
|
||||||
|
if components_metadata:
|
||||||
|
components_metadata = components_metadata.components
|
||||||
|
component_meta = next(filter(lambda component: component["name"] == new_name, components_metadata), None)
|
||||||
|
if component_meta:
|
||||||
|
component_meta.invalid = True
|
||||||
|
component_meta.invalid_details = "wrong custom property value, overwrite them by changing the values in the ui or change them & regenerate"
|
||||||
|
|
||||||
|
errors.append( "wrong custom property values to generate target component: object: '" + object.name + "', error: " + str(error))
|
||||||
|
|
||||||
|
progress = index / total
|
||||||
|
context.window_manager.components_rename_progress = progress
|
||||||
|
|
||||||
|
try:
|
||||||
|
# now force refresh the ui
|
||||||
|
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||||
|
except: pass # this is to allow this to run in cli/headless mode
|
||||||
|
|
||||||
|
if len(errors) > 0:
|
||||||
|
self.report({'ERROR'}, "Failed to rename component: Errors:" + str(errors))
|
||||||
|
else:
|
||||||
|
self.report({'INFO'}, "Sucessfully renamed component")
|
||||||
|
|
||||||
|
#clear data after we are done
|
||||||
|
self.original_name = ""
|
||||||
|
context.window_manager.bevy_component_rename_helper.original_name = ""
|
||||||
|
context.window_manager.components_rename_progress = -1.0
|
||||||
|
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class GenerateComponent_From_custom_property_Operator(Operator):
|
class GenerateComponent_From_custom_property_Operator(Operator):
|
||||||
"""generate components from custom property"""
|
"""generate components from custom property"""
|
||||||
bl_idname = "object.generate_bevy_component_from_custom_property"
|
bl_idname = "object.generate_bevy_component_from_custom_property"
|
||||||
|
@ -143,6 +296,31 @@ class GenerateComponent_From_custom_property_Operator(Operator):
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class Fix_Component_Operator(Operator):
|
||||||
|
"""attempt to fix component"""
|
||||||
|
bl_idname = "object.fix_bevy_component"
|
||||||
|
bl_label = "Fix component (attempts to)"
|
||||||
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
|
component_name: StringProperty(
|
||||||
|
name="component name",
|
||||||
|
description="component to fix",
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
object = context.object
|
||||||
|
error = False
|
||||||
|
try:
|
||||||
|
apply_propertyGroup_values_to_object_customProperties_for_component(object, self.component_name)
|
||||||
|
except Exception as error:
|
||||||
|
if "__disable__update" in object:
|
||||||
|
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||||
|
error = True
|
||||||
|
self.report({'ERROR'}, "Failed to fix component: Error:" + str(error))
|
||||||
|
if not error:
|
||||||
|
self.report({'INFO'}, "Sucessfully fixed component (please double check component & its custom property value)")
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
class Toggle_ComponentVisibility(Operator):
|
class Toggle_ComponentVisibility(Operator):
|
||||||
"""toggles components visibility"""
|
"""toggles components visibility"""
|
||||||
bl_idname = "object.toggle_bevy_component_visibility"
|
bl_idname = "object.toggle_bevy_component_visibility"
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import json
|
import json
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
from ..registry.operators import COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT
|
||||||
from .metadata import do_object_custom_properties_have_missing_metadata
|
from .metadata import do_object_custom_properties_have_missing_metadata
|
||||||
from .operators import AddComponentOperator, CopyComponentOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, Toggle_ComponentVisibility
|
from .operators import AddComponentOperator, CopyComponentOperator, Fix_Component_Operator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, Toggle_ComponentVisibility
|
||||||
|
|
||||||
def draw_propertyGroup( propertyGroup, layout, nesting =[], rootName=None):
|
def draw_propertyGroup( propertyGroup, layout, nesting =[], rootName=None):
|
||||||
is_enum = getattr(propertyGroup, "with_enum")
|
is_enum = getattr(propertyGroup, "with_enum")
|
||||||
|
@ -193,6 +195,16 @@ class BEVY_COMPONENTS_PT_ComponentsPanel(bpy.types.Panel):
|
||||||
row.label(text=error_message)
|
row.label(text=error_message)
|
||||||
|
|
||||||
# "footer" with additional controls
|
# "footer" with additional controls
|
||||||
|
if component_invalid:
|
||||||
|
if root_propertyGroup_name:
|
||||||
|
propertyGroup = getattr(component_meta, root_propertyGroup_name, None)
|
||||||
|
if propertyGroup:
|
||||||
|
unit_struct = len(propertyGroup.field_names) == 0
|
||||||
|
if unit_struct:
|
||||||
|
op = row.operator(Fix_Component_Operator.bl_idname, text="", icon="SHADERFX")
|
||||||
|
op.component_name = component_name
|
||||||
|
row.separator()
|
||||||
|
|
||||||
op = row.operator(RemoveComponentOperator.bl_idname, text="", icon="X")
|
op = row.operator(RemoveComponentOperator.bl_idname, text="", icon="X")
|
||||||
op.component_name = component_name
|
op.component_name = component_name
|
||||||
row.separator()
|
row.separator()
|
||||||
|
|
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 14 KiB |
|
@ -224,8 +224,9 @@ def property_group_value_from_custom_property_value(property_group, definition,
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
pass
|
if len(value) > 2: #a unit struct should be two chars long :()
|
||||||
#print("struct with zero fields")
|
#print("struct with zero fields")
|
||||||
|
raise Exception("input string too big for a unit struct")
|
||||||
|
|
||||||
elif type_info == "Tuple":
|
elif type_info == "Tuple":
|
||||||
custom_property_values = parse_tuplestruct_string(value, start_nesting=1 if len(nesting) == 1 else 1)
|
custom_property_values = parse_tuplestruct_string(value, start_nesting=1 if len(nesting) == 1 else 1)
|
||||||
|
|
|
@ -42,11 +42,26 @@ class COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL(Operator):
|
||||||
bl_label = "Apply Registry to all objects"
|
bl_label = "Apply Registry to all objects"
|
||||||
bl_options = {"UNDO"}
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register(cls):
|
||||||
|
bpy.types.WindowManager.custom_properties_from_components_progress_all = bpy.props.FloatProperty(default=-1.0) #bpy.props.PointerProperty(type=RenameHelper)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unregister(cls):
|
||||||
|
del bpy.types.WindowManager.custom_properties_from_components_progress_all
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
print("apply registry to all")
|
print("apply registry to all")
|
||||||
#context.window_manager.components_registry.load_schema()
|
#context.window_manager.components_registry.load_schema()
|
||||||
for object in bpy.data.objects:
|
total = len(bpy.data.objects)
|
||||||
|
|
||||||
|
for index, object in enumerate(bpy.data.objects):
|
||||||
apply_propertyGroup_values_to_object_customProperties(object)
|
apply_propertyGroup_values_to_object_customProperties(object)
|
||||||
|
progress = index / total
|
||||||
|
context.window_manager.custom_properties_from_components_progress_all = progress
|
||||||
|
# now force refresh the ui
|
||||||
|
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||||
|
context.window_manager.custom_properties_from_components_progress_all = -1.0
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
@ -56,10 +71,23 @@ class COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT(Operator):
|
||||||
bl_label = "Apply Registry to current object"
|
bl_label = "Apply Registry to current object"
|
||||||
bl_options = {"UNDO"}
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register(cls):
|
||||||
|
bpy.types.WindowManager.custom_properties_from_components_progress = bpy.props.FloatProperty(default=-1.0) #bpy.props.PointerProperty(type=RenameHelper)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unregister(cls):
|
||||||
|
del bpy.types.WindowManager.custom_properties_from_components_progress
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
print("apply registry to current object")
|
print("apply registry to current object")
|
||||||
object = context.object
|
object = context.object
|
||||||
|
context.window_manager.custom_properties_from_components_progress = 0.5
|
||||||
|
# now force refresh the ui
|
||||||
|
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||||
apply_propertyGroup_values_to_object_customProperties(object)
|
apply_propertyGroup_values_to_object_customProperties(object)
|
||||||
|
|
||||||
|
context.window_manager.custom_properties_from_components_progress = -1.0
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,44 +97,79 @@ class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT(Operator):
|
||||||
bl_label = "Apply custom_properties to current object"
|
bl_label = "Apply custom_properties to current object"
|
||||||
bl_options = {"UNDO"}
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register(cls):
|
||||||
|
bpy.types.WindowManager.components_from_custom_properties_progress = bpy.props.FloatProperty(default=-1.0) #bpy.props.PointerProperty(type=RenameHelper)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unregister(cls):
|
||||||
|
del bpy.types.WindowManager.components_from_custom_properties_progress
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
print("apply custom properties to current object")
|
print("apply custom properties to current object")
|
||||||
object = context.object
|
object = context.object
|
||||||
error = False
|
error = False
|
||||||
try:
|
try:
|
||||||
apply_customProperty_values_to_object_propertyGroups(object)
|
apply_customProperty_values_to_object_propertyGroups(object)
|
||||||
|
progress = 0.5
|
||||||
|
context.window_manager.components_from_custom_properties_progress = progress
|
||||||
|
try:
|
||||||
|
# now force refresh the ui
|
||||||
|
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||||
|
except:pass # ony run in ui
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||||
error = True
|
error = True
|
||||||
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Error:" + str(error))
|
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Error:" + str(error))
|
||||||
if not error:
|
if not error:
|
||||||
self.report({'INFO'}, "Sucessfully generated UI values for custom properties for selected object")
|
self.report({'INFO'}, "Sucessfully generated UI values for custom properties for selected object")
|
||||||
|
context.window_manager.components_from_custom_properties_progress = -1.0
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL(Operator):
|
class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL(Operator):
|
||||||
"""Update UI values from custom properties to ALL object"""
|
"""Update UI values from custom properties to ALL object"""
|
||||||
bl_idname = "object.refresh_ui_from_custom_properties_all"
|
bl_idname = "object.refresh_ui_from_custom_properties_all"
|
||||||
bl_label = "Apply custom_properties to all objects"
|
bl_label = "Apply custom_properties to all objects"
|
||||||
bl_options = {"UNDO"}
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register(cls):
|
||||||
|
bpy.types.WindowManager.components_from_custom_properties_progress_all = bpy.props.FloatProperty(default=-1.0) #bpy.props.PointerProperty(type=RenameHelper)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unregister(cls):
|
||||||
|
del bpy.types.WindowManager.components_from_custom_properties_progress_all
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
print("apply custom properties to all object")
|
print("apply custom properties to all object")
|
||||||
bpy.context.window_manager.components_registry.disable_all_object_updates = True
|
bpy.context.window_manager.components_registry.disable_all_object_updates = True
|
||||||
errors = []
|
errors = []
|
||||||
for object in bpy.data.objects:
|
total = len(bpy.data.objects)
|
||||||
|
|
||||||
|
for index, object in enumerate(bpy.data.objects):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
apply_customProperty_values_to_object_propertyGroups(object)
|
apply_customProperty_values_to_object_propertyGroups(object)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||||
errors.append( "object: '" + object.name + "', error: " + str(error))
|
errors.append( "object: '" + object.name + "', error: " + str(error))
|
||||||
|
|
||||||
|
progress = index / total
|
||||||
|
context.window_manager.components_from_custom_properties_progress_all = progress
|
||||||
|
# now force refresh the ui
|
||||||
|
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if len(errors) > 0:
|
if len(errors) > 0:
|
||||||
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Errors:" + str(errors))
|
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Errors:" + str(errors))
|
||||||
else:
|
else:
|
||||||
self.report({'INFO'}, "Sucessfully generated UI values for custom properties for all objects")
|
self.report({'INFO'}, "Sucessfully generated UI values for custom properties for all objects")
|
||||||
bpy.context.window_manager.components_registry.disable_all_object_updates = False
|
bpy.context.window_manager.components_registry.disable_all_object_updates = False
|
||||||
|
context.window_manager.components_from_custom_properties_progress_all = -1.0
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
class OT_OpenFilebrowser(Operator, ImportHelper):
|
class OT_OpenFilebrowser(Operator, ImportHelper):
|
||||||
|
@ -133,3 +196,41 @@ class OT_OpenFilebrowser(Operator, ImportHelper):
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class OT_select_object(Operator):
|
||||||
|
"""Select object by name"""
|
||||||
|
bl_idname = "object.select"
|
||||||
|
bl_label = "Select object"
|
||||||
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
|
object_name: StringProperty(
|
||||||
|
name="object_name",
|
||||||
|
description="object to select's name ",
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if self.object_name:
|
||||||
|
object = bpy.data.objects[self.object_name]
|
||||||
|
scenes_of_object = list(object.users_scene)
|
||||||
|
if len(scenes_of_object) > 0:
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
bpy.context.window.scene = scenes_of_object[0]
|
||||||
|
object.select_set(True)
|
||||||
|
bpy.context.view_layer.objects.active = object
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class OT_select_component_name_to_replace(Operator):
|
||||||
|
"""Select component name to replace"""
|
||||||
|
bl_idname = "object.select_component_name_to_replace"
|
||||||
|
bl_label = "Select component name for bulk replace"
|
||||||
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
|
component_name: StringProperty(
|
||||||
|
name="component_name",
|
||||||
|
description="component name to replace",
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
context.window_manager.bevy_component_rename_helper.original_name = self.component_name
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
|
@ -241,9 +241,6 @@ class ComponentsRegistry(PropertyGroup):
|
||||||
|
|
||||||
del bpy.types.WindowManager.components_registry
|
del bpy.types.WindowManager.components_registry
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def load_schema(self):
|
def load_schema(self):
|
||||||
print("load schema", self)
|
print("load schema", self)
|
||||||
# cleanup previous data if any
|
# cleanup previous data if any
|
||||||
|
@ -352,9 +349,9 @@ class ComponentsRegistry(PropertyGroup):
|
||||||
return propGroupName
|
return propGroupName
|
||||||
|
|
||||||
def get_propertyGroupName_from_shortName(self, shortName):
|
def get_propertyGroupName_from_shortName(self, shortName):
|
||||||
|
|
||||||
return self.short_names_to_propgroup_names.get(shortName, None)
|
return self.short_names_to_propgroup_names.get(shortName, None)
|
||||||
|
|
||||||
|
###########
|
||||||
|
|
||||||
"""
|
"""
|
||||||
object[component_definition.name] = 0.5
|
object[component_definition.name] = 0.5
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
|
import json
|
||||||
import bpy
|
import bpy
|
||||||
from bpy_types import (UIList)
|
from bpy_types import (UIList)
|
||||||
|
from bpy.props import (StringProperty)
|
||||||
|
|
||||||
|
from ..components.operators import OT_rename_component, RemoveComponentFromAllObjectsOperator, RemoveComponentOperator
|
||||||
from .operators import(
|
from .operators import(
|
||||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
||||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT,
|
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT,
|
||||||
OT_OpenFilebrowser, ReloadRegistryOperator,
|
OT_OpenFilebrowser,
|
||||||
|
OT_select_component_name_to_replace,
|
||||||
|
OT_select_object, ReloadRegistryOperator,
|
||||||
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL,
|
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL,
|
||||||
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT)
|
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT)
|
||||||
|
|
||||||
|
@ -21,8 +27,7 @@ class BEVY_COMPONENTS_PT_Configuration(bpy.types.Panel):
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
registry = context.window_manager.components_registry
|
registry = context.window_manager.components_registry
|
||||||
registry_has_type_infos = registry.has_type_infos()
|
|
||||||
selected_object = context.selected_objects[0] if len(context.selected_objects) > 0 else None
|
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
col = row.column()
|
col = row.column()
|
||||||
|
@ -43,31 +48,212 @@ class BEVY_COMPONENTS_PT_Configuration(bpy.types.Panel):
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
|
|
||||||
|
class BEVY_COMPONENTS_PT_AdvancedToolsPanel(bpy.types.Panel):
|
||||||
|
"""panel listing all the missing bevy types in the schema"""
|
||||||
|
bl_idname = "BEVY_COMPONENTS_PT_AdvancedToolsPanel"
|
||||||
|
bl_label = "Advanced tools"
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'UI'
|
||||||
|
bl_category = "Bevy Components"
|
||||||
|
bl_context = "objectmode"
|
||||||
|
bl_parent_id = "BEVY_COMPONENTS_PT_MainPanel"
|
||||||
|
bl_options = {'DEFAULT_CLOSED'}
|
||||||
|
bl_description = "advanced tooling"
|
||||||
|
|
||||||
|
|
||||||
|
def draw_invalid_or_unregistered_header(self, layout, items):
|
||||||
|
row = layout.row()
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
col = row.column()
|
||||||
|
col.label(text=item)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_invalid_or_unregistered(self, layout, status, component_name, object):
|
||||||
|
available_components = bpy.context.window_manager.components_list
|
||||||
|
registry = bpy.context.window_manager.components_registry
|
||||||
|
registry_has_type_infos = registry.has_type_infos()
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
col.label(text=component_name)
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
operator = col.operator(OT_select_object.bl_idname, text=object.name)
|
||||||
|
operator.object_name = object.name
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
col.label(text=status)
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
col.prop(available_components, "list", text="")
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
operator = col.operator(OT_rename_component.bl_idname, text="", icon="SHADERFX") #rename
|
||||||
|
new_name = registry.type_infos[available_components.list]['short_name'] if available_components.list in registry.type_infos else ""
|
||||||
|
operator.original_name = component_name
|
||||||
|
operator.target_objects = json.dumps([object.name])
|
||||||
|
operator.new_name = new_name
|
||||||
|
col.enabled = registry_has_type_infos and component_name != "" and component_name != new_name
|
||||||
|
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
operator = col.operator(RemoveComponentOperator.bl_idname, text="", icon="X")
|
||||||
|
operator.object_name = object.name
|
||||||
|
operator.component_name = component_name
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
col = row.column()
|
||||||
|
operator = col.operator(OT_select_component_name_to_replace.bl_idname, text="", icon="EYEDROPPER") #text="select for rename",
|
||||||
|
operator.component_name = component_name
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
registry = bpy.context.window_manager.components_registry
|
||||||
|
registry_has_type_infos = registry.has_type_infos()
|
||||||
|
selected_object = context.selected_objects[0] if len(context.selected_objects) > 0 else None
|
||||||
|
available_components = bpy.context.window_manager.components_list
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
box= row.box()
|
||||||
|
box.label(text="Invalid/ unregistered components")
|
||||||
|
|
||||||
|
objects_with_invalid_components = []
|
||||||
|
invalid_component_names = []
|
||||||
|
|
||||||
|
self.draw_invalid_or_unregistered_header(layout, ["Component", "Object", "Status", "Target"])
|
||||||
|
|
||||||
|
for object in bpy.data.objects: # TODO: very inneficent
|
||||||
|
if len(object.keys()) > 0:
|
||||||
|
if "components_meta" in object:
|
||||||
|
components_metadata = object.components_meta.components
|
||||||
|
comp_names = []
|
||||||
|
for index, component_meta in enumerate(components_metadata):
|
||||||
|
short_name = component_meta.name
|
||||||
|
if component_meta.invalid:
|
||||||
|
self.draw_invalid_or_unregistered(layout, "Invalid", short_name, object)
|
||||||
|
|
||||||
|
if not object.name in objects_with_invalid_components:
|
||||||
|
objects_with_invalid_components.append(object.name)
|
||||||
|
|
||||||
|
if not short_name in invalid_component_names:
|
||||||
|
invalid_component_names.append(short_name)
|
||||||
|
|
||||||
|
|
||||||
|
comp_names.append(short_name)
|
||||||
|
|
||||||
|
for custom_property in object.keys():
|
||||||
|
if custom_property != 'components_meta' and custom_property not in comp_names:
|
||||||
|
self.draw_invalid_or_unregistered(layout, "Unregistered", custom_property, object)
|
||||||
|
|
||||||
|
if not object.name in objects_with_invalid_components:
|
||||||
|
objects_with_invalid_components.append(object.name)
|
||||||
|
if not short_name in invalid_component_names:
|
||||||
|
invalid_component_names.append(custom_property)
|
||||||
|
layout.separator()
|
||||||
|
layout.separator()
|
||||||
|
original_name = bpy.context.window_manager.bevy_component_rename_helper.original_name
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
col = row.column()
|
||||||
|
col.label(text="Original")
|
||||||
|
col = row.column()
|
||||||
|
col.label(text="New")
|
||||||
|
col = row.column()
|
||||||
|
col.label(text="------")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
col = row.column()
|
||||||
|
box = col.box()
|
||||||
|
box.label(text=original_name)
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
col.prop(available_components, "list", text="")
|
||||||
|
#row.prop(available_components, "filter",text="Filter")
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
components_rename_progress = context.window_manager.components_rename_progress
|
||||||
|
|
||||||
|
if components_rename_progress == -1.0:
|
||||||
|
operator = col.operator(OT_rename_component.bl_idname, text="apply", icon="SHADERFX")
|
||||||
|
operator.target_objects = json.dumps(objects_with_invalid_components)
|
||||||
|
new_name = registry.type_infos[available_components.list]['short_name'] if available_components.list in registry.type_infos else ""
|
||||||
|
operator.new_name = new_name
|
||||||
|
col.enabled = registry_has_type_infos and original_name != "" and original_name != new_name
|
||||||
|
else:
|
||||||
|
if hasattr(layout,"progress") : # only for Blender > 4.0
|
||||||
|
col.progress(factor = components_rename_progress, text=f"updating {components_rename_progress * 100.0:.2f}%")
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
remove_components_progress = context.window_manager.components_remove_progress
|
||||||
|
if remove_components_progress == -1.0:
|
||||||
|
operator = row.operator(RemoveComponentFromAllObjectsOperator.bl_idname, text="", icon="X")
|
||||||
|
operator.component_name = context.window_manager.bevy_component_rename_helper.original_name
|
||||||
|
col.enabled = registry_has_type_infos and original_name != ""
|
||||||
|
else:
|
||||||
|
if hasattr(layout,"progress") : # only for Blender > 4.0
|
||||||
|
col.progress(factor = remove_components_progress, text=f"updating {remove_components_progress * 100.0:.2f}%")
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.separator()
|
||||||
|
row = layout.row()
|
||||||
|
box= row.box()
|
||||||
|
box.label(text="Conversions between custom properties and components & vice-versa")
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.label(text="WARNING ! The following operations will overwrite your existing custom properties if they have matching types on the bevy side !")
|
row.label(text="WARNING ! The following operations will overwrite your existing custom properties if they have matching types on the bevy side !")
|
||||||
row.alert = True
|
row.alert = True
|
||||||
|
|
||||||
|
##
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update custom properties of current object" , icon="LOOP_FORWARDS")
|
custom_properties_from_components_progress_current = context.window_manager.custom_properties_from_components_progress
|
||||||
row.enabled = registry_has_type_infos and selected_object is not None
|
|
||||||
|
if custom_properties_from_components_progress_current == -1.0:
|
||||||
|
row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update custom properties of current object" , icon="LOOP_FORWARDS")
|
||||||
|
row.enabled = registry_has_type_infos and selected_object is not None
|
||||||
|
else:
|
||||||
|
if hasattr(layout,"progress") : # only for Blender > 4.0
|
||||||
|
layout.progress(factor = custom_properties_from_components_progress_current, text=f"updating {custom_properties_from_components_progress_current * 100.0:.2f}%")
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL.bl_idname, text="update custom properties of ALL objects" , icon="LOOP_FORWARDS")
|
custom_properties_from_components_progress_all = context.window_manager.custom_properties_from_components_progress_all
|
||||||
row.enabled = registry_has_type_infos
|
|
||||||
|
if custom_properties_from_components_progress_all == -1.0:
|
||||||
|
row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL.bl_idname, text="update custom properties of ALL objects" , icon="LOOP_FORWARDS")
|
||||||
|
row.enabled = registry_has_type_infos
|
||||||
|
else:
|
||||||
|
if hasattr(layout,"progress") : # only for Blender > 4.0
|
||||||
|
layout.progress(factor = custom_properties_from_components_progress_all, text=f"updating {custom_properties_from_components_progress_all * 100.0:.2f}%")
|
||||||
|
|
||||||
|
########################
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.label(text="WARNING ! The following operations will try to overwrite your existing ui values if they have matching types on the bevy side !")
|
row.label(text="WARNING ! The following operations will try to overwrite your existing ui values if they have matching types on the bevy side !")
|
||||||
row.alert = True
|
row.alert = True
|
||||||
|
|
||||||
|
components_from_custom_properties_progress_current = context.window_manager.components_from_custom_properties_progress
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.operator(COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update UI FROM custom properties of current object" , icon="LOOP_BACK")
|
if components_from_custom_properties_progress_current == -1.0:
|
||||||
row.enabled = registry_has_type_infos and selected_object is not None
|
row.operator(COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update UI FROM custom properties of current object" , icon="LOOP_BACK")
|
||||||
|
row.enabled = registry_has_type_infos and selected_object is not None
|
||||||
|
else:
|
||||||
|
if hasattr(layout,"progress") : # only for Blender > 4.0
|
||||||
|
layout.progress(factor = components_from_custom_properties_progress_current, text=f"updating {components_from_custom_properties_progress_current * 100.0:.2f}%")
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.operator(COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL.bl_idname, text="update UI FROM custom properties of ALL objects" , icon="LOOP_BACK")
|
components_from_custom_properties_progress_all = context.window_manager.components_from_custom_properties_progress_all
|
||||||
row.enabled = registry_has_type_infos
|
|
||||||
|
if components_from_custom_properties_progress_all == -1.0:
|
||||||
|
row.operator(COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL.bl_idname, text="update UI FROM custom properties of ALL objects" , icon="LOOP_BACK")
|
||||||
|
row.enabled = registry_has_type_infos
|
||||||
|
else:
|
||||||
|
if hasattr(layout,"progress") : # only for Blender > 4.0
|
||||||
|
layout.progress(factor = components_from_custom_properties_progress_all, text=f"updating {components_from_custom_properties_progress_all * 100.0:.2f}%")
|
||||||
|
|
||||||
|
|
||||||
class BEVY_COMPONENTS_PT_MissingTypesPanel(bpy.types.Panel):
|
class BEVY_COMPONENTS_PT_MissingTypesPanel(bpy.types.Panel):
|
||||||
|
|
|
@ -15,6 +15,7 @@ expected_custom_property_values = {'AComponentWithAnExtremlyExageratedOrMaybeNot
|
||||||
'0.0, low_frequency_boost_curvature: 0.0, prefilter_settings: (threshold: 0.0, threshold_softness: '
|
'0.0, low_frequency_boost_curvature: 0.0, prefilter_settings: (threshold: 0.0, threshold_softness: '
|
||||||
'0.0))',
|
'0.0))',
|
||||||
'BlueprintName': '(" ")',
|
'BlueprintName': '(" ")',
|
||||||
|
'BlueprintsList': '("")',
|
||||||
'BorderColor': '(Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0))',
|
'BorderColor': '(Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0))',
|
||||||
'Button': '()',
|
'Button': '()',
|
||||||
'CalculatedClip': '(clip: (max: Vec2(x:0.0, y:0.0), min: Vec2(x:0.0, y:0.0)))',
|
'CalculatedClip': '(clip: (max: Vec2(x:0.0, y:0.0), min: Vec2(x:0.0, y:0.0)))',
|
||||||
|
@ -214,6 +215,7 @@ expected_custom_property_values_randomized = {'AComponentWithAnExtremlyExagerate
|
||||||
'0.8133212327957153, prefilter_settings: (threshold: 0.8235888481140137, threshold_softness: '
|
'0.8133212327957153, prefilter_settings: (threshold: 0.8235888481140137, threshold_softness: '
|
||||||
'0.6534725427627563))',
|
'0.6534725427627563))',
|
||||||
'BlueprintName': '("sbnpsago")',
|
'BlueprintName': '("sbnpsago")',
|
||||||
|
'BlueprintsList': '("")',
|
||||||
'BorderColor': '(Rgba(red:0.5714026093482971, green:0.42888906598091125, blue:0.5780913233757019, '
|
'BorderColor': '(Rgba(red:0.5714026093482971, green:0.42888906598091125, blue:0.5780913233757019, '
|
||||||
'alpha:0.20609822869300842))',
|
'alpha:0.20609822869300842))',
|
||||||
'Button': '()',
|
'Button': '()',
|
||||||
|
|
|
@ -48,12 +48,12 @@ def test_components_should_generate_correct_custom_properties(setup_data):
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
errors.append(error)
|
errors.append(error)
|
||||||
|
|
||||||
'''pp = pprint.PrettyPrinter(depth=14, width=120)
|
pp = pprint.PrettyPrinter(depth=14, width=120)
|
||||||
print("CUSTOM PROPERTY VALUES")
|
print("CUSTOM PROPERTY VALUES")
|
||||||
pp.pprint(custom_property_values)'''
|
pp.pprint(custom_property_values)
|
||||||
|
|
||||||
assert len(errors) == 0
|
assert len(errors) == 0
|
||||||
assert len(added_components) == 158
|
assert len(added_components) == 159
|
||||||
|
|
||||||
|
|
||||||
def test_components_should_generate_correct_custom_properties_with_randomized_values(setup_data):
|
def test_components_should_generate_correct_custom_properties_with_randomized_values(setup_data):
|
||||||
|
@ -105,7 +105,7 @@ def test_components_should_generate_correct_custom_properties_with_randomized_va
|
||||||
|
|
||||||
print("error_components", error_components)
|
print("error_components", error_components)
|
||||||
assert len(errors) == 0
|
assert len(errors) == 0
|
||||||
assert len(added_components) == 158
|
assert len(added_components) == 159
|
||||||
|
|
||||||
def test_components_should_generate_correct_propertyGroup_values_from_custom_properties(setup_data):
|
def test_components_should_generate_correct_propertyGroup_values_from_custom_properties(setup_data):
|
||||||
registry = bpy.context.window_manager.components_registry
|
registry = bpy.context.window_manager.components_registry
|
||||||
|
@ -163,7 +163,7 @@ def test_components_should_generate_correct_propertyGroup_values_from_custom_pro
|
||||||
for index, error in enumerate(errors):
|
for index, error in enumerate(errors):
|
||||||
print("ERROR", error, failing_components[index])
|
print("ERROR", error, failing_components[index])
|
||||||
assert len(errors) == 0
|
assert len(errors) == 0
|
||||||
assert len(added_components) == 158
|
assert len(added_components) == 159
|
||||||
|
|
||||||
|
|
||||||
def test_remove_components(setup_data):
|
def test_remove_components(setup_data):
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import bpy
|
||||||
|
import pprint
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from .setup_data import setup_data
|
||||||
|
|
||||||
|
# small helpers
|
||||||
|
def get_component_metadata(object, component_name):
|
||||||
|
target_components_metadata = object.components_meta.components
|
||||||
|
component_meta = next(filter(lambda component: component["name"] == component_name, target_components_metadata), None)
|
||||||
|
return component_meta
|
||||||
|
|
||||||
|
def get_component_propGroup(registry, component_name, component_meta):
|
||||||
|
# component_type = registry.short_names_to_long_names[component_name]
|
||||||
|
# add_component_operator = bpy.ops.object.add_bevy_component
|
||||||
|
property_group_name = registry.get_propertyGroupName_from_shortName(component_name)
|
||||||
|
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||||
|
return propertyGroup
|
||||||
|
|
||||||
|
|
||||||
|
def test_rename_component_single_unit_struct(setup_data):
|
||||||
|
registry = bpy.context.window_manager.components_registry
|
||||||
|
registry.schemaPath = setup_data["schema_path"]
|
||||||
|
bpy.ops.object.reload_registry()
|
||||||
|
|
||||||
|
rename_component_operator = bpy.ops.object.rename_bevy_component
|
||||||
|
object = bpy.context.object
|
||||||
|
|
||||||
|
|
||||||
|
source_component_name = "SomeOldUnitStruct"
|
||||||
|
target_component_name = "UnitTest"
|
||||||
|
object[source_component_name] = '()'
|
||||||
|
|
||||||
|
rename_component_operator(original_name=source_component_name, new_name=target_component_name, target_objects=json.dumps([object.name]))
|
||||||
|
|
||||||
|
is_old_component_in_object = source_component_name in object
|
||||||
|
is_new_component_in_object = target_component_name in object
|
||||||
|
assert is_old_component_in_object == False
|
||||||
|
assert is_new_component_in_object == True
|
||||||
|
assert object[target_component_name] == '()'
|
||||||
|
assert get_component_propGroup(registry, target_component_name, get_component_metadata(object, target_component_name)) != None
|
||||||
|
|
||||||
|
|
||||||
|
def test_rename_component_single_complex_struct(setup_data):
|
||||||
|
registry = bpy.context.window_manager.components_registry
|
||||||
|
registry.schemaPath = setup_data["schema_path"]
|
||||||
|
bpy.ops.object.reload_registry()
|
||||||
|
|
||||||
|
rename_component_operator = bpy.ops.object.rename_bevy_component
|
||||||
|
object = bpy.context.object
|
||||||
|
|
||||||
|
|
||||||
|
source_component_name = "ProxyCollider"
|
||||||
|
target_component_name = "Collider"
|
||||||
|
object[source_component_name] = 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||||
|
|
||||||
|
rename_component_operator(original_name=source_component_name, new_name=target_component_name, target_objects=json.dumps([object.name]))
|
||||||
|
|
||||||
|
is_old_component_in_object = source_component_name in object
|
||||||
|
is_new_component_in_object = target_component_name in object
|
||||||
|
assert is_old_component_in_object == False
|
||||||
|
assert is_new_component_in_object == True
|
||||||
|
assert object[target_component_name] == 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||||
|
assert get_component_propGroup(registry, target_component_name, get_component_metadata(object, target_component_name)) != None
|
||||||
|
|
||||||
|
|
||||||
|
def test_rename_component_bulk(setup_data):
|
||||||
|
registry = bpy.context.window_manager.components_registry
|
||||||
|
registry.schemaPath = setup_data["schema_path"]
|
||||||
|
bpy.ops.object.reload_registry()
|
||||||
|
|
||||||
|
rename_component_operator = bpy.ops.object.rename_bevy_component
|
||||||
|
|
||||||
|
source_component_name = "SomeOldUnitStruct"
|
||||||
|
target_component_name = "UnitTest"
|
||||||
|
objects_names = []
|
||||||
|
for object in bpy.data.objects:
|
||||||
|
object[source_component_name] = '()'
|
||||||
|
objects_names.append(object.name)
|
||||||
|
|
||||||
|
# bulk rename
|
||||||
|
rename_component_operator(original_name=source_component_name, new_name=target_component_name, target_objects=json.dumps(objects_names))
|
||||||
|
|
||||||
|
for object in bpy.data.objects:
|
||||||
|
is_old_component_in_object = source_component_name in object
|
||||||
|
is_new_component_in_object = target_component_name in object
|
||||||
|
assert is_old_component_in_object == False
|
||||||
|
assert is_new_component_in_object == True
|
||||||
|
assert object[target_component_name] == '()'
|
||||||
|
assert get_component_propGroup(registry, target_component_name, get_component_metadata(object, target_component_name)) != None
|
||||||
|
|
||||||
|
def test_rename_component_single_error_handling(setup_data):
|
||||||
|
registry = bpy.context.window_manager.components_registry
|
||||||
|
registry.schemaPath = setup_data["schema_path"]
|
||||||
|
bpy.ops.object.reload_registry()
|
||||||
|
|
||||||
|
rename_component_operator = bpy.ops.object.rename_bevy_component
|
||||||
|
object = bpy.context.object
|
||||||
|
|
||||||
|
|
||||||
|
source_component_name = "SomeOldUnitStruct"
|
||||||
|
target_component_name = "UnitTest"
|
||||||
|
object[source_component_name] = 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||||
|
|
||||||
|
expected_error = f'Error: Failed to rename component: Errors:["wrong custom property values to generate target component: object: \'{object.name}\', error: input string too big for a unit struct"]\n'
|
||||||
|
expected_error = re.escape(expected_error)
|
||||||
|
with pytest.raises(Exception, match=expected_error):
|
||||||
|
rename_component_operator(original_name=source_component_name, new_name=target_component_name, target_objects=json.dumps([object.name]))
|
||||||
|
|
||||||
|
target_component_metadata = get_component_metadata(object, target_component_name)
|
||||||
|
|
||||||
|
is_old_component_in_object = source_component_name in object
|
||||||
|
is_new_component_in_object = target_component_name in object
|
||||||
|
assert is_old_component_in_object == False
|
||||||
|
assert is_new_component_in_object == True
|
||||||
|
assert object[target_component_name] == 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||||
|
assert get_component_propGroup(registry, target_component_name, target_component_metadata) != None
|
||||||
|
assert target_component_metadata.invalid == True
|
||||||
|
|
||||||
|
assert target_component_metadata.invalid_details == 'wrong custom property value, overwrite them by changing the values in the ui or change them & regenerate'
|
||||||
|
|
||||||
|
def test_rename_component_single_error_handling_clean_errors(setup_data):
|
||||||
|
registry = bpy.context.window_manager.components_registry
|
||||||
|
registry.schemaPath = setup_data["schema_path"]
|
||||||
|
bpy.ops.object.reload_registry()
|
||||||
|
|
||||||
|
rename_component_operator = bpy.ops.object.rename_bevy_component
|
||||||
|
object = bpy.context.object
|
||||||
|
|
||||||
|
|
||||||
|
source_component_name = "SomeOldUnitStruct"
|
||||||
|
target_component_name = "UnitTest"
|
||||||
|
object[source_component_name] = 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||||
|
|
||||||
|
expected_error = f'Error: Failed to rename component: Errors:["wrong custom property values to generate target component: object: \'{object.name}\', error: input string too big for a unit struct"]\n'
|
||||||
|
expected_error = re.escape(expected_error)
|
||||||
|
with pytest.raises(Exception, match=expected_error):
|
||||||
|
rename_component_operator(original_name=source_component_name, new_name=target_component_name, target_objects=json.dumps([object.name]))
|
||||||
|
|
||||||
|
target_component_metadata = get_component_metadata(object, target_component_name)
|
||||||
|
|
||||||
|
is_old_component_in_object = source_component_name in object
|
||||||
|
is_new_component_in_object = target_component_name in object
|
||||||
|
assert is_old_component_in_object == False
|
||||||
|
assert is_new_component_in_object == True
|
||||||
|
assert object[target_component_name] == 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||||
|
assert get_component_propGroup(registry, target_component_name, target_component_metadata) != None
|
||||||
|
assert target_component_metadata.invalid == True
|
||||||
|
|
||||||
|
assert target_component_metadata.invalid_details == 'wrong custom property value, overwrite them by changing the values in the ui or change them & regenerate'
|
||||||
|
|
||||||
|
# if we fix the custom property value & regen the ui, it should be all good
|
||||||
|
regen_component_operator = bpy.ops.object.refresh_ui_from_custom_properties_current
|
||||||
|
object[target_component_name] = ''
|
||||||
|
regen_component_operator()
|
||||||
|
|
||||||
|
assert target_component_metadata.invalid == False
|
|
@ -1,7 +1,7 @@
|
||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "gltf_auto_export",
|
"name": "gltf_auto_export",
|
||||||
"author": "kaosigh",
|
"author": "kaosigh",
|
||||||
"version": (0, 15, 0),
|
"version": (0, 16, 0),
|
||||||
"blender": (3, 4, 0),
|
"blender": (3, 4, 0),
|
||||||
"location": "File > Import-Export",
|
"location": "File > Import-Export",
|
||||||
"description": "glTF/glb auto-export",
|
"description": "glTF/glb auto-export",
|
||||||
|
|
|
@ -4,7 +4,7 @@ import bpy
|
||||||
from ..helpers.generate_and_export import generate_and_export
|
from ..helpers.generate_and_export import generate_and_export
|
||||||
from .export_gltf import (generate_gltf_export_preferences, export_gltf)
|
from .export_gltf import (generate_gltf_export_preferences, export_gltf)
|
||||||
from ..modules.bevy_dynamic import is_object_dynamic, is_object_static
|
from ..modules.bevy_dynamic import is_object_dynamic, is_object_static
|
||||||
from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into
|
from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into, inject_blueprints_list_into_main_scene, remove_blueprints_list_from_main_scene
|
||||||
|
|
||||||
|
|
||||||
# export all main scenes
|
# export all main scenes
|
||||||
|
@ -16,6 +16,7 @@ def export_main_scene(scene, folder_path, addon_prefs, library_collections):
|
||||||
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
|
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
|
||||||
export_output_folder = getattr(addon_prefs,"export_output_folder")
|
export_output_folder = getattr(addon_prefs,"export_output_folder")
|
||||||
export_blueprints = getattr(addon_prefs,"export_blueprints")
|
export_blueprints = getattr(addon_prefs,"export_blueprints")
|
||||||
|
legacy_mode = getattr(addon_prefs, "export_legacy_mode")
|
||||||
export_separate_dynamic_and_static_objects = getattr(addon_prefs, "export_separate_dynamic_and_static_objects")
|
export_separate_dynamic_and_static_objects = getattr(addon_prefs, "export_separate_dynamic_and_static_objects")
|
||||||
|
|
||||||
gltf_output_path = os.path.join(folder_path, export_output_folder, scene.name)
|
gltf_output_path = os.path.join(folder_path, export_output_folder, scene.name)
|
||||||
|
@ -29,6 +30,9 @@ def export_main_scene(scene, folder_path, addon_prefs, library_collections):
|
||||||
}
|
}
|
||||||
|
|
||||||
if export_blueprints :
|
if export_blueprints :
|
||||||
|
if not legacy_mode:
|
||||||
|
inject_blueprints_list_into_main_scene(scene)
|
||||||
|
|
||||||
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
|
||||||
|
@ -67,5 +71,8 @@ def export_main_scene(scene, folder_path, addon_prefs, library_collections):
|
||||||
print(" exporting gltf to", gltf_output_path, ".gltf/glb")
|
print(" exporting gltf to", gltf_output_path, ".gltf/glb")
|
||||||
export_gltf(gltf_output_path, export_settings)
|
export_gltf(gltf_output_path, export_settings)
|
||||||
|
|
||||||
|
if not legacy_mode:
|
||||||
|
remove_blueprints_list_from_main_scene(scene)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,16 +36,23 @@ def get_marked_collections(scene, addon_prefs):
|
||||||
return (collection_names, marked_collections)
|
return (collection_names, marked_collections)
|
||||||
|
|
||||||
# gets all collections within collections that might also be relevant
|
# gets all collections within collections that might also be relevant
|
||||||
def get_sub_collections(collections, parent, children_per_collection):
|
def get_sub_collections(collections, parent=None, children_per_collection=None):
|
||||||
|
if parent == None:
|
||||||
|
parent = CollectionNode()
|
||||||
|
if children_per_collection == None:
|
||||||
|
children_per_collection = {}
|
||||||
|
|
||||||
collection_names = set()
|
collection_names = set()
|
||||||
used_collections = []
|
used_collections = []
|
||||||
|
|
||||||
for root_collection in collections:
|
for root_collection in collections:
|
||||||
node = Node(name=root_collection.name, parent=parent)
|
#print("collections", collections)
|
||||||
|
node = CollectionNode(name=root_collection.name, parent=parent)
|
||||||
parent.children.append(node)
|
parent.children.append(node)
|
||||||
|
|
||||||
#print("root collection", root_collection.name)
|
#print("root collection", root_collection.name)
|
||||||
for collection in traverse_tree(root_collection): # TODO: filter out COLLECTIONS that have the flatten flag (unlike the flatten flag on colleciton instances themselves)
|
for collection in traverse_tree(root_collection): # TODO: filter out COLLECTIONS that have the flatten flag (unlike the flatten flag on colleciton instances themselves)
|
||||||
|
#print("sub", collection)
|
||||||
node_name = collection.name
|
node_name = collection.name
|
||||||
children_per_collection[node_name] = []
|
children_per_collection[node_name] = []
|
||||||
#print(" scanning", collection.name)
|
#print(" scanning", collection.name)
|
||||||
|
@ -53,12 +60,15 @@ def get_sub_collections(collections, parent, children_per_collection):
|
||||||
#print("FLATTEN", object.name, 'Flatten' in object)
|
#print("FLATTEN", object.name, 'Flatten' in object)
|
||||||
if object.instance_type == 'COLLECTION' : # and not 'Flatten' in object:
|
if object.instance_type == 'COLLECTION' : # and not 'Flatten' in object:
|
||||||
collection_name = object.instance_collection.name
|
collection_name = object.instance_collection.name
|
||||||
|
#print("sub obj", collection_name)
|
||||||
|
# FIXME: not sure:
|
||||||
|
children_per_collection[node_name].append(collection_name)
|
||||||
|
|
||||||
(sub_names, sub_collections) = get_sub_collections([object.instance_collection], node, children_per_collection)
|
(sub_names, sub_collections) = get_sub_collections([object.instance_collection], node, children_per_collection)
|
||||||
if len(list(sub_names)) > 0:
|
if len(list(sub_names)) > 0:
|
||||||
children_per_collection[node_name] += (list(sub_names))
|
children_per_collection[node_name] += (list(sub_names))
|
||||||
#print(" found sub collection in use", object.name, object.instance_collection)
|
#print(" found sub collection in use", object.name, object.instance_collection)
|
||||||
|
|
||||||
|
|
||||||
if not collection_name in collection_names:
|
if not collection_name in collection_names:
|
||||||
collection_names.add(collection_name)
|
collection_names.add(collection_name)
|
||||||
used_collections.append(object.instance_collection)
|
used_collections.append(object.instance_collection)
|
||||||
|
@ -77,7 +87,7 @@ def flatten_collection_tree(node, children_per_collection):
|
||||||
children_per_collection[node.name] = list(set( children_per_collection[node.name]))
|
children_per_collection[node.name] = list(set( children_per_collection[node.name]))
|
||||||
|
|
||||||
|
|
||||||
class Node :
|
class CollectionNode :
|
||||||
def __init__(self, name="", parent=None):
|
def __init__(self, name="", parent=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.children = []
|
self.children = []
|
||||||
|
@ -93,7 +103,7 @@ def get_exportable_collections(main_scenes, library_scenes, addon_prefs):
|
||||||
|
|
||||||
all_collections = []
|
all_collections = []
|
||||||
all_collection_names = []
|
all_collection_names = []
|
||||||
root_node = Node()
|
root_node = CollectionNode()
|
||||||
root_node.name = "root"
|
root_node.name = "root"
|
||||||
children_per_collection = {}
|
children_per_collection = {}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
import json
|
||||||
import bpy
|
import bpy
|
||||||
from .helpers_collections import (set_active_collection)
|
from .helpers_collections import (CollectionNode, get_sub_collections, get_used_collections, set_active_collection)
|
||||||
from .object_makers import (make_empty)
|
from .object_makers import (make_empty)
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,6 +74,17 @@ def copy_hollowed_collection_into(source_collection, destination_collection, par
|
||||||
empty_obj['BlueprintName'] = '"'+collection_name+'"' if legacy_mode else '("'+collection_name+'")'
|
empty_obj['BlueprintName'] = '"'+collection_name+'"' if legacy_mode else '("'+collection_name+'")'
|
||||||
empty_obj['SpawnHere'] = '()'
|
empty_obj['SpawnHere'] = '()'
|
||||||
|
|
||||||
|
# we also inject a list of all sub blueprints, so that the bevy side can preload them
|
||||||
|
if not legacy_mode:
|
||||||
|
root_node = CollectionNode()
|
||||||
|
root_node.name = "root"
|
||||||
|
children_per_collection = {}
|
||||||
|
print("collection stuff", original_name)
|
||||||
|
get_sub_collections([object.instance_collection], root_node, children_per_collection)
|
||||||
|
empty_obj["BlueprintsList"] = f"({json.dumps(dict(children_per_collection))})"
|
||||||
|
#empty_obj["Assets"] = {"Animations": [], "Materials": [], "Models":[], "Textures":[], "Audio":[], "Other":[]}
|
||||||
|
|
||||||
|
|
||||||
# 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(object, component_name): #copy only valid properties
|
if component_name not in custom_properties_to_filter_out and is_component_valid(object, component_name): #copy only valid properties
|
||||||
|
@ -149,3 +161,45 @@ def get_scenes(addon_prefs):
|
||||||
return [level_scene_names, level_scenes, library_scene_names, library_scenes]
|
return [level_scene_names, level_scenes, library_scene_names, library_scenes]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def inject_blueprints_list_into_main_scene(scene):
|
||||||
|
print("injecting assets/blueprints data into scene")
|
||||||
|
root_collection = scene.collection
|
||||||
|
assets_list = None
|
||||||
|
assets_list_name = f"assets_list_{scene.name}_components"
|
||||||
|
for object in scene.objects:
|
||||||
|
if object.name == assets_list_name:
|
||||||
|
assets_list = object
|
||||||
|
break
|
||||||
|
|
||||||
|
if assets_list is None:
|
||||||
|
assets_list = make_empty(assets_list_name, [0,0,0], [0,0,0], [0,0,0], root_collection)
|
||||||
|
|
||||||
|
# find all blueprints used in a scene
|
||||||
|
# TODO: export a tree rather than a flat list ? because you could have potential clashing items in flat lists (amongst other issues)
|
||||||
|
(collection_names, collections) = get_used_collections(scene)
|
||||||
|
root_node = CollectionNode()
|
||||||
|
root_node.name = "root"
|
||||||
|
children_per_collection = {}
|
||||||
|
|
||||||
|
#print("collection_names", collection_names, "collections", collections)
|
||||||
|
get_sub_collections(collections, root_node, children_per_collection)
|
||||||
|
# what about marked assets ?
|
||||||
|
# what about audio assets ?
|
||||||
|
# what about materials ?
|
||||||
|
# object['MaterialInfo'] = '(name: "'+material.name+'", source: "'+current_project_name + '")'
|
||||||
|
|
||||||
|
#assets_list["blueprints_direct"] = list(collection_names)
|
||||||
|
assets_list["BlueprintsList"] = f"({json.dumps(dict(children_per_collection))})"
|
||||||
|
#assets_list["Materials"]= '()'
|
||||||
|
|
||||||
|
def remove_blueprints_list_from_main_scene(scene):
|
||||||
|
assets_list = None
|
||||||
|
assets_list_name = f"assets_list_{scene.name}_components"
|
||||||
|
|
||||||
|
for object in scene.objects:
|
||||||
|
if object.name == assets_list_name:
|
||||||
|
assets_list = object
|
||||||
|
if assets_list is not None:
|
||||||
|
bpy.data.objects.remove(assets_list, do_unlink=True)
|
||||||
|
|
|
@ -19,6 +19,8 @@ def setup_data(request):
|
||||||
|
|
||||||
def finalizer():
|
def finalizer():
|
||||||
print("\nPerforming teardown...")
|
print("\nPerforming teardown...")
|
||||||
|
get_orphan_data()
|
||||||
|
|
||||||
if os.path.exists(models_path):
|
if os.path.exists(models_path):
|
||||||
shutil.rmtree(models_path)
|
shutil.rmtree(models_path)
|
||||||
|
|
||||||
|
@ -33,9 +35,14 @@ def setup_data(request):
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_orphan_data():
|
||||||
|
orphan_meshes = [m.name for m in bpy.data.meshes if m.users == 0]
|
||||||
|
# print("orphan meshes before", orphan_meshes)
|
||||||
|
|
||||||
def test_export_do_not_export_blueprints(setup_data):
|
def test_export_do_not_export_blueprints(setup_data):
|
||||||
auto_export_operator = bpy.ops.export_scenes.auto_gltf
|
auto_export_operator = bpy.ops.export_scenes.auto_gltf
|
||||||
|
|
||||||
# first, configure things
|
# first, configure things
|
||||||
# we use the global settings for that
|
# we use the global settings for that
|
||||||
export_props = {
|
export_props = {
|
||||||
|
@ -57,7 +64,6 @@ def test_export_do_not_export_blueprints(setup_data):
|
||||||
|
|
||||||
def test_export_custom_blueprints_path(setup_data):
|
def test_export_custom_blueprints_path(setup_data):
|
||||||
auto_export_operator = bpy.ops.export_scenes.auto_gltf
|
auto_export_operator = bpy.ops.export_scenes.auto_gltf
|
||||||
|
|
||||||
# first, configure things
|
# first, configure things
|
||||||
# we use the global settings for that
|
# we use the global settings for that
|
||||||
export_props = {
|
export_props = {
|
||||||
|
@ -210,3 +216,27 @@ def test_export_separate_dynamic_and_static_objects(setup_data):
|
||||||
|
|
||||||
assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True
|
assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True
|
||||||
assert os.path.exists(os.path.join(setup_data["models_path"], "World_dynamic.glb")) == True
|
assert os.path.exists(os.path.join(setup_data["models_path"], "World_dynamic.glb")) == True
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_should_not_generate_orphan_data(setup_data):
|
||||||
|
auto_export_operator = bpy.ops.export_scenes.auto_gltf
|
||||||
|
|
||||||
|
# first, configure things
|
||||||
|
# we use the global settings for that
|
||||||
|
export_props = {
|
||||||
|
"main_scene_names" : ['World'],
|
||||||
|
"library_scene_names": ['Library']
|
||||||
|
}
|
||||||
|
stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")
|
||||||
|
stored_settings.clear()
|
||||||
|
stored_settings.write(json.dumps(export_props))
|
||||||
|
|
||||||
|
auto_export_operator(
|
||||||
|
direct_mode=True,
|
||||||
|
export_output_folder="./models",
|
||||||
|
export_scene_settings=True,
|
||||||
|
export_blueprints=False,
|
||||||
|
)
|
||||||
|
assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True
|
||||||
|
assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint1.glb")) == False
|
||||||
|
|
||||||
|
|
|
@ -16,18 +16,16 @@ def setup_data(request):
|
||||||
root_path = "../../testing/bevy_example"
|
root_path = "../../testing/bevy_example"
|
||||||
assets_root_path = os.path.join(root_path, "assets")
|
assets_root_path = os.path.join(root_path, "assets")
|
||||||
models_path = os.path.join(assets_root_path, "models")
|
models_path = os.path.join(assets_root_path, "models")
|
||||||
#materials_path = os.path.join("../../testing", "materials")
|
materials_path = os.path.join(assets_root_path, "materials")
|
||||||
#other_materials_path = os.path.join("../../testing", "other_materials")
|
#other_materials_path = os.path.join("../../testing", "other_materials")
|
||||||
|
|
||||||
print("\nPerforming teardown...")
|
print("\nPerforming teardown...")
|
||||||
if os.path.exists(models_path):
|
if os.path.exists(models_path):
|
||||||
shutil.rmtree(models_path)
|
shutil.rmtree(models_path)
|
||||||
|
|
||||||
"""if os.path.exists(materials_path):
|
if os.path.exists(materials_path):
|
||||||
shutil.rmtree(materials_path)
|
shutil.rmtree(materials_path)
|
||||||
|
|
||||||
if os.path.exists(other_materials_path):
|
|
||||||
shutil.rmtree(other_materials_path)"""
|
|
||||||
diagnostics_file_path = os.path.join(root_path, "bevy_diagnostics.json")
|
diagnostics_file_path = os.path.join(root_path, "bevy_diagnostics.json")
|
||||||
if os.path.exists(diagnostics_file_path):
|
if os.path.exists(diagnostics_file_path):
|
||||||
os.remove(diagnostics_file_path)
|
os.remove(diagnostics_file_path)
|
||||||
|
@ -75,7 +73,8 @@ def test_export_complex(setup_data):
|
||||||
export_scene_settings=True,
|
export_scene_settings=True,
|
||||||
export_blueprints=True,
|
export_blueprints=True,
|
||||||
export_legacy_mode=False,
|
export_legacy_mode=False,
|
||||||
export_animations=True
|
export_animations=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
|
||||||
|
@ -84,7 +83,6 @@ def test_export_complex(setup_data):
|
||||||
# blueprint5 => has NO instance, not marked as asset, should NOT export
|
# blueprint5 => has NO instance, not marked as asset, should NOT export
|
||||||
|
|
||||||
assert os.path.exists(os.path.join(models_path, "World.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "World.glb")) == True
|
||||||
|
|
||||||
assert os.path.exists(os.path.join(models_path, "library", "Blueprint1.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "library", "Blueprint1.glb")) == True
|
||||||
assert os.path.exists(os.path.join(models_path, "library", "Blueprint2.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "library", "Blueprint2.glb")) == True
|
||||||
assert os.path.exists(os.path.join(models_path, "library", "Blueprint3.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "library", "Blueprint3.glb")) == True
|
||||||
|
@ -93,13 +91,14 @@ def test_export_complex(setup_data):
|
||||||
assert os.path.exists(os.path.join(models_path, "library", "Blueprint6_animated.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "library", "Blueprint6_animated.glb")) == True
|
||||||
assert os.path.exists(os.path.join(models_path, "library", "Blueprint7_hierarchy.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "library", "Blueprint7_hierarchy.glb")) == True
|
||||||
|
|
||||||
|
# 'assets_list_'+scene.name+"_components" should have been removed after the export
|
||||||
|
assets_list_object_name = "assets_list_"+"World"+"_components"
|
||||||
|
assets_list_object_present = assets_list_object_name in bpy.data.objects
|
||||||
|
assert assets_list_object_present == False
|
||||||
|
|
||||||
# now run bevy
|
# now run bevy
|
||||||
command = "cargo run --features bevy/dynamic_linking"
|
command = "cargo run --features bevy/dynamic_linking"
|
||||||
# assert getattr(propertyGroup, 'a') == 0.5714026093482971
|
|
||||||
FNULL = open(os.devnull, 'w') #use this if you want to suppress output to stdout from the subprocess
|
FNULL = open(os.devnull, 'w') #use this if you want to suppress output to stdout from the subprocess
|
||||||
filename = "my_file.dat"
|
|
||||||
args = command
|
|
||||||
#subprocess.call(args, stdout=FNULL, stderr=FNULL, shell=False, cwd=bevy_run_exec_path)
|
|
||||||
return_code = subprocess.call(["cargo", "run", "--features", "bevy/dynamic_linking"], cwd=root_path)
|
return_code = subprocess.call(["cargo", "run", "--features", "bevy/dynamic_linking"], cwd=root_path)
|
||||||
print("RETURN CODE OF BEVY APP", return_code)
|
print("RETURN CODE OF BEVY APP", return_code)
|
||||||
assert return_code == 0
|
assert return_code == 0
|
||||||
|
@ -110,6 +109,7 @@ def test_export_complex(setup_data):
|
||||||
assert diagnostics["animations"] == True
|
assert diagnostics["animations"] == True
|
||||||
assert diagnostics["cylinder_found"] == True
|
assert diagnostics["cylinder_found"] == True
|
||||||
assert diagnostics["empty_found"] == True
|
assert diagnostics["empty_found"] == True
|
||||||
|
assert diagnostics["blueprints_list_found"] == True
|
||||||
|
|
||||||
# last but not least, do a visual compare
|
# last but not least, do a visual compare
|
||||||
screenshot_expected_path = os.path.join(root_path, "expected_screenshot.png")
|
screenshot_expected_path = os.path.join(root_path, "expected_screenshot.png")
|
||||||
|
|