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
This commit is contained in:
kaosat.dev 2024-06-17 11:44:08 +02:00
parent e86920168a
commit 2abdb7f64e
6 changed files with 54 additions and 36 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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()

View File

@ -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)
###########