mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-22 20:00:53 +00:00
Compare commits
6 Commits
185c25f7b2
...
cf87ace91f
Author | SHA1 | Date | |
---|---|---|---|
|
cf87ace91f | ||
|
e7118f2565 | ||
|
f224148e1d | ||
|
ff68109f0f | ||
|
98a654095b | ||
|
ca02c1df8c |
36
RELEASE_NOTES.md
Normal file
36
RELEASE_NOTES.md
Normal file
@ -0,0 +1,36 @@
|
||||
This new release has a lot of breaking changes in the Blender tooling as well as in the Bevy crates
|
||||
Here is a rundown + rationale behing the changes:
|
||||
|
||||
|
||||
- Blender add-ons
|
||||
- Auto_export:
|
||||
- spliting out of gltf exporter settings
|
||||
up until now , the auto export add-on provided a *subset* of gltf settings, causing issues due to lack of features (in particular for animation settings),
|
||||
and it required me to play catch up every time there where added / changed settings
|
||||
the Ui of Blender's gltf exporter is NOT reusable in any form or way inside another add-on . I tried various solutions such as turning the auto exporter
|
||||
into a 'extension' of the standard exporter, but none of them worked in a reliable or 'nice to use' way
|
||||
|
||||
So I decided to split the gltf settings from the auto_export setting, this has multiple advantages:
|
||||
* access to ALL gltf settings
|
||||
* the gltf UI & settings will always keep up with the official releases
|
||||
* less maintenance work
|
||||
|
||||
The only disadvantage is that the standard gltf exporter is a normal exporter, so it generates a 'fake' gltf file that immediatly gets deleted after export,
|
||||
so you will need to use the new side panel to set your gltf settings:
|
||||
* this is also done to ensure that the gltf settings settings used for auto export are NOT interfering with the ones you might use when exporting gltf files normally
|
||||
|
||||
- change detection :
|
||||
after spending MANY hours analysing the issues with change detection (using some of Blender's built in logic) I came to the conclusion that it was not going to be reliable enough, so I opted for a 'hand made' brute force approach to change detection:
|
||||
* every time you save (& thus export), all your scenes are serialized / hashed and that hashed version is compared to the one from the last save to determine what changed and what did not
|
||||
|
||||
- handling of external/ embeded / split etc collections
|
||||
while adding tests I also realised that the detection of which main scenes & blueprints that needed to be exported was faulty, so I rewrote all the code in charge of that : this means that in general , based on your settings, the add-on will more accuratly export only those levels/blueprints that really NEED to be exported
|
||||
|
||||
- improved handling of multi-blend file projects
|
||||
Up until now, all export paths where relative ** to the blend file itself** which could lead to issues when working with multiple blend files
|
||||
Also for future improvements regarding assets managment, I changed the export paths to be relative to a new "project root" folder which is your *Bevy project's root folder*
|
||||
- the levels/worlds now also got a seperate setting so you can easilly set where to export them too (they are not dumped out into the main export folder anymore), giving you more control over your non blueprint exports
|
||||
|
||||
- bevy_components
|
||||
Up until now , it was not possible to have multiple components with the same name (ie ) as all the logic was based on short names
|
||||
This required completely changing HOW/WHERE components are stored in objects, and they are now stored inside a 'bevy_components' custom property
|
@ -31,8 +31,7 @@ pub fn add_components_from_gltf_extras(world: &mut World) {
|
||||
);
|
||||
|
||||
let type_registry: &AppTypeRegistry = world.resource();
|
||||
let type_registry = type_registry.read();
|
||||
|
||||
let type_registry = type_registry.read();
|
||||
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
|
||||
|
||||
// we assign the components specified /xxx_components objects to their parent node
|
||||
|
@ -13,10 +13,84 @@ pub fn ronstring_to_reflect_component(
|
||||
) -> Vec<(Box<dyn Reflect>, TypeRegistration)> {
|
||||
let lookup: HashMap<String, Value> = ron::from_str(ron_string).unwrap();
|
||||
let mut components: Vec<(Box<dyn Reflect>, TypeRegistration)> = Vec::new();
|
||||
for (key, value) in lookup.into_iter() {
|
||||
let type_string = key.replace("component: ", "").trim().to_string();
|
||||
let capitalized_type_name = capitalize_first_letter(type_string.as_str());
|
||||
// println!("ron_string {:?}", ron_string);
|
||||
for (name, value) in lookup.into_iter() {
|
||||
|
||||
|
||||
let parsed_value: String;
|
||||
match value.clone() {
|
||||
Value::String(str) => {
|
||||
parsed_value = str;
|
||||
}
|
||||
_ => parsed_value = ron::to_string(&value).unwrap().to_string(),
|
||||
}
|
||||
|
||||
if name.as_str() == "bevy_components" {
|
||||
bevy_components_string_to_components(parsed_value, type_registry, &mut components)
|
||||
} else {
|
||||
components_string_to_components(name, value, parsed_value, type_registry, &mut components)
|
||||
}
|
||||
}
|
||||
components
|
||||
}
|
||||
|
||||
fn components_string_to_components(
|
||||
name: String,
|
||||
value: Value,
|
||||
parsed_value: String,
|
||||
type_registry: &TypeRegistry,
|
||||
components: &mut Vec<(Box<dyn Reflect>, TypeRegistration)>
|
||||
){
|
||||
let type_string = name.replace("component: ", "").trim().to_string();
|
||||
let capitalized_type_name = capitalize_first_letter(type_string.as_str());
|
||||
|
||||
if let Some(type_registration) =
|
||||
type_registry.get_with_short_type_path(capitalized_type_name.as_str())
|
||||
{
|
||||
debug!("TYPE INFO {:?}", type_registration.type_info());
|
||||
|
||||
let ron_string = format!(
|
||||
"{{ \"{}\":{} }}",
|
||||
type_registration.type_info().type_path(),
|
||||
parsed_value
|
||||
);
|
||||
|
||||
// usefull to determine what an entity looks like Serialized
|
||||
/*let test_struct = CameraRenderGraph::new("name");
|
||||
let serializer = ReflectSerializer::new(&test_struct, &type_registry);
|
||||
let serialized =
|
||||
ron::ser::to_string_pretty(&serializer, ron::ser::PrettyConfig::default()).unwrap();
|
||||
println!("serialized Component {}", serialized);*/
|
||||
|
||||
debug!("component data ron string {}", ron_string);
|
||||
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str())
|
||||
.expect("deserialzer should have been generated from string");
|
||||
let reflect_deserializer = UntypedReflectDeserializer::new(type_registry);
|
||||
let component = reflect_deserializer
|
||||
.deserialize(&mut deserializer)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"failed to deserialize component {} with value: {:?}",
|
||||
name, value
|
||||
)
|
||||
});
|
||||
|
||||
debug!("component {:?}", component);
|
||||
debug!("real type {:?}", component.get_represented_type_info());
|
||||
components.push((component, type_registration.clone()));
|
||||
debug!("found type registration for {}", capitalized_type_name);
|
||||
} else {
|
||||
warn!("no type registration for {}", capitalized_type_name);
|
||||
}
|
||||
}
|
||||
|
||||
fn bevy_components_string_to_components(
|
||||
parsed_value: String,
|
||||
type_registry: &TypeRegistry,
|
||||
components: &mut Vec<(Box<dyn Reflect>, TypeRegistration)>
|
||||
){
|
||||
let lookup: HashMap<String, Value> = ron::from_str(&parsed_value).unwrap();
|
||||
for (key, value) in lookup.into_iter() {
|
||||
let parsed_value: String;
|
||||
match value.clone() {
|
||||
Value::String(str) => {
|
||||
@ -26,7 +100,7 @@ pub fn ronstring_to_reflect_component(
|
||||
}
|
||||
|
||||
if let Some(type_registration) =
|
||||
type_registry.get_with_short_type_path(capitalized_type_name.as_str())
|
||||
type_registry.get_with_type_path(key.as_str())
|
||||
{
|
||||
debug!("TYPE INFO {:?}", type_registration.type_info());
|
||||
|
||||
@ -36,13 +110,6 @@ pub fn ronstring_to_reflect_component(
|
||||
parsed_value
|
||||
);
|
||||
|
||||
// usefull to determine what an entity looks like Serialized
|
||||
/*let test_struct = CameraRenderGraph::new("name");
|
||||
let serializer = ReflectSerializer::new(&test_struct, &type_registry);
|
||||
let serialized =
|
||||
ron::ser::to_string_pretty(&serializer, ron::ser::PrettyConfig::default()).unwrap();
|
||||
println!("serialized Component {}", serialized);*/
|
||||
|
||||
debug!("component data ron string {}", ron_string);
|
||||
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str())
|
||||
.expect("deserialzer should have been generated from string");
|
||||
@ -59,10 +126,9 @@ pub fn ronstring_to_reflect_component(
|
||||
debug!("component {:?}", component);
|
||||
debug!("real type {:?}", component.get_represented_type_info());
|
||||
components.push((component, type_registration.clone()));
|
||||
debug!("found type registration for {}", capitalized_type_name);
|
||||
debug!("found type registration for {}", key);
|
||||
} else {
|
||||
warn!("no type registration for {}", capitalized_type_name);
|
||||
warn!("no type registration for {}", key);
|
||||
}
|
||||
}
|
||||
components
|
||||
}
|
||||
}
|
@ -3168,6 +3168,117 @@
|
||||
"type": "string",
|
||||
"typeInfo": "Enum"
|
||||
},
|
||||
"bevy_example::test_components::HashmapTestIntColor": {
|
||||
"additionalProperties": false,
|
||||
"isComponent": true,
|
||||
"isResource": false,
|
||||
"properties": {
|
||||
"inner": {
|
||||
"type": {
|
||||
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<u32, bevy_render::color::Color, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"inner"
|
||||
],
|
||||
"short_name": "HashmapTestIntColor",
|
||||
"title": "bevy_example::test_components::HashmapTestIntColor",
|
||||
"type": "object",
|
||||
"typeInfo": "Struct"
|
||||
},
|
||||
"bevy_example::test_components::HashmapTestIntString": {
|
||||
"additionalProperties": false,
|
||||
"isComponent": true,
|
||||
"isResource": false,
|
||||
"properties": {
|
||||
"named_animations": {
|
||||
"type": {
|
||||
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<u32, alloc::string::String, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"named_animations"
|
||||
],
|
||||
"short_name": "HashmapTestIntString",
|
||||
"title": "bevy_example::test_components::HashmapTestIntString",
|
||||
"type": "object",
|
||||
"typeInfo": "Struct"
|
||||
},
|
||||
"bevy_example::test_components::HashmapTestSimple": {
|
||||
"additionalProperties": false,
|
||||
"isComponent": true,
|
||||
"isResource": false,
|
||||
"properties": {
|
||||
"named_animations": {
|
||||
"type": {
|
||||
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<alloc::string::String, alloc::string::String, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"named_animations"
|
||||
],
|
||||
"short_name": "HashmapTestSimple",
|
||||
"title": "bevy_example::test_components::HashmapTestSimple",
|
||||
"type": "object",
|
||||
"typeInfo": "Struct"
|
||||
},
|
||||
"bevy_example::test_components::HashmapTestStringColor": {
|
||||
"additionalProperties": false,
|
||||
"isComponent": true,
|
||||
"isResource": false,
|
||||
"properties": {
|
||||
"inner": {
|
||||
"type": {
|
||||
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_render::color::Color, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"inner"
|
||||
],
|
||||
"short_name": "HashmapTestStringColor",
|
||||
"title": "bevy_example::test_components::HashmapTestStringColor",
|
||||
"type": "object",
|
||||
"typeInfo": "Struct"
|
||||
},
|
||||
"bevy_example::test_components::HashmapTestStringColorFlat": {
|
||||
"isComponent": true,
|
||||
"isResource": false,
|
||||
"items": false,
|
||||
"prefixItems": [
|
||||
{
|
||||
"type": {
|
||||
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_render::color::Color, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
|
||||
}
|
||||
}
|
||||
],
|
||||
"short_name": "HashmapTestStringColorFlat",
|
||||
"title": "bevy_example::test_components::HashmapTestStringColorFlat",
|
||||
"type": "array",
|
||||
"typeInfo": "TupleStruct"
|
||||
},
|
||||
"bevy_example::test_components::HashmapTestStringFloat": {
|
||||
"additionalProperties": false,
|
||||
"isComponent": true,
|
||||
"isResource": false,
|
||||
"properties": {
|
||||
"named_animations": {
|
||||
"type": {
|
||||
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<alloc::string::String, f32, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"named_animations"
|
||||
],
|
||||
"short_name": "HashmapTestStringFloat",
|
||||
"title": "bevy_example::test_components::HashmapTestStringFloat",
|
||||
"type": "object",
|
||||
"typeInfo": "Struct"
|
||||
},
|
||||
"bevy_example::test_components::NestedTupleStuff": {
|
||||
"isComponent": true,
|
||||
"isResource": false,
|
||||
@ -11026,6 +11137,19 @@
|
||||
"type": "object",
|
||||
"typeInfo": "Value"
|
||||
},
|
||||
"bevy_utils::hashbrown::HashMap<alloc::string::String, alloc::string::String, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||
"additionalProperties": {
|
||||
"type": {
|
||||
"$ref": "#/$defs/alloc::string::String"
|
||||
}
|
||||
},
|
||||
"isComponent": false,
|
||||
"isResource": false,
|
||||
"short_name": "HashMap<String, String, DefaultHashBuilder>",
|
||||
"title": "bevy_utils::hashbrown::HashMap<alloc::string::String, alloc::string::String, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>",
|
||||
"type": "object",
|
||||
"typeInfo": "Map"
|
||||
},
|
||||
"bevy_utils::hashbrown::HashMap<alloc::string::String, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||
"additionalProperties": {
|
||||
"type": {
|
||||
@ -11039,6 +11163,19 @@
|
||||
"type": "object",
|
||||
"typeInfo": "Map"
|
||||
},
|
||||
"bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_render::color::Color, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||
"additionalProperties": {
|
||||
"type": {
|
||||
"$ref": "#/$defs/bevy_render::color::Color"
|
||||
}
|
||||
},
|
||||
"isComponent": false,
|
||||
"isResource": false,
|
||||
"short_name": "HashMap<String, Color, DefaultHashBuilder>",
|
||||
"title": "bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_render::color::Color, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>",
|
||||
"type": "object",
|
||||
"typeInfo": "Map"
|
||||
},
|
||||
"bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_utils::hashbrown::HashMap<u32, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||
"additionalProperties": {
|
||||
"type": {
|
||||
@ -11052,6 +11189,32 @@
|
||||
"type": "object",
|
||||
"typeInfo": "Map"
|
||||
},
|
||||
"bevy_utils::hashbrown::HashMap<alloc::string::String, f32, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||
"additionalProperties": {
|
||||
"type": {
|
||||
"$ref": "#/$defs/f32"
|
||||
}
|
||||
},
|
||||
"isComponent": false,
|
||||
"isResource": false,
|
||||
"short_name": "HashMap<String, f32, DefaultHashBuilder>",
|
||||
"title": "bevy_utils::hashbrown::HashMap<alloc::string::String, f32, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>",
|
||||
"type": "object",
|
||||
"typeInfo": "Map"
|
||||
},
|
||||
"bevy_utils::hashbrown::HashMap<u32, alloc::string::String, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||
"additionalProperties": {
|
||||
"type": {
|
||||
"$ref": "#/$defs/alloc::string::String"
|
||||
}
|
||||
},
|
||||
"isComponent": false,
|
||||
"isResource": false,
|
||||
"short_name": "HashMap<u32, String, DefaultHashBuilder>",
|
||||
"title": "bevy_utils::hashbrown::HashMap<u32, alloc::string::String, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>",
|
||||
"type": "object",
|
||||
"typeInfo": "Map"
|
||||
},
|
||||
"bevy_utils::hashbrown::HashMap<u32, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||
"additionalProperties": {
|
||||
"type": {
|
||||
@ -11065,6 +11228,19 @@
|
||||
"type": "object",
|
||||
"typeInfo": "Map"
|
||||
},
|
||||
"bevy_utils::hashbrown::HashMap<u32, bevy_render::color::Color, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||
"additionalProperties": {
|
||||
"type": {
|
||||
"$ref": "#/$defs/bevy_render::color::Color"
|
||||
}
|
||||
},
|
||||
"isComponent": false,
|
||||
"isResource": false,
|
||||
"short_name": "HashMap<u32, Color, DefaultHashBuilder>",
|
||||
"title": "bevy_utils::hashbrown::HashMap<u32, bevy_render::color::Color, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>",
|
||||
"type": "object",
|
||||
"typeInfo": "Map"
|
||||
},
|
||||
"bevy_utils::smallvec::SmallVec<[bevy_ecs::entity::Entity; 8]>": {
|
||||
"isComponent": false,
|
||||
"isResource": false,
|
||||
|
@ -145,11 +145,11 @@ impl Plugin for GamePlugin {
|
||||
.add_systems(Update, play_animations)
|
||||
.add_systems(Update, react_to_animation_markers)
|
||||
|
||||
.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once
|
||||
/*.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once
|
||||
.add_systems(
|
||||
Update,
|
||||
exit_game.run_if(on_timer(Duration::from_secs_f32(0.5))),
|
||||
) // shut down the app after this time
|
||||
) // shut down the app after this time*/
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -152,6 +152,43 @@ impl MaterialExtension for MyExtension {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
use bevy::utils::HashMap;
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct HashmapTestSimple {
|
||||
pub named_animations: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct HashmapTestStringFloat {
|
||||
pub named_animations: HashMap<String, f32>,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct HashmapTestIntString {
|
||||
pub named_animations: HashMap<u32, String>,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct HashmapTestIntColor {
|
||||
pub inner: HashMap<u32, Color>,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct HashmapTestStringColor {
|
||||
pub inner: HashMap<String, Color>,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct HashmapTestStringColorFlat(HashMap<String, Color>);
|
||||
|
||||
pub struct ComponentsTestPlugin;
|
||||
impl Plugin for ComponentsTestPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
@ -185,6 +222,20 @@ impl Plugin for ComponentsTestPlugin {
|
||||
.register_type::<Vec<f32>>()
|
||||
// .register_type::<AAAAddedCOMPONENT>()
|
||||
.register_type::<AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut>()
|
||||
.register_type::<HashMap<String, String>>()
|
||||
.register_type::<HashmapTestSimple>()
|
||||
.register_type::<HashMap<String, f32>>()
|
||||
.register_type::<HashmapTestStringFloat>()
|
||||
.register_type::<HashMap<u32, String>>()
|
||||
.register_type::<HashmapTestIntString>()
|
||||
|
||||
.register_type::<HashMap<u32, Color>>()
|
||||
.register_type::<HashmapTestIntColor>()
|
||||
|
||||
.register_type::<HashMap<String, Color>>()
|
||||
.register_type::<HashmapTestStringColor>()
|
||||
.register_type::<HashmapTestStringColorFlat>()
|
||||
|
||||
.add_plugins(MaterialPlugin::<
|
||||
ExtendedMaterial<StandardMaterial, MyExtension>,
|
||||
>::default());
|
||||
|
@ -206,4 +206,14 @@ UI:
|
||||
- [x] delete => remove
|
||||
|
||||
- [x] clean up reloading of registry settings
|
||||
- [x] clean up file watcher
|
||||
- [x] clean up file watcher
|
||||
|
||||
|
||||
=========================================
|
||||
Restructuring of storage of components
|
||||
- [x] marking of invalid root propgroups/components should be based on long name
|
||||
- [ ] overhaul & check each prop group type's use of short names => long names
|
||||
- [ ] lists
|
||||
|
||||
- [ ] in conversions from propgroups
|
||||
component_name = definition["short_name"]
|
||||
|
@ -22,9 +22,9 @@ 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, OT_select_component_name_to_replace, OT_select_object, ReloadRegistryOperator, OT_OpenFilebrowser)
|
||||
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 .propGroups.prop_groups import (generate_propertyGroups_for_components)
|
||||
from .components.metadata import (ComponentMetadata, ComponentsMeta)
|
||||
from .components.lists import GENERIC_LIST_OT_actions, Generic_LIST_OT_AddItem, Generic_LIST_OT_RemoveItem, Generic_LIST_OT_SelectItem
|
||||
from .components.maps import GENERIC_MAP_OT_actions
|
||||
from .components.definitions_list import (ComponentDefinitionsList, ClearComponentDefinitionsList)
|
||||
from .components.ui import (BEVY_COMPONENTS_PT_ComponentsPanel)
|
||||
|
||||
@ -123,7 +123,9 @@ classes = [
|
||||
Generic_LIST_OT_SelectItem,
|
||||
Generic_LIST_OT_AddItem,
|
||||
Generic_LIST_OT_RemoveItem,
|
||||
GENERIC_LIST_OT_actions
|
||||
GENERIC_LIST_OT_actions,
|
||||
|
||||
GENERIC_MAP_OT_actions
|
||||
]
|
||||
|
||||
from bpy.app.handlers import persistent
|
||||
|
@ -47,12 +47,12 @@ class ComponentDefinitionsList(bpy.types.PropertyGroup):
|
||||
description="list",
|
||||
# items argument required to initialize, just filled with empty values
|
||||
items = add_component_to_ui_list,
|
||||
)
|
||||
) # type: ignore
|
||||
filter: StringProperty(
|
||||
name="component filter",
|
||||
description="filter for the components list",
|
||||
options={'TEXTEDIT_UPDATE'}
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
|
||||
class ClearComponentDefinitionsList(bpy.types.Operator):
|
||||
|
@ -10,19 +10,19 @@ class Generic_LIST_OT_AddItem(Operator):
|
||||
property_group_path: StringProperty(
|
||||
name="property group path",
|
||||
description="",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
component_name: StringProperty(
|
||||
name="component name",
|
||||
description="",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
print("")
|
||||
object = context.object
|
||||
# information is stored in component meta
|
||||
components_in_object = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == self.component_name, components_in_object), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == self.component_name, components_in_object), None)
|
||||
|
||||
propertyGroup = component_meta
|
||||
for path_item in json.loads(self.property_group_path):
|
||||
@ -47,19 +47,19 @@ class Generic_LIST_OT_RemoveItem(Operator):
|
||||
property_group_path: StringProperty(
|
||||
name="property group path",
|
||||
description="",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
component_name: StringProperty(
|
||||
name="component name",
|
||||
description="",
|
||||
)
|
||||
) # type: ignore
|
||||
def execute(self, context):
|
||||
print("remove from list", context.object)
|
||||
|
||||
object = context.object
|
||||
# information is stored in component meta
|
||||
components_in_object = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == self.component_name, components_in_object), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == self.component_name, components_in_object), None)
|
||||
|
||||
propertyGroup = component_meta
|
||||
for path_item in json.loads(self.property_group_path):
|
||||
@ -81,14 +81,14 @@ class Generic_LIST_OT_SelectItem(Operator):
|
||||
property_group_path: StringProperty(
|
||||
name="property group path",
|
||||
description="",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
component_name: StringProperty(
|
||||
name="component name",
|
||||
description="",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
selection_index: IntProperty()
|
||||
selection_index: IntProperty() # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
print("select in list", context.object)
|
||||
@ -96,7 +96,7 @@ class Generic_LIST_OT_SelectItem(Operator):
|
||||
object = context.object
|
||||
# information is stored in component meta
|
||||
components_in_object = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == self.component_name, components_in_object), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == self.component_name, components_in_object), None)
|
||||
|
||||
propertyGroup = component_meta
|
||||
for path_item in json.loads(self.property_group_path):
|
||||
@ -121,23 +121,23 @@ class GENERIC_LIST_OT_actions(Operator):
|
||||
('UP', "Up", ""),
|
||||
('DOWN', "Down", ""),
|
||||
('REMOVE', "Remove", ""),
|
||||
('ADD', "Add", "")))
|
||||
('ADD', "Add", ""))) # type: ignore
|
||||
|
||||
property_group_path: StringProperty(
|
||||
name="property group path",
|
||||
description="",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
component_name: StringProperty(
|
||||
name="component name",
|
||||
description="",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def invoke(self, context, event):
|
||||
object = context.object
|
||||
# information is stored in component meta
|
||||
components_in_object = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == self.component_name, components_in_object), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == self.component_name, components_in_object), None)
|
||||
|
||||
propertyGroup = component_meta
|
||||
for path_item in json.loads(self.property_group_path):
|
||||
|
71
tools/bevy_components/components/maps.py
Normal file
71
tools/bevy_components/components/maps.py
Normal file
@ -0,0 +1,71 @@
|
||||
import json
|
||||
from bpy_types import Operator, UIList
|
||||
from bpy.props import (StringProperty, EnumProperty, PointerProperty, FloatVectorProperty, IntProperty)
|
||||
|
||||
class GENERIC_MAP_OT_actions(Operator):
|
||||
"""Move items up and down, add and remove"""
|
||||
bl_idname = "generic_map.map_action"
|
||||
bl_label = "List Actions"
|
||||
bl_description = "Move items up and down, add and remove"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
action: EnumProperty(
|
||||
items=(
|
||||
('UP', "Up", ""),
|
||||
('DOWN', "Down", ""),
|
||||
('REMOVE', "Remove", ""),
|
||||
('ADD', "Add", ""))) # type: ignore
|
||||
|
||||
property_group_path: StringProperty(
|
||||
name="property group path",
|
||||
description="",
|
||||
) # type: ignore
|
||||
|
||||
component_name: StringProperty(
|
||||
name="component name",
|
||||
description="",
|
||||
) # type: ignore
|
||||
|
||||
def invoke(self, context, event):
|
||||
object = context.object
|
||||
# information is stored in component meta
|
||||
components_in_object = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["long_name"] == self.component_name, components_in_object), None)
|
||||
|
||||
propertyGroup = component_meta
|
||||
for path_item in json.loads(self.property_group_path):
|
||||
propertyGroup = getattr(propertyGroup, path_item)
|
||||
|
||||
target_list = getattr(propertyGroup, "list")
|
||||
index = getattr(propertyGroup, "list_index")
|
||||
|
||||
values_list = getattr(propertyGroup, "values_list")
|
||||
values_index = getattr(propertyGroup, "values_list_index")
|
||||
|
||||
|
||||
if self.action == 'DOWN' and index < len(target_list) - 1:
|
||||
#item_next = scn.rule_list[index + 1].name
|
||||
target_list.move(index, index + 1)
|
||||
propertyGroup.list_index += 1
|
||||
|
||||
elif self.action == 'UP' and index >= 1:
|
||||
#item_prev = scn.rule_list[index - 1].name
|
||||
target_list.move(index, index - 1)
|
||||
propertyGroup.list_index -= 1
|
||||
|
||||
elif self.action == 'REMOVE':
|
||||
target_list.remove(index)
|
||||
propertyGroup.list_index = min(max(0, index - 1), len(target_list) - 1)
|
||||
|
||||
if self.action == 'ADD':
|
||||
key = target_list.add()
|
||||
value = values_list.add()
|
||||
|
||||
propertyGroup.list_index = index + 1 # we use this to force the change detection
|
||||
propertyGroup.values_index = index + 1 # we use this to force the change detection
|
||||
|
||||
|
||||
#info = '"%s" added to list' % (item.name)
|
||||
#self.report({'INFO'}, info)
|
||||
|
||||
return {"FINISHED"}
|
@ -10,51 +10,51 @@ class ComponentMetadata(bpy.types.PropertyGroup):
|
||||
name : bpy.props.StringProperty(
|
||||
name = "name",
|
||||
default = ""
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
long_name : bpy.props.StringProperty(
|
||||
name = "long name",
|
||||
default = ""
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
type_name : bpy.props.StringProperty(
|
||||
name = "Type",
|
||||
default = ""
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
values: bpy.props.StringProperty(
|
||||
name = "Value",
|
||||
default = ""
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
enabled: BoolProperty(
|
||||
name="enabled",
|
||||
description="component enabled",
|
||||
default=True
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
invalid: BoolProperty(
|
||||
name="invalid",
|
||||
description="component is invalid, because of missing registration/ other issues",
|
||||
default=False
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
invalid_details: StringProperty(
|
||||
name="invalid details",
|
||||
description="detailed information about why the component is invalid",
|
||||
default=""
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
visible: BoolProperty( # REALLY dislike doing this for UI control, but ok hack for now
|
||||
default=True
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
class ComponentsMeta(PropertyGroup):
|
||||
infos_per_component: StringProperty(
|
||||
name="infos per component",
|
||||
description="component"
|
||||
)
|
||||
components: bpy.props.CollectionProperty(type = ComponentMetadata)
|
||||
) # type: ignore
|
||||
components: bpy.props.CollectionProperty(type = ComponentMetadata) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def register(cls):
|
||||
@ -72,7 +72,9 @@ def get_component_metadata_by_short_name(object, short_name):
|
||||
|
||||
# remove no longer valid metadata from object
|
||||
def cleanup_invalid_metadata(object):
|
||||
bevy_components = json.loads(object['bevy_components']) if 'bevy_components' in object else {}
|
||||
bevy_components = get_bevy_components(object)
|
||||
if len(bevy_components.keys()) == 0: # no components, bail out
|
||||
return
|
||||
components_metadata = object.components_meta.components
|
||||
to_remove = []
|
||||
for index, component_meta in enumerate(components_metadata):
|
||||
@ -93,6 +95,10 @@ def find_component_definition_from_short_name(short_name):
|
||||
return registry.type_infos.get(long_name, None)
|
||||
return None
|
||||
|
||||
def find_component_definition_from_long_name(long_name):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
return registry.type_infos.get(long_name, None)
|
||||
|
||||
# FIXME: feels a bit heavy duty, should only be done
|
||||
# if the components panel is active ?
|
||||
def ensure_metadata_for_all_objects():
|
||||
@ -123,31 +129,45 @@ def do_object_custom_properties_have_missing_metadata(object):
|
||||
return missing_metadata
|
||||
|
||||
|
||||
# adds metadata to object only if it is missing
|
||||
def add_metadata_to_components_without_metadata(object):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
|
||||
for component_name in dict(object) :
|
||||
if component_name == "components_meta":
|
||||
continue
|
||||
upsert_component_in_object(object, component_name, registry)
|
||||
|
||||
|
||||
import json
|
||||
def inject_component(object, long_name, value):
|
||||
def upsert_bevy_component(object, long_name, value):
|
||||
if not 'bevy_components' in object:
|
||||
object['bevy_components'] = '{}'
|
||||
previous = json.loads(object['bevy_components'])
|
||||
previous[long_name] = value
|
||||
object['bevy_components'] = json.dumps(previous)
|
||||
bevy_components = json.loads(object['bevy_components'])
|
||||
bevy_components[long_name] = value
|
||||
object['bevy_components'] = json.dumps(bevy_components)
|
||||
#object['bevy_components'][long_name] = value # Sigh, this does not work, hits Blender's 63 char length limit
|
||||
|
||||
def bla_component(object, long_name):
|
||||
def remove_bevy_component(object, long_name):
|
||||
if 'bevy_components' in object:
|
||||
current = json.loads(object['bevy_components'])
|
||||
del current[long_name]
|
||||
object['bevy_components'] = json.dumps(current)
|
||||
|
||||
def get_bevy_components(object):
|
||||
if 'bevy_components' in object:
|
||||
bevy_components = json.loads(object['bevy_components'])
|
||||
return bevy_components
|
||||
return {}
|
||||
|
||||
def get_bevy_component_value_by_long_name(object, long_name):
|
||||
bevy_components = get_bevy_components(object)
|
||||
if len(bevy_components.keys()) == 0 :
|
||||
return None
|
||||
return bevy_components.get(long_name, None)
|
||||
|
||||
def is_bevy_component_in_object(object, long_name):
|
||||
return get_bevy_component_value_by_long_name(object, long_name) is not None
|
||||
|
||||
# adds metadata to object only if it is missing
|
||||
def add_metadata_to_components_without_metadata(object):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
|
||||
for component_name in get_bevy_components(object) :
|
||||
if component_name == "components_meta":
|
||||
continue
|
||||
upsert_component_in_object(object, component_name, registry)
|
||||
|
||||
# adds a component to an object (including metadata) using the provided component definition & optional value
|
||||
def add_component_to_object(object, component_definition, value=None):
|
||||
cleanup_invalid_metadata(object)
|
||||
@ -158,7 +178,6 @@ def add_component_to_object(object, component_definition, value=None):
|
||||
if not registry.has_type_infos():
|
||||
raise Exception('registry type infos have not been loaded yet or are missing !')
|
||||
definition = registry.type_infos[long_name]
|
||||
print("HEAAAY", value)
|
||||
# now we use our pre_generated property groups to set the initial value of our custom property
|
||||
(_, propertyGroup) = upsert_component_in_object(object, long_name=long_name, registry=registry)
|
||||
if value == None:
|
||||
@ -170,7 +189,7 @@ def add_component_to_object(object, component_definition, value=None):
|
||||
|
||||
# object[short_name] = value
|
||||
print("ADDING VAALUEEE", value)
|
||||
inject_component(object, long_name, value)
|
||||
upsert_bevy_component(object, long_name, value)
|
||||
#ping_depsgraph_update(object)
|
||||
|
||||
|
||||
@ -226,14 +245,14 @@ def copy_propertyGroup_values_to_another_object(source_object, target_object, co
|
||||
if source_object == None or target_object == None or component_name == None:
|
||||
raise Exception('missing input data, cannot copy component propertryGroup')
|
||||
|
||||
component_definition = find_component_definition_from_short_name(component_name)
|
||||
short_name = component_definition["short_name"]
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
component_definition = find_component_definition_from_long_name(component_name)
|
||||
long_name = component_name
|
||||
property_group_name = registry.get_propertyGroupName_from_longName(long_name)
|
||||
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
|
||||
source_components_metadata = source_object.components_meta.components
|
||||
source_componentMeta = next(filter(lambda component: component["name"] == short_name, source_components_metadata), None)
|
||||
source_componentMeta = next(filter(lambda component: component["long_name"] == long_name, source_components_metadata), None)
|
||||
# matching component means we already have this type of component
|
||||
source_propertyGroup = getattr(source_componentMeta, property_group_name)
|
||||
|
||||
@ -241,28 +260,27 @@ def copy_propertyGroup_values_to_another_object(source_object, target_object, co
|
||||
(_, target_propertyGroup) = upsert_component_in_object(target_object, component_name, registry)
|
||||
# add to object
|
||||
value = property_group_value_to_custom_property_value(target_propertyGroup, component_definition, registry, None)
|
||||
target_object[short_name] = value
|
||||
upsert_bevy_component(target_object, long_name, value)
|
||||
|
||||
# copy the values over
|
||||
for field_name in source_propertyGroup.field_names:
|
||||
if field_name in source_propertyGroup:
|
||||
target_propertyGroup[field_name] = source_propertyGroup[field_name]
|
||||
apply_propertyGroup_values_to_object_customProperties(target_object)
|
||||
ping_depsgraph_update(object)
|
||||
|
||||
|
||||
# TODO: move to propgroups ?
|
||||
def apply_propertyGroup_values_to_object_customProperties(object):
|
||||
cleanup_invalid_metadata(object)
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
for component_name in dict(object) :
|
||||
if component_name == "components_meta":
|
||||
continue
|
||||
for component_name in get_bevy_components(object) :
|
||||
"""if component_name == "components_meta":
|
||||
continue"""
|
||||
(_, propertyGroup) = upsert_component_in_object(object, component_name, registry)
|
||||
component_definition = find_component_definition_from_short_name(component_name)
|
||||
component_definition = find_component_definition_from_long_name(component_name)
|
||||
if component_definition != None:
|
||||
value = property_group_value_to_custom_property_value(propertyGroup, component_definition, registry, None)
|
||||
object[component_name] = value
|
||||
upsert_bevy_component(object=object, long_name=component_name, value=value)
|
||||
|
||||
# apply component value(s) to custom property of a single component
|
||||
def apply_propertyGroup_values_to_object_customProperties_for_component(object, component_name):
|
||||
@ -304,7 +322,7 @@ def apply_customProperty_values_to_object_propertyGroups(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):
|
||||
bla_component(object, component_name)
|
||||
remove_bevy_component(object, component_name)
|
||||
|
||||
components_metadata = getattr(object, "components_meta", None)
|
||||
if components_metadata == None:
|
||||
@ -319,16 +337,14 @@ def remove_component_from_object(object, component_name):
|
||||
break
|
||||
for index in to_remove:
|
||||
components_metadata.remove(index)
|
||||
ping_depsgraph_update(object)
|
||||
return True
|
||||
|
||||
def add_component_from_custom_property(object):
|
||||
add_metadata_to_components_without_metadata(object)
|
||||
apply_customProperty_values_to_object_propertyGroups(object)
|
||||
ping_depsgraph_update(object)
|
||||
|
||||
def toggle_component(object, component_name):
|
||||
components_in_object = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == component_name, components_in_object), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == component_name, components_in_object), None)
|
||||
if component_meta != None:
|
||||
component_meta.visible = not component_meta.visible
|
||||
|
@ -4,7 +4,7 @@ import bpy
|
||||
from bpy_types import Operator
|
||||
from bpy.props import (StringProperty)
|
||||
|
||||
from .metadata import add_component_from_custom_property, 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, toggle_component
|
||||
from .metadata import add_component_from_custom_property, 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, get_bevy_component_value_by_long_name, get_bevy_components, is_bevy_component_in_object, remove_component_from_object, toggle_component
|
||||
|
||||
class AddComponentOperator(Operator):
|
||||
"""Add Bevy component to object"""
|
||||
@ -36,7 +36,7 @@ class CopyComponentOperator(Operator):
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
source_component_name: StringProperty(
|
||||
name="source component_name",
|
||||
name="source component_name (long)",
|
||||
description="name of the component to copy",
|
||||
) # type: ignore
|
||||
|
||||
@ -80,10 +80,10 @@ class PasteComponentOperator(Operator):
|
||||
self.report({"ERROR"}, "The source object to copy a component from does not exist")
|
||||
else:
|
||||
component_name = context.window_manager.copied_source_component_name
|
||||
if not component_name in source_object:
|
||||
component_value = get_bevy_component_value_by_long_name(source_object, component_name)
|
||||
if component_value is None:
|
||||
self.report({"ERROR"}, "The source component to copy from does not exist")
|
||||
else:
|
||||
component_value = source_object[component_name]
|
||||
print("pasting component to object: component name:", str(component_name), "component value:" + str(component_value))
|
||||
print (context.object)
|
||||
registry = context.window_manager.components_registry
|
||||
@ -114,10 +114,15 @@ class RemoveComponentOperator(Operator):
|
||||
else:
|
||||
object = bpy.data.objects[self.object_name]
|
||||
print("removing component ", self.component_name, "from object '"+object.name+"'")
|
||||
if object is not None and 'bevy_components' in object and self.component_name in object['bevy_components']:
|
||||
remove_component_from_object(object, self.component_name)
|
||||
|
||||
if object is not None and 'bevy_components' in object :
|
||||
component_value = get_bevy_component_value_by_long_name(object, self.component_name)
|
||||
if component_value is not None:
|
||||
remove_component_from_object(object, self.component_name)
|
||||
else :
|
||||
self.report({"ERROR"}, "The component to remove ("+ self.component_name +") does not exist")
|
||||
else:
|
||||
self.report({"ERROR"}, "The object/ component to remove ("+ self.component_name +") does not exist")
|
||||
self.report({"ERROR"}, "The object to remove ("+ self.component_name +") from does not exist")
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@ -145,7 +150,7 @@ class RemoveComponentFromAllObjectsOperator(Operator):
|
||||
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:
|
||||
if object is not None and is_bevy_component_in_object(object, self.component_name):
|
||||
remove_component_from_object(object, self.component_name)
|
||||
|
||||
progress = index / total
|
||||
@ -222,7 +227,7 @@ class OT_rename_component(Operator):
|
||||
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)
|
||||
component_meta = next(filter(lambda component: component["long_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"
|
||||
@ -240,7 +245,7 @@ class OT_rename_component(Operator):
|
||||
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)
|
||||
component_meta = next(filter(lambda component: component["long_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"
|
||||
|
@ -8,6 +8,7 @@ from .operators import AddComponentOperator, CopyComponentOperator, Fix_Componen
|
||||
def draw_propertyGroup( propertyGroup, layout, nesting =[], rootName=None):
|
||||
is_enum = getattr(propertyGroup, "with_enum")
|
||||
is_list = getattr(propertyGroup, "with_list")
|
||||
is_map = getattr(propertyGroup, "with_map")
|
||||
#nesting = nesting + [current_short_name] # we need this convoluted "nested path strings " workaround so that operators working on a given
|
||||
# item in our components hierarchy can get the correct propertyGroup by STRINGS because of course, we cannot pass objects to operators...sigh
|
||||
|
||||
@ -83,6 +84,43 @@ def draw_propertyGroup( propertyGroup, layout, nesting =[], rootName=None):
|
||||
op.component_name = rootName
|
||||
op.property_group_path = json.dumps(nesting)
|
||||
|
||||
elif is_map:
|
||||
keys_list = getattr(propertyGroup, "list")
|
||||
values_list = getattr(propertyGroup, "values_list")
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text="key")
|
||||
row.label(text="value")
|
||||
#values_setter = getattr(propertyGroup, "values_setter")
|
||||
# draw_propertyGroup(values_setter, row, nesting, rootName)
|
||||
|
||||
split = box.split(factor=0.9)
|
||||
list_column, buttons_column = (split.column(),split.column())
|
||||
list_column = list_column.box()
|
||||
|
||||
for index, item in enumerate(keys_list):
|
||||
row = list_column.row()
|
||||
#row.label(text=str(index))
|
||||
draw_propertyGroup(item, row, nesting, rootName)
|
||||
|
||||
value = values_list[index]
|
||||
draw_propertyGroup(value, row, nesting, rootName)
|
||||
|
||||
|
||||
#various control buttons
|
||||
buttons_column.separator()
|
||||
row = buttons_column.row()
|
||||
op = row.operator('generic_map.map_action', icon='ADD', text="")
|
||||
op.action = 'ADD'
|
||||
op.component_name = rootName
|
||||
op.property_group_path = json.dumps(nesting)
|
||||
|
||||
row = buttons_column.row()
|
||||
op = row.operator('generic_map.map_action', icon='REMOVE', text="")
|
||||
op.action = 'REMOVE'
|
||||
op.component_name = rootName
|
||||
op.property_group_path = json.dumps(nesting)
|
||||
|
||||
else:
|
||||
for fname in field_names:
|
||||
subrow = layout.row()
|
||||
@ -178,12 +216,12 @@ class BEVY_COMPONENTS_PT_ComponentsPanel(bpy.types.Panel):
|
||||
|
||||
# we fetch the matching ui property group
|
||||
root_propertyGroup_name = registry.get_propertyGroupName_from_longName(component_name)
|
||||
print("root_propertyGroup_name", root_propertyGroup_name)
|
||||
print("component_meta", component_meta, component_invalid)
|
||||
"""print("root_propertyGroup_name", root_propertyGroup_name)
|
||||
print("component_meta", component_meta, component_invalid)"""
|
||||
|
||||
if root_propertyGroup_name:
|
||||
propertyGroup = getattr(component_meta, root_propertyGroup_name, None)
|
||||
print("propertyGroup", propertyGroup)
|
||||
"""print("propertyGroup", propertyGroup)"""
|
||||
if propertyGroup:
|
||||
# if the component has only 0 or 1 field names, display inline, otherwise change layout
|
||||
single_field = len(propertyGroup.field_names) < 2
|
||||
|
@ -5,8 +5,8 @@ conversion_tables = {
|
||||
|
||||
"char": lambda value: '"'+value+'"',
|
||||
"str": lambda value: '"'+value+'"',
|
||||
"alloc::string::String": lambda value: '"'+value+'"',
|
||||
"alloc::borrow::Cow<str>": lambda value: '"'+value+'"',
|
||||
"alloc::string::String": lambda value: '"'+str(value)+'"',
|
||||
"alloc::borrow::Cow<str>": lambda value: '"'+str(value)+'"',
|
||||
|
||||
"glam::Vec2": lambda value: "Vec2(x:"+str(value[0])+ ", y:"+str(value[1])+")",
|
||||
"glam::DVec2": lambda value: "DVec2(x:"+str(value[0])+ ", y:"+str(value[1])+")",
|
||||
@ -28,20 +28,22 @@ conversion_tables = {
|
||||
#converts the value of a property group(no matter its complexity) into a single custom property value
|
||||
# this is more or less a glorified "to_ron()" method (not quite but close to)
|
||||
def property_group_value_to_custom_property_value(property_group, definition, registry, parent=None, value=None):
|
||||
component_name = definition["short_name"]
|
||||
component_name = definition["short_name"] # FIXME: we should operate based on long names
|
||||
type_info = definition["typeInfo"] if "typeInfo" in definition else None
|
||||
type_def = definition["type"] if "type" in definition else None
|
||||
type_name = definition["title"]
|
||||
is_value_type = type_name in conversion_tables
|
||||
#print("computing custom property", component_name, type_info, type_def, type_name)
|
||||
print("computing custom property: component name:", component_name, "type_info", type_info, "type_def", type_def, "type_name", type_name)
|
||||
|
||||
if is_value_type:
|
||||
value = conversion_tables[type_name](value)
|
||||
elif type_info == "Struct":
|
||||
print("generating string for struct")
|
||||
values = {}
|
||||
if len(property_group.field_names) ==0:
|
||||
value = '()'
|
||||
else:
|
||||
print("toto", type_def, definition, property_group)
|
||||
for index, field_name in enumerate(property_group.field_names):
|
||||
item_type_name = definition["properties"][field_name]["type"]["$ref"].replace("#/$defs/", "")
|
||||
item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
|
||||
@ -74,8 +76,10 @@ def property_group_value_to_custom_property_value(property_group, definition, re
|
||||
elif type_info == "TupleStruct":
|
||||
values = {}
|
||||
for index, field_name in enumerate(property_group.field_names):
|
||||
#print("toto", index, definition["prefixItems"][index]["type"]["$ref"])
|
||||
item_type_name = definition["prefixItems"][index]["type"]["$ref"].replace("#/$defs/", "")
|
||||
item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
|
||||
# print("here", item_type_name, item_definition)
|
||||
|
||||
value = getattr(property_group, field_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
@ -88,6 +92,7 @@ def property_group_value_to_custom_property_value(property_group, definition, re
|
||||
|
||||
value = tuple(e for e in list(values.values()))
|
||||
elif type_info == "Enum":
|
||||
# TODO: perhaps use a mapping of (long) component name to a short ID , like we do in get_propertyGroupName_from_longName
|
||||
selected = getattr(property_group, component_name)
|
||||
|
||||
if type_def == "object":
|
||||
@ -134,6 +139,38 @@ def property_group_value_to_custom_property_value(property_group, definition, re
|
||||
else:
|
||||
item_value = '""'
|
||||
value.append(item_value)
|
||||
|
||||
elif type_info == "Map":
|
||||
print("MAAAAAP", property_group)
|
||||
keys_list = getattr(property_group, "list", {})
|
||||
values_list = getattr(property_group, "values_list")
|
||||
value = {}
|
||||
for index, key in enumerate(keys_list):
|
||||
# first get the keys
|
||||
key_type_name = getattr(key, "type_name")
|
||||
definition = registry.type_infos[key_type_name] if key_type_name in registry.type_infos else None
|
||||
if definition != None:
|
||||
key_value = property_group_value_to_custom_property_value(key, definition, registry, component_name, None)
|
||||
if key_type_name.startswith("wrapper_"): #if we have a "fake" tupple for aka for value types, we need to remove one nested level
|
||||
key_value = key_value[0]
|
||||
else:
|
||||
key_value = '""'
|
||||
# and then the values
|
||||
val = values_list[index]
|
||||
value_type_name = getattr(val, "type_name")
|
||||
definition = registry.type_infos[value_type_name] if value_type_name in registry.type_infos else None
|
||||
print("value definition", definition)
|
||||
if definition != None:
|
||||
val_value = property_group_value_to_custom_property_value(val, definition, registry, component_name, None)
|
||||
if value_type_name.startswith("wrapper_"): #if we have a "fake" tupple for aka for value types, we need to remove one nested level
|
||||
val_value = val_value[0]
|
||||
else:
|
||||
val_value = '""'
|
||||
|
||||
|
||||
value[key_value] = val_value
|
||||
print("MAP VALUES", value)
|
||||
|
||||
else:
|
||||
value = conversion_tables[type_name](value) if is_value_type else value
|
||||
value = '""' if isinstance(value, PropertyGroup) else value
|
||||
@ -145,7 +182,7 @@ def property_group_value_to_custom_property_value(property_group, definition, re
|
||||
if parent == None:
|
||||
value = str(value).replace("'", "")
|
||||
value = value.replace(",)",")")
|
||||
value = value.replace("{", "(").replace("}", ")")
|
||||
value = value.replace("{", "(").replace("}", ")") # FIXME: deal with hashmaps
|
||||
value = value.replace("True", "true").replace("False", "false")
|
||||
return value
|
||||
|
||||
|
@ -5,8 +5,9 @@ from . import process_structs
|
||||
from . import process_tupples
|
||||
from . import process_enum
|
||||
from . import process_list
|
||||
from . import process_map
|
||||
|
||||
def process_component(registry, definition, update, extras=None, nesting = []):
|
||||
def process_component(registry, definition, update, extras=None, nesting = [], nesting_long_names = []):
|
||||
component_name = definition['title']
|
||||
short_name = definition["short_name"]
|
||||
type_info = definition["typeInfo"] if "typeInfo" in definition else None
|
||||
@ -18,6 +19,7 @@ def process_component(registry, definition, update, extras=None, nesting = []):
|
||||
has_prefixItems = len(prefixItems) > 0
|
||||
is_enum = type_info == "Enum"
|
||||
is_list = type_info == "List"
|
||||
is_map = type_info == "Map"
|
||||
|
||||
# print("processing", short_name, component_name, type_def, type_info)
|
||||
|
||||
@ -28,25 +30,30 @@ def process_component(registry, definition, update, extras=None, nesting = []):
|
||||
with_items = False
|
||||
with_enum = False
|
||||
with_list = False
|
||||
with_map = False
|
||||
|
||||
|
||||
if has_properties:
|
||||
__annotations__ = __annotations__ | process_structs.process_structs(registry, definition, properties, update, nesting)
|
||||
__annotations__ = __annotations__ | process_structs.process_structs(registry, definition, properties, update, nesting, nesting_long_names)
|
||||
with_properties = True
|
||||
tupple_or_struct = "struct"
|
||||
|
||||
if has_prefixItems:
|
||||
__annotations__ = __annotations__ | process_tupples.process_tupples(registry, definition, prefixItems, update, nesting)
|
||||
__annotations__ = __annotations__ | process_tupples.process_tupples(registry, definition, prefixItems, update, nesting, nesting_long_names)
|
||||
with_items = True
|
||||
tupple_or_struct = "tupple"
|
||||
|
||||
if is_enum:
|
||||
__annotations__ = __annotations__ | process_enum.process_enum(registry, definition, update, nesting)
|
||||
__annotations__ = __annotations__ | process_enum.process_enum(registry, definition, update, nesting, nesting_long_names)
|
||||
with_enum = True
|
||||
|
||||
if is_list:
|
||||
__annotations__ = __annotations__ | process_list.process_list(registry, definition, update, nesting)
|
||||
__annotations__ = __annotations__ | process_list.process_list(registry, definition, update, nesting, nesting_long_names)
|
||||
with_list= True
|
||||
|
||||
if is_map:
|
||||
__annotations__ = __annotations__ | process_map.process_map(registry, definition, update, nesting, nesting_long_names)
|
||||
with_map = True
|
||||
|
||||
field_names = []
|
||||
for a in __annotations__:
|
||||
@ -56,7 +63,7 @@ def process_component(registry, definition, update, extras=None, nesting = []):
|
||||
extras = extras if extras is not None else {
|
||||
"type_name": component_name
|
||||
}
|
||||
root_component = nesting[0] if len(nesting) > 0 else component_name
|
||||
root_component = nesting_long_names[0] if len(nesting_long_names) > 0 else component_name
|
||||
# print("DONE:",short_name,"__annotations__", __annotations__)
|
||||
# print("")
|
||||
property_group_params = {
|
||||
@ -64,7 +71,7 @@ def process_component(registry, definition, update, extras=None, nesting = []):
|
||||
'__annotations__': __annotations__,
|
||||
'tupple_or_struct': tupple_or_struct,
|
||||
'field_names': field_names,
|
||||
**dict(with_properties = with_properties, with_items= with_items, with_enum= with_enum, with_list= with_list, short_name= short_name),
|
||||
**dict(with_properties = with_properties, with_items= with_items, with_enum= with_enum, with_list= with_list, with_map = with_map, short_name= short_name),
|
||||
'root_component': root_component
|
||||
}
|
||||
#FIXME: YIKES, but have not found another way:
|
||||
@ -74,7 +81,7 @@ def process_component(registry, definition, update, extras=None, nesting = []):
|
||||
-BasicTest => the registration & update callback of this one overwrites the first "basicTest"
|
||||
have not found a cleaner workaround so far
|
||||
"""
|
||||
property_group_name = registry.generate_propGroup_name(nesting, short_name, component_name)
|
||||
property_group_name = registry.generate_propGroup_name(nesting, component_name)
|
||||
(property_group_pointer, property_group_class) = property_group_from_infos(property_group_name, property_group_params)
|
||||
# add our component propertyGroup to the registry
|
||||
registry.register_component_propertyGroup(property_group_name, property_group_pointer)
|
||||
|
@ -1,13 +1,17 @@
|
||||
from bpy.props import (StringProperty)
|
||||
from . import process_component
|
||||
|
||||
def process_enum(registry, definition, update, nesting):
|
||||
def process_enum(registry, definition, update, nesting, nesting_long_names):
|
||||
blender_property_mapping = registry.blender_property_mapping
|
||||
short_name = definition["short_name"]
|
||||
long_name = definition["title"]
|
||||
|
||||
type_def = definition["type"] if "type" in definition else None
|
||||
values = definition["oneOf"]
|
||||
|
||||
nesting = nesting + [short_name]
|
||||
nesting_long_names = nesting_long_names = [long_name]
|
||||
|
||||
__annotations__ = {}
|
||||
original_type_name = "enum"
|
||||
|
||||
@ -25,12 +29,12 @@ def process_enum(registry, definition, update, nesting):
|
||||
if "prefixItems" in item:
|
||||
#print("tupple variant in enum", short_name, item)
|
||||
registry.add_custom_type(item_short_name, item)
|
||||
(sub_component_group, _) = process_component.process_component(registry, item, update, {"nested": True}, nesting)
|
||||
(sub_component_group, _) = process_component.process_component(registry, item, update, {"nested": True}, nesting, nesting_long_names)
|
||||
additional_annotations[variant_name] = sub_component_group
|
||||
elif "properties" in item:
|
||||
#print("struct variant in enum", short_name, item)
|
||||
registry.add_custom_type(item_short_name, item)
|
||||
(sub_component_group, _) = process_component.process_component(registry, item, update, {"nested": True}, nesting)
|
||||
(sub_component_group, _) = process_component.process_component(registry, item, update, {"nested": True}, nesting, nesting_long_names)
|
||||
additional_annotations[variant_name] = sub_component_group
|
||||
else: # for the cases where it's neither a tupple nor a structs: FIXME: not 100% sure of this
|
||||
#print("other variant in enum", short_name)
|
||||
|
@ -2,12 +2,16 @@ from bpy.props import (StringProperty, IntProperty, CollectionProperty)
|
||||
from .utils import generate_wrapper_propertyGroup
|
||||
from . import process_component
|
||||
|
||||
def process_list(registry, definition, update, nesting=[]):
|
||||
def process_list(registry, definition, update, nesting=[], nesting_long_names=[]):
|
||||
value_types_defaults = registry.value_types_defaults
|
||||
type_infos = registry.type_infos
|
||||
|
||||
short_name = definition["short_name"]
|
||||
long_name = definition["title"]
|
||||
ref_name = definition["items"]["type"]["$ref"].replace("#/$defs/", "")
|
||||
|
||||
nesting = nesting+[short_name]
|
||||
nesting_long_names = nesting_long_names + [long_name]
|
||||
|
||||
item_definition = type_infos[ref_name]
|
||||
item_long_name = item_definition["title"]
|
||||
@ -17,12 +21,11 @@ def process_list(registry, definition, update, nesting=[]):
|
||||
property_group_class = None
|
||||
#if the content of the list is a unit type, we need to generate a fake wrapper, otherwise we cannot use layout.prop(group, "propertyName") as there is no propertyName !
|
||||
if is_item_value_type:
|
||||
property_group_class = generate_wrapper_propertyGroup(short_name, item_long_name, definition["items"]["type"]["$ref"],registry, update)
|
||||
property_group_class = generate_wrapper_propertyGroup(long_name, item_long_name, definition["items"]["type"]["$ref"], registry, update)
|
||||
else:
|
||||
(_, list_content_group_class) = process_component.process_component(registry, item_definition, update, {"nested": True, "type_name": item_long_name}, nesting)
|
||||
property_group_class = list_content_group_class
|
||||
|
||||
nesting = nesting+[short_name]
|
||||
item_collection = CollectionProperty(type=property_group_class)
|
||||
|
||||
item_short_name = item_short_name if not is_item_value_type else "wrapper_" + item_short_name
|
||||
|
71
tools/bevy_components/propGroups/process_map.py
Normal file
71
tools/bevy_components/propGroups/process_map.py
Normal file
@ -0,0 +1,71 @@
|
||||
from bpy.props import (StringProperty, IntProperty, CollectionProperty)
|
||||
from .utils import generate_wrapper_propertyGroup
|
||||
from . import process_component
|
||||
|
||||
def process_map(registry, definition, update, nesting=[], nesting_long_names=[]):
|
||||
print("IS HASHMAP")
|
||||
value_types_defaults = registry.value_types_defaults
|
||||
type_infos = registry.type_infos
|
||||
|
||||
short_name = definition["short_name"]
|
||||
long_name = definition["title"]
|
||||
|
||||
nesting = nesting + [short_name]
|
||||
nesting_long_names = nesting_long_names + [long_name]
|
||||
|
||||
#ref_name = definition["items"]["type"]["$ref"].replace("#/$defs/", "")
|
||||
value_ref_name = definition["additionalProperties"]["type"]["$ref"].replace("#/$defs/", "")
|
||||
key_ref_name = long_name.split(',')[0].split('<')[1]# FIXME: hack !!!
|
||||
key_type = ''
|
||||
value_type = ''
|
||||
print("infos", short_name, "long name", long_name)
|
||||
print("value ref", value_ref_name, "key ref", key_ref_name)
|
||||
#print("definition", definition)
|
||||
|
||||
if value_ref_name in type_infos:
|
||||
value_definition = type_infos[value_ref_name]
|
||||
original_type_name = value_definition["title"]
|
||||
original_short_name = value_definition["short_name"]
|
||||
is_value_value_type = original_type_name in value_types_defaults
|
||||
definition_link = definition["additionalProperties"]["type"]["$ref"]#f"#/$defs/{value_ref_name}"
|
||||
|
||||
print("hashmap VALUE type", original_type_name)
|
||||
|
||||
#if the content of the list is a unit type, we need to generate a fake wrapper, otherwise we cannot use layout.prop(group, "propertyName") as there is no propertyName !
|
||||
if is_value_value_type:
|
||||
values_property_group_class = generate_wrapper_propertyGroup(long_name, original_type_name, definition_link, registry, update)
|
||||
else:
|
||||
(_, list_content_group_class) = process_component.process_component(registry, value_definition, update, {"nested": True, "type_name": original_type_name}, nesting)
|
||||
values_property_group_class = list_content_group_class
|
||||
|
||||
values_collection = CollectionProperty(type=values_property_group_class)
|
||||
|
||||
if key_ref_name in type_infos:
|
||||
key_definition = type_infos[key_ref_name]
|
||||
original_type_name = key_definition["title"]
|
||||
original_short_name = key_definition["short_name"]
|
||||
is_key_value_type = original_type_name in value_types_defaults
|
||||
definition_link = f"#/$defs/{key_ref_name}"
|
||||
|
||||
#if the content of the list is a unit type, we need to generate a fake wrapper, otherwise we cannot use layout.prop(group, "propertyName") as there is no propertyName !
|
||||
if is_key_value_type:
|
||||
keys_property_group_class = generate_wrapper_propertyGroup(long_name+'_values', original_type_name, definition_link, registry, update)
|
||||
else:
|
||||
(_, list_content_group_class) = process_component.process_component(registry, key_definition, update, {"nested": True, "type_name": original_type_name}, nesting)
|
||||
keys_property_group_class = list_content_group_class
|
||||
|
||||
keys_collection = CollectionProperty(type=keys_property_group_class)
|
||||
|
||||
__annotations__ = {
|
||||
"list": keys_collection,
|
||||
"list_index": IntProperty(name = "Index for keys", default = 0, update=update),
|
||||
"values_list": values_collection,
|
||||
"values_list_index": IntProperty(name = "Index for values", default = 0, update=update),
|
||||
}
|
||||
'''
|
||||
"values_setter": values_setter,
|
||||
"values_list": values_collection,
|
||||
"values_list_index": IntProperty(name = "Index for values", default = 0, update=update),
|
||||
'''
|
||||
|
||||
return __annotations__
|
@ -1,19 +1,24 @@
|
||||
from bpy.props import (StringProperty)
|
||||
from . import process_component
|
||||
|
||||
def process_structs(registry, definition, properties, update, nesting):
|
||||
def process_structs(registry, definition, properties, update, nesting, nesting_long_names):
|
||||
value_types_defaults = registry.value_types_defaults
|
||||
blender_property_mapping = registry.blender_property_mapping
|
||||
type_infos = registry.type_infos
|
||||
long_name = definition["title"]
|
||||
short_name = definition["short_name"]
|
||||
|
||||
__annotations__ = {}
|
||||
default_values = {}
|
||||
nesting = nesting + [short_name]
|
||||
nesting_long_names = nesting_long_names + [long_name]
|
||||
|
||||
if short_name == "HashmapTestSimple":
|
||||
print("Struct", short_name)
|
||||
for property_name in properties.keys():
|
||||
ref_name = properties[property_name]["type"]["$ref"].replace("#/$defs/", "")
|
||||
|
||||
if short_name == "HashmapTestSimple":
|
||||
print("ref name", ref_name)
|
||||
if ref_name in type_infos:
|
||||
original = type_infos[ref_name]
|
||||
original_type_name = original["title"]
|
||||
@ -21,6 +26,9 @@ def process_structs(registry, definition, properties, update, nesting):
|
||||
value = value_types_defaults[original_type_name] if is_value_type else None
|
||||
default_values[property_name] = value
|
||||
|
||||
if short_name == "HashmapTestSimple":
|
||||
print("original",original, original_type_name, is_value_type, value)
|
||||
|
||||
if is_value_type:
|
||||
if original_type_name in blender_property_mapping:
|
||||
blender_property_def = blender_property_mapping[original_type_name]
|
||||
@ -33,7 +41,7 @@ def process_structs(registry, definition, properties, update, nesting):
|
||||
__annotations__[property_name] = blender_property
|
||||
else:
|
||||
original_long_name = original["title"]
|
||||
(sub_component_group, _) = process_component.process_component(registry, original, update, {"nested": True, "type_name": original_long_name}, nesting)
|
||||
(sub_component_group, _) = process_component.process_component(registry, original, update, {"nested": True, "type_name": original_long_name}, nesting, nesting_long_names)
|
||||
__annotations__[property_name] = sub_component_group
|
||||
# if there are sub fields, add an attribute "sub_fields" possibly a pointer property ? or add a standard field to the type , that is stored under "attributes" and not __annotations (better)
|
||||
else:
|
||||
@ -41,6 +49,6 @@ def process_structs(registry, definition, properties, update, nesting):
|
||||
__annotations__[property_name] = StringProperty(default="N/A")
|
||||
registry.add_missing_typeInfo(ref_name)
|
||||
# the root component also becomes invalid (in practice it is not always a component, but good enough)
|
||||
registry.add_invalid_component(nesting[0])
|
||||
registry.add_invalid_component(nesting_long_names[0])
|
||||
|
||||
return __annotations__
|
||||
|
@ -1,13 +1,15 @@
|
||||
from bpy.props import (StringProperty)
|
||||
from . import process_component
|
||||
|
||||
def process_tupples(registry, definition, prefixItems, update, nesting=[]):
|
||||
def process_tupples(registry, definition, prefixItems, update, nesting=[], nesting_long_names=[]):
|
||||
value_types_defaults = registry.value_types_defaults
|
||||
blender_property_mapping = registry.blender_property_mapping
|
||||
type_infos = registry.type_infos
|
||||
long_name = definition["title"]
|
||||
short_name = definition["short_name"]
|
||||
|
||||
nesting = nesting+[short_name]
|
||||
nesting_long_names = nesting_long_names + [long_name]
|
||||
__annotations__ = {}
|
||||
|
||||
default_values = []
|
||||
@ -46,7 +48,7 @@ def process_tupples(registry, definition, prefixItems, update, nesting=[]):
|
||||
__annotations__[property_name] = StringProperty(default="N/A")
|
||||
registry.add_missing_typeInfo(ref_name)
|
||||
# the root component also becomes invalid (in practice it is not always a component, but good enough)
|
||||
registry.add_invalid_component(nesting[0])
|
||||
registry.add_invalid_component(nesting_long_names[0])
|
||||
|
||||
|
||||
return __annotations__
|
||||
|
@ -3,6 +3,7 @@ from .conversions_from_prop_group import property_group_value_to_custom_property
|
||||
from .process_component import process_component
|
||||
from .utils import update_calback_helper
|
||||
|
||||
import json
|
||||
## main callback function, fired whenever any property changes, no matter the nesting level
|
||||
def update_component(self, context, definition, component_name):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
@ -14,12 +15,17 @@ def update_component(self, context, definition, component_name):
|
||||
print("")
|
||||
print("update in component", component_name, self, "current_object", current_object.name)
|
||||
components_in_object = current_object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == component_name, components_in_object), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == component_name, components_in_object), None)
|
||||
print("component_meta", component_meta)
|
||||
|
||||
if component_meta != None:
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(component_name)
|
||||
self = getattr(component_meta, property_group_name)
|
||||
property_group_name = registry.get_propertyGroupName_from_longName(component_name)
|
||||
property_group = getattr(component_meta, property_group_name)
|
||||
# we use our helper to set the values
|
||||
context.object[component_name] = property_group_value_to_custom_property_value(self, definition, registry, None)
|
||||
object = context.object
|
||||
previous = json.loads(object['bevy_components'])
|
||||
previous[component_name] = property_group_value_to_custom_property_value(property_group, definition, registry, None)
|
||||
object['bevy_components'] = json.dumps(previous)
|
||||
|
||||
|
||||
def generate_propertyGroups_for_components():
|
||||
@ -31,9 +37,8 @@ def generate_propertyGroups_for_components():
|
||||
|
||||
for component_name in type_infos:
|
||||
definition = type_infos[component_name]
|
||||
short_name = definition["short_name"]
|
||||
is_component = definition['isComponent'] if "isComponent" in definition else False
|
||||
root_property_name = short_name if is_component else None
|
||||
root_property_name = component_name if is_component else None
|
||||
process_component(registry, definition, update_calback_helper(definition, update_component, root_property_name), None, [])
|
||||
|
||||
# if we had to add any wrapper types on the fly, process them now
|
||||
|
@ -8,13 +8,16 @@ from bpy_types import PropertyGroup
|
||||
|
||||
# this helper creates a "fake"/wrapper property group that is NOT a real type in the registry
|
||||
# usefull for things like value types in list items etc
|
||||
def generate_wrapper_propertyGroup(short_name, item_long_name, definition, registry, update):
|
||||
def generate_wrapper_propertyGroup(wrapped_type_long_name_name, item_long_name, definition_link, registry, update):
|
||||
value_types_defaults = registry.value_types_defaults
|
||||
blender_property_mapping = registry.blender_property_mapping
|
||||
is_item_value_type = item_long_name in value_types_defaults
|
||||
|
||||
wrapper_name = "wrapper_" + short_name
|
||||
wrapper_name = "wrapper_" + wrapped_type_long_name_name
|
||||
print("WRAPPER NAME", wrapper_name)
|
||||
|
||||
|
||||
# FIXME: this is not a correct generic value for hashmaps !!?
|
||||
wrapper_definition = {
|
||||
"isComponent": False,
|
||||
"isResource": False,
|
||||
@ -22,17 +25,21 @@ def generate_wrapper_propertyGroup(short_name, item_long_name, definition, regis
|
||||
"prefixItems": [
|
||||
{
|
||||
"type": {
|
||||
"$ref": definition
|
||||
"$ref": definition_link
|
||||
}
|
||||
}
|
||||
],
|
||||
"short_name": wrapper_name,
|
||||
"short_name": wrapper_name, # FIXME !!!
|
||||
"title": wrapper_name,
|
||||
"type": "array",
|
||||
"typeInfo": "TupleStruct"
|
||||
}
|
||||
|
||||
# we generate a very small 'hash' for the component name
|
||||
property_group_name = registry.generate_propGroup_name(nesting=[], longName=wrapper_name)
|
||||
registry.add_custom_type(wrapper_name, wrapper_definition)
|
||||
|
||||
|
||||
blender_property = StringProperty(default="", update=update)
|
||||
if item_long_name in blender_property_mapping:
|
||||
value = value_types_defaults[item_long_name] if is_item_value_type else None
|
||||
@ -51,10 +58,9 @@ def generate_wrapper_propertyGroup(short_name, item_long_name, definition, regis
|
||||
'__annotations__': wrapper_annotations,
|
||||
'tupple_or_struct': "tupple",
|
||||
'field_names': ['0'],
|
||||
**dict(with_properties = False, with_items= True, with_enum= False, with_list= False, short_name= wrapper_name, type_name=wrapper_name),
|
||||
#'root_component': root_component
|
||||
**dict(with_properties = False, with_items= True, with_enum= False, with_list= False, with_map =False, short_name=wrapper_name, type_name=wrapper_name),
|
||||
}
|
||||
property_group_class = type(wrapper_name, (PropertyGroup,), property_group_params)
|
||||
property_group_class = type(property_group_name, (PropertyGroup,), property_group_params)
|
||||
bpy.utils.register_class(property_group_class)
|
||||
|
||||
return property_group_class
|
@ -317,6 +317,7 @@ class ComponentsRegistry(PropertyGroup):
|
||||
self.type_infos[type_name] = self.custom_types_to_add[type_name]
|
||||
self.custom_types_to_add.clear()
|
||||
|
||||
# add an invalid component to the list (long name)
|
||||
def add_invalid_component(self, component_name):
|
||||
self.invalid_components.append(component_name)
|
||||
|
||||
@ -335,7 +336,7 @@ class ComponentsRegistry(PropertyGroup):
|
||||
long_names_to_propgroup_names = {}
|
||||
|
||||
# generate propGroup name from nesting level & shortName: each shortName + nesting is unique
|
||||
def generate_propGroup_name(self, nesting, shortName, longName):
|
||||
def generate_propGroup_name(self, nesting, longName):
|
||||
#print("gen propGroup name for", shortName, nesting)
|
||||
#if shortName in self.short_names_to_propgroup_names and len(nesting) == 0:
|
||||
# return self.get_propertyGroupName_from_shortName(shortName)
|
||||
@ -350,7 +351,7 @@ class ComponentsRegistry(PropertyGroup):
|
||||
self.short_names_to_propgroup_names[key] = propGroupName"""
|
||||
# FIXME:
|
||||
key = str(nesting) + longName if len(nesting) > 0 else longName
|
||||
self.long_names_to_propgroup_names[longName] = propGroupName
|
||||
self.long_names_to_propgroup_names[key] = propGroupName
|
||||
return propGroupName
|
||||
|
||||
def get_propertyGroupName_from_shortName(self, shortName):
|
||||
@ -358,7 +359,9 @@ class ComponentsRegistry(PropertyGroup):
|
||||
|
||||
def get_propertyGroupName_from_longName(self, longName):
|
||||
return self.long_names_to_propgroup_names.get(longName, None)
|
||||
|
||||
|
||||
def long_name_to_key():
|
||||
pass
|
||||
|
||||
###########
|
||||
|
||||
|
@ -39,7 +39,7 @@ def test_components_should_generate_correct_custom_properties(setup_data):
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
added_components.append(component_type)
|
||||
custom_property_values[short_name] = object[short_name]
|
||||
@ -87,7 +87,7 @@ def test_components_should_generate_correct_custom_properties_with_randomized_va
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
component_values_shuffler(seed= 10, property_group=propertyGroup, definition=definition, registry=registry)
|
||||
|
||||
@ -136,7 +136,7 @@ def test_components_should_generate_correct_propertyGroup_values_from_custom_pro
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
added_components.append(component_type)
|
||||
# randomise values
|
||||
@ -195,7 +195,7 @@ def test_remove_components(setup_data):
|
||||
object = bpy.context.object
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
# print("propertyGroup", propertyGroup, propertyGroup.field_names)
|
||||
added_components.append(component_type)
|
||||
@ -231,7 +231,7 @@ def test_copy_paste_components(setup_data):
|
||||
object = context.object
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
setattr(propertyGroup, propertyGroup.field_names[0], 25.0)
|
||||
@ -246,7 +246,7 @@ def test_copy_paste_components(setup_data):
|
||||
# change name
|
||||
new_cube.name = "TargetCube"
|
||||
target_components_metadata = new_cube.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
|
||||
# first check that there is no component currently
|
||||
assert component_meta == None
|
||||
@ -255,7 +255,7 @@ def test_copy_paste_components(setup_data):
|
||||
paste_component_operator()
|
||||
|
||||
target_components_metadata = new_cube.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
|
||||
# now after pasting to the new object, it should have component meta
|
||||
assert component_meta != None
|
||||
|
@ -16,7 +16,7 @@ def test_blend(setup_data):
|
||||
object = bpy.context.object
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
|
||||
|
@ -9,7 +9,7 @@ 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)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == component_name, target_components_metadata), None)
|
||||
return component_meta
|
||||
|
||||
def get_component_propGroup(registry, component_name, component_meta):
|
||||
|
@ -19,7 +19,7 @@ def test_shuffler(setup_data):
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
@ -38,7 +38,7 @@ def test_shuffler(setup_data):
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
@ -57,7 +57,7 @@ def test_shuffler(setup_data):
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
@ -75,7 +75,7 @@ def test_shuffler(setup_data):
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
@ -95,7 +95,7 @@ def test_shuffler(setup_data):
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
@ -113,7 +113,7 @@ def test_shuffler(setup_data):
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
@ -130,7 +130,7 @@ def test_shuffler(setup_data):
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
@ -148,7 +148,7 @@ def test_shuffler(setup_data):
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
component_meta = next(filter(lambda component: component["long_name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
|
@ -5,9 +5,6 @@ from ..helpers.helpers_scenes import (get_scenes, )
|
||||
# IF collection_instances_combine_mode is not 'split' check for each scene if any object in changes_per_scene has an instance in the scene
|
||||
def changed_object_in_scene(scene_name, changes_per_scene, blueprints_data, collection_instances_combine_mode):
|
||||
# Embed / EmbedExternal
|
||||
"""if collection_instances_combine_mode == "Split": # 1 => Embed
|
||||
return False"""
|
||||
|
||||
blueprints_from_objects = blueprints_data.blueprints_from_objects
|
||||
|
||||
blueprint_instances_in_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene_name, None)
|
||||
@ -34,7 +31,6 @@ def changed_object_in_scene(scene_name, changes_per_scene, blueprints_data, coll
|
||||
# changes => list of changed objects (regardless of wether they have been changed in main scene or in lib scene)
|
||||
# wich of those objects are blueprint instances
|
||||
# we need a list of changed objects that are blueprint instances
|
||||
|
||||
return level_needs_export
|
||||
return False
|
||||
|
||||
|
@ -178,10 +178,10 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
|
||||
# if there were no setting before, it is new, we need export
|
||||
changed = False
|
||||
if previous_auto_settings == None:
|
||||
print("previous settings missing, exporting")
|
||||
#print("previous settings missing, exporting")
|
||||
changed = True
|
||||
elif previous_gltf_settings == None:
|
||||
print("previous gltf settings missing, exporting")
|
||||
#print("previous gltf settings missing, exporting")
|
||||
previous_gltf_settings = bpy.data.texts.new(".gltf_auto_export_gltf_settings_previous")
|
||||
previous_gltf_settings.write(json.dumps({}))
|
||||
if current_gltf_settings == None:
|
||||
@ -194,18 +194,17 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
|
||||
auto_settings_changed = sorted(json.loads(previous_auto_settings.as_string()).items()) != sorted(json.loads(current_auto_settings.as_string()).items()) if current_auto_settings != None else False
|
||||
gltf_settings_changed = sorted(json.loads(previous_gltf_settings.as_string()).items()) != sorted(json.loads(current_gltf_settings.as_string()).items()) if current_gltf_settings != None else False
|
||||
|
||||
print("auto settings previous", sorted(json.loads(previous_auto_settings.as_string()).items()))
|
||||
"""print("auto settings previous", sorted(json.loads(previous_auto_settings.as_string()).items()))
|
||||
print("auto settings current", sorted(json.loads(current_auto_settings.as_string()).items()))
|
||||
print("auto_settings_changed", auto_settings_changed)
|
||||
|
||||
print("gltf settings previous", sorted(json.loads(previous_gltf_settings.as_string()).items()))
|
||||
print("gltf settings current", sorted(json.loads(current_gltf_settings.as_string()).items()))
|
||||
print("gltf_settings_changed", gltf_settings_changed)
|
||||
print("gltf_settings_changed", gltf_settings_changed)"""
|
||||
|
||||
changed = auto_settings_changed or gltf_settings_changed
|
||||
# now write the current settings to the "previous settings"
|
||||
if current_auto_settings != None:
|
||||
print("writing settings")
|
||||
previous_auto_settings = bpy.data.texts[".gltf_auto_export_settings_previous"] if ".gltf_auto_export_settings_previous" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings_previous")
|
||||
previous_auto_settings.clear()
|
||||
previous_auto_settings.write(current_auto_settings.as_string()) # TODO : check if this is always valid
|
||||
@ -294,15 +293,9 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
|
||||
changes_per_scene[scene][object_name] = bpy.data.objects[object_name]
|
||||
bubble_up_changes(bpy.data.objects[object_name], changes_per_scene[scene])
|
||||
# now bubble up for instances & parents
|
||||
|
||||
|
||||
|
||||
|
||||
previous_stored.clear()
|
||||
previous_stored.write(json.dumps(current))
|
||||
|
||||
|
||||
|
||||
print("changes per scene alternative", changes_per_scene)
|
||||
return changes_per_scene
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user