Blender_bevy_components_wor.../tools/bevy_components/propGroups/conversions.py
Mark Moissette cfbda24da7
feat(tools/bevy_blueprints): bevy plugin + blender addon for components UI to more easily create components (#70)
* adds a new crate: ```bevy_registry_export``` to be able to create a json import of the registered component/type definitions
* adds a new Blender addon: ```bevy_components``` that takes that json data to generate custom UIs for components , to be to add & edit components easily in Blender 
   * also adds component metadata per object for more advanced features
   * etc
* updates to bevy_gltf_components & bevy_gltf_blueprints to add legacy_mode to support the "old"/current style component definitions
* same with gltf_auto_export Blender add_on
* closes #60
2024-02-05 23:01:19 +01:00

222 lines
12 KiB
Python

import json
from bpy_types import PropertyGroup
conversion_tables = {
"bool": lambda value: value,
"char": lambda value: '"'+value+'"',
"str": lambda value: '"'+value+'"',
"alloc::string::String": lambda value: '"'+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])+")",
"glam::UVec2": lambda value: "UVec2(x:"+str(value[0])+ ", y:"+str(value[1])+")",
"glam::Vec3": lambda value: "Vec3(x:"+str(value[0])+ ", y:"+str(value[1])+ ", z:"+str(value[2])+")",
"glam::Vec3A": lambda value: "Vec3A(x:"+str(value[0])+ ", y:"+str(value[1])+ ", z:"+str(value[2])+")",
"glam::UVec3": lambda value: "UVec3(x:"+str(value[0])+ ", y:"+str(value[1])+ ", z:"+str(value[2])+")",
"glam::Vec4": lambda value: "Vec4(x:"+str(value[0])+ ", y:"+str(value[1])+ ", z:"+str(value[2])+ ", w:"+str(value[3])+")",
"glam::DVec4": lambda value: "DVec4(x:"+str(value[0])+ ", y:"+str(value[1])+ ", z:"+str(value[2])+ ", w:"+str(value[3])+")",
"glam::UVec4": lambda value: "UVec4(x:"+str(value[0])+ ", y:"+str(value[1])+ ", z:"+str(value[2])+ ", w:"+str(value[3])+")",
"glam::Quat": lambda value: "Quat(x:"+str(value[0])+ ", y:"+str(value[1])+ ", z:"+str(value[2])+ ", w:"+str(value[3])+")",
"bevy_render::color::Color": lambda value: "Rgba(red:"+str(value[0])+ ", green:"+str(value[1])+ ", blue:"+str(value[2])+ ", alpha:"+str(value[3])+ ")",
}
#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"]
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)
if is_value_type:
value = conversion_tables[type_name](value)
elif type_info == "Struct":
values = {}
if len(property_group.field_names) ==0:
value = ''
else:
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
value = getattr(property_group, field_name)
is_property_group = isinstance(value, PropertyGroup)
child_property_group = value if is_property_group else None
if item_definition != None:
value = property_group_value_to_custom_property_value(child_property_group, item_definition, registry, parent=component_name, value=value)
else:
value = '""'
values[field_name] = value
value = values
elif type_info == "Tuple":
values = {}
for index, field_name in enumerate(property_group.field_names):
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
value = getattr(property_group, field_name)
is_property_group = isinstance(value, PropertyGroup)
child_property_group = value if is_property_group else None
if item_definition != None:
value = property_group_value_to_custom_property_value(child_property_group, item_definition, registry, parent=component_name, value=value)
else:
value = '""'
values[field_name] = value
value = tuple(e for e in list(values.values()))
elif type_info == "TupleStruct":
values = {}
for index, field_name in enumerate(property_group.field_names):
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
value = getattr(property_group, field_name)
is_property_group = isinstance(value, PropertyGroup)
child_property_group = value if is_property_group else None
if item_definition != None:
value = property_group_value_to_custom_property_value(child_property_group, item_definition, registry, parent=component_name, value=value)
else:
value = '""'
values[field_name] = value
value = tuple(e for e in list(values.values()))
elif type_info == "Enum":
selected = getattr(property_group, component_name)
if type_def == "object":
selection_index = property_group.field_names.index("variant_"+selected)
variant_name = property_group.field_names[selection_index]
variant_definition = definition["oneOf"][selection_index-1]
if "prefixItems" in variant_definition:
value = getattr(property_group, variant_name)
is_property_group = isinstance(value, PropertyGroup)
child_property_group = value if is_property_group else None
value = property_group_value_to_custom_property_value(child_property_group, variant_definition, registry, parent=component_name, value=value)
value = selected + str(value,)
elif "properties" in variant_definition:
value = getattr(property_group, variant_name)
is_property_group = isinstance(value, PropertyGroup)
child_property_group = value if is_property_group else None
value = property_group_value_to_custom_property_value(child_property_group, variant_definition, registry, parent=component_name, value=value)
value = selected + str(value,)
else:
print("basic enum stuff")
value = selected # here the value of the enum is just the name of the variant
else:
value = selected
elif type_info == "List":
item_list = getattr(property_group, "list")
#item_type = getattr(property_group, "type_name_short")
value = []
for item in item_list:
item_type_name = getattr(item, "type_name")
definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
if definition != None:
item_value = property_group_value_to_custom_property_value(item, definition, registry, component_name, None)
if item_type_name.startswith("wrapper_"): #if we have a "fake" tupple for aka for value types, we need to remove one nested level
item_value = item_value[0]
else:
item_value = '""'
value.append(item_value)
else:
value = conversion_tables[type_name](value) if is_value_type else value
#print("VALUE", value, type(value))
#print("generating custom property value", value, type(value))
if parent == None:
value = str(value).replace("'", "")
value = value.replace(",)",")")
value = value.replace("{", "(").replace("}", ")")
value = value.replace("True", "true").replace("False", "false")
return value
import re
#converts the value of a single custom property into a value (values) of a property group
def property_group_value_from_custom_property_value(property_group, definition, registry, custom_property_value):
#print(" ")
#print("setting property group value", property_group, definition, custom_property_value)
type_infos = registry.type_infos
value_types_defaults = registry.value_types_defaults
print("custom_property_value", custom_property_value)
def parse_field(item, property_group, definition, field_name):
type_info = definition["typeInfo"] if "typeInfo" in definition else None
type_def = definition["type"] if "type" in definition else None
properties = definition["properties"] if "properties" in definition else {}
prefixItems = definition["prefixItems"] if "prefixItems" in definition else []
has_properties = len(properties.keys()) > 0
has_prefixItems = len(prefixItems) > 0
is_enum = type_info == "Enum"
is_list = type_info == "List"
is_value_type = type_def in value_types_defaults
print("parsing field", item, "type infos", type_info, "type_def", type_def)
if type_info == "Struct":
print("is object")
for field_name in property_group.field_names:
print("field name", field_name)
# sub field
if isinstance(getattr(property_group, field_name), PropertyGroup):
sub_prop_group = getattr(property_group, field_name)
ref_name = properties[field_name]["type"]["$ref"].replace("#/$defs/", "")
sub_definition = type_infos[ref_name]
parse_field(item[field_name], sub_prop_group, sub_definition, field_name)
else:
setattr(property_group, field_name, item[field_name])
if has_prefixItems:
if len(property_group.field_names) == 1:
setattr(property_group, "0", item) # FIXME: not ideal
else:
for field_name in property_group.field_names:
setattr(property_group, field_name, item)
if is_enum:
if type_def == "object":
regexp = re.search('(^[^\(]+)(\((.*)\))', item)
chosen_variant = regexp.group(1)
chosen_variant_value = regexp.group(3).replace("'", '"').replace("(", "[").replace(")","]")
chosen_variant_value = json.loads(chosen_variant_value)
# first set chosen selection
field_name = property_group.field_names[0]
setattr(property_group, field_name, chosen_variant)
# thenlook for the information about the matching variant
sub_definition= None
for variant in definition["oneOf"]:
if variant["title"] == chosen_variant:
ref_name = variant["prefixItems"][0]["type"]["$ref"].replace("#/$defs/", "")
sub_definition = type_infos[ref_name]
break
variant_name = "variant_"+chosen_variant
if isinstance(getattr(property_group, variant_name), PropertyGroup):
sub_prop_group = getattr(property_group, variant_name)
parse_field(chosen_variant_value, sub_prop_group, sub_definition, variant_name)
else:
setattr(property_group, variant_name, chosen_variant_value)
else:
field_name = property_group.field_names[0]
setattr(property_group, field_name, item)
if is_list:
print("is list")
if is_value_type:
print("is value type")
try:
parse_field(custom_property_value, property_group, definition, None)
except Exception as error:
print("failed to parse raw custom property data", error)