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:
parent
e86920168a
commit
2abdb7f64e
|
@ -95,7 +95,7 @@ Components:
|
||||||
-> VERY likely due to the int-offset computation for hashes of components
|
-> VERY likely due to the int-offset computation for hashes of components
|
||||||
- now switched to tiger_hash
|
- now switched to tiger_hash
|
||||||
- [x] add warning about hash colision (not much we can/ could do if it is the case ?)
|
- [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
|
- [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
|
- [ ] remove/replace bevy editor pls with some native ui to display hierarchies
|
||||||
- [ ] switch to bevy rc2
|
- [ ] switch to bevy rc2
|
||||||
|
|
||||||
|
- [ ] simplify examples:
|
||||||
|
- [ ] a full fledged demo (including physics & co)
|
||||||
|
- [ ] other examples without interactions or physics
|
||||||
|
|
||||||
|
|
||||||
- [x] overall cleanup
|
- [x] overall cleanup
|
||||||
|
|
|
@ -220,7 +220,7 @@ def upsert_component_in_item(item, long_name, registry):
|
||||||
if property_group_name in registry.component_propertyGroups:
|
if property_group_name in registry.component_propertyGroups:
|
||||||
# we have found a matching property_group, so try to inject it
|
# we have found a matching property_group, so try to inject it
|
||||||
# now inject property group
|
# 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)
|
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||||
|
|
||||||
# now deal with property groups details
|
# now deal with property groups details
|
||||||
|
|
|
@ -30,13 +30,14 @@ def process_component(registry, definition, update, extras=None, nesting_long_na
|
||||||
with_list = False
|
with_list = False
|
||||||
with_map = 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)
|
#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:
|
if has_properties:
|
||||||
__annotations__ = __annotations__ | process_structs.process_structs(registry, definition, properties, update, nesting_long_names)
|
__annotations__ = __annotations__ | process_structs.process_structs(registry, definition, properties, update, nesting_long_names)
|
||||||
with_properties = True
|
with_properties = True
|
||||||
tupple_or_struct = "struct"
|
tupple_or_struct = "struct"
|
||||||
|
#print(f"{padding}struct")
|
||||||
|
|
||||||
if has_prefixItems:
|
if has_prefixItems:
|
||||||
__annotations__ = __annotations__ | process_tupples.process_tupples(registry, definition, prefixItems, update, nesting_long_names)
|
__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__:
|
for a in __annotations__:
|
||||||
field_names.append(a)
|
field_names.append(a)
|
||||||
|
|
||||||
|
|
||||||
extras = extras if extras is not None else {
|
extras = extras if extras is not None else {
|
||||||
"long_name": long_name
|
"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),
|
**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
|
'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
|
-BasicTest
|
||||||
- NestingTestLevel2
|
- NestingTestLevel2
|
||||||
-BasicTest => the registration & update callback of this one overwrites the first "basicTest"
|
-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
|
# 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)
|
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)
|
|
|
@ -33,7 +33,7 @@ def process_structs(registry, definition, properties, update, nesting_long_names
|
||||||
__annotations__[property_name] = blender_property
|
__annotations__[property_name] = blender_property
|
||||||
else:
|
else:
|
||||||
original_long_name = original["long_name"]
|
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
|
__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)
|
# 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:
|
else:
|
||||||
|
|
|
@ -34,12 +34,10 @@ def generate_propertyGroups_for_components():
|
||||||
|
|
||||||
type_infos = registry.type_infos
|
type_infos = registry.type_infos
|
||||||
|
|
||||||
for component_name in type_infos:
|
for root_type_name in type_infos:
|
||||||
definition = type_infos[component_name]
|
definition = type_infos[root_type_name]
|
||||||
is_component = definition['isComponent'] if "isComponent" in definition else False
|
#print("root property", component_name,f"({is_component})")
|
||||||
root_property_name = component_name# if is_component else None
|
process_component(registry, definition, update_calback_helper(definition, update_component, root_type_name), extras=None, nesting_long_names=[])
|
||||||
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=[])
|
|
||||||
|
|
||||||
# if we had to add any wrapper types on the fly, process them now
|
# if we had to add any wrapper types on the fly, process them now
|
||||||
registry.process_custom_types()
|
registry.process_custom_types()
|
|
@ -7,6 +7,8 @@ from bpy_types import (PropertyGroup)
|
||||||
from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty)
|
from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty)
|
||||||
from ..components.metadata import ComponentMetadata
|
from ..components.metadata import ComponentMetadata
|
||||||
from .hashing.tiger import hash as tiger_hash
|
from .hashing.tiger import hash as tiger_hash
|
||||||
|
|
||||||
|
|
||||||
# helper class to store missing bevy types information
|
# helper class to store missing bevy types information
|
||||||
class MissingBevyType(bpy.types.PropertyGroup):
|
class MissingBevyType(bpy.types.PropertyGroup):
|
||||||
long_name: bpy.props.StringProperty(
|
long_name: bpy.props.StringProperty(
|
||||||
|
@ -14,6 +16,15 @@ class MissingBevyType(bpy.types.PropertyGroup):
|
||||||
) # type: ignore
|
) # 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
|
# this is where we store the information for all available components
|
||||||
class ComponentsRegistry(PropertyGroup):
|
class ComponentsRegistry(PropertyGroup):
|
||||||
registry: bpy.props. StringProperty(
|
registry: bpy.props. StringProperty(
|
||||||
|
@ -140,6 +151,8 @@ class ComponentsRegistry(PropertyGroup):
|
||||||
type_infos = {}
|
type_infos = {}
|
||||||
type_infos_missing = []
|
type_infos_missing = []
|
||||||
component_propertyGroups = {}
|
component_propertyGroups = {}
|
||||||
|
component_property_group_classes = []
|
||||||
|
|
||||||
custom_types_to_add = {}
|
custom_types_to_add = {}
|
||||||
invalid_components = []
|
invalid_components = []
|
||||||
|
|
||||||
|
@ -152,8 +165,18 @@ class ComponentsRegistry(PropertyGroup):
|
||||||
for propgroup_name in cls.component_propertyGroups.keys():
|
for propgroup_name in cls.component_propertyGroups.keys():
|
||||||
try:
|
try:
|
||||||
delattr(ComponentMetadata, propgroup_name)
|
delattr(ComponentMetadata, propgroup_name)
|
||||||
|
#print("sucess REMOVAL from Metadata")
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
pass
|
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
|
del bpy.types.WindowManager.components_registry
|
||||||
|
|
||||||
|
@ -167,7 +190,10 @@ class ComponentsRegistry(PropertyGroup):
|
||||||
self.missing_types_list.clear()
|
self.missing_types_list.clear()
|
||||||
self.type_infos.clear()
|
self.type_infos.clear()
|
||||||
self.type_infos_missing.clear()
|
self.type_infos_missing.clear()
|
||||||
|
|
||||||
self.component_propertyGroups.clear()
|
self.component_propertyGroups.clear()
|
||||||
|
self.component_property_group_classes.clear()
|
||||||
|
|
||||||
self.custom_types_to_add.clear()
|
self.custom_types_to_add.clear()
|
||||||
self.invalid_components.clear()
|
self.invalid_components.clear()
|
||||||
# now prepare paths to load data
|
# now prepare paths to load data
|
||||||
|
@ -188,10 +214,6 @@ class ComponentsRegistry(PropertyGroup):
|
||||||
def has_type_infos(self):
|
def has_type_infos(self):
|
||||||
return len(self.type_infos.keys()) != 0
|
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
|
# 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):
|
def add_missing_typeInfo(self, long_name):
|
||||||
if not long_name in self.type_infos_missing:
|
if not long_name in self.type_infos_missing:
|
||||||
|
@ -217,18 +239,25 @@ class ComponentsRegistry(PropertyGroup):
|
||||||
|
|
||||||
long_names_to_propgroup_names = {}
|
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
|
# generate propGroup name from nesting level: each longName + nesting is unique
|
||||||
def generate_propGroup_name(self, nesting):
|
def generate_propGroup_name(self, nesting):
|
||||||
#print("gen propGroup name for", shortName, nesting)
|
|
||||||
key = str(nesting)
|
key = str(nesting)
|
||||||
|
|
||||||
propGroupHash = tiger_hash(key)
|
propGroupHash = tiger_hash(key)
|
||||||
propGroupName = propGroupHash + "_ui"
|
propGroupName = propGroupHash + "_ui"
|
||||||
|
|
||||||
# check for collision
|
# check for collision
|
||||||
padding = " " * (len(nesting) + 1)
|
#padding = " " * (len(nesting) + 1)
|
||||||
|
#print(f"{padding}--computing hash for", nesting)
|
||||||
print(f"{padding}--computing hash for", nesting)
|
|
||||||
if propGroupName in self.long_names_to_propgroup_names.values():
|
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)
|
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):
|
def get_propertyGroupName_from_longName(self, longName):
|
||||||
return self.long_names_to_propgroup_names.get(str([longName]), None)
|
return self.long_names_to_propgroup_names.get(str([longName]), None)
|
||||||
|
|
||||||
|
|
||||||
###########
|
###########
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue