Skip to content

Commit

Permalink
Fix some properties not being reset on rig export
Browse files Browse the repository at this point in the history
  • Loading branch information
greisane committed Aug 6, 2023
1 parent 8b7e6b2 commit c19f9a0
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 63 deletions.
2 changes: 1 addition & 1 deletion anim/channels_delete_unavailable.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def execute(self, context):

for fc in action.fcurves:
try:
prop = obj.path_resolve(fc.data_path, False)
obj.path_resolve(fc.data_path)
except ValueError:
if delete_invalid:
print(f"Removing curve, can't resolve {fc.data_path}")
Expand Down
4 changes: 2 additions & 2 deletions math.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

class Rect(namedtuple("Rect", ["x0", "y0", "x1", "y1"])):
@classmethod
def from_size(self, x, y, width, height):
return Rect(x, y, x + width, y + height)
def from_size(cls, x, y, width, height):
return cls(x, y, x + width, y + height)

@property
def width(self):
Expand Down
4 changes: 2 additions & 2 deletions operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ class GRET_OT_property_warning(bpy.types.Operator):
bl_label = "Not Overridable"
bl_options = {'INTERNAL'}

def draw_warning_if_not_overridable(layout, obj, prop_path):
def draw_warning_if_not_overridable(layout, obj, data_path):
if obj and obj.override_library:
try:
if not obj.is_property_overridable_library(prop_path):
if not obj.is_property_overridable_library(data_path):
layout.operator(GRET_OT_property_warning.bl_idname,
icon='ERROR', text="", emboss=False, depress=True)
return True
Expand Down
80 changes: 63 additions & 17 deletions rig/helpers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from collections import namedtuple
from fnmatch import fnmatch
from itertools import chain
from mathutils import Vector, Quaternion, Euler
import bpy
import re

from ..patcher import FunctionPatcher
from ..helpers import intercept, get_context, select_only
from ..helpers import intercept, get_context, select_only, titlecase
from ..log import log, logd

arp_export_module_names = [
Expand Down Expand Up @@ -60,6 +62,61 @@
'y_scale': 2, # Bone original
}
default_pose_values = {}
custom_prop_pattern = re.compile(r'(.+)?\["([^"]+)"\]')
prop_pattern = re.compile(r'(?:(.+)\.)?([^"\.]+)')

class PropertyWrapper(namedtuple('PrettyProperty', ['data', 'path', 'is_custom'])):
# To set a property given a data path it's necessary to split the object and attribute name
# `data.path_resolve(path, False)` returns a bpy_prop, and bpy_prop.data holds the object
# Unfortunately bpy_prop knows the attribute name but doesn't expose it (see `bpy_prop.__str__`)
# Also need to determine if it's a custom property, since those are accessed dict-like instead
# For these reasons, resort to the less robust way of just parsing the data path with regexes

@classmethod
def from_path(cls, data, data_path):
try:
prop_match = custom_prop_pattern.fullmatch(data_path)
if prop_match:
if prop_match[1]:
data = data.path_resolve(prop_match[1])
data_path = f'["{prop_match[2]}"]'
data.path_resolve(data_path) # Make sure the property exists
return cls(data, data_path, True)

prop_match = prop_pattern.fullmatch(data_path)
if prop_match:
if prop_match[1]:
data = data.path_resolve(prop_match[1])
data_path = prop_match[2]
data.path_resolve(data_path) # Make sure the property exists
return cls(data, data_path, False)
except ValueError:
return None

@property
def title(self):
if self.is_custom:
return titlecase(self.path[2:-2]) # Custom property name should be descriptive enough
else:
return f"{self.data.name} {titlecase(self.path)}"

@property
def default_value(self):
if self.is_custom:
return self.data.id_properties_ui(self.path[2:-2]).as_dict()['default']
else:
return self.data.bl_rna.properties[self.path].default

@property
def value(self):
return self.data.path_resolve(self.path, True)

@value.setter
def value(self, new_value):
if self.is_custom:
self.data[self.path[2:-2]] = new_value
else:
setattr(self.data, self.path, new_value)

