126 lines
3.6 KiB
Python
126 lines
3.6 KiB
Python
|
import bpy
|
||
|
import re
|
||
|
|
||
|
def get_addon_prefs():
|
||
|
'''
|
||
|
function to read current addon preferences properties
|
||
|
access with : get_addon_prefs().super_special_option
|
||
|
'''
|
||
|
import os
|
||
|
addon_name = os.path.splitext(__name__)[0]
|
||
|
preferences = bpy.context.preferences
|
||
|
addon_prefs = preferences.addons[addon_name].preferences
|
||
|
return (addon_prefs)
|
||
|
|
||
|
def helper(name: str = '') -> str:
|
||
|
'''Return name and arguments from calling obj as str
|
||
|
:name: - replace definition name by your own str
|
||
|
'''
|
||
|
if not get_addon_prefs().debug:
|
||
|
return
|
||
|
import inspect
|
||
|
func = inspect.currentframe().f_back
|
||
|
name = name or f'def {func.f_code.co_name}'
|
||
|
args = inspect.getargvalues(func).locals
|
||
|
arguments = ', '.join([f'{k}={v}' for k, v in args.items()])
|
||
|
return(f'{name}({arguments})')
|
||
|
|
||
|
|
||
|
def get_gnd():
|
||
|
for o in bpy.context.scene.objects:
|
||
|
if o.type == 'MESH' and o.name.lower() in ('ground', 'gnd'):
|
||
|
return o
|
||
|
# nothing found
|
||
|
print('ERROR', 'No "gnd" object found')
|
||
|
|
||
|
|
||
|
def get_follow_curve_from_armature(arm):
|
||
|
"""Return curve and constraint or a tuple of string ('error', 'message')
|
||
|
"""
|
||
|
pref = get_addon_prefs()
|
||
|
name = pref.tgt_bone
|
||
|
|
||
|
parents = []
|
||
|
root = b.id_data.pose.bones.get(name)
|
||
|
for c in root.constraints:
|
||
|
if c.type == 'FOLLOW_PATH':
|
||
|
const = c
|
||
|
if c.type == 'CHILD_OF':
|
||
|
print(f'found child-of on {name}')
|
||
|
if c.target:
|
||
|
parents.append(c.target)
|
||
|
|
||
|
if not const:
|
||
|
for p in parents:
|
||
|
for c in p.constraints:
|
||
|
if c.type == 'FOLLOW_PATH':
|
||
|
print('INFO', f'follow_path found on "{p.name}" parent object')
|
||
|
const = c
|
||
|
break
|
||
|
|
||
|
if not const:
|
||
|
return ('ERROR', 'No constraints founds')
|
||
|
|
||
|
curve = const.target
|
||
|
if not curve:
|
||
|
return ('ERROR', f'no target set for {curve.name}')
|
||
|
|
||
|
return curve, const
|
||
|
|
||
|
def get_obj_action(obj):
|
||
|
print(helper())
|
||
|
act = obj.animation_data
|
||
|
if not act:
|
||
|
print('ERROR', f'no animation data on {obj.name}')
|
||
|
return
|
||
|
|
||
|
act = act.action
|
||
|
if not act:
|
||
|
print('ERROR', f'no action on {obj.name}')
|
||
|
return
|
||
|
return act
|
||
|
|
||
|
def set_generated_action(obj):
|
||
|
'''Backup object action and return a new action suffixed '_autogen'
|
||
|
associated with the object
|
||
|
'''
|
||
|
print(helper())
|
||
|
act = get_obj_action(obj)
|
||
|
if not act: return
|
||
|
|
||
|
regen = re.compile(r'_autogen\.?\d{0,3}$')
|
||
|
|
||
|
if regen.search(act.name):
|
||
|
# is an autogenerated one
|
||
|
org_action_name = regen.sub('', act.name)
|
||
|
org_action = bpy.data.actions.get(org_action_name)
|
||
|
if not org_action:
|
||
|
print('ERROR', f'{org_action_name} not found')
|
||
|
return
|
||
|
|
||
|
obj.animation_data.action = org_action
|
||
|
bpy.data.actions.remove(act)
|
||
|
|
||
|
act = org_action
|
||
|
|
||
|
# backup action before doing anything crazy
|
||
|
act.use_fake_user = True
|
||
|
new_act = act.copy()
|
||
|
new_act.name = act.name + '_autogen'
|
||
|
obj.animation_data.action = new_act
|
||
|
return new_act
|
||
|
|
||
|
|
||
|
def get_curve_length(ob):
|
||
|
dg = bpy.context.evaluated_depsgraph_get()
|
||
|
obeval = ob.evaluated_get(dg)#.copy()
|
||
|
baked_me = obeval.to_mesh(preserve_all_data_layers=False, depsgraph=dg)
|
||
|
mat = obeval.matrix_world
|
||
|
total_length = 0
|
||
|
for i, _v in enumerate(baked_me.vertices):
|
||
|
if i == 0:
|
||
|
continue
|
||
|
total_length += ((mat @ baked_me.vertices[i-1].co) - (mat @ baked_me.vertices[i].co)).length
|
||
|
|
||
|
print("total_length", total_length)#Dbg
|
||
|
return total_length
|