From 2abdb7f64ec04247ee0d8b4c950b3c9c1053be24 Mon Sep 17 00:00:00 2001 From: "kaosat.dev" Date: Mon, 17 Jun 2024 11:44:08 +0200 Subject: [PATCH] feat(Blenvy): fixed & overhauled components processing & hashing * now correctly using nested long names + attribute name for structs * so no more hashing collisions for all test cases ! * restructured internals of property group generation & registration * dynamically generated property group classes are now unregistered correctly * lots of minor related tweaks & improvements --- tools/blenvy/TODO.md | 7 +-- .../bevy_components/components/metadata.py | 2 +- .../propGroups/process_component.py | 23 +++------- .../propGroups/process_structs.py | 2 +- .../bevy_components/propGroups/prop_groups.py | 10 ++-- .../bevy_components/registry/registry.py | 46 +++++++++++++++---- 6 files changed, 54 insertions(+), 36 deletions(-) diff --git a/tools/blenvy/TODO.md b/tools/blenvy/TODO.md index 6e48192..f0428d4 100644 --- a/tools/blenvy/TODO.md +++ b/tools/blenvy/TODO.md @@ -95,7 +95,7 @@ Components: -> VERY likely due to the int-offset computation for hashes of components - now switched to tiger_hash - [x] add warning about hash colision (not much we can/ could do if it is the case ?) - - [ ] double check weird collisions AND/OR reuse existing if applicable + - [x] double check weird collisions AND/OR reuse existing if applicable - [x] annoying default path for registry, should be relative to the assets path @@ -163,8 +163,9 @@ General issues: - [ ] remove/replace bevy editor pls with some native ui to display hierarchies - [ ] switch to bevy rc2 - - +- [ ] simplify examples: + - [ ] a full fledged demo (including physics & co) + - [ ] other examples without interactions or physics - [x] overall cleanup diff --git a/tools/blenvy/add_ons/bevy_components/components/metadata.py b/tools/blenvy/add_ons/bevy_components/components/metadata.py index 057edfa..2d415b5 100644 --- a/tools/blenvy/add_ons/bevy_components/components/metadata.py +++ b/tools/blenvy/add_ons/bevy_components/components/metadata.py @@ -220,7 +220,7 @@ def upsert_component_in_item(item, long_name, registry): if property_group_name in registry.component_propertyGroups: # we have found a matching property_group, so try to inject it # now inject property group - setattr(ComponentMetadata, property_group_name, registry.component_propertyGroups[property_group_name]) # FIXME: not ideal as all ComponentMetadata get the propGroup, but have not found a way to assign it per instance + setattr(ComponentMetadata, property_group_name, registry.component_propertyGroups[property_group_name]) # FIXME: not ideal as ALL instances of ComponentMetadata get the propGroup, but have not found a way to assign it per instance propertyGroup = getattr(component_meta, property_group_name, None) # now deal with property groups details diff --git a/tools/blenvy/add_ons/bevy_components/propGroups/process_component.py b/tools/blenvy/add_ons/bevy_components/propGroups/process_component.py index 847ffa8..16a15e9 100644 --- a/tools/blenvy/add_ons/bevy_components/propGroups/process_component.py +++ b/tools/blenvy/add_ons/bevy_components/propGroups/process_component.py @@ -30,13 +30,14 @@ def process_component(registry, definition, update, extras=None, nesting_long_na with_list = False with_map = False - padding = " " * (len(nesting_long_names) + 1) + #padding = " " * (len(nesting_long_names) + 1) #print(f"{padding}process component", long_name, "nesting_long_names",nesting_long_names, "foo", has_properties, has_prefixItems, is_enum, is_list, is_map) if has_properties: __annotations__ = __annotations__ | process_structs.process_structs(registry, definition, properties, update, nesting_long_names) with_properties = True tupple_or_struct = "struct" + #print(f"{padding}struct") if has_prefixItems: __annotations__ = __annotations__ | process_tupples.process_tupples(registry, definition, prefixItems, update, nesting_long_names) @@ -61,7 +62,6 @@ def process_component(registry, definition, update, extras=None, nesting_long_na for a in __annotations__: field_names.append(a) - extras = extras if extras is not None else { "long_name": long_name } @@ -78,25 +78,16 @@ def process_component(registry, definition, update, extras=None, nesting_long_na **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, long_name=long_name), 'root_component': root_component } - #FIXME: YIKES, but have not found another way: - """ Withouth this ; the following does not work + + # we need to pass the full hierarchy to disambiguate between components + # Withouth this ; the following does not work + """ -BasicTest - NestingTestLevel2 -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_long_names) - (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) + (property_group_pointer, property_group_class) = registry.register_component_propertyGroup(nesting_long_names, property_group_params) return (property_group_pointer, property_group_class) -def property_group_from_infos(property_group_name, property_group_parameters): - # print("creating property group", property_group_name) - property_group_class = type(property_group_name, (PropertyGroup,), property_group_parameters) - - bpy.utils.register_class(property_group_class) - property_group_pointer = PointerProperty(type=property_group_class) - - return (property_group_pointer, property_group_class) \ No newline at end of file diff --git a/tools/blenvy/add_ons/bevy_components/propGroups/process_structs.py b/tools/blenvy/add_ons/bevy_components/propGroups/process_structs.py index c3ec85b..255111e 100644 --- a/tools/blenvy/add_ons/bevy_components/propGroups/process_structs.py +++ b/tools/blenvy/add_ons/bevy_components/propGroups/process_structs.py @@ -33,7 +33,7 @@ def process_structs(registry, definition, properties, update, nesting_long_names __annotations__[property_name] = blender_property else: original_long_name = original["long_name"] - (sub_component_group, _) = process_component.process_component(registry, original, update, {"nested": True, "long_name": original_long_name}, nesting_long_names) + (sub_component_group, _) = process_component.process_component(registry, original, update, {"nested": True, "long_name": original_long_name}, nesting_long_names+[property_name]) __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: diff --git a/tools/blenvy/add_ons/bevy_components/propGroups/prop_groups.py b/tools/blenvy/add_ons/bevy_components/propGroups/prop_groups.py index d288acc..735ad3d 100644 --- a/tools/blenvy/add_ons/bevy_components/propGroups/prop_groups.py +++ b/tools/blenvy/add_ons/bevy_components/propGroups/prop_groups.py @@ -34,12 +34,10 @@ def generate_propertyGroups_for_components(): type_infos = registry.type_infos - for component_name in type_infos: - definition = type_infos[component_name] - is_component = definition['isComponent'] if "isComponent" in definition else False - root_property_name = component_name# if is_component else None - print("root property", component_name,f"({is_component})") - process_component(registry, definition, update_calback_helper(definition, update_component, root_property_name), extras=None, nesting_long_names=[]) + for root_type_name in type_infos: + definition = type_infos[root_type_name] + #print("root property", component_name,f"({is_component})") + process_component(registry, definition, update_calback_helper(definition, update_component, root_type_name), extras=None, nesting_long_names=[]) # if we had to add any wrapper types on the fly, process them now registry.process_custom_types() \ No newline at end of file diff --git a/tools/blenvy/add_ons/bevy_components/registry/registry.py b/tools/blenvy/add_ons/bevy_components/registry/registry.py index 256ee4c..7f519dc 100644 --- a/tools/blenvy/add_ons/bevy_components/registry/registry.py +++ b/tools/blenvy/add_ons/bevy_components/registry/registry.py @@ -7,6 +7,8 @@ from bpy_types import (PropertyGroup) from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty) from ..components.metadata import ComponentMetadata from .hashing.tiger import hash as tiger_hash + + # helper class to store missing bevy types information class MissingBevyType(bpy.types.PropertyGroup): long_name: bpy.props.StringProperty( @@ -14,6 +16,15 @@ class MissingBevyType(bpy.types.PropertyGroup): ) # type: ignore +def property_group_from_infos(property_group_name, property_group_parameters): + # print("creating property group", property_group_name) + property_group_class = type(property_group_name, (PropertyGroup,), property_group_parameters) + + bpy.utils.register_class(property_group_class) + property_group_pointer = PointerProperty(type=property_group_class) + + return (property_group_pointer, property_group_class) + # this is where we store the information for all available components class ComponentsRegistry(PropertyGroup): registry: bpy.props. StringProperty( @@ -140,6 +151,8 @@ class ComponentsRegistry(PropertyGroup): type_infos = {} type_infos_missing = [] component_propertyGroups = {} + component_property_group_classes = [] + custom_types_to_add = {} invalid_components = [] @@ -152,8 +165,18 @@ class ComponentsRegistry(PropertyGroup): for propgroup_name in cls.component_propertyGroups.keys(): try: delattr(ComponentMetadata, propgroup_name) + #print("sucess REMOVAL from Metadata") except Exception as error: pass + #print("failed to unregister") + + for propgroup_class in cls.component_property_group_classes: + try: + bpy.utils.unregister_class(propgroup_class) + #print("sucess UNREGISTER") + except Exception as error: + pass + #print("NEW failed to unregister") del bpy.types.WindowManager.components_registry @@ -167,7 +190,10 @@ class ComponentsRegistry(PropertyGroup): self.missing_types_list.clear() self.type_infos.clear() self.type_infos_missing.clear() + self.component_propertyGroups.clear() + self.component_property_group_classes.clear() + self.custom_types_to_add.clear() self.invalid_components.clear() # now prepare paths to load data @@ -188,10 +214,6 @@ class ComponentsRegistry(PropertyGroup): def has_type_infos(self): return len(self.type_infos.keys()) != 0 - # we keep a list of component propertyGroup around - def register_component_propertyGroup(self, name, propertyGroup): - self.component_propertyGroups[name] = propertyGroup - # to be able to give the user more feedback on any missin/unregistered types in their schema file def add_missing_typeInfo(self, long_name): if not long_name in self.type_infos_missing: @@ -217,18 +239,25 @@ class ComponentsRegistry(PropertyGroup): long_names_to_propgroup_names = {} + # we keep a list of component propertyGroup around + def register_component_propertyGroup(self, nesting, property_group_params): + property_group_name = self.generate_propGroup_name(nesting) + (property_group_pointer, property_group_class) = property_group_from_infos(property_group_name, property_group_params) + self.component_propertyGroups[property_group_name] = property_group_pointer + self.component_property_group_classes.append(property_group_class) + + return (property_group_pointer, property_group_class) + # generate propGroup name from nesting level: each longName + nesting is unique def generate_propGroup_name(self, nesting): - #print("gen propGroup name for", shortName, nesting) key = str(nesting) propGroupHash = tiger_hash(key) propGroupName = propGroupHash + "_ui" # check for collision - padding = " " * (len(nesting) + 1) - - print(f"{padding}--computing hash for", nesting) + #padding = " " * (len(nesting) + 1) + #print(f"{padding}--computing hash for", nesting) if propGroupName in self.long_names_to_propgroup_names.values(): print(" WARNING !! you have a collision between the hash of multiple component names: collision for", nesting) @@ -238,7 +267,6 @@ class ComponentsRegistry(PropertyGroup): def get_propertyGroupName_from_longName(self, longName): return self.long_names_to_propgroup_names.get(str([longName]), None) - ###########