def is_object_arp(obj):
"""Returns whether the object is an Auto-Rig Pro armature."""
Expand Down Expand Up @@ -96,24 +153,13 @@ def clear_pose(obj, clear_gret_props=True, clear_armature_props=False, clear_bon
return

if clear_gret_props:
properties = obj.get('properties', [])
for prop_path in properties:
for data_path in obj.get('properties', []):
try:
bpy_prop = obj.path_resolve(prop_path, False)
prop_data = bpy_prop.data
dot_pos = prop_path.rfind(".")
if dot_pos >= 0:
# Native property
prop_name = prop_path[dot_pos+1:]
default_value = prop_data.bl_rna.properties[prop_name].default
setattr(prop_data, prop_name, default_value)
else:
# Custom property
prop_name = prop_path[2:-2]
default_value = prop_data.id_properties_ui(prop_name).as_dict()["default"]
prop_data[prop_name] = default_value
prop_wrapper = PropertyWrapper.from_path(obj, data_path)
if prop_wrapper:
prop_wrapper.value = prop_wrapper.default_value
except Exception as e:
logd(f"Couldn't clear property \"{prop_path}\": {e}")
logd(f"Couldn't clear property \"{data_path}\": {e}")

if clear_armature_props:
for prop_name, prop_value in obj.items():
Expand Down
52 changes: 11 additions & 41 deletions rig/properties.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,7 @@
import bpy
import re

from ..helpers import titlecase
from ..operator import draw_warning_if_not_overridable

custom_prop_re = re.compile(r'(.+)?\["([^"]+)"\]$')
prop_re = re.compile(r'(.+)\.([^"\.]+)$')

def parse_prop_path(obj, prop_path):
# Returns target data, property path and pretty property text if the property was found
# Otherwise returns None, None, prop_path

try:
prop_match = custom_prop_re.search(prop_path)
if prop_match:
if prop_match[1]:
obj = obj.path_resolve(prop_match[1])
prop_path = f'["{prop_match[2]}"]'
# Fetch value to make sure the property exists
value = obj.path_resolve(prop_path)
# Don't attach the object name to text, custom property name should be descriptive enough
text = titlecase(prop_match[2])
return obj, prop_path, text

prop_match = prop_re.search(prop_path)
if prop_match:
obj = obj.path_resolve(prop_match[1])
prop_path = prop_match[2]
# Fetch value to make sure the property exists
value = obj.path_resolve(prop_path)
text = f"{obj.name} {titlecase(prop_match[2])}"
return obj, prop_path, text
except ValueError:
pass

return None, None, prop_path
from .helpers import PropertyWrapper

class GRET_OT_property_add(bpy.types.Operator):
"""Add a property to the list"""
Expand Down Expand Up @@ -70,15 +37,18 @@ def execute(self, context):

properties = list(obj.get('properties', []))
properties.append(self.path)
properties.sort(key=lambda prop_path: parse_prop_path(obj, prop_path)[2])
def get_property_title(data_path):
prop_wrapper = PropertyWrapper.from_path(obj, data_path)
return prop_wrapper.title if prop_wrapper else data_path
properties.sort(key=get_property_title)
obj['properties'] = properties

return {'FINISHED'}

def invoke(self, context, event):
# Check if the clipboard already has a correct path and paste it
clipboard = bpy.context.window_manager.clipboard
self.path = clipboard if parse_prop_path(context.active_object, clipboard)[0] else ""
self.path = clipboard if PropertyWrapper.from_path(context.active_object, clipboard) else ""
return context.window_manager.invoke_props_dialog(self)

def draw(self, context):
Expand Down Expand Up @@ -133,15 +103,15 @@ def draw_panel(self, context):
if properties:
col = box.column(align=True)

for idx, prop_path in enumerate(properties):
for idx, data_path in enumerate(properties):
row = col.row(align=True)
data, prop_path, label = parse_prop_path(obj, prop_path)
prop_wrapper = PropertyWrapper.from_path(obj, data_path)

if data:
row.prop(data, prop_path, text=label)
if prop_wrapper:
row.prop(prop_wrapper.data, prop_wrapper.path, text=prop_wrapper.title)
else:
row.alert = True
row.label(text=f"Missing: {label}")
row.label(text=f"Missing: {data_path}")

if settings.properties_show_edit:
row.operator('gret.property_remove', icon='X', text="").index = idx
Expand Down

0 comments on commit c19f9a0

Please sign in to comment.