2025-12-01

This commit is contained in:
2026-03-17 14:58:51 -06:00
parent 183e865f8b
commit 4b82b57113
6846 changed files with 954887 additions and 162606 deletions
File diff suppressed because it is too large Load Diff
@@ -23,7 +23,7 @@ thumb_ref_list = [j for i, j in thumb_ref_dict.items()]
thumb_control_dict = {'base':'c_thumb1_base', '1':'c_thumb1', '2':'c_thumb2', '3':'c_thumb3'}
thumb_control_list = [j for i, j in thumb_control_dict.items()]
thumb_intern_dict = {'base':'thumb1', 'bend_all':'thumb_bend_all', 'rot1':'c_thumb1_rot', 'rot2':'c_thumb2_rot', 'rot3':'c_thumb3_rot'}
thumb_intern_dict = {'base':'thumb1', 'bend_all':'thumb_bend_all', 'rot1':'thumb1_rot', 'rot2':'thumb2_rot', 'rot3':'thumb3_rot'}
thumb_intern_list = [j for i, j in thumb_intern_dict.items()]
# index
@@ -87,12 +87,13 @@ arm_bones_dict = {
'shoulder':{'control':'c_shoulder', 'deform':'shoulder', 'track_pole':'shoulder_track_pole', 'pole':'shoulder_pole'},
'arm':{'base':'arm', 'twist':'arm_twist', 'twist_twk':'arm_twist_twk', 'control_twist':'c_arm_twist_offset', 'stretch':'arm_stretch', 'control_fk':'c_arm_fk', 'fk':'arm_fk', 'ik':'arm_ik', 'control_ik':'c_arm_ik', 'ik_scale_fix':'arm_ik_nostr_scale_fix', 'ik_nostr':'arm_ik_nostr', 'secondary_00':'c_shoulder_bend','secondary_01':'c_arm_bend'},
'forearm':{'base':'forearm', 'control_fk':'c_forearm_fk', 'fk':'forearm_fk', 'ik':'forearm_ik', 'ik_nostr':'forearm_ik_nostr', 'stretch':'forearm_stretch', 'twist':'forearm_twist', 'secondary_00':'c_elbow_bend', 'secondary_01':'c_forearm_bend', 'secondary_02':'c_wrist_bend'},
'hand':{'deform':'hand', 'control_fk':'c_hand_fk', 'control_ik':'c_hand_ik', 'control_ik_offset':'c_hand_ik_offset', 'fk_scale_fix':'c_hand_fk_scale_fix', 'rot_twist':'hand_rot_twist', 'secondary_00':'hand_bend'},
'hand':{'deform':'hand', 'control_fk':'c_hand_fk', 'control_ik':'c_hand_ik', 'control_ik_offset':'c_hand_ik_offset', 'ik_pivot':'hand_ik_pivot', 'control_ik_pivot':'c_hand_ik_pivot', 'fk_scale_fix':'hand_fk_scale_fix', 'rot_twist':'hand_rot_twist', 'secondary_00':'hand_bend'},
'prepole':'arm_fk_pre_pole',
'fk_pole':'arm_fk_pole',
'control_pin':'c_stretch_arm_pin',
'control_stretch':'c_stretch_arm',
'control_pole_ik': 'c_arms_pole'}
'control_pole_ik': 'c_arms_pole',
'pole_line': 'arm_pole_line'}
arm_bones = []
for i, j in arm_bones_dict.items():
@@ -184,10 +185,11 @@ leg_bones_dict = {
'upthigh_helper': {'1': 'thigh_b_h', '2': 'thigh_b_loc'},
'thigh': {'base':'thigh', 'fk':'thigh_fk', 'ik':'thigh_ik', 'ik_nostr':'thigh_ik_nostr', 'control_fk':'c_thigh_fk', 'control_ik':'c_thigh_ik', 'twist':'thigh_twist', 'stretch':'thigh_stretch', 'secondary_00':'c_thigh_bend_contact', 'secondary_01':'c_thigh_bend_01', 'secondary_02':'c_thigh_bend_02'},
'calf':{'base':'leg', 'fk':'leg_fk', 'ik':'leg_ik', 'ik_nostr':'leg_ik_nostr', 'control_fk':'c_leg_fk', 'control_ik3':'c_leg_ik3', 'twist':'leg_twist', 'stretch':'leg_stretch', 'secondary_00':'c_knee_bend', 'secondary_01':'c_leg_bend_01', 'secondary_02':'c_leg_bend_02', 'secondary_03':'c_ankle_bend'},
'foot':{'fk':'foot_fk', 'control_fk':'c_foot_fk', 'snap_fk':'foot_snap_fk', 'ik':'foot_ik', 'ik_target':'foot_ik_target', 'control_ik':'c_foot_ik', 'deform':'foot', 'pole':'foot_pole', 'fk_scale_fix':'c_foot_fk_scale_fix', 'shape_override_fk':'c_p_foot_fk', 'shape_override_ik':'c_p_foot_ik', 'bank_01':'c_foot_bank_01', 'bank_02':'c_foot_bank_02', 'foot_heel':'c_foot_heel', 'control_reverse':'c_foot_01', 'pole_01':'foot_01_pole', 'roll':'c_foot_roll', 'control_roll':'c_foot_roll_cursor', 'secondary_00':'foot_bend', 'control_ik_offset':'c_foot_ik_offset', 'foot_fans_helper':'foot_fans_h'},
'toes':{'01': 'toes_01', '02': 'toes_02', '01_ik': 'toes_01_ik', 'control_fk': 'c_toes_fk', 'control_ik':'c_toes_ik', 'toes_track': 'c_toes_track', 'toes_end': 'c_toes_end', 'toes_end_01':'c_toes_end_01', 'control_pivot':'c_toes_pivot'},
'foot':{'fk':'foot_fk', 'control_fk':'c_foot_fk', 'snap_fk':'foot_snap_fk', 'ik':'foot_ik', 'ik_target':'foot_ik_target', 'control_ik':'c_foot_ik', 'deform':'foot', 'pole':'foot_pole', 'fk_scale_fix':'foot_fk_scale_fix', 'shape_override_fk':'c_p_foot_fk', 'shape_override_ik':'c_p_foot_ik', 'bank_01':'foot_bank_01', 'bank_02':'foot_bank_02', 'foot_heel':'foot_heel', 'control_reverse':'c_foot_01', 'pole_01':'foot_01_pole', 'roll':'foot_roll', 'control_roll':'c_foot_roll_cursor', 'secondary_00':'foot_bend', 'ik_pivot':'foot_ik_pivot', 'control_ik_pivot':'c_foot_ik_pivot','control_ik_offset':'c_foot_ik_offset', 'foot_fans_helper':'foot_fans_h'},
'toes':{'01': 'toes_01', '02': 'toes_02', '01_ik': 'toes_01_ik', 'control_fk': 'c_toes_fk', 'control_ik':'c_toes_ik', 'toes_track': 'toes_track', 'toes_end': 'toes_end', 'toes_end_01':'toes_end_01', 'control_pivot':'c_toes_pivot'},
'prepole':'leg_fk_pre_pole',
'control_pole_ik':'c_leg_pole',
'pole_line': 'leg_pole_line',
'fk_pole':'leg_fk_pole',
'control_stretch':'c_stretch_leg',
'control_pin':'c_stretch_leg_pin',
@@ -240,7 +242,7 @@ leg_deform = [
leg_bones_dict['upthigh2']]
leg_control = [
leg_bones_dict['upthigh'], leg_bones_dict['thigh']['control_fk'], leg_bones_dict['thigh']['control_ik'], leg_bones_dict['calf']['control_fk'], leg_bones_dict['toes_pinky1'], leg_bones_dict['toes_pinky2'], leg_bones_dict['toes_pinky3'], leg_bones_dict['toes_ring1'], leg_bones_dict['toes_ring2'], leg_bones_dict['toes_ring3'], leg_bones_dict['toes_middle1'], leg_bones_dict['toes_middle2'], leg_bones_dict['toes_middle3'], leg_bones_dict['toes_index1'], leg_bones_dict['toes_index2'], leg_bones_dict['toes_index3'], leg_bones_dict['toes_thumb1'], leg_bones_dict['toes_thumb2'], leg_bones_dict['foot']['control_fk'], leg_bones_dict['toes']['control_fk'], leg_bones_dict['control_stretch'], leg_bones_dict['calf']['secondary_03'], leg_bones_dict['calf']['secondary_02'], leg_bones_dict['calf']['secondary_01'], leg_bones_dict['calf']['secondary_00'], leg_bones_dict['thigh']['secondary_02'], leg_bones_dict['thigh']['secondary_01'], leg_bones_dict['thigh']['secondary_00'], leg_bones_dict['control_pin'], leg_bones_dict['foot']['control_ik'], leg_bones_dict['foot']['control_ik_offset'], leg_bones_dict['toes']['control_ik'], leg_bones_dict['foot']['control_reverse'], leg_bones_dict['foot']['control_roll'], leg_bones_dict['control_pole_ik']]
leg_bones_dict['upthigh'], leg_bones_dict['thigh']['control_fk'], leg_bones_dict['thigh']['control_ik'], leg_bones_dict['calf']['control_fk'], leg_bones_dict['toes_pinky1'], leg_bones_dict['toes_pinky2'], leg_bones_dict['toes_pinky3'], leg_bones_dict['toes_ring1'], leg_bones_dict['toes_ring2'], leg_bones_dict['toes_ring3'], leg_bones_dict['toes_middle1'], leg_bones_dict['toes_middle2'], leg_bones_dict['toes_middle3'], leg_bones_dict['toes_index1'], leg_bones_dict['toes_index2'], leg_bones_dict['toes_index3'], leg_bones_dict['toes_thumb1'], leg_bones_dict['toes_thumb2'], leg_bones_dict['foot']['control_fk'], leg_bones_dict['toes']['control_fk'], leg_bones_dict['control_stretch'], leg_bones_dict['calf']['secondary_03'], leg_bones_dict['calf']['secondary_02'], leg_bones_dict['calf']['secondary_01'], leg_bones_dict['calf']['secondary_00'], leg_bones_dict['thigh']['secondary_02'], leg_bones_dict['thigh']['secondary_01'], leg_bones_dict['thigh']['secondary_00'], leg_bones_dict['control_pin'], leg_bones_dict['foot']['control_ik'], leg_bones_dict['foot']['control_ik_offset'], leg_bones_dict['foot']['control_ik_pivot'],leg_bones_dict['toes']['control_ik'], leg_bones_dict['foot']['control_reverse'], leg_bones_dict['foot']['control_roll'], leg_bones_dict['control_pole_ik']]
leg_props = {'soft_ik': 'leg_softik', 'auto_ik_roll': 'leg_auto_ik_roll'}
@@ -275,7 +277,6 @@ def get_leg_toes_ikfk(leg_side, btype='ALL', no_side=False):
return list
def get_leg_joint_fans(leg_side, btype='ALL', no_side=False):
types = [btype]
if btype == 'ALL':
@@ -336,6 +337,7 @@ mouth_bones_ref_dict = {'lips_top_mid': 'lips_top_ref.x',
'lips_roll_top': 'lips_roll_top_ref.x',
'lips_roll_bot': 'lips_roll_bot_ref.x',
'lips_offset': 'lips_offset_ref.x',
'muzzle': 'muzzle_ref.x',
'jaw':'jaw_ref.x'
}
@@ -368,7 +370,8 @@ mouth_bones_dict = {
'c_lips_smile_offset': {'name':'c_lips_smile_offset', 'deform':False, 'control':False},
'c_lips_smile': {'name':'c_lips_smile', 'deform':True, 'control':True},
'c_lips_corner_mini': {'name':'c_lips_corner_mini', 'deform':True, 'control':True},
'c_lips_offset': {'name':'c_lips_offset.x', 'deform':False, 'control':True}
'c_lips_offset': {'name':'c_lips_offset.x', 'deform':False, 'control':True},
'c_muzzle': {'name':'c_muzzle.x', 'deform':False, 'control':True}
}
@@ -403,7 +406,7 @@ def get_variable_lips(head_side, btype='REFERENCE', no_side=False, levels=['top_
lips_list = []
for subtype in types:
for lip_id in range(1,32):
for lip_id in range(1, 32):
for _side in ['.l', '.r']:
for lvl in levels:
bname = ''
@@ -533,7 +536,6 @@ for i in teeth_ref_base:
teeth_ref.append(i+'.r')
# tongues
tongue_bones_ref_dict = {'tong_01':'tong_01_ref.x',
'tong_02':'tong_02_ref.x',
'tong_03':'tong_03_ref.x'
@@ -550,6 +552,33 @@ tongue_bones_dict = {'c_tong_01': {'name':'c_tong_01.x', 'deform':False, 'contro
tongue_ref = [j for i, j in tongue_bones_ref_dict.items()]
tongue_bones = [tongue_bones_dict[i]['name'] for i in tongue_bones_dict]
def get_tongues(side='.x', type='ALL', no_side=True, side_x=False):
list = []
ctrl = []
ref = []
deform = []
_side = '' if no_side else side
_sidex = '.x' if side_x else ''
for _i in range(1, 33):
stri = '%02d' % _i
if bpy.context.active_object.data.bones.get('tong_'+stri+'_ref'+side):
ctrl.append('c_tong_'+stri+_side+_sidex)
ref.append('tong_'+stri+'_ref'+_side+_sidex)
deform.append('tong_'+stri+_side+_sidex)
if type == 'ALL':
list = ctrl + ref + deform
elif type == 'CTRL':
list = ctrl
elif type == 'REF':
list = ref
elif type == 'DEF':
list = deform
elif type == 'NON_REF':
list = ctrl + deform
return list
# eyes
@@ -662,26 +691,24 @@ eyebrow_bones_left = [i+'.l' for i in eyebrow_bones] + [i+'.l' for i in eyebrow_
eyebrow_bones_right = [i+'.r' for i in eyebrow_bones] + [i+'.r' for i in eyebrow_ref]
def get_eyebrows(type='ALL', include_full=True):
def get_eyebrows(side='.l', type='ALL', include_full=True):
list = []
main_ctrl = []
ref = []
main_ctrl = [eyebrow_bones_dict['eyebrow_01_end']['name'],
eyebrow_bones_dict['eyebrow_01']['name'],
eyebrow_bones_dict['eyebrow_02']['name'],
eyebrow_bones_dict['eyebrow_03']['name']]
ref = [eyebrow_bones_ref_dict['eyebrow_01_end'],
eyebrow_bones_ref_dict['eyebrow_01'],
eyebrow_bones_ref_dict['eyebrow_02'],
eyebrow_bones_ref_dict['eyebrow_03']]
if side.endswith('.x'):
side = side[:-2]+'.l'# same count for left and right brows for now
for _i in range(0, 32):
stri = '%02d' % _i if _i > 0 else '01_end'
if bpy.context.active_object.data.bones.get('eyebrow_'+stri+'_ref'+side):
main_ctrl.append('c_eyebrow_'+stri)
ref.append('eyebrow_'+stri+'_ref')
master_ctrl = [eyebrow_bones_dict['eyebrow_full']['name']]
master_ctrl = [eyebrow_bones_dict['eyebrow_full']['name']]
master_ref = [eyebrow_bones_ref_dict['eyebrow_full']]
offsets = []
if type == 'ALL':
list = main_ctrl + ref
if include_full:
@@ -885,9 +912,6 @@ def get_spline_ik(rig, side):
return None
# Tail
tail_bones = ['c_tail_master']
#SMART FACIAL MARKERS
facial_markers = {'eyebrow_01_end.l': 15, 'eyebrow_01.l':16, 'eyebrow_02.l':17, 'eyebrow_03.l':18, 'eyebrow_01_end.r':40, 'eyebrow_01.r':41, 'eyebrow_02.r':42, 'eyebrow_03.r':43,
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
import bpy, os, platform, sys, ast
import bpy, os, platform, sys, ast, subprocess
def update_all_tab_names(self, context):
try:
@@ -79,11 +79,13 @@ class ARP_OT_save_prefs(bpy.types.Operator):
'arp_tab_name':get_prefs().arp_tab_name,
'arp_tools_tab_name':get_prefs().arp_tools_tab_name,
'beginner_mode': get_prefs().beginner_mode,
'xray_display': get_prefs().xray_display,
'custom_armatures_path': get_prefs().custom_armatures_path,
'custom_limb_path': get_prefs().custom_limb_path,
'rig_layers_path': get_prefs().rig_layers_path,
'remap_presets_path': get_prefs().remap_presets_path,
'ge_presets_path': get_prefs().ge_presets_path,
'ai_presets_path': get_prefs().ai_presets_path,
#'prefs_presets_path': get_prefs().prefs_presets_path,
'default_ikfk_arm':get_prefs().default_ikfk_arm,
'default_ikfk_leg': get_prefs().default_ikfk_leg,
@@ -104,6 +106,70 @@ class ARP_OT_save_prefs(bpy.types.Operator):
print(fp)
return {'FINISHED'}
def get_downloads_folder():
"""Get the default downloads folder path based on the operating system."""
system = platform.system().lower()
home = os.path.expanduser("~") # Gets the user's home directory
downloads = os.path.join(home, "Downloads")
# Ensure the path exists (optional, remove if you just want the path)
if not os.path.exists(downloads):
os.makedirs(downloads, exist_ok=True)
return downloads
def extract_zip(source_zip, destination_folder):
import zipfile, stat
os.makedirs(destination_folder, exist_ok=True)
print("Unzip...")
# Use unzip process instead of zipfile for better fidelity on Linux and Mac
if platform.system() in ["Darwin", "Linux"]:
if platform.system() == "Darwin":
result = subprocess.run(['xattr', source_zip], capture_output=True, text=True)
if 'com.apple.quarantine' in result.stdout:
subprocess.run(['xattr', '-d', 'com.apple.quarantine', source_zip], check=True)
result = subprocess.run(["unzip", "-o", source_zip, "-d", destination_folder], capture_output=True, text=True)
if result.returncode != 0:
raise Exception(f"unzip failed: {result.stderr}")
print(f"unzip output: {result.stdout}")
else:
# Fallback to zipfile for other platforms
with zipfile.ZipFile(source_zip, 'r') as zip_ref:
zip_ref.extractall(destination_folder)
return True, f"Successfully extracted {source_zip} to {destination_folder}"
class ARP_OT_install_ext(bpy.types.Operator):
"""Install external dependencies"""
bl_idname = 'arp.install_ext'
bl_label = 'Install Files'
filepath: bpy.props.StringProperty(subtype='FILE_PATH', default='name')
filename_ext = '.zip'
def invoke(self, context, event):
self.filepath = downloads_path = get_downloads_folder()
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
def execute(self, context):
if not self.filepath.endswith('.zip'):
self.report({'ERROR'}, 'Select the zip file to install')
return {'FINISHED'}
print("Extract zip...")
extract_zip(self.filepath, get_prefs().ai_presets_path)
return {'FINISHED'}
class ARP_MT_arp_addon_preferences(bpy.types.AddonPreferences):
@@ -111,62 +177,123 @@ class ARP_MT_arp_addon_preferences(bpy.types.AddonPreferences):
arp_tab_name : bpy.props.StringProperty(name='Interface Tab', description='Name of the tab to display the interface in', default='ARP', update=update_all_tab_names)
arp_tools_tab_name : bpy.props.StringProperty(name='Tools Interface Tab', description='Name of the tab to display the tools (IK-FK snap...) interface in', default='Tool', update=update_all_tab_names)
beginner_mode: bpy.props.BoolProperty(name='Beginner Mode', default=True)
beginner_mode: bpy.props.BoolProperty(name='Beginner Mode', default=True, description='Show helper icons in the menus, to open contextual documentation links')
xray_display: bpy.props.BoolProperty(name='XRay Display', default=False, description='Always show the rig in X-Ray display/ In Front\nApplied when Match to Rig')
custom_armatures_path: bpy.props.StringProperty(name='Armatures', subtype='FILE_PATH', default=get_documents_path('Armatures Presets'), description='Path to store armature presets')
custom_limb_path: bpy.props.StringProperty(name='Limbs', subtype='FILE_PATH', default=get_documents_path('Custom Limbs'), description='Path to store custom limb presets')
rig_layers_path: bpy.props.StringProperty(name='Rig Layers', subtype='FILE_PATH', default=get_documents_path('Rig Layers'), description='Path to store rig layers presets')
remap_presets_path: bpy.props.StringProperty(name='Remap Presets', subtype='FILE_PATH', default=get_documents_path('Remap Presets'), description='Path to store remap presets')
ge_presets_path: bpy.props.StringProperty(name='Export Presets', subtype='FILE_PATH', default=get_documents_path('Game Engine Presets'), description='Path to store game engine export presets')
#prefs_presets_path: bpy.props.StringProperty(name='Preferences', subtype='FILE_PATH', default=get_documents_path('Preferences'), description='Path to store Auto-Rig Pro preferences')
ai_presets_path: bpy.props.StringProperty(name='AI', subtype='FILE_PATH', default=get_documents_path('AI'), description='Path to store AI executables')
parent_bound_objects: bpy.props.BoolProperty(default=True, description='Parent the objects to the armature when binding.\nTurning it off *is not recommended* as it can lead to issues, but it may be useful for some custom workflows')
default_ikfk_arm: bpy.props.EnumProperty(items=(('IK', 'IK', 'IK'), ('FK', 'FK', 'FK')), description='Default value for arms IK-FK switch', name='IK-FK Arms Default')
default_ikfk_leg: bpy.props.EnumProperty(items=(('IK', 'IK', 'IK'), ('FK', 'FK', 'FK')), description='Default value for legs IK-FK switch', name='IK-FK Legs Default')
default_head_lock: bpy.props.BoolProperty(default=True, name='Head Lock Default', description='Default value for the Head Lock switch')
remove_existing_arm_mods: bpy.props.BoolProperty(default=True, name='Remove Armature Modifiers', description='Remove existing armature modifiers when binding')
remove_existing_vgroups: bpy.props.BoolProperty(default=True, name='Remove Existing Vertex Groups', description='Remove existing vertex groups when binding')
remove_existing_arm_mods: bpy.props.BoolProperty(default=True, name='Remove Armature Modifiers', description='Remove any existing armature modifiers on meshes, when binding.\nIt is generally recommended to enable it, in order to avoid older modifiers to interfere with new ones')
remove_existing_vgroups: bpy.props.BoolProperty(default=True, name='Remove Existing Vertex Groups', description='Remove existing vertex groups when binding. It is generally recommended to enable it, in order to avoid older vertex groups to interfere with new ones')
rem_arm_mods_set: bpy.props.BoolProperty(default=False, description='Toggle to be executed the first time binding, to set default prefs')
rem_vgroups_set: bpy.props.BoolProperty(default=False, description='Toggle to be executed the first time binding, to set default prefs')
show_export_popup: bpy.props.BoolProperty(default=True, description='Show a popup notification on export completion')
arp_master_collec_name: bpy.props.StringProperty(default='', description='Group all Auto-Rig Pro armature collections under this collection. None if blank name\nApplied when Match to Rig')
def draw(self, context):
col = self.layout.column(align=True)
layout = self.layout
col = layout.column(align=True)
col.operator(ARP_OT_save_prefs.bl_idname, text='Save Preferences')
col.prop(self, 'beginner_mode', text='Beginner Mode (help buttons)')
col.separator()
col.label(text='Rig:')
col.prop(self, 'remove_existing_arm_mods', text='Remove Existing Armature Modifiers when Binding')
col.prop(self, 'remove_existing_vgroups', text='Remove Existing Vertex Groups when Binding')
col.separator()
col.prop(self, 'default_ikfk_arm', text='IK-FK Arms')
col.prop(self, 'default_ikfk_leg', text='IK-FK Legs')
col.prop(self, 'default_head_lock', text='Head Lock')
def show_rig_ui(panel):
if panel:
col = panel.column(align=True)
else:
col = layout.column(align=True)
col.label(text='Rig:')
col.prop(self, 'arp_master_collec_name', text='Master ARP Collec')
col.prop(self, 'xray_display', text='X-Ray Display')
col.prop(self, 'parent_bound_objects', text='Parent Objects when Binding')
col.prop(self, 'remove_existing_arm_mods', text='Remove Existing Armature Modifiers when Binding')
col.prop(self, 'remove_existing_vgroups', text='Remove Existing Vertex Groups when Binding')
col.separator()
col.prop(self, 'default_ikfk_arm', text='IK-FK Arms')
col.prop(self, 'default_ikfk_leg', text='IK-FK Legs')
col.prop(self, 'default_head_lock', text='Head Lock')
col.separator()
if bpy.app.version >= (4,1,0):
header_rig, panel_rig = layout.panel("arp_pref_ui_rig", default_closed=False)
header_rig.label(text="Rig")
if panel_rig:# None if collapsed
show_rig_ui(panel_rig)
else:
show_rig_ui(None)
col.separator()
col.separator()
col.label(text='Interface:')
col.prop(self, 'arp_tab_name', text='Main ARP Tab')
col.prop(self, 'arp_tools_tab_name', text='Tools Tab')
col.prop(self, 'show_export_popup', text='Show Popup when Export Finished')
col.separator()
col.separator()
col.label(text='Paths:')
#col.prop(self, 'prefs_presets_path')
col.prop(self, 'custom_armatures_path')
col.prop(self, 'custom_limb_path')
col.prop(self, 'rig_layers_path')
col.prop(self, 'remap_presets_path')
col.prop(self, 'ge_presets_path')
col.separator()
col.separator()
col.label(text='Special-Debug:', icon='ERROR')
col.prop(context.scene, 'arp_disable_smart_fx')
col.prop(context.scene, 'arp_debug_mode')
col.prop(context.scene, 'arp_debug_bind')
col.prop(context.scene, 'arp_experimental_mode')
def show_interface_ui(panel):
if panel:
col = panel.column(align=True)
else:
col = layout.column(align=True)
col.label(text='Interface:')
row = col.row(align=True)
row.prop(self, 'beginner_mode', text='Beginner Mode (help buttons)')
col.separator()
col.prop(self, 'arp_tab_name', text='Main ARP Tab')
col.prop(self, 'arp_tools_tab_name', text='Tools Tab')
col.prop(self, 'show_export_popup', text='Show Popup when Export Finished')
col.separator()
if bpy.app.version >= (4,1,0):
header_interface, panel_interface = layout.panel("arp_pref_ui_interface", default_closed=False)
header_interface.label(text="Interface")
if panel_interface:# None if collapsed
show_interface_ui(panel_interface)
else:
show_interface_ui(None)
def show_paths_ui(panel):
if panel:
col = panel.column(align=True)
else:
col = layout.column(align=True)
col.label(text='Paths:')
col.prop(self, 'custom_armatures_path')
col.prop(self, 'custom_limb_path')
col.prop(self, 'rig_layers_path')
col.prop(self, 'remap_presets_path')
col.prop(self, 'ge_presets_path')
col.prop(self, 'ai_presets_path')
col.operator('arp.install_ext', text='Install AI files...')
col.separator()
if bpy.app.version >= (4,1,0):
header_paths, panel_paths = layout.panel("arp_pref_ui_paths", default_closed=False)
header_paths.label(text="Paths")
if panel_paths:# None if collapsed
show_paths_ui(panel_paths)
else:
show_paths_ui(None)
def show_debug_ui(panel):
if panel:
col = panel.column()
else:
col = layout.column(align=True)
col.label(text='Debug:')
col.label(text='Special-Debug:', icon='ERROR')
col.prop(context.scene, 'arp_disable_smart_fx')
col.prop(context.scene, 'arp_debug_mode')
col.prop(context.scene, 'arp_debug_bind')
col.prop(context.scene, 'arp_experimental_mode')
if bpy.app.version >= (4,1,0):
header_debug, panel_debug = layout.panel("arp_pref_ui_debug", default_closed=True)
header_debug.label(text="Debug")
if panel_debug:# None if collapsed
show_debug_ui(panel_debug)
else:
show_debug_ui(None)
def load_arp_prefs():
@@ -214,6 +341,7 @@ def register():
try:
register_class(ARP_MT_arp_addon_preferences)
register_class(ARP_OT_save_prefs)
register_class(ARP_OT_install_ext)
except:
pass
@@ -225,6 +353,7 @@ def register():
bpy.types.Scene.arp_experimental_mode = bpy.props.BoolProperty(name='Experimental Mode', default=False, description = 'Enable experimental, unstable tools. Warning, can lead to errors. Use it at your own risks.', options={'HIDDEN'})
bpy.types.Scene.arp_disable_smart_fx = bpy.props.BoolProperty(name='Disable Smart FX', default=False, description='Disable Smart markers FX for debug purposes, such as Mac systems not supporting some graphics. Safe to use.', options={'HIDDEN'})
def unregister():
from bpy.utils import unregister_class
unregister_class(ARP_MT_arp_addon_preferences)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
{'verts': [(-0.09828728437423706, -0.09828728437423706, -0.09828728437423706), (-0.09828728437423706, -0.09828728437423706, 0.09828728437423706), (-0.09828728437423706, 0.09828728437423706, -0.09828728437423706), (-0.09828728437423706, 0.09828728437423706, 0.09828728437423706), (0.09828728437423706, -0.09828728437423706, -0.09828728437423706), (0.09828728437423706, -0.09828728437423706, 0.09828728437423706), (0.09828728437423706, 0.09828728437423706, -0.09828728437423706), (0.09828728437423706, 0.09828728437423706, 0.09828728437423706)], 'edges': [(2, 0), (0, 1), (1, 3), (3, 2), (6, 2), (3, 7), (7, 6), (4, 6), (7, 5), (5, 4), (0, 4), (5, 1)], 'faces': []}
@@ -0,0 +1 @@
{'verts': [(2.2649766151516815e-07, 0.15570558607578278, -7.078047481456906e-09), (-0.059585727751255035, 0.14385320246219635, -7.078047481456906e-09), (-0.11010024696588516, 0.11010047048330307, -7.0780492578137455e-09), (-0.14385297894477844, 0.05958595126867294, -7.0780492578137455e-09), (-0.15570536255836487, -6.806108121537591e-09, -7.078051922349005e-09), (-0.14385297894477844, -0.05958595871925354, -7.078052810527424e-09), (-0.11010024696588516, -0.11010047048330307, -7.078053698705844e-09), (-0.059585727751255035, -0.14385320246219635, -7.078055475062683e-09), (2.0298676872698707e-07, -0.15570558607578278, -7.078056363241103e-09), (0.059586141258478165, -0.14385321736335754, -7.078055475062683e-09), (0.11010067909955978, -0.11010051518678665, -7.078053698705844e-09), (0.14385342597961426, -0.059585969895124435, -7.078052810527424e-09), (0.15570582449436188, 1.856770626140758e-09, -7.078051922349005e-09), (0.14385342597961426, 0.05958597734570503, -7.0780492578137455e-09), (0.11010066419839859, 0.11010051518678665, -7.0780492578137455e-09), (0.05958610400557518, 0.14385323226451874, -7.078047481456906e-09), (2.2649766151516815e-07, -1.2993086784263141e-07, -0.15570560097694397), (-0.05958572402596474, -1.2993086784263141e-07, -0.14385321736335754), (-0.11010023206472397, -9.280776680498093e-08, -0.11010048538446426), (-0.14385296404361725, -5.1044271032196775e-08, -0.05958595499396324), (-0.15570534765720367, 6.084951285385701e-15, -2.7194335672220404e-10), (-0.14385296404361725, 5.1044271032196775e-08, 0.05958595499396324), (-0.11010023206472397, 9.280776680498093e-08, 0.11010045558214188), (-0.059585727751255035, 1.2993086784263141e-07, 0.14385318756103516), (2.0298676872698707e-07, 1.2993086784263141e-07, 0.15570557117462158), (0.059586141258478165, 1.2993086784263141e-07, 0.14385321736335754), (0.11010066419839859, 9.280776680498093e-08, 0.11010050028562546), (0.14385342597961426, 5.1044271032196775e-08, 0.05958596616983414), (0.1557057946920395, -1.6595322553699244e-15, -8.934822659512065e-09), (0.14385341107845306, -5.1044271032196775e-08, -0.05958598479628563), (0.110100656747818, -9.280776680498093e-08, -0.11010053008794785), (0.05958609655499458, -1.2993086784263141e-07, -0.14385324716567993), (2.0112997844989877e-07, 0.15570557117462158, -3.244572610583418e-08), (2.5433996597712394e-07, 0.14385318756103516, -0.05958598479628563), (3.007438635904691e-07, 0.11010044813156128, -0.11010050773620605), (3.471477612038143e-07, 0.05958591774106026, -0.14385321736335754), (3.5642852935779956e-07, -3.217376942643568e-08, -0.15570560097694397), (3.657092975117848e-07, -0.05958598107099533, -0.14385320246219635), (3.3786699304982903e-07, -0.11010048538446426, -0.11010046303272247), (3.007438635904691e-07, -0.14385320246219635, -0.059585943818092346), (2.518653445804375e-07, -0.15570557117462158, -5.22126430979597e-09), (1.9865532863150293e-07, -0.14385320246219635, 0.05958593636751175), (1.5225145943986718e-07, -0.11010048538446426, 0.11010045558214188), (1.0584753340481257e-07, -0.059585943818092346, 0.14385321736335754), (9.656679367253673e-08, 2.7224428933436684e-08, 0.15570557117462158), (8.728602551855147e-08, 0.059585995972156525, 0.14385317265987396), (1.1512832998050726e-07, 0.11010052263736725, 0.11010041832923889), (1.5225145943986718e-07, 0.14385323226451874, 0.059585850685834885)], 'edges': [(1, 0), (2, 1), (3, 2), (4, 3), (5, 4), (6, 5), (7, 6), (8, 7), (9, 8), (10, 9), (11, 10), (12, 11), (13, 12), (14, 13), (15, 14), (0, 15), (17, 16), (18, 17), (19, 18), (20, 19), (21, 20), (22, 21), (23, 22), (24, 23), (25, 24), (26, 25), (27, 26), (28, 27), (29, 28), (30, 29), (31, 30), (16, 31), (33, 32), (34, 33), (35, 34), (36, 35), (37, 36), (38, 37), (39, 38), (40, 39), (41, 40), (42, 41), (43, 42), (44, 43), (45, 44), (46, 45), (47, 46), (32, 47)], 'faces': []}
@@ -0,0 +1 @@
{'verts': [(2.2649766151516815e-07, 0.6557056903839111, 0.000808675482403487), (-0.059585727751255035, 0.6438533067703247, 0.000808675482403487), (-0.11010024696588516, 0.6101005673408508, 0.000808675482403487), (-0.14385297894477844, 0.5595860481262207, 0.000808675482403487), (-0.15570536255836487, 0.5000001192092896, 0.000808675482403487), (-0.14385297894477844, 0.440414160490036, 0.000808675482403487), (-0.11010024696588516, 0.3898996412754059, 0.000808675482403487), (-0.059585727751255035, 0.3561469316482544, 0.000808675482403487), (2.0298676872698707e-07, 0.34429454803466797, 0.000808675482403487), (0.059586141258478165, 0.356146901845932, 0.000808675482403487), (0.11010067909955978, 0.3898996114730835, 0.000808675482403487), (0.14385342597961426, 0.440414160490036, 0.000808675482403487), (0.15570582449436188, 0.5000001192092896, 0.000808675482403487), (0.14385342597961426, 0.5595861077308655, 0.000808675482403487), (0.11010066419839859, 0.6101006269454956, 0.000808675482403487), (0.05958610400557518, 0.6438533663749695, 0.000808675482403487), (2.2649766151516815e-07, 0.5, -0.15489688515663147), (-0.05958572402596474, 0.5, -0.14304450154304504), (-0.11010023206472397, 0.5, -0.10929182916879654), (-0.14385296404361725, 0.5000000596046448, -0.058777254074811935), (-0.15570534765720367, 0.5000001192092896, 0.000808675482403487), (-0.14385296404361725, 0.5000001788139343, 0.06039463356137276), (-0.11010023206472397, 0.5000002384185791, 0.1109091266989708), (-0.059585727751255035, 0.5000002384185791, 0.14466187357902527), (2.0298676872698707e-07, 0.5000002384185791, 0.1565142571926117), (0.059586141258478165, 0.5000002384185791, 0.14466190338134766), (0.11010066419839859, 0.5000002384185791, 0.11090918630361557), (0.14385342597961426, 0.5000001788139343, 0.06039463356137276), (0.1557057946920395, 0.5000001192092896, 0.000808675482403487), (0.14385341107845306, 0.5000000596046448, -0.05877731367945671), (0.110100656747818, 0.5, -0.10929182916879654), (0.05958609655499458, 0.5, -0.14304456114768982), (2.0112997844989877e-07, 0.6557056903839111, 0.0008086158777587116), (2.5433996597712394e-07, 0.6438533067703247, -0.05877731367945671), (3.007438635904691e-07, 0.6101005673408508, -0.10929182916879654), (3.471477612038143e-07, 0.5595860481262207, -0.14304450154304504), (3.5642852935779956e-07, 0.5000000596046448, -0.15489688515663147), (3.657092975117848e-07, 0.4404141306877136, -0.14304450154304504), (3.3786699304982903e-07, 0.3898996412754059, -0.10929176956415176), (3.007438635904691e-07, 0.3561469316482544, -0.058777254074811935), (2.518653445804375e-07, 0.34429454803466797, 0.000808675482403487), (1.9865532863150293e-07, 0.3561469316482544, 0.06039460375905037), (1.5225145943986718e-07, 0.3898996412754059, 0.1109091266989708), (1.0584753340481257e-07, 0.4404141902923584, 0.14466190338134766), (9.656679367253673e-08, 0.5000001192092896, 0.1565142571926117), (8.728602551855147e-08, 0.5595861077308655, 0.14466187357902527), (1.1512832998050726e-07, 0.6101006269454956, 0.11090909689664841), (1.5225145943986718e-07, 0.6438533663749695, 0.060394514352083206)], 'edges': [(1, 0), (2, 1), (3, 2), (4, 3), (5, 4), (6, 5), (7, 6), (8, 7), (9, 8), (10, 9), (11, 10), (12, 11), (13, 12), (14, 13), (15, 14), (0, 15), (17, 16), (18, 17), (19, 18), (20, 19), (21, 20), (22, 21), (23, 22), (24, 23), (25, 24), (26, 25), (27, 26), (28, 27), (29, 28), (30, 29), (31, 30), (16, 31), (33, 32), (34, 33), (35, 34), (36, 35), (37, 36), (38, 37), (39, 38), (40, 39), (41, 40), (42, 41), (43, 42), (44, 43), (45, 44), (46, 45), (47, 46), (32, 47)], 'faces': []}
@@ -0,0 +1 @@
{'verts': [(2.2649766151516815e-07, 1.1557058095932007, 0.000906810280866921), (-0.059585727751255035, 1.1438534259796143, 0.000906810280866921), (-0.11010024696588516, 1.1101007461547852, 0.000906810280866921), (-0.14385297894477844, 1.0595861673355103, 0.000906810280866921), (-0.15570536255836487, 1.000000238418579, 0.000906810280866921), (-0.14385297894477844, 0.940414309501648, 0.000906810280866921), (-0.11010024696588516, 0.889899730682373, 0.000906810280866921), (-0.059585727751255035, 0.856147050857544, 0.000906810280866921), (2.0298676872698707e-07, 0.8442946672439575, 0.000906810280866921), (0.059586141258478165, 0.856147050857544, 0.000906810280866921), (0.11010067909955978, 0.889899730682373, 0.000906810280866921), (0.14385342597961426, 0.940414309501648, 0.000906810280866921), (0.15570582449436188, 1.000000238418579, 0.000906810280866921), (0.14385342597961426, 1.0595862865447998, 0.000906810280866921), (0.11010066419839859, 1.1101007461547852, 0.000906810280866921), (0.05958610400557518, 1.1438534259796143, 0.000906810280866921), (2.2649766151516815e-07, 1.0000001192092896, -0.1547987461090088), (-0.05958572402596474, 1.0000001192092896, -0.14294636249542236), (-0.11010023206472397, 1.0000001192092896, -0.10919369757175446), (-0.14385296404361725, 1.000000238418579, -0.05867911875247955), (-0.15570534765720367, 1.000000238418579, 0.000906810280866921), (-0.14385296404361725, 1.000000238418579, 0.06049273908138275), (-0.11010023206472397, 1.0000003576278687, 0.11100725829601288), (-0.059585727751255035, 1.0000003576278687, 0.14476001262664795), (2.0298676872698707e-07, 1.0000003576278687, 0.15661239624023438), (0.059586141258478165, 1.0000003576278687, 0.14476001262664795), (0.11010066419839859, 1.0000003576278687, 0.11100731790065765), (0.14385342597961426, 1.000000238418579, 0.06049273908138275), (0.1557057946920395, 1.000000238418579, 0.000906810280866921), (0.14385341107845306, 1.000000238418579, -0.058679237961769104), (0.110100656747818, 1.0000001192092896, -0.10919369757175446), (0.05958609655499458, 1.0000001192092896, -0.14294636249542236), (2.0112997844989877e-07, 1.1557058095932007, 0.000906810280866921), (2.5433996597712394e-07, 1.1438534259796143, -0.058679237961769104), (3.007438635904691e-07, 1.1101007461547852, -0.10919369757175446), (3.471477612038143e-07, 1.0595861673355103, -0.14294636249542236), (3.5642852935779956e-07, 1.000000238418579, -0.1547987461090088), (3.657092975117848e-07, 0.9404142498970032, -0.14294636249542236), (3.3786699304982903e-07, 0.889899730682373, -0.10919369757175446), (3.007438635904691e-07, 0.856147050857544, -0.05867911875247955), (2.518653445804375e-07, 0.8442946672439575, 0.000906810280866921), (1.9865532863150293e-07, 0.856147050857544, 0.06049273908138275), (1.5225145943986718e-07, 0.889899730682373, 0.11100725829601288), (1.0584753340481257e-07, 0.940414309501648, 0.14476001262664795), (9.656679367253673e-08, 1.000000238418579, 0.15661239624023438), (8.728602551855147e-08, 1.0595862865447998, 0.14476001262664795), (1.1512832998050726e-07, 1.1101007461547852, 0.1110071986913681), (1.5225145943986718e-07, 1.1438534259796143, 0.0604926198720932)], 'edges': [(1, 0), (2, 1), (3, 2), (4, 3), (5, 4), (6, 5), (7, 6), (8, 7), (9, 8), (10, 9), (11, 10), (12, 11), (13, 12), (14, 13), (15, 14), (0, 15), (17, 16), (18, 17), (19, 18), (20, 19), (21, 20), (22, 21), (23, 22), (24, 23), (25, 24), (26, 25), (27, 26), (28, 27), (29, 28), (30, 29), (31, 30), (16, 31), (33, 32), (34, 33), (35, 34), (36, 35), (37, 36), (38, 37), (39, 38), (40, 39), (41, 40), (42, 41), (43, 42), (44, 43), (45, 44), (46, 45), (47, 46), (32, 47)], 'faces': []}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{'verts': [(-0.09828728437423706, 0.40223199129104614, -0.09828728437423706), (-0.09828728437423706, 0.40223199129104614, 0.09828728437423706), (-0.09828728437423706, 0.5988066792488098, -0.09828728437423706), (-0.09828728437423706, 0.5988066792488098, 0.09828728437423706), (0.09828728437423706, 0.40223199129104614, -0.09828728437423706), (0.09828728437423706, 0.40223199129104614, 0.09828728437423706), (0.09828728437423706, 0.5988066792488098, -0.09828728437423706), (0.09828728437423706, 0.5988066792488098, 0.09828728437423706)], 'edges': [(2, 0), (0, 1), (1, 3), (3, 2), (6, 2), (3, 7), (7, 6), (4, 6), (7, 5), (5, 4), (0, 4), (5, 1)], 'faces': []}
@@ -0,0 +1 @@
{'verts': [(-0.09828728437423706, 0.9076300859451294, -0.09828728437423706), (-0.09828728437423706, 0.9076300859451294, 0.09828728437423706), (-0.09828728437423706, 1.104204773902893, -0.09828728437423706), (-0.09828728437423706, 1.104204773902893, 0.09828728437423706), (0.09828728437423706, 0.9076300859451294, -0.09828728437423706), (0.09828728437423706, 0.9076300859451294, 0.09828728437423706), (0.09828728437423706, 1.104204773902893, -0.09828728437423706), (0.09828728437423706, 1.104204773902893, 0.09828728437423706), (0.00014829635620117188, 1.0, 0.0)], 'edges': [(2, 0), (0, 1), (1, 3), (3, 2), (6, 2), (3, 7), (7, 6), (4, 6), (7, 5), (5, 4), (0, 4), (5, 1)], 'faces': []}
@@ -224,6 +224,7 @@ def fbx_template_def_light(scene, settings, override_defaults=None, nbr_users=0)
b"CastLight": (True, "p_bool", False),
b"Color": ((1.0, 1.0, 1.0), "p_color", True),
b"Intensity": (100.0, "p_number", True), # Times 100 compared to Blender values...
b"Exposure" : (0.0, "p_number", True ),
b"DecayType": (2, "p_enum", False), # Quadratic.
b"DecayStart": (30.0 * gscale, "p_double", False),
b"CastShadows": (True, "p_bool", False),
@@ -611,11 +612,12 @@ def fbx_data_light_elements(root, lamp, scene_data):
light_key = scene_data.data_lights[lamp]
do_light = True
do_shadow = False
# NOTE: this was removed from lamps, always write black.
shadow_color = Vector((0.0, 0.0, 0.0))
if lamp.type not in {'HEMI'}:
do_light = True
do_shadow = lamp.use_shadow
shadow_color = lamp.shadow_color
# `shadow_color = lamp.shadow_color`: now removed.
light = elem_data_single_int64(root, b"NodeAttribute", get_fbx_uuid_from_key(light_key))
light.add_string(fbx_name_class(lamp.name.encode(), b"NodeAttribute"))
@@ -623,12 +625,20 @@ def fbx_data_light_elements(root, lamp, scene_data):
elem_data_single_int32(light, b"GeometryVersion", FBX_GEOMETRY_VERSION) # Sic...
intensity = lamp.energy * 100.0 * pow(2.0, lamp.exposure)
color = lamp.color.copy()
if lamp.use_temperature:
temperature_color = lamp.temperature_color
color[0] *= temperature_color[0]
color[1] *= temperature_color[1]
color[2] *= temperature_color[2]
tmpl = elem_props_template_init(scene_data.templates, b"Light")
props = elem_properties(light)
elem_props_template_set(tmpl, props, "p_enum", b"LightType", FBX_LIGHT_TYPES[lamp.type])
elem_props_template_set(tmpl, props, "p_bool", b"CastLight", do_light)
elem_props_template_set(tmpl, props, "p_color", b"Color", lamp.color)
elem_props_template_set(tmpl, props, "p_number", b"Intensity", lamp.energy * 100.0)
elem_props_template_set(tmpl, props, "p_color", b"Color", color)
elem_props_template_set(tmpl, props, "p_number", b"Intensity", intensity)
elem_props_template_set(tmpl, props, "p_enum", b"DecayType", FBX_LIGHT_DECAY_TYPES['INVERSE_SQUARE'])
elem_props_template_set(tmpl, props, "p_double", b"DecayStart", 25.0 * gscale) # 25 is old Blender default
elem_props_template_set(tmpl, props, "p_bool", b"CastShadows", do_shadow)
@@ -838,7 +848,16 @@ def fbx_data_mesh_shapes_elements(root, me_obj, me, scene_data, fbx_me_tmpl, fbx
mv_shape_verts_idx = shape_verts_idx.data
vg_idx = me_obj.bdata.vertex_groups[shape.vertex_group].index
for sk_idx, v_idx in enumerate(mv_shape_verts_idx):
curr_groups = []
for vg in vertices[v_idx].groups:
# weird bug, some vertices were reported with corrupted vgroups data (two times the same vertex group!).
# Ensure this does not happen
vgname = me_obj.bdata.vertex_groups[vg.group].name
if vgname in curr_groups:
continue
curr_groups.append(vgname)
if vg.group == vg_idx:
mv_shape_verts_weights[sk_idx] = vg.weight
break
@@ -931,8 +950,9 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
fbx_data_element_custom_properties(props, me)
# Subdivision levels. Take them from the first found subsurf modifier from the
# first object that has the mesh. Write crease information if the object has
# and subsurf modifier.
# first object that has the mesh. Always write crease information if present,
# if the modifier explicitly uses creases ("use_creases" setting) and mesh lacks them,
# still provide zeros (see TODO comment below)
write_crease = False
if scene_data.settings.use_subsurf:
last_subsurf = None
@@ -956,6 +976,7 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
elem_data_single_int32(geom, b"PropagateEdgeHardness", 0)
write_crease = last_subsurf.use_creases
write_crease = (write_crease or me.edge_creases)
elem_data_single_int32(geom, b"GeometryVersion", FBX_GEOMETRY_VERSION)
@@ -1091,7 +1112,7 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
# And now, layers!
# Smoothing.
if smooth_type in {'FACE', 'EDGE'}:
if smooth_type in {'FACE', 'EDGE', 'SMOOTH_GROUP'}:
ps_fbx_dtype = np.int32
_map = b""
if smooth_type == 'FACE':
@@ -1106,6 +1127,10 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
# The mesh has no "sharp_face" attribute, so every face is smooth.
t_ps = np.ones(len(me.polygons), dtype=ps_fbx_dtype)
_map = b"ByPolygon"
elif smooth_type == 'SMOOTH_GROUP':
smoothing_groups = me.calc_smooth_groups(use_bitflags=True, use_boundary_vertices_for_bitflags=True)[0]
t_ps = np.asarray(smoothing_groups, dtype=ps_fbx_dtype)
_map = b"ByPolygon"
else: # EDGE
_map = b"ByEdge"
if t_pvi_edge_indices.size:
@@ -1231,8 +1256,7 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
# normal_source = me.polygon_normals
# normal_mapping = b"ByPolygon"
case 'CORNER' | 'FACE':
# We have a mix of sharp/smooth edges/faces or custom split normals, so need to get normals from
# corners.
# We have a mix of sharp/smooth edges/faces or custom normals, so need to get normals from corners.
normal_source = me.corner_normals
normal_mapping = b"ByPolygonVertex"
case _:
@@ -1250,13 +1274,19 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
# FBX SDK documentation says that normals should use IndexToDirect.
elem_data_single_string(lay_nor, b"ReferenceInformationType", b"IndexToDirect")
# Tuple of unique sorted normals and then the index in the unique sorted normals of each normal in t_normal.
# Since we don't care about how the normals are sorted, only that they're unique, we can use the fast unique
# helper function.
t_normal, t_normal_idx = fast_first_axis_unique(t_normal.reshape(-1, 3), return_inverse=True)
# Workaround for Unity FBX import issue where the normals are considered invalid if any normals are
# deduplicated. See #123088.
if normal_mapping == b"ByVertice":
# Write every normal without any deduplication, so the indices array will be [0, 1, 2, ..., n].
t_normal_idx = np.arange(len(t_normal.reshape(-1, 3)), dtype=normal_idx_fbx_dtype)
else:
# Tuple of unique sorted normals and then the index in the unique sorted normals of each normal in t_normal.
# Since we don't care about how the normals are sorted, only that they're unique, we can use the fast unique
# helper function.
t_normal, t_normal_idx = fast_first_axis_unique(t_normal.reshape(-1, 3), return_inverse=True)
# Convert to the type for fbx
t_normal_idx = astype_view_signedness(t_normal_idx, normal_idx_fbx_dtype)
# Convert to the type for fbx
t_normal_idx = astype_view_signedness(t_normal_idx, normal_idx_fbx_dtype)
elem_data_single_float64_array(lay_nor, b"Normals", t_normal)
# Normal weights, no idea what it is.
@@ -1288,13 +1318,15 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
num_loops = len(me.loops)
t_ln = np.empty(num_loops * 3, dtype=normal_bl_dtype)
# t_lnw = np.zeros(len(me.loops), dtype=np.float64)
uv_names = [uvlayer.name for uvlayer in me.uv_layers]
# Annoying, `me.calc_tangent` errors in case there is no geometry...
if num_loops > 0:
for name in uv_names:
me.calc_tangents(uvmap=name)
for idx, uvlayer in enumerate(me.uv_layers):
name = uvlayer.name
# WARNING: Since tangent layers are recomputed inside the loop, do not directly iterate over the
# uvlayers. Instead, cache their keys (names), and use this cached data inside the loop to compute
# the tangent layers.
uvlayer_names = [uvl.name for uvl in me.uv_layers]
for idx, name in enumerate(uvlayer_names):
# Annoying, `me.calc_tangent` errors in case there is no geometry...
if num_loops > 0:
me.calc_tangents(uvmap=name)
# Loop bitangents (aka binormals).
# NOTE: this is not supported by importer currently.
me.loops.foreach_get("bitangent", t_ln)
@@ -1556,14 +1588,14 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
lay_tan = elem_empty(layer, b"LayerElement")
elem_data_single_string(lay_tan, b"Type", b"LayerElementTangent")
elem_data_single_int32(lay_tan, b"TypedIndex", 0)
if smooth_type in {'FACE', 'EDGE'}:
if smooth_type in {'FACE', 'EDGE', 'SMOOTH_GROUP'}:
lay_smooth = elem_empty(layer, b"LayerElement")
elem_data_single_string(lay_smooth, b"Type", b"LayerElementSmoothing")
elem_data_single_int32(lay_smooth, b"TypedIndex", 0)
if write_crease:
lay_smooth = elem_empty(layer, b"LayerElement")
elem_data_single_string(lay_smooth, b"Type", b"LayerElementEdgeCrease")
elem_data_single_int32(lay_smooth, b"TypedIndex", 0)
lay_crease = elem_empty(layer, b"LayerElement")
elem_data_single_string(lay_crease, b"Type", b"LayerElementEdgeCrease")
elem_data_single_int32(lay_crease, b"TypedIndex", 0)
if vcolnumber:
lay_vcol = elem_empty(layer, b"LayerElement")
elem_data_single_string(lay_vcol, b"Type", b"LayerElementColor")
@@ -1808,7 +1840,7 @@ def fbx_data_video_elements(root, vid, scene_data):
with open(filepath, 'br') as f:
elem_data_single_bytes(fbx_vid, b"Content", f.read())
except Exception as e:
print("WARNING: embedding file {} failed ({})".format(filepath, e))
print("WARNING: embedding file {:s} failed ({:s})".format(filepath, str(e)))
elem_data_single_bytes(fbx_vid, b"Content", b"")
msetts.embedded_set.add(filepath)
# Looks like we'd rather not write any 'Content' element in this case (see T44442).
@@ -1885,7 +1917,15 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
valid_idxs = set(bo_vg_idx.values())
vgroups = {vg.index: {} for vg in ob.vertex_groups}
for idx, v in enumerate(me.vertices):
curr_groups = []
for vg in v.groups:
# weird bug, some vertices were reported with corrupted vgroups data (two times the same vertex group!).
# Ensure this does not happen
vgname = ob.vertex_groups[vg.group].name
if vgname in curr_groups:
continue
curr_groups.append(vgname)
if (w := vg.weight) and (vg_idx := vg.group) in valid_idxs:
vgroups[vg_idx][idx] = w
@@ -1923,7 +1963,6 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
elif bpy.context.scene.arp_ge_force_rest_pose_export:
# Auto-Rig Pro implant to export the bind pose when there are no deformers (skeleton only)
# warning, UE doesn't import skeleton only. Unity does
print("Skeleton only, export bind pose anyway...")
mat_world_obj, mat_world_bones = fbx_data_bindpose_only(root, scene_data, armature=arm_obj, mat_world_armature=mat_world_arm, bones_list=bones)
@@ -2325,7 +2364,7 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
depsgraph = scene_data.depsgraph
force_keying = scene_data.settings.bake_anim_use_all_bones
force_sek = scene_data.settings.bake_anim_force_startend_keying
force_sek_sk = scene_data.settings.bake_anim_force_startend_keying_sk
force_sek_sk = scene_data.settings.bake_anim_force_startend_keying_sk# Auto Rig Pro Implant
gscale = scene_data.settings.global_scale
if objects is not None:
@@ -2375,7 +2414,7 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
is_animated = False
act_sk = me.shape_keys.animation_data.action
if act_sk:
fc = act_sk.fcurves.find(f'key_blocks["{shape.name}"].value')
fc = find_fcurve(act_sk, f'key_blocks["{shape.name}"].value')
is_animated = fc != None
if (is_animated or is_driven) or force_sek_sk:# Auto-Rig Pro Implant
@@ -2652,7 +2691,7 @@ def fbx_animations(scene_data):
# All actions.
if scene_data.settings.bake_anim_use_all_actions:
def validate_actions(act, path_resolve):
for fc in act.fcurves:
for fc in get_action_fcurves(act):
data_path = fc.data_path
if fc.array_index:
data_path = data_path + "[%d]" % fc.array_index
@@ -2746,7 +2785,7 @@ def fbx_animations(scene_data):
for pbo, mat in zip(ob.pose.bones, pbones_matrices):
pbo.matrix_basis = mat.copy()
ob.animation_data.action = org_act
bpy.data.objects.remove(ob_copy)
scene.frame_set(scene.frame_current, subframe=0.0)
@@ -2890,14 +2929,19 @@ def fbx_data_from_scene(scene, depsgraph, settings):
tmp_me = bpy.data.meshes.new_from_object(
ob_to_convert, preserve_all_data_layers=True, depsgraph=depsgraph)
# Usually the materials of the evaluated object will be the same, but modifiers, such as Geometry Nodes,
# can change the materials.
orig_mats = tuple(slot.material for slot in ob.material_slots)
eval_mats = tuple(slot.material.original if slot.material else None
for slot in ob_to_convert.material_slots)
# Usually the materials of the evaluated Object converted to a Mesh will be the same as the original
# Object, but modifiers, such as Geometry Nodes, can change the materials.
orig_mats = [slot.material for slot in ob.material_slots]
eval_mats = list(tmp_me.materials)
if orig_mats != eval_mats:
# Override the default behaviour of getting materials from ob_obj.bdata.material_slots.
ob_obj.override_materials = eval_mats
# An object-linked material slot replaces the material on the data at the slot's index. If applying
# modifiers changes the materials on the data, the object-linked material slot will replace the new
# material at the same index as before.
for i, slot in zip(range(len(eval_mats)), ob.material_slots):
if slot.link == 'OBJECT':
eval_mats[i] = slot.material
# Override the default behavior of getting materials from `ob_obj.bdata.material_slots`.
ob_obj.override_materials = tuple(eval_mats)
elif do_convert:
tmp_me = bpy.data.meshes.new_from_object(ob, preserve_all_data_layers=True, depsgraph=depsgraph)
elif do_copy:
@@ -2926,15 +2970,6 @@ def fbx_data_from_scene(scene, depsgraph, settings):
# store the object and corresponding tmp_me for renaming later
# must be done out of this loop, otherwise tmp_me names are incorrect
obj_tmp_meshes[ob] = tmp_me
'''
# since shape keys are removed after the mesh conversion with modifiers applied,
# then copy them back from the original to the converted object if no changes in the mesh topology were found
if ob_to_convert.data.shape_keys:
if len(ob_to_convert.data.vertices) == len(tmp_me.vertices):
temp_obj = bpy.data.objects.new("temp_obj_for_sk_transfer", tmp_me)# a temp object must be created to add shape keys
copy_shape_keys(ob_to_convert, temp_obj)
bpy.data.objects.remove(temp_obj)
'''
# Change armatures back.
for armature, pose_position in backup_pose_positions:
@@ -3284,7 +3319,7 @@ def fbx_data_from_scene(scene, depsgraph, settings):
idx = _objs_indices[ob_obj] = _objs_indices.get(ob_obj, -1) + 1
# XXX If a mesh has multiple material slots with the same material, they are combined into one slot.
# Even if duplicate materials were exported without combining them into one slot, keeping duplicate
# materials separated does not appear to be common behaviour of external software when importing FBX.
# materials separated does not appear to be common behavior of external software when importing FBX.
mesh_material_indices.setdefault(me, {})[ma] = idx
del _objs_indices
@@ -3717,7 +3752,6 @@ def save_single(operator, scene, depsgraph, filepath="",
).to_4x4()
bone_correction_matrix_inv = bone_correction_matrix.inverted()
media_settings = FBXExportSettingsMedia(
path_mode,
os.path.dirname(bpy.data.filepath), # base_src
@@ -3748,7 +3782,7 @@ def save_single(operator, scene, depsgraph, filepath="",
import bpy_extras.io_utils
print('\nFBX export starting... %r' % filepath)
start_time = time.process_time()
start_time = time.time()
# Generate some data about exported scene...
scene_data = fbx_data_from_scene(scene, depsgraph, settings)
@@ -3791,7 +3825,7 @@ def save_single(operator, scene, depsgraph, filepath="",
if not media_settings.embed_textures:
bpy_extras.io_utils.path_reference_copy(media_settings.copy_set)
print('export finished in %.4f sec.' % (time.process_time() - start_time))
print('export finished in %.4f sec.' % (time.time() - start_time))
return {'FINISHED'}
@@ -3836,11 +3870,13 @@ def defaults_unity3d():
"batch_mode": 'OFF',
}
def arp_save(operator, context,
filepath="",
use_selection=False,
use_visible=False,
use_active_collection=False,
collection="",
batch_mode='OFF',
use_batch_own_dir=False,
**kwargs
@@ -3861,13 +3897,23 @@ def arp_save(operator, context,
if batch_mode == 'OFF':
kwargs_mod = kwargs.copy()
source_collection = None
if use_active_collection:
if use_selection:
ctx_objects = tuple(obj
for obj in context.view_layer.active_layer_collection.collection.all_objects
if obj.select_get())
source_collection = context.view_layer.active_layer_collection.collection
elif collection:
local_collection = bpy.data.collections.get((collection, None))
if local_collection:
source_collection = local_collection
else:
ctx_objects = context.view_layer.active_layer_collection.collection.all_objects
operator.report({'ERROR'}, "Collection '%s' was not found" % collection)
return {'CANCELLED'}
if source_collection:
if use_selection:
ctx_objects = tuple(obj for obj in source_collection.all_objects if obj.select_get())
else:
ctx_objects = source_collection.all_objects
else:
if use_selection:
ctx_objects = context.selected_objects
@@ -3876,9 +3922,12 @@ def arp_save(operator, context,
if use_visible:
ctx_objects = tuple(obj for obj in ctx_objects if obj.visible_get())
# Sort exported objects by their names.
ctx_objects = sorted(ctx_objects, key=lambda ob: ob.name)
# Ensure no Objects are in Edit mode.
# Copy to a tuple for safety, to avoid the risk of modifying ctx_objects while iterating.
for obj in tuple(ctx_objects):
for obj in ctx_objects:
if not ensure_object_not_in_edit_mode(context, obj):
operator.report({'ERROR'}, "%s could not be set out of Edit Mode, so cannot be exported" % obj.name)
return {'CANCELLED'}
@@ -1944,5 +1944,5 @@ FBXImportSettings = namedtuple("FBXImportSettings", (
"use_custom_props", "use_custom_props_enum_as_string",
"nodal_material_wrap_map", "image_cache",
"ignore_leaf_bones", "force_connect_children", "automatic_bone_orientation", "bone_correction_matrix",
"use_prepost_rot", "colors_type",
"use_prepost_rot", "colors_type", "mtl_name_collision_mode",
))
@@ -247,6 +247,11 @@ class ARP_OT_export_fbx_wrap(bpy.types.Operator, ExportHelper):
description="Always add a keyframe at start and end of actions for animated channels",
default=True,
)
bake_anim_force_startend_keying_sk: BoolProperty(
name="Force Start/End Keying for Shape Keys",
description="Always add a keyframe at start and end of actions for animated channels",
default=False,
)
bake_anim_step: FloatProperty(
name="Sampling Rate",
description="How often to evaluate animated values (in frames)",
@@ -2320,6 +2320,7 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
depsgraph = scene_data.depsgraph
force_keying = scene_data.settings.bake_anim_use_all_bones
force_sek = scene_data.settings.bake_anim_force_startend_keying
force_sek_sk = scene_data.settings.bake_anim_force_startend_keying_sk# Auto Rig Pro Implant
gscale = scene_data.settings.global_scale
if objects is not None:
@@ -2359,10 +2360,22 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
if not me.shape_keys.use_relative:
continue
for shape, (channel_key, geom_key, _shape_verts_co, _shape_verts_idx) in shapes.items():
acnode = AnimationCurveNodeWrapper(channel_key, 'SHAPE_KEY', force_key, force_sek, (0.0,))
# Sooooo happy to have to twist again like a mad snake... Yes, we need to write those curves twice. :/
acnode.add_group(me_key, shape.name, shape.name, (shape.name,))
animdata_shapes[channel_key] = (acnode, me, shape)
# Auto-Rig Pro Implant
# Only export animation for driven or keyframed shapes
dr = me.shape_keys.animation_data.drivers.find(f'key_blocks["{shape.name}"].value')
is_driven = dr != None
is_animated = False
act_sk = me.shape_keys.animation_data.action
if act_sk:
fc = act_sk.fcurves.find(f'key_blocks["{shape.name}"].value')
is_animated = fc != None
if (is_animated or is_driven) or force_sek_sk:# Auto-Rig Pro Implant
acnode = AnimationCurveNodeWrapper(channel_key, 'SHAPE_KEY', force_key, force_sek, (0.0,))
# Sooooo happy to have to twist again like a mad snake... Yes, we need to write those curves twice. :/
acnode.add_group(me_key, shape.name, shape.name, (shape.name,))
animdata_shapes[channel_key] = (acnode, me, shape)
animdata_cameras = {}
for cam_obj, cam_key in scene_data.data_cameras.items():
@@ -3523,6 +3536,7 @@ def save_single(operator, scene, depsgraph, filepath="",
bake_anim_step=1.0,
bake_anim_simplify_factor=1.0,
bake_anim_force_startend_keying=True,
bake_anim_force_startend_keying_sk=False,
add_leaf_bones=False,
primary_bone_axis='Y',
secondary_bone_axis='X',
@@ -3609,7 +3623,7 @@ def save_single(operator, scene, depsgraph, filepath="",
bake_anim, bake_anim_use_all_bones, bake_anim_use_nla_strips, bake_anim_use_all_actions,
bake_anim_step, bake_anim_simplify_factor, bake_anim_force_startend_keying,
False, media_settings, use_custom_props, colors_type, prioritize_active_color,
shape_keys_baked_data_dict, export_action_only#Auto-Rig Pro implant
shape_keys_baked_data_dict, export_action_only, bake_anim_force_startend_keying_sk#Auto-Rig Pro implant
)
import bpy_extras.io_utils
@@ -1565,7 +1565,7 @@ FBXExportSettings = namedtuple("FBXExportSettings", (
"bake_anim", "bake_anim_use_all_bones", "bake_anim_use_nla_strips", "bake_anim_use_all_actions",
"bake_anim_step", "bake_anim_simplify_factor", "bake_anim_force_startend_keying",
"use_metadata", "media_settings", "use_custom_props", "colors_type", "prioritize_active_color",
"shape_keys_baked_data_dict", "export_action_only"# Auto-Rig Pro implant
"shape_keys_baked_data_dict", "export_action_only", "bake_anim_force_startend_keying_sk"# Auto-Rig Pro implant
))
# Helper container gathering some data we need multiple times:
@@ -5,15 +5,61 @@ from .version import *
from .sys_print import *
def create_action(actionname):
new_act = bpy.data.actions.new(actionname)
if bpy.app.version >= (5,0,0):
slot = new_act.slots.new('OBJECT', 'Slot 1')
actlay = new_act.layers.new('Layer')
actlay.strips.new(type='KEYFRAME')
return new_act
def assign_armature_action(armature, _action, _slot_idx=0):
if armature.animation_data:
armature.animation_data.action = _action
if bpy.app.version >= (4,4,0) and _action != None:
if len(_action.slots):# debug old files compatibility
armature.animation_data.action_slot = _action.slots[_slot_idx]
def get_action_slot_idx(act, act_slot):
for sloti, slot in enumerate(act.slots):
if slot == act_slot:
return sloti
def get_action_slot_name(act, act_slot_idx):
for sloti, slot in enumerate(act.slots):
if sloti == act_slot_idx:
return slot.name_display
def get_action_slot_frame_range(act, slot_idx):
cb = act.layers[0].strips[0].channelbag(act.slots[slot_idx])
min = float('inf')
max = -min
if cb:
for fc in cb.fcurves:
for kf in fc.keyframe_points:
kf_x = kf.co[0]
if kf_x < min:
min = kf_x
if kf_x > max:
max = kf_x
return min, max
def nla_exit_tweak():
active_obj = bpy.context.active_object
if active_obj.animation_data:
if active_obj.animation_data.use_tweak_mode:
print('NLA is in tweak mode, disable it')
active_action = active_obj.animation_data.action
#active_action = active_obj.animation_data.action
active_obj.animation_data.use_tweak_mode = False
# on exit, the active action is set to None. Bring it back
active_obj.animation_data.action = active_action
# Disable current action restore for now, buggy in some cases. To investigate later
# on exit, the active action is set to None. Bring it back
# active_obj.animation_data.action = active_action
return True
return False
@@ -102,7 +148,7 @@ def bake_anim(frame_start=0, frame_end=10, only_selected=False, bake_bones=True,
matrices_dict = {}
for pbone in armature.pose.bones:
if only_selected and not pbone.bone.select:
if only_selected and not is_pbone_selected(pbone):#pbone.bone.select:
continue
def_matrix = None
@@ -218,18 +264,20 @@ def bake_anim(frame_start=0, frame_end=10, only_selected=False, bake_bones=True,
_self.shape_keys_data[dict_entry] = sk.value
print_progress_bar("Baking phase 1", f-frame_start, frame_end-frame_start)
print_progress_bar("Baking", f-frame_start, frame_end-frame_start, start_percent=0, end_percent=90)
f += sampling_rate
f = round(f, 3)# round frame value because of decimals issues
print("")
#print("")
# set new action
action = None
if new_action:
action = bpy.data.actions.new(new_action_name)
#action = bpy.data.actions.new(new_action_name)
action = create_action(new_action_name)
anim_data = armature.animation_data_create()
anim_data.action = action
#anim_data.action = action
assign_armature_action(armature, action)
else:
action = armature.animation_data.action
@@ -241,17 +289,16 @@ def bake_anim(frame_start=0, frame_end=10, only_selected=False, bake_bones=True,
keyframes[fc_key].extend((frame, value))
# set transforms and store keyframes
# set transforms and store keyframes
if bake_bones:
bone_count = 0
total_bone_count = len(armature.pose.bones)
for pbone in armature.pose.bones:
bone_count += 1
print_progress_bar("Baking phase 2", bone_count, total_bone_count)
print_progress_bar("Baking", bone_count, total_bone_count, start_percent=90, end_percent=100)
if only_selected and not pbone.bone.select:
if only_selected and not is_pbone_selected(pbone):#pbone.bone.select:
continue
euler_prev = None
@@ -260,10 +307,12 @@ def bake_anim(frame_start=0, frame_end=10, only_selected=False, bake_bones=True,
for (f, matrix) in bones_data:
# optional, only keyframe given frames
if keyframes_dict:
if keyframes_dict and len(keyframes_dict):
if not pbone.name in keyframes_dict: continue
keyf_list = keyframes_dict[pbone.name]
if not f in keyf_list:
continue
if not f in keyf_list: continue
pbone.matrix_basis = matrix[pbone.name].copy()
@@ -307,13 +356,16 @@ def bake_anim(frame_start=0, frame_end=10, only_selected=False, bake_bones=True,
for fc_key, key_values in keyframes.items():
data_path, index = fc_key
fcurve = action.fcurves.find(data_path=data_path, index=index)
#fcurve = action.fcurves.find(data_path=data_path, index=index)
fcurve = find_fcurve(action, data_path, fc_index=index)
if new_action == False and fcurve:# for now always remove existing keyframes if overwriting current action, must be driven by constraints only
action.fcurves.remove(fcurve)
fcurve = action.fcurves.new(data_path, index=index, action_group=pbone.name)
#fcurve = action.fcurves.new(data_path, index=index, action_group=pbone.name)
fcurve = create_fcurve(action, data_path, fc_index=index, action_group=pbone.name)
if fcurve == None:
fcurve = action.fcurves.new(data_path, index=index, action_group=pbone.name)
#fcurve = action.fcurves.new(data_path, index=index, action_group=pbone.name)
fcurve = create_fcurve(action, data_path, fc_index=index, action_group=pbone.name)
# set keyframes points
num_keys = len(key_values) // 2
fcurve.keyframe_points.add(num_keys)
@@ -390,35 +442,39 @@ def bake_anim(frame_start=0, frame_end=10, only_selected=False, bake_bones=True,
print("\n")
def get_bone_keyframes_list(pb, act):
def get_bone_keyframes_list(pb, act, all_rot_modes=False, bonename=''):
# return a list containing all keyframes frames of the given pose bone
key_list = []
if pb: bonename = pb.name
# loc
for i in range(0,3):
fc = act.fcurves.find('pose.bones["'+pb.name+'"].location', index=i)
fc = get_action_fcurves(act, as_list=False).find('pose.bones["'+bonename+'"].location', index=i)
if fc:
for k in fc.keyframe_points:
if not k.co[0] in key_list:
key_list.append(k.co[0])
# rot
_range = 3
rot_path = 'rotation_euler'
if pb.rotation_mode == 'QUATERNION':
_range = 4
rot_path = 'rotation_quaternion'
for i in range(0,_range):# rot
fc = act.fcurves.find('pose.bones["'+pb.name+'"].'+rot_path, index=i)
if fc:
for k in fc.keyframe_points:
if not k.co[0] in key_list:
key_list.append(k.co[0])
rot_modes = []
if all_rot_modes:
rot_modes = ['rotation_euler', 'rotation_quaternion']
else:
rot_path = 'rotation_quaternion' if pb.rotation_mode == 'QUATERNION' else 'rotation_euler'
rot_modes.append(rot_path)
for rotmode in rot_modes:
_range = 3 if rotmode == 'rotation_euler' else 4
for i in range(0, _range):
fc = get_action_fcurves(act, as_list=False).find('pose.bones["'+bonename+'"].'+rotmode, index=i)
if fc:
for k in fc.keyframe_points:
if not k.co[0] in key_list:
key_list.append(k.co[0])
# scale
for i in range(0,3):
fc = act.fcurves.find('pose.bones["'+pb.name+'"].scale', index=i)
fc = get_action_fcurves(act, as_list=False).find('pose.bones["'+bonename+'"].scale', index=i)
if fc:
for k in fc.keyframe_points:
if not k.co[0] in key_list:
@@ -4,13 +4,12 @@ from .objects import *
class ARP_BonesData:
custom_bones_list = []
softlink_bones = []
const_interp_bones = []
armature_name = ''
#renamed_bones = {}
def init_values(self):
self.custom_bones_list = []
self.softlink_bones = []
#self.renamed_bones = {}
self.softlink_bones = []
self.const_interp_bones = []
def collect(self, arm_name):
@@ -37,12 +36,8 @@ class ARP_BonesData:
if "softlink" in b.keys():
if not b.name in self.softlink_bones:
self.softlink_bones.append(b.name)
add_stretch_bones(b)
#if 'rename' in b.keys():
# if not b.name in self.renamed_bones:
# self.renamed_bones[b.name] = b['rename']
add_stretch_bones(b)
if 'const_interp' in b.keys():
if not b.name in self.const_interp_bones:
self.const_interp_bones.append(b.name)
@@ -72,12 +67,8 @@ class ARP_BonesData:
if "softlink" in b.keys():
if not b.name in self.softlink_bones:
self.softlink_bones.append(b.name)
add_stretch_bones(b)
#if 'rename' in b.keys():
# if not b.name in self.renamed_bones:
# self.renamed_bones[b.name] = b['rename']
add_stretch_bones(b)
if 'const_interp' in b.keys():
if not b.name in self.const_interp_bones:
self.const_interp_bones.append(b.name)
@@ -121,7 +112,7 @@ def retarget_bone_side(bone_name, target_side, dupli_only=False):#"head.x", "_du
base_name = get_bone_base_name(bone_name)#'head'
new_name = ""
if dupli_only:# we only want to set the dupli target side and preserve the left/right/center end letters
if dupli_only:# we only want to set the dupli ID and preserve the left/right/center side suffix
current_side_letters = bone_name[-2:]#.l
dupli_side = target_side[:-2]#'_dupli_001' or ''
new_name = base_name+dupli_side+current_side_letters #'eyelid'+'_dupli_001'+'.l'
@@ -162,3 +153,17 @@ def duplicate(type=None):
bpy.ops.armature.duplicate_move(ARMATURE_OT_duplicate={}, TRANSFORM_OT_translate={"value": (0.0, 0.0, 0.0), "constraint_axis": (False, False, False),"orient_type": 'LOCAL', "mirror": False, "use_proportional_edit": False, "snap": False, "remove_on_cancel": False, "release_confirm": False})
elif type == "OBJECT":
bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
def get_bone_children(b):
children_list = []
children_list = get_bone_children_recur(b, list=children_list)
return children_list
def get_bone_children_recur(b, list=None):
if b.children:
for child in b.children:
list.append(child)
get_bone_children_recur(child, list=list)
return list
@@ -100,19 +100,19 @@ def create_edit_bone(bone_name, deform=False, tag=None):
return _b
def select_edit_bone(name, mode=1):
o = bpy.context.active_object
ebone = get_edit_bone(name)
def select_edit_bone(_name, mode=1):
_o = bpy.context.active_object
_ebone = get_edit_bone(_name)
if mode == 1:
o.data.bones.active = o.pose.bones[name].bone
_o.data.bones.active = _o.pose.bones[_name].bone
elif mode == 2:
o.data.edit_bones.active = o.data.edit_bones[name]
o.data.edit_bones.active.select = True
_o.data.edit_bones.active = _o.data.edit_bones[_name]
_o.data.edit_bones.active.select = True
ebone.select_head = True
ebone.select_tail = True
ebone.select = True
_ebone.select_head = True
_ebone.select_tail = True
_ebone.select = True
def delete_edit_bone(editbone):
@@ -10,7 +10,7 @@ def get_selected_pose_bones():
def get_pose_bone(name):
return bpy.context.active_object.pose.bones.get(name)
return bpy.context.active_object.pose.bones.get(name)
def get_custom_shape_scale_prop_name():
@@ -34,27 +34,33 @@ def set_custom_shape_scale(pbone, scale):
pbone.custom_shape_scale = scale
def scale_custom_shape(custom_shape, scale, origin='cog'):
cs_base_name = custom_shape.name
cs_scaled_name = cs_base_name+'_scaled_'+str(scale)
cs_base_scaled = get_object(cs_scaled_name)
def scale_custom_shape(custom_shape, scale, origin='cog', copy=True):
# scale the custom shape vertices and return the object
if copy:
cs_base_name = custom_shape.name
cs_scaled_name = cs_base_name+'_scaled_'+str(scale)
cs_base_scaled = get_object(cs_scaled_name)
if cs_base_scaled:
return cs_base_scaled
# make
cs_base_scaled = duplicate_object(new_name=cs_scaled_name, method='data', obj=custom_shape)
cs_base_scaled.data.name = cs_scaled_name
cog = Vector((0.0,0.0,0.0))
if cs_base_scaled:# if the scaled copy already exists, skip
return cs_base_scaled
# make
cs_base_scaled = duplicate_object(new_name=cs_scaled_name, method='data', obj=custom_shape)
cs_base_scaled.data.name = cs_scaled_name
else:
cs_base_scaled = custom_shape
# get centroid
cog = Vector((0.0,0.0,0.0))
if origin == 'cog':
for v in cs_base_scaled.data.vertices:
cog += v.co
cog = cog/len(cs_base_scaled.data.vertices)
elif origin == 'zero':
cog = Vector((0.0,0.0,0.0))
# scale verts
for v in cs_base_scaled.data.vertices:
scale_vec = cog - v.co
v.co = v.co + (scale_vec * (1-scale))
@@ -62,7 +68,9 @@ def scale_custom_shape(custom_shape, scale, origin='cog'):
return cs_base_scaled
def get_custom_shape_scale(pbone, uniform=True, as_list=False):
def get_custom_shape_scale(pbone, uniform=True, as_list=False):
# returns the custom shape scale setting values
if bpy.app.version >= (3,0,0):
if uniform:
# uniform scale
@@ -79,21 +87,55 @@ def get_custom_shape_scale(pbone, uniform=True, as_list=False):
# pre-Blender 3.0
else:
return pbone.custom_shape_scale
def get_custom_shape_translation(pbone, as_list=False):
if bpy.app.version >= (3,0,0):
if as_list:
return vector_to_list(pbone.custom_shape_translation)
else:
return pbone.custom_shape_translation
else:
return [0,0,0]
def get_custom_shape_rotation(pbone, as_list=False):
if bpy.app.version >= (3,0,0):
if as_list:
return vector_to_list(pbone.custom_shape_rotation_euler)
else:
return pbone.custom_shape_rotation_euler
else:
return [0,0,0]
def set_bone_custom_shape(pbone, cs_name):
cs = get_object(cs_name)
# set the bone custom shape object
# append first the object from the master file if not present in current file
_sel_rig = bpy.context.active_object
cs = None
# is the custom shape already there?
for _o in bpy.data.objects:
if _o.name.split('.')[0] == cs_name:# support multiple rigs per file, duplicate shapes
if _o.parent:
if _o.parent.parent:
if _o.parent.parent == _sel_rig.parent:# parented to the active rig's char_grp empty
cs = _o
break
if cs == None:
# load custom shape
append_from_arp(nodes=[cs_name], type='object')
cs = get_object(cs_name)
elif len(cs.users_collection) == 0:
# custom shape is found, but not in any collection. Fix it
cs_grp = None
for __o in bpy.context.scene.objects:
if __o.name.startswith('cs_grp') and __o.type == 'EMPTY':
if __o.name.startswith('cs_grp') and __o.type == 'EMPTY' and __o.parent == _sel_rig.parent:
cs_grp = __o
break
if cs_grp:
@@ -208,9 +250,12 @@ def get_bone_colors(bone_data, list=False):
return bone_data.color.palette
def set_bone_color(bone_data, bcolors):
def set_bone_color(bone_data, bcolors, assign_only_if_empty=False):
# Blender 4 and higher only
if assign_only_if_empty:# do not change color group if a group is already assigned
if bone_data.color.palette != 'DEFAULT':
return
if type(bcolors) == str:# set the color palette string
bone_data.color.palette = bcolors
else:# set the color lists
@@ -133,4 +133,50 @@ def search_layer_collection(layerColl, collName):
for layer in layerColl.children:
found = search_layer_collection(layer, collName)
if found:
return found
return found
def set_collection_viz(collection_name, show, set_render=False):
# set collection visibility
# returns true if a change was necessary
collection = bpy.data.collections.get(collection_name)
collection_has_switched = False
if collection:
if collection.hide_viewport == show:
collection.hide_viewport = not show
collection_has_switched = True
layer_col = search_layer_collection(bpy.context.view_layer.layer_collection, collection_name)
if layer_col:
if layer_col.hide_viewport == show:
layer_col.hide_viewport = not show
collection_has_switched = True
if set_render:
if collection.hide_render == show:
collection.hide_render = not show
collection_has_switched = True
if collection_has_switched:
return True
def get_obj_collections(obj):
# returns all collections that an object belongs to, including recursive parent collections
collections = set()
def find_parent_collections(collection):
for parent in bpy.data.collections:
if collection.name in parent.children.keys():
collections.add(parent)
find_parent_collections(parent)
# Find all direct collections the object belongs to
for collection in bpy.data.collections:
if obj.name in collection.objects.keys():
collections.add(collection)
find_parent_collections(collection)
return collections
@@ -4,6 +4,38 @@ from mathutils import *
from math import *
def restore_actions_cns_lib(dict):
for pbname in dict:
cns_dict = dict[pbname]
pb = get_pose_bone(pbname)
for cns_name in cns_dict:
lib_action_name = cns_dict[cns_name]
lib_action = None
for _act in bpy.data.actions:
if _act.name == lib_action_name and _act.library:
lib_action = _act
break
if lib_action:
cns = pb.constraints.get(cns_name)
cns.action = lib_action
#print("Replace with lib action:", pbname, lib_action_name)
def save_actions_cns_lib():
actions_cns_lib_dict = {}
for pb in bpy.context.active_object.pose.bones:
cnss = {}
for cns in pb.constraints:
if cns.type == "ACTION":
if cns.action and cns.action.library:
cnss[cns.name] = cns.action.name
actions_cns_lib_dict[pb.name] = cnss
return actions_cns_lib_dict
def enable_constraint(cns, value):
if bpy.app.version >= (3,0,0):
cns.enabled = value
@@ -3,6 +3,24 @@ from .objects import *
from .bone_pose import *
from .context import *
def remove_constraint_drivers(pb_name, cns_name):
_dp_cns = 'pose.bones["'+pb_name+'"].constraints["'+cns_name+'"].'
_to_del = []
# collect constraint drivers
for dra in bpy.context.active_object.animation_data.drivers:
if dra.data_path.startswith(_dp_cns):
_to_del.append((dra.data_path, dra.array_index))
# remove
for drdp in _to_del:
_dp, arridx = drdp[0], drdp[1]
drb = bpy.context.active_object.animation_data.drivers.find(_dp, index=arridx)
if drb:# maybe superfluous
bpy.context.active_object.animation_data.drivers.remove(drb)
print("Removed cns driver:", _dp, arridx)
def add_driver_to_prop(obj, dr_dp, tar_dp, array_idx=-1, exp="var", multi_var=False):
if obj.animation_data == None:
obj.animation_data_create()
@@ -3,6 +3,119 @@ from math import *
from mathutils import *
import numpy as np
def get_axis(matrix, axis):
if axis == 0:
return Vector((matrix[0][0], matrix[1][0], matrix[2][0])).normalized()
elif axis == 1:
return Vector((matrix[0][1], matrix[1][1], matrix[2][1])).normalized()
elif axis == 2:
return Vector((matrix[0][2], matrix[1][2], matrix[2][2])).normalized()
def lerp(a, b, t):
return a + t * (b - a)
def matrix_blend(matrix1, matrix2, blend_factor):
matrix1 = matrix1.to_4x4()
matrix2 = matrix2.to_4x4()
blend_factor = max(0.0, min(1.0, blend_factor))
result = Matrix()
for __i in range(4):
for __j in range(4):
result[__i][__j] = lerp(matrix1[__i][__j], matrix2[__i][__j], blend_factor)
return result
def damped_track(matrix, target, fac=1.0):
# Return the rotation matrix making Y axis looking at a given target point in space
# minimize rotations on other axes
curr_pos = matrix.to_translation()
current_y = Vector((matrix[0][1], matrix[1][1], matrix[2][1])).normalized()
desired_y = Vector(target - curr_pos).normalized()
dot = current_y.dot(desired_y)
if dot > 0.999999:
# Handle the case when vectors are very close or opposite
result = Matrix(matrix)
elif dot < -0.999999:
# Pointing opposite direction, pick current x_axis
up_axis = 0
up = Vector((matrix[0][up_axis], matrix[1][up_axis], matrix[2][up_axis])).normalized()
right = up.cross(desired_y).normalized()
up = desired_y.cross(right).normalized()
rotation_matrix = Matrix((
[up[0], desired_y[0], right[0], 0],
[up[1], desired_y[1], right[1], 0],
[up[2], desired_y[2], right[2], 0],
[0, 0, 0, 1]
))
result = rotation_matrix
else:
# Normal case: calculate minimal rotation
axis = current_y.cross(desired_y).normalized()
angle = current_y.angle(desired_y)
rotation = Matrix.Rotation(angle, 4, axis)
# Apply rotation to original matrix while preserving translation
result = rotation @ matrix
result.translation = curr_pos
if fac != 1.0:
return matrix_blend(matrix.copy(), result, fac)
else:
return result
def lookat_up(matrix, target, up_axis):
# Return the rotation matrix making Y axis looking at a given target point in space
# using a given up vector
eye = matrix.to_translation()
forward = Vector(target-eye).normalized()
up = up_axis.normalized()
right = up.cross(forward).normalized()
up = forward.cross(right).normalized()
rotation_matrix = Matrix((
[up[0], forward[0], right[0], 0],
[up[1], forward[1], right[1], 0],
[up[2], forward[2], right[2], 0],
[0, 0, 0, 1]
))
return rotation_matrix
def lookat_up_camera(matrix, target, up_axis):
# Return the rotation matrix making -Z axis looking at a given target point in space
# using Y as up vector
eye = matrix.to_translation()
forward = Vector(eye - target).normalized() # -Z direction, so reverse the vector
up = up_axis.normalized()
right = up.cross(forward).normalized()
up = forward.cross(right).normalized()
rotation_matrix = Matrix((
[right[0], up[0], forward[0], 0], # -forward for -Z
[right[1], up[1], forward[1], 0],
[right[2], up[2], forward[2], 0],
[0, 0, 0, 1]
))
return rotation_matrix
def compare_transform(transf1, transf2):
for i, j in enumerate(transf1):
if j == transf2[i]:
@@ -11,31 +124,78 @@ def compare_transform(transf1, transf2):
return False
return True
def catmull_rom_spline(p0, p1, p2, p3, num_points=20):
t = np.linspace(0, 1, num_points)
t2 = t**2
t3 = t**3
f0 = -0.5*t3 + t2 - 0.5*t
f1 = 1.5*t3 - 2.5*t2 + 1
f2 = -1.5*t3 + 2.0*t2 + 0.5*t
f3 = 0.5*t3 - 0.5*t2
return np.outer(f0, p0) + np.outer(f1, p1) + np.outer(f2, p2) + np.outer(f3, p3)
def generate_catrom_curve(points, num_points=20, closed=True):
points = np.array(points)
num_key_points = len(points)
curve = []
for i in range(num_key_points):
p0 = points[i - 1]
if not closed and i == 0:
p0 = points[i]
p1 = points[i]
p2 = points[(i + 1) % num_key_points]
if not closed and i >= num_key_points-1:
p2 = points[i]
p3 = points[(i + 2) % num_key_points]
if not closed and i >= num_key_points-2:
p3 = points[i]
segment = catmull_rom_spline(p0, p1, p2, p3, num_points)
curve.append(segment)
_pts = np.vstack(curve).tolist()
_pts.pop(len(_pts)-1)# remove double cyclic point
vec_list = [Vector((i[0], i[1], i[2])) for i in _pts]
return vec_list
def resample_curve(coords, length=1.0, amount=5, symmetrical=True, generate_normals=False):
def resample_curve(coords, length=1.0, amount=5, symmetrical=True, generate_normals=False, offset_first=True, keep_tip=False):
# resample a given set of points belonging to a curve
# only works by reduction
resampled_coords = []
dist_sum = 0.0
dist = length/amount
dist = length/(amount-1) if keep_tip else length/amount
for i, coord in enumerate(coords):
# special case, since we need symmetrical positioning,
# the first coord must be positioned half-distance
if len(resampled_coords) == 0 and symmetrical:
if coord == coords[0]:
if coord == coords[0]:# skip first
continue
p_prev = coords[i-1]
p_prev = coords[i-1]# average second
cur_dist = (coord-p_prev).magnitude
dist_sum += cur_dist
if dist_sum >= dist/2:
dist_sum = 0.0
resampled_coords.append(coord.copy())
resampled_coords.append(coord.copy())
else:
p_prev = coords[i-1]
if i == 0:
if offset_first:
p_prev = coords[i-1]
else:
resampled_coords.append(coord.copy())
continue
else:
p_prev = coords[i-1]
cur_dist = (coord-p_prev).magnitude
dist_sum += cur_dist
@@ -47,10 +207,8 @@ def resample_curve(coords, length=1.0, amount=5, symmetrical=True, generate_norm
# In case of precision error, the last coord did not fit in.
# Make sure to include it
#print('resampled_coords', len(resampled_coords), 'amount', amount)
# Make sure to include it
#print('resampled_coords', len(resampled_coords), 'amount', amount)
if len(resampled_coords) == amount - 1:
#print('Curve resampling error, add last coord as tip coord')
tip_coord = coords[len(coords)-2]
@@ -139,29 +297,27 @@ def generate_nurbs_curve(points, degree=3, num_points=100):
if len(points) < degree + 1:
raise ValueError("Number of points should be at least degree + 1.")
# Convert control points to numpy array
control_points = np.array(points)
# Calculate the number of knots needed for a closed curve
# calculate the number of knots needed for a closed curve
num_knots = len(control_points) + degree + 1
# Create a list of equally spaced parameter values for the control points
# create a list of equally spaced parameter values for the control points
parameter_values = np.linspace(0, 1, len(control_points))
# Compute the knot vector (closed curve)
# compute the knot vector (closed curve)
knots = np.zeros(num_knots)
knots[degree:-degree] = np.linspace(0, 1, num_knots - 2*degree)
knots[-degree:] = 1
# Evaluate the NURBS curve at 'num_points' points
# evaluate the NURBS curve at 'num_points' points
u_new = np.linspace(0, 1, num_points)
x = np.zeros(num_points)
y = np.zeros(num_points)
z = np.zeros(num_points)
for i in range(len(u_new)):
if i == len(u_new)-1:# the last one must be set manually, sigh
if i == len(u_new)-1:# the last one must be set manually
x[i] += control_points[len(points)-1, 0]
y[i] += control_points[len(points)-1, 1]
z[i] += control_points[len(points)-1, 2]
@@ -182,9 +338,26 @@ def generate_nurbs_curve(points, degree=3, num_points=100):
def signed_angle(vector_u, vector_v, normal):
normal = normal.normalized()
a = vector_u.angle(vector_v)
if vector_u.cross(vector_v).angle(normal) < 1:
a = -a
vector_u = vector_u.normalized()
vector_v = vector_v.normalized()
a = vector_u.angle(vector_v)
cross = vector_u.cross(vector_v)
# parallel vectors case
if cross.length < 1e-6:
if vector_u.dot(vector_v) > 0:
return 0.0
else:
arbitrary = vector_u.orthogonal()
if arbitrary.dot(normal) < 0:
return -a
else:
return a
else:
if cross.angle(normal) < 1:
a = -a
return a
@@ -1,5 +1,4 @@
import bpy, bmesh
from .objects import *
from .bone_data import *
from .version import *
@@ -34,6 +33,19 @@ def overwrite_vgroup(obj, vgroup, new_vgname):
vgroup.name = new_vgname
def export_mesh_data(mesh):
_verts = [(v.co[0], v.co[1], v.co[2]) for v in mesh.vertices]
_edges = [(edge.vertices[0], edge.vertices[1]) for edge in mesh.edges]
_faces = []
for face in mesh.polygons:
face_verts = []
for v in face.vertices:
face_verts.append(v)
_faces.append(face_verts)
return _verts, _edges, _faces
def create_mesh_data(mesh_name, verts, edges, faces):
# create an new mesh data given verts, edges and faces data
new_mesh = bpy.data.meshes.new(name=mesh_name)
@@ -42,11 +54,18 @@ def create_mesh_data(mesh_name, verts, edges, faces):
def create_object_mesh(obj_name, verts, edges, faces):
# create an new object with given mesh data
shape_mesh = create_mesh_data(obj_name, verts, edges, faces)
# create object
shape = bpy.data.objects.new(obj_name, shape_mesh)
return shape
def create_object_mesh_empty(obj_name):
_mesh = bpy.data.meshes.new(name=obj_name)
_obj = bpy.data.objects.new(name=obj_name, object_data=_mesh)
bpy.context.collection.objects.link(_obj)
return _obj
def transfer_shape_keys(source_obj, target_obj):
if source_obj == None or target_obj == None:
@@ -92,9 +111,10 @@ def transfer_shape_keys(source_obj, target_obj):
def transfer_shape_keys_deformed(source_obj, target_obj, apply_mods=False):
if source_obj == None or target_obj == None:
return
failed_sk = []
#print("Disable mods...")
# disable all non-armature modifiers to solve issues when baking the mesh
disabled_mod = {}
if apply_mods == False:
@@ -116,16 +136,17 @@ def transfer_shape_keys_deformed(source_obj, target_obj, apply_mods=False):
if source_obj.data.shape_keys == None:
return
source_shape_keys = source_obj.data.shape_keys.key_blocks
basis_index = 0
# pin the Basis key
#print("Pin Basis...")
source_obj.active_shape_key_index = basis_index
source_obj.show_only_shape_key = True
#print("Update Depsgraph...")
bpy.context.evaluated_depsgraph_get().update()
# store the vert coords in basis shape keys
#print("Store vert coords...")
mesh_baked = bmesh.new()
if bpy.app.version < (2,93,0):
mesh_baked.from_object(source_obj, bpy.context.evaluated_depsgraph_get(), deform=True, face_normals=False)
@@ -139,8 +160,10 @@ def transfer_shape_keys_deformed(source_obj, target_obj, apply_mods=False):
if 'mesh_baked' in locals():
del mesh_baked
source_shape_keys = source_obj.data.shape_keys.key_blocks
# store the vert coords in basis shape keys
#print("Iterate through shapes...")
for sk_index, sk in enumerate(source_shape_keys):
if sk_index == basis_index:
@@ -226,7 +249,7 @@ def transfer_shape_keys_deformed(source_obj, target_obj, apply_mods=False):
# restore disabled modifiers
for objname in disabled_mod:
ob = get_object(objname)
ob = bpy.data.objects.get(objname)
for modname in disabled_mod[objname]:
ob.modifiers[modname].show_viewport = True
@@ -377,13 +400,13 @@ def transfer_weight_verts(object=None, dict=None, list=None, target_group_name=N
for vert in object.data.vertices:
if len(vert.groups) == 0:
continue
for grp in vert.groups:
try:
grp_name = object.vertex_groups[grp.group].name
except:
continue
transfer_weight(object=object, vertice=vert, vertex_weight=grp.weight, group_name=grp_name, dict=dict, list=list, target_group_name=target_group_name, use_side=use_side)
# operate on a copy, creating vgroups in the same loop corrupts pointers on Mac OS
grps_dict = {object.vertex_groups[grp.group].name: grp.weight for grp in vert.groups if grp.group < len(object.vertex_groups)}# sic, there may be corrupted vgroup ID anyway
for grp_name in grps_dict:
weight = grps_dict[grp_name]
transfer_weight(object=object, vertice=vert, vertex_weight=weight, group_name=grp_name, dict=dict, list=list, target_group_name=target_group_name, use_side=use_side)
def transfer_weight(object=None, vertice=None, vertex_weight=None, group_name=None, dict=None, list=None, target_group_name=None, use_side=False):
@@ -419,6 +442,7 @@ def transfer_weight_prefix_verts(object=None, prefix='', tar_grp_base_name=''):
for vert in object.data.vertices:
if len(vert.groups) == 0:
continue
for grp in vert.groups:
try:
grp_name = object.vertex_groups[grp.group].name
@@ -436,7 +460,7 @@ def transfer_weight_prefix(object=None, vertice=None, vertex_weight=None, group_
object.vertex_groups[tar_group_name].add([vertice.index], vertex_weight, 'ADD')
def copy_vgroup(object=None, dict=None, use_side=False):
def copy_vgroup(object=None, dict=None, use_side=False, copy_method='OPERATOR'):
# dict = {'arm_stretch': ['c_arm_twist_offset'],...}
vgroups_names_copy = [i.name for i in object.vertex_groups]
@@ -458,10 +482,26 @@ def copy_vgroup(object=None, dict=None, use_side=False):
object.vertex_groups.remove(tar_grp)
# copy source group
object.vertex_groups.active_index = vg.index
bpy.ops.object.vertex_group_copy()
copy_idx = object.vertex_groups.active_index
copy_grp = object.vertex_groups[copy_idx]
copy_grp.name = tar_grp_name+side
if copy_method == 'OPERATOR':
bpy.ops.object.vertex_group_copy()
copy_idx = object.vertex_groups.active_index
copy_grp = object.vertex_groups[copy_idx]
copy_grp.name = tar_grp_name+side
elif copy_method == 'RAW':
copy_grp = object.vertex_groups.new(name=tar_grp_name+side)
for vert in object.data.vertices:
try:
weight = vg.weight(vert.index)
copy_grp.add([vert.index], weight, 'REPLACE')
except:
continue
def copy_vertex_group(obj, source_vg_name, new_vg_name):
source_vg = obj.vertex_groups.get(source_vg_name)
def multiply_weight(object=None, vertice=None, vertex_weight=None, group_name=None, dict=None):
@@ -495,4 +535,45 @@ def clamp_weights(object=None, vertice=None, vertex_weight=None, group_name=None
target_weight = grp.weight
def_weight = min(vertex_weight, target_weight)
object.vertex_groups[group_name].add([vertice.index], def_weight, 'REPLACE')
object.vertex_groups[group_name].add([vertice.index], def_weight, 'REPLACE')
def count_closed_mesh_elements(obj):
# count the amount of islands/closed elements contained in the same mesh object
if obj.type != 'MESH':
print("Cannot count mesh elements, not a mesh:", obj.name)
return 0
# Create a BMesh from the object's mesh data
bm = bmesh.new()
bm.from_mesh(obj.data)
# Find connected components
islands = []
visited_faces = set()
for face in bm.faces:
if face.index not in visited_faces:
# Perform a breadth-first search (BFS) to find all connected faces
island = set()
stack = [face]
while stack:
current_face = stack.pop()
if current_face.index not in visited_faces:
visited_faces.add(current_face.index)
island.add(current_face.index)
# Add neighboring faces to the stack
for edge in current_face.edges:
for linked_face in edge.link_faces:
if linked_face.index not in visited_faces:
stack.append(linked_face)
islands.append(island)
# Free the BMesh
bm.free()
# Count the islands
return len(islands)
@@ -1,7 +1,14 @@
import bpy, os
from .bone_edit import *
from .context import *
from .armature import *
from .collections import *
import bpy, os, bmesh, sys, ast
def create_empty(empty_name, empty_pos):
empty = bpy.data.objects.new(empty_name, None)
bpy.context.collection.objects.link(empty)
empty.location = empty_pos
def get_object_boundaries(obj):
@@ -37,93 +44,140 @@ def get_object_boundaries(obj):
return {'front':front, 'back':back, 'left':left, 'right':right, 'top':top, 'bottom':bottom}
def append_from_arp(nodes=None, type=None):
def append_from_arp(nodes=None, type=None, from_raw_file=False, new_name=None):
context = bpy.context
scene = context.scene
addon_directory = os.path.dirname(os.path.abspath(__file__))
addon_directory = os.path.dirname(addon_directory)
addon_directory = os.path.dirname(addon_directory)
filepath = addon_directory + "/armature_presets/" + "master.blend"
if type == "object":
# Clean the cs_ materials names (avoid .001, .002...)
for mat in bpy.data.materials:
if mat.name[:3] == "cs_":
if mat.name[-3:].isdigit() and bpy.data.materials.get(mat.name[:-4]) == None:
mat.name = mat.name[:-4]
# make a list of current custom shapes objects in the scene for removal later
cs_objects = [obj.name for obj in bpy.data.objects if obj.name.startswith('cs_')]
# Load the objects data in the file
with bpy.data.libraries.load(filepath, link=False) as (data_from, data_to):
data_to.objects = [name for name in data_from.objects if name in nodes]
if from_raw_file:
# get cs_grp
cs_grp = None
for _ob in bpy.context.scene.objects:
if _ob.name.startswith('cs_grp') and _ob.type == 'EMPTY':
cs_grp = _ob
print('FOUND cs_grp', cs_grp.name)
break
def _create_mesh_data(mesh_name, verts, edges, faces):
# create an new mesh data given verts, edges and faces data
new_mesh = bpy.data.meshes.new(name=mesh_name)
new_mesh.from_pydata(verts, edges, faces)
return new_mesh
# Add the objects in the scene
for obj in data_to.objects:
if obj:
# link
bpy.context.scene.collection.objects.link(obj)
def _create_object_mesh(obj_name, verts, edges, faces):
# create an new object with given mesh data
shape_mesh = _create_mesh_data(obj_name, verts, edges, faces)
shape = bpy.data.objects.new(obj_name, shape_mesh)
return shape
for cs_name in nodes:
filepath = addon_directory+'/src/cs/'+cs_name+'.py'
file = open(filepath, 'r') if sys.version_info >= (3, 11) else open(filepath, 'rU')
file_lines = file.readlines()
dict = ast.literal_eval(file_lines[0])
file.close()
new_cs_name = new_name if new_name != None else cs_name
cs_obj = _create_object_mesh(new_cs_name, dict['verts'], dict['edges'], dict['faces'])
# get cs_grp
cs_grp = None
for _ob in bpy.context.scene.objects:
if _ob.name.startswith('cs_grp') and _ob.type == 'EMPTY':
cs_grp = _ob
break
# apply existing scene material if exists
if len(obj.material_slots):
mat_name = obj.material_slots[0].name
found_mat = None
for mat in bpy.data.materials:
if mat.name == mat_name[:-4]: # substract .001, .002...
found_mat = mat.name
break
# assign existing material if already in file and delete the imported one
if found_mat:
obj.material_slots[0].material = bpy.data.materials[found_mat]
bpy.data.materials.remove(bpy.data.materials[mat_name], do_unlink=True)
# If we append a custom shape
if obj.name.startswith('cs_') or 'c_sphere' in obj.name:
if cs_grp:
# parent the custom shape
obj.parent = cs_grp
# assign to new collection
assigned_collections = []
for collec in cs_grp.users_collection:
collec.objects.link(obj)
assigned_collections.append(collec)
if len(assigned_collections):
# remove previous collections
for i in obj.users_collection:
if not i in assigned_collections:
i.objects.unlink(obj)
# and the scene collection
try:
bpy.context.scene.collection.objects.unlink(obj)
except:
pass
# If we append other objects,
# find added/useless custom shapes and delete them
# parent to cs_grp, collec
if cs_grp:
cs_obj.parent = cs_grp
for collec in cs_grp.users_collection:
collec.objects.link(cs_obj)
else:
for obj in bpy.data.objects:
if obj.name.startswith('cs_'):
if not obj.name in cs_objects:
bpy.data.objects.remove(obj, do_unlink=True)
bpy.context.scene.collection.objects.link(cs_obj)
else:
filepath = addon_directory + "/armature_presets/" + "cs.blend"
# Clean the cs_ materials names (avoid .001, .002...)
for mat in bpy.data.materials:
if mat.name.startswith("cs_"):
if mat.name[-3:].isdigit() and bpy.data.materials.get(mat.name[:-4]) == None:
mat.name = mat.name[:-4]
if 'obj' in locals():
del obj
# make a list of current custom shapes objects in the scene for removal later
cs_objects = [obj.name for obj in bpy.data.objects if obj.name.startswith('cs_')]
# Load the objects data in the file
with bpy.data.libraries.load(filepath, link=False) as (data_from, data_to):
data_to.objects = [name for name in data_from.objects if name in nodes]
# get cs_grp
cs_grp = None
for _ob in bpy.context.scene.objects:
if _ob.name.startswith('cs_grp') and _ob.type == 'EMPTY':
cs_grp = _ob
#print('FOUND cs_grp', cs_grp.name)
break
# Add the objects in the scene
for obj in data_to.objects:
if obj:
# link
bpy.context.scene.collection.objects.link(obj)
# apply existing scene material if exists
if len(obj.material_slots):
mat_name = obj.material_slots[0].name
found_mat = None
for mat in bpy.data.materials:
if mat.name == mat_name[:-4]: # substract .001, .002...
found_mat = mat.name
break
# assign existing material if already in file and delete the imported one
if found_mat:
obj.material_slots[0].material = bpy.data.materials[found_mat]
bpy.data.materials.remove(bpy.data.materials[mat_name], do_unlink=True)
# If we append a custom shape
if obj.name.startswith('cs_') or 'c_sphere' in obj.name:
if cs_grp:
# parent the custom shape
obj.parent = cs_grp
# assign to new collection
assigned_collections = []
for collec in cs_grp.users_collection:
try:
collec.objects.link(obj)
assigned_collections.append(collec)
except:# already in collec
pass
if len(assigned_collections):
# remove previous collections
for i in obj.users_collection:
if not i in assigned_collections:
i.objects.unlink(obj)
# and the scene collection
try:
bpy.context.scene.collection.objects.unlink(obj)
except:
pass
# If we append other objects,
# find added/useless custom shapes and delete them
else:
for obj in bpy.data.objects:
if obj.name.startswith('cs_'):
if not obj.name in cs_objects:
bpy.data.objects.remove(obj, do_unlink=True)
if 'obj' in locals():
del obj
if type == "text":
filepath = addon_directory + "/armature_presets/" + "master.blend"
# Load the objects data in the file
with bpy.data.libraries.load(filepath, link=False) as (data_from, data_to):
data_to.texts = [name for name in data_from.texts if name in nodes]
@@ -131,6 +185,8 @@ def append_from_arp(nodes=None, type=None):
bpy.context.evaluated_depsgraph_get().update()
if type == "font":
filepath = addon_directory + "/armature_presets/" + "master.blend"
# Load the data in the file
with bpy.data.libraries.load(filepath, link=False) as (data_from, data_to):
data_to.fonts = [name for name in data_from.fonts if name in nodes]
@@ -186,10 +242,31 @@ def delete_object(obj):
bpy.data.objects.remove(obj, do_unlink=True)
def set_active_object(object_name):
def set_active_object(object_name, force_display=False):
# set active/select object
# return the collections that were hidden and are now displayed, if force_display=True
bpy.context.view_layer.objects.active = bpy.data.objects[object_name]
enabled_collections = []
object_unhidden = False
if force_display:
# ensure the object is visible, and collections too
_obj_cols = get_obj_collections(bpy.data.objects.get(object_name))
for _obj_col in _obj_cols:
has_switched = set_collection_viz(_obj_col.name, True)
if has_switched:
enabled_collections.append(_obj_col.name)
# unhide object
if is_object_hidden(bpy.data.objects[object_name]):
unhide_object(bpy.data.objects[object_name])
object_unhidden = True
bpy.data.objects[object_name].select_set(state=True)
if force_display:
return enabled_collections, object_unhidden
def hide_object(obj_to_set):
try:# object may not be in current view layer
@@ -223,23 +300,38 @@ def unhide_object(obj_to_set):
def duplicate_object(new_name="", method='operator', obj=None):
if method == 'operator':
if method == 'operator':
try:
bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
except:
bpy.ops.object.duplicate('TRANSLATION', False)
if new_name != "":
bpy.context.active_object.name = new_name
elif method == 'data':
if obj:
obj_dupli = obj.copy()
_obj_dupli = obj.copy()
for col in obj.users_collection:
col.objects.link(obj_dupli)
obj_dupli.data = obj_dupli.data.copy()
obj_dupli.name = new_name
return obj_dupli
else:
print('Cannot duplicate object, not found')
col.objects.link(_obj_dupli)
_obj_dupli.data = _obj_dupli.data.copy()
if new_name != "":
_obj_dupli.name = new_name
return _obj_dupli
else:
print('Cannot duplicate object, not found')
elif method == 'raw':
if obj:
new_mesh = bpy.data.meshes.new(new_name)
bm = bmesh.new()
bm.from_mesh(obj.data)
_obj_dupli = bpy.data.objects.new(new_name, new_mesh)
bm.to_mesh(new_mesh)
bm.free()
for col in obj.users_collection:
col.objects.link(_obj_dupli)
new_mesh.update()
return _obj_dupli
def delete_children(passed_node, type):
@@ -1,9 +1,14 @@
import sys
def print_progress_bar(job_title, progress, length):
def print_progress_bar(job_title, progress, length, start_percent=0, end_percent=100):
if length != 0:
progress = int((progress * 100) / length)
else:
progress = 100
# optional remap
progress = start_percent + progress/(100 / (end_percent-start_percent))
sys.stdout.write("\r " + job_title + " %d%%" % progress)
sys.stdout.flush()
try:# unknown compatibility breakage with flush() in a rare case reported by a user
sys.stdout.flush()
except: pass
@@ -1,6 +1,15 @@
import bpy
from math import *
from mathutils import *
def propmatrix_to_matrix(arr):
if bpy.app.version >= (5,0,0):
return Matrix([arr[0:4], arr[4:8], arr[8:12], arr[12:16]])
else:
return Matrix(arr)
def vectorize3(list):
return Vector((list[0], list[1], list[2]))
@@ -1,5 +1,6 @@
import bpy
import addon_utils
import addon_utils
from bpy_extras import anim_utils
from .. import auto_rig_datas as ard
from .collections import *
from .armature import *
@@ -18,6 +19,18 @@ class ARP_blender_version:
blender_version = ARP_blender_version()
def is_pbone_selected(pb):
if bpy.app.version >= (5,0,0):
return pb.select
else: return pb.bone.select
def select_pb_db(pbone, state=True):
if bpy.app.version >= (5,0,0):
pbone.select = state
else: pbone.bone.select = state
def is_proxy(obj):
# proxy atttribute removed in Blender 3.3
if 'proxy' in dir(obj):
@@ -70,7 +83,7 @@ def convert_drivers_cs_to_xyz(armature):
# tag in prop
armature.data["arp_updated_3.0"] = True
print("Converted custom shape scale drivers to xyz")
#print("Converted custom shape scale drivers to xyz")
def convert_armature_layers_to_collection(armature):
@@ -90,7 +103,7 @@ def convert_armature_layers_to_collection(armature):
for col_name in col_names:#armature.data.collections_all:
_col = get_armature_collections(armature).get(col_name)
if _col == None:# debug...
print(" Collec is None, error when removing collection! Exit")
#print(" Collec is None, error when removing collection! Exit")
continue
# remove deprecated bones color groups
@@ -103,11 +116,13 @@ def convert_armature_layers_to_collection(armature):
_col.name = 'color_'+_col.name
# rename bones collections
print('Rename collections...')
#print('Rename collections...')
get_armature_collections(armature).update()
for i, _col in enumerate(get_armature_collections(armature)):
if _col == None:#Debug, for some reasons Mac throws a None collection when appending the Bird armature
continue
if _col.name.startswith('Layer '):
lidx = int(_col.name.split(' ')[1])-1
@@ -142,7 +157,7 @@ def convert_armature_layers_to_collection(armature):
for col_name in ard.layer_col_map:
if col_name[0].isupper():# only main collections with capital letters
if get_armature_collections(armature).get(col_name) == None:
print('Create collection', col_name)
#print('Create collection', col_name)
armature.data.collections.new(col_name)
sort_armature_collections(armature)
@@ -281,7 +296,7 @@ def invert_angle_with_blender_versions(angle=None, bone=False, axis=None):
if invert:
angle = -angle
return angle
return angle
def disable_bone_inherit_scale(editbone):
@@ -298,6 +313,50 @@ def enable_bone_inherit_scale(editbone):
editbone.use_inherit_scale = True
def find_fcurve(act, dp, slot_idx=0, fc_index=0):
if bpy.app.version >= (5,0,0):
cb = anim_utils.action_get_channelbag_for_slot(act, act.slots[slot_idx])
if cb == None: return None
return cb.fcurves.find(dp, index=fc_index)
else:
return act.fcurves.find(data_path=dp, index=fc_index)
def create_fcurve(act, dp, slot_idx=0, fc_index=0, action_group=''):
if bpy.app.version >= (5,0,0):
cb = anim_utils.action_ensure_channelbag_for_slot(act, act.slots[slot_idx])
return cb.fcurves.new(dp, index=fc_index, group_name=action_group)
else:
return act.fcurves.new(dp, index=fc_index, action_group=action_group)
def delete_fcurve(act, fc, slot_idx=0):
if bpy.app.version >= (5,0,0):
cb = anim_utils.action_ensure_channelbag_for_slot(act, act.slots[slot_idx])
cb.fcurves.remove(fc)
else:
act.fcurves.remove(fc)
def get_action_fcurves(act, slot_idx=0, as_list=True):
if bpy.app.version >= (4,4,0):
if bpy.app.version >= (5,0,0):
cb = anim_utils.action_ensure_channelbag_for_slot(act, act.slots[slot_idx])
else:
cb = act.layers[0].strips[0].channelbag(act.slots[slot_idx])
if cb:
if as_list:
return [_fc for _fc in cb.fcurves if _fc != None]# check for None curve, debug
else:
return cb.fcurves
else:
print("No fcurves found in this slot:", slot_idx)
return []
else:
return act.fcurves
def get_prefs():
if bpy.app.version >= (4,2,0):
return bpy.context.preferences.addons[__package__[:-8]].preferences
@@ -0,0 +1,127 @@
from mathutils import Matrix
matrix_coords_l = {'c_pinky1_base.l': Matrix(((-0.1321406364440918, 0.543448269367218, 0.8289769887924194, 0.4933325946331024),
(-0.9845015406608582, -0.16924019157886505, -0.04598342627286911, -0.10675017535686493),
(0.11530676484107971, -0.8222053647041321, 0.5573893189430237, 1.0162558555603027),
(0.0, 0.0, 0.0, 1.0))), 'c_pinky1.l': Matrix(((0.3489053249359131, 0.41421136260032654, 0.8406510353088379, 0.5218664407730103),
(-0.9321953058242798, 0.06120216101408005, 0.3567441999912262, -0.11563616245985031),
(0.0963180884718895, -0.9081208109855652, 0.40747979283332825, 0.9730858206748962),
(0.0, 0.0, 0.0, 1.0))), 'c_pinky2.l': Matrix(((0.4049581289291382, 0.5478314161300659, 0.7320449352264404, 0.5371885895729065),
(-0.894914984703064, 0.07334459573030472, 0.4401679039001465, -0.11337228119373322),
(0.1874462366104126, -0.8333672881126404, 0.5199640989303589, 0.9394932985305786),
(0.0, 0.0, 0.0, 1.0))), 'c_pinky3.l': Matrix(((0.4344693124294281, 0.6926425099372864, 0.5757458209991455, 0.5472332239151001),
(-0.8724089860916138, 0.16470646858215332, 0.4601897597312927, -0.11202748119831085),
(0.2239179015159607, -0.7022236585617065, 0.6758272647857666, 0.9242132306098938),
(0.0, 0.0, 0.0, 1.0))), 'c_ring1_base.l': Matrix(((-0.24063082039356232, 0.573573112487793, 0.7830138802528381, 0.4974002540111542),
(-0.9458432197570801, -0.3196682929992676, -0.05650714412331581, -0.11789528280496597),
(0.21789370477199554, -0.7542055249214172, 0.619432270526886, 1.0223618745803833),
(0.0, 0.0, 0.0, 1.0))), 'c_ring1.l': Matrix(((-0.05169704183936119, 0.4065243601799011, 0.9121761918067932, 0.5282756686210632),
(-0.9341012239456177, -0.34276989102363586, 0.09982069581747055, -0.1351030468940735),
(0.3532460331916809, -0.8469040393829346, 0.3974550664424896, 0.9817630052566528),
(0.0, 0.0, 0.0, 1.0))), 'c_ring2.l': Matrix(((0.07245957851409912, 0.6446757316589355, 0.7610143423080444, 0.5470014214515686),
(-0.9164383411407471, -0.25805044174194336, 0.3058597445487976, -0.15089207887649536),
(0.3935604691505432, -0.7195852994918823, 0.5721074342727661, 0.942751944065094),
(0.0, 0.0, 0.0, 1.0))), 'c_ring3.l': Matrix(((0.2263454794883728, 0.8068910241127014, 0.5456141233444214, 0.5622686743736267),
(-0.8494088053703308, -0.11064670979976654, 0.5160052180290222, -0.15700331330299377),
(0.47673046588897705, -0.5802450180053711, 0.6603360176086426, 0.9257106781005859),
(0.0, 0.0, 0.0, 1.0))), 'c_middle1_base.l': Matrix(((-0.41135188937187195, 0.5918521881103516, 0.6931815147399902, 0.49848878383636475),
(-0.8688518404960632, -0.4844593107700348, -0.10195799171924591, -0.12799011170864105),
(0.2754744291305542, -0.6442126035690308, 0.7135152816772461, 1.028594970703125),
(0.0, 0.0, 0.0, 1.0))), 'c_middle1.l': Matrix(((-0.29555466771125793, 0.41192948818206787, 0.8619521856307983, 0.5332725048065186),
(-0.860168993473053, -0.5072991251945496, -0.05250336974859238, -0.15646222233772278),
(0.4156401455402374, -0.756942093372345, 0.5042637586593628, 0.9907339215278625),
(0.0, 0.0, 0.0, 1.0))), 'c_middle2.l': Matrix(((-0.2989175617694855, 0.5045099854469299, 0.8100113868713379, 0.5524937510490417),
(-0.819093644618988, -0.5711621046066284, 0.053475141525268555, -0.18013334274291992),
(0.48962658643722534, -0.6474900841712952, 0.5839712619781494, 0.9554142355918884),
(0.0, 0.0, 0.0, 1.0))), 'c_middle3.l': Matrix(((0.03374910354614258, 0.6838750839233398, 0.728818416595459, 0.5666195154190063),
(-0.7648073434829712, -0.45176589488983154, 0.4593227207660675, -0.19612523913383484),
(0.6433745622634888, -0.5729071497917175, 0.5077859163284302, 0.9372851252555847),
(0.0, 0.0, 0.0, 1.0))), 'c_index1_base.l': Matrix(((-0.5358661413192749, 0.556251585483551, 0.6351626515388489, 0.4885375201702118),
(-0.7715515494346619, -0.6281226873397827, -0.10084662586450577, -0.13851317763328552),
(0.3428639769554138, -0.5441008806228638, 0.7657666206359863, 1.033432960510254),
(0.0, 0.0, 0.0, 1.0))), 'c_index1.l': Matrix(((-0.5446334481239319, 0.4174560010433197, 0.7273959517478943, 0.5232923626899719),
(-0.6641221046447754, -0.7443320751190186, -0.07008165121078491, -0.17775851488113403),
(0.5121680498123169, -0.5212484002113342, 0.6826301217079163, 0.9994372725486755),
(0.0, 0.0, 0.0, 1.0))), 'c_index2.l': Matrix(((-0.250714510679245, 0.43092647194862366, 0.8668590188026428, 0.5410243272781372),
(-0.7369382381439209, -0.665624737739563, 0.11775188148021698, -0.20937474071979523),
(0.6277450919151306, -0.6092994809150696, 0.484448105096817, 0.9772966504096985),
(0.0, 0.0, 0.0, 1.0))), 'c_index3.l': Matrix(((-0.42731034755706787, 0.5787864923477173, 0.6945587396621704, 0.5504752993583679),
(-0.7405635714530945, -0.6647520661354065, 0.09833458065986633, -0.2239731252193451),
(0.5186238884925842, -0.47234559059143066, 0.7126837968826294, 0.9639335870742798),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb1_base.l': Matrix(((-0.742074191570282, 0.16673608124256134, -0.6492496132850647, 0.4722246825695038),
(0.06269612163305283, -0.9470593333244324, -0.3148776590824127, -0.14717695116996765),
(-0.6673793196678162, -0.2743678689002991, 0.6923345923423767, 1.0296953916549683),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb1.l': Matrix(((-0.8646366000175476, -0.03816646337509155, -0.5009462237358093, 0.47222477197647095),
(0.11270685493946075, -0.9864310026168823, -0.11937803030014038, -0.14717693626880646),
(-0.48959267139434814, -0.15967853367328644, 0.857205867767334, 1.0296955108642578),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb2.l': Matrix(((-0.9756026864051819, -0.10725779831409454, -0.19156037271022797, 0.4707222878932953),
(0.15552102029323578, -0.9534976482391357, -0.25817763805389404, -0.1860094517469406),
(-0.1549607515335083, -0.2816705107688904, 0.9469156265258789, 1.0234094858169556),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb3.l': Matrix(((-0.8365055918693542, -0.21642035245895386, -0.5034093260765076, 0.46735304594039917),
(0.17929044365882874, -0.9762313961982727, 0.12176759541034698, -0.21596266329288483),
(-0.5177969336509705, 0.011602729558944702, 0.8554251194000244, 1.0145611763000488),
(0.0, 0.0, 0.0, 1.0)))}
matrix_coords_r = {'c_pinky1_base.r': Matrix(((-0.13214083015918732, -0.5434483885765076, -0.8289767503738403, -0.49333253502845764),
(0.9845014810562134, -0.1692400574684143, -0.04598373547196388, -0.10675013810396194),
(-0.11530639231204987, -0.8222052454948425, 0.557389497756958, 1.0162558555603027),
(0.0, 0.0, 0.0, 1.0))), 'c_pinky1.r': Matrix(((0.3489048182964325, -0.4142115116119385, -0.8406510353088379, -0.5218664407730103),
(0.9321953058242798, 0.061202313750982285, 0.356743723154068, -0.11563613265752792),
(-0.09631766378879547, -0.9081205725669861, 0.40748000144958496, 0.9730857014656067),
(0.0, 0.0, 0.0, 1.0))), 'c_pinky2.r': Matrix(((0.40495792031288147, -0.547831654548645, -0.7320449352264404, -0.537188708782196),
(0.8949152231216431, 0.07334486395120621, 0.4401676654815674, -0.11337225884199142),
(-0.18744593858718872, -0.833367109298706, 0.5199644565582275, 0.93949294090271),
(0.0, 0.0, 0.0, 1.0))), 'c_pinky3.r': Matrix(((0.4344694912433624, -0.6926425695419312, -0.5757454633712769, -0.547233521938324),
(0.8724088072776794, 0.1647067666053772, 0.4601900577545166, -0.11202739179134369),
(-0.2239178717136383, -0.7022233605384827, 0.6758275032043457, 0.9242128133773804),
(0.0, 0.0, 0.0, 1.0))), 'c_ring1_base.r': Matrix(((-0.24063074588775635, -0.5735732316970825, -0.7830137610435486, -0.4974001944065094),
(0.9458429217338562, -0.3196679651737213, -0.056507356464862823, -0.11789528280496597),
(-0.2178933024406433, -0.7542052865028381, 0.6194326281547546, 1.0223618745803833),
(0.0, 0.0, 0.0, 1.0))), 'c_ring1.r': Matrix(((-0.051697127521038055, -0.40652453899383545, -0.9121761322021484, -0.5282756090164185),
(0.9341010451316833, -0.34276944398880005, 0.09982036799192429, -0.13510306179523468),
(-0.35324567556381226, -0.8469040989875793, 0.39745569229125977, 0.9817630052566528),
(0.0, 0.0, 0.0, 1.0))), 'c_ring2.r': Matrix(((0.07245972752571106, -0.6446757912635803, -0.7610142230987549, -0.5470013618469238),
(0.9164387583732605, -0.2580502927303314, 0.3058595061302185, -0.15089204907417297),
(-0.39356034994125366, -0.7195854187011719, 0.5721078515052795, 0.9427520632743835),
(0.0, 0.0, 0.0, 1.0))), 'c_ring3.r': Matrix(((0.2263452708721161, -0.8068908452987671, -0.5456140041351318, -0.5622686147689819),
(0.8494092226028442, -0.11064684391021729, 0.516004741191864, -0.15700317919254303),
(-0.47673019766807556, -0.5802450180053711, 0.6603364944458008, 0.9257107377052307),
(0.0, 0.0, 0.0, 1.0))), 'c_middle1_base.r': Matrix(((-0.4113517999649048, -0.5918522477149963, -0.6931816339492798, -0.49848875403404236),
(0.8688518404960632, -0.48445942997932434, -0.10195799916982651, -0.12799015641212463),
(-0.27547430992126465, -0.6442127823829651, 0.7135154008865356, 1.028594970703125),
(0.0, 0.0, 0.0, 1.0))), 'c_middle1.r': Matrix(((-0.2955547571182251, -0.41192927956581116, -0.8619523048400879, -0.5332725644111633),
(0.860168993473053, -0.5072991251945496, -0.052503712475299835, -0.15646234154701233),
(-0.41563984751701355, -0.7569423317909241, 0.5042638778686523, 0.9907339215278625),
(0.0, 0.0, 0.0, 1.0))), 'c_middle2.r': Matrix(((-0.298917680978775, -0.5045096278190613, -0.8100114464759827, -0.5524936318397522),
(0.819093644618988, -0.5711621642112732, 0.053474873304367065, -0.18013350665569305),
(-0.48962661623954773, -0.6474902033805847, 0.5839712023735046, 0.9554141163825989),
(0.0, 0.0, 0.0, 1.0))), 'c_middle3.r': Matrix(((0.03374888375401497, -0.6838746070861816, -0.7288186550140381, -0.566619336605072),
(0.7648075819015503, -0.4517658054828644, 0.4593222737312317, -0.19612538814544678),
(-0.6433743834495544, -0.5729072093963623, 0.507785975933075, 0.9372850656509399),
(0.0, 0.0, 0.0, 1.0))), 'c_index1_base.r': Matrix(((-0.5358662605285645, -0.5562516450881958, -0.6351625323295593, -0.4885374903678894),
(0.7715515494346619, -0.6281226873397827, -0.10084672272205353, -0.13851313292980194),
(-0.3428637981414795, -0.5441008806228638, 0.7657667398452759, 1.033432960510254),
(0.0, 0.0, 0.0, 1.0))), 'c_index1.r': Matrix(((-0.5446333289146423, -0.4174560308456421, -0.7273959517478943, -0.5232923030853271),
(0.6641222238540649, -0.7443320751190186, -0.07008160650730133, -0.17775849997997284),
(-0.5121681690216064, -0.5212485194206238, 0.6826300024986267, 0.9994373321533203),
(0.0, 0.0, 0.0, 1.0))), 'c_index2.r': Matrix(((-0.25071465969085693, -0.43092650175094604, -0.8668591380119324, -0.5410242676734924),
(0.7369382977485657, -0.6656247973442078, 0.1177515834569931, -0.20937487483024597),
(-0.6277451515197754, -0.6092994213104248, 0.48444831371307373, 0.9772967100143433),
(0.0, 0.0, 0.0, 1.0))), 'c_index3.r': Matrix(((-0.42731064558029175, -0.5787865519523621, -0.69455885887146, -0.5504752993583679),
(0.7405636310577393, -0.6647522449493408, 0.09833402186632156, -0.22397328913211823),
(-0.5186238288879395, -0.47234559059143066, 0.7126840949058533, 0.9639335870742798),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb1_base.r': Matrix(((-0.7420739531517029, -0.16673603653907776, 0.6492499709129333, -0.47222471237182617),
(-0.0626964196562767, -0.947059154510498, -0.31487780809402466, -0.14717699587345123),
(0.6673798561096191, -0.2743680477142334, 0.6923341155052185, 1.0296955108642578),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb1.r': Matrix(((-0.8646363615989685, 0.038166627287864685, 0.5009466409683228, -0.4722246825695038),
(-0.11270713806152344, -0.9864309430122375, -0.11937820911407471, -0.14717702567577362),
(0.4895932376384735, -0.15967872738838196, 0.8572055697441101, 1.0296955108642578),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb2.r': Matrix(((-0.9756026268005371, 0.10725804418325424, 0.19156040251255035, -0.4707222580909729),
(-0.1555212289094925, -0.9534975290298462, -0.25817784667015076, -0.18600955605506897),
(0.15496063232421875, -0.2816707491874695, 0.946915864944458, 1.0234094858169556),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb3.r': Matrix(((-0.8365054726600647, 0.21642057597637177, 0.5034092664718628, -0.4673529863357544),
(-0.1792907863855362, -0.9762312769889832, 0.12176735699176788, -0.2159627228975296),
(0.5177968740463257, 0.011602476239204407, 0.8554254174232483, 1.0145611763000488),
(0.0, 0.0, 0.0, 1.0)))}
@@ -0,0 +1,141 @@
from mathutils import Matrix
hand_mat_l = Matrix(((-0.35261183977127075, 0.516721785068512, 0.7801688313484192, 0.4798411726951599),
(-0.8112916350364685, -0.5842888355255127, 0.020308198407292366, -0.11578542739152908),
(0.4663377106189728, -0.6257834434509277, 0.6252393126487732, 1.0348492860794067),
(0.0, 0.0, 0.0, 1.0)))
hand_mat_r = Matrix(((-0.3526119887828827, -0.516721785068512, -0.7801688313484192, -0.47984111309051514),
(0.8112916946411133, -0.5842887759208679, 0.020307956263422966, -0.11578543484210968),
(-0.4663374722003937, -0.6257835030555725, 0.6252394914627075, 1.0348492860794067),
(0.0, 0.0, 0.0, 1.0)))
matrix_coords_l = {'c_pinky1_base.l': Matrix(((-0.3588298559188843, 0.424203485250473, 0.8314400315284729, 0.49337443709373474),
(-0.8281897306442261, -0.5555429458618164, -0.07398698478937149, -0.10673553496599197),
(0.43051522970199585, -0.7151384949684143, 0.5506664514541626, 1.0163047313690186),
(0.0, 0.0, 0.0, 1.0))),
'c_pinky1.l': Matrix(((-0.05721341073513031, -0.9644581079483032, 0.25796768069267273, 0.5156472325325012),
(-0.8807923793792725, -0.0728902518749237, -0.4678591787815094, -0.1359044313430786),
(0.47003400325775146, -0.25398388504981995, -0.8453166484832764, 0.9787562489509583),
(0.0, 0.0, 0.0, 1.0))),
'c_pinky2.l': Matrix(((0.03900858014822006, 0.06956508010625839, -0.9968141913414001, 0.47997069358825684),
(-0.919760525226593, 0.39238572120666504, -0.008609596639871597, -0.13860078155994415),
(0.3905368149280548, 0.9171664714813232, 0.07928968966007233, 0.9693610668182373),
(0.0, 0.0, 0.0, 1.0))),
'c_pinky3.l': Matrix(((-0.012231852859258652, 0.9586328268051147, 0.28438279032707214, 0.4812462031841278),
(-0.9238848090171814, -0.11962065100669861, 0.363493949174881, -0.1314062774181366),
(0.3824753165245056, -0.25829076766967773, 0.8871296048164368, 0.9861776828765869),
(0.0, 0.0, 0.0, 1.0))),
'c_ring1_base.l': Matrix(((-0.39991500973701477, 0.47596868872642517, 0.7832762598991394, 0.49744197726249695),
(-0.8209208250045776, -0.5660759210586548, -0.07515108585357666, -0.1178804263472557),
(0.4076242744922638, -0.6730616688728333, 0.6171146631240845, 1.0224111080169678),
(0.0, 0.0, 0.0, 1.0))), 'c_ring1.l': Matrix(((-0.14413531124591827, -0.9160363078117371, 0.37430238723754883, 0.5230634212493896),
(-0.8027083277702332, -0.11296883970499039, -0.5855748057365417, -0.1483522206544876),
(0.5786923766136169, -0.38485753536224365, -0.7190269827842712, 0.9861801862716675),
(0.0, 0.0, 0.0, 1.0))), 'c_ring2.l': Matrix(((-0.10324987769126892, -0.19098883867263794, -0.9761467576026917, 0.48086801171302795),
(-0.8736379742622375, 0.48656898736953735, -0.002792816609144211, -0.15355591475963593),
(0.4754961431026459, 0.8525105118751526, -0.21709322929382324, 0.9684524536132812),
(0.0, 0.0, 0.0, 1.0))), 'c_ring3.l': Matrix(((-0.286427766084671, 0.9244988560676575, 0.2515174150466919, 0.47634512186050415),
(-0.848092257976532, -0.3667835295200348, 0.38237467408180237, -0.1420329213142395),
(0.44575732946395874, -0.10378727316856384, 0.8891169428825378, 0.9886415600776672),
(0.0, 0.0, 0.0, 1.0))), 'c_middle1_base.l': Matrix(((-0.49470439553260803, 0.5256193280220032, 0.6920921802520752, 0.4985300898551941),
(-0.7925525307655334, -0.5995886921882629, -0.11114682257175446, -0.1279752105474472),
(0.35654982924461365, -0.6035042405128479, 0.7132004499435425, 1.0286451578140259),
(0.0, 0.0, 0.0, 1.0))), 'c_middle1.l': Matrix(((-0.18281525373458862, -0.8775211572647095, 0.4433228373527527, 0.5294212102890015),
(-0.8266451358795166, -0.10689479857683182, -0.5524771809577942, -0.16321365535259247),
(0.5321995615959167, -0.46747225522994995, -0.7058566212654114, 0.9931765794754028),
(0.0, 0.0, 0.0, 1.0))), 'c_middle2.l': Matrix(((-0.09921594709157944, -0.1985192596912384, -0.9750621318817139, 0.48847508430480957),
(-0.8331379890441895, 0.5523718595504761, -0.027686286717653275, -0.16820143163204193),
(0.5440933704376221, 0.8096145987510681, -0.22019770741462708, 0.9713637232780457),
(0.0, 0.0, 0.0, 1.0))), 'c_middle3.l': Matrix(((-0.45292720198631287, 0.8585988879203796, 0.24013452231884003, 0.48291686177253723),
(-0.7653985619544983, -0.5125921964645386, 0.38912028074264526, -0.15273569524288177),
(0.45718955993652344, -0.007555872201919556, 0.8893374800682068, 0.9940320253372192),
(0.0, 0.0, 0.0, 1.0))), 'c_index1_base.l': Matrix(((-0.5358661413192749, 0.556251585483551, 0.6351626515388489, 0.4885786473751068),
(-0.7715515494346619, -0.6281226873397827, -0.10084662586450577, -0.1384982317686081),
(0.3428639769554138, -0.5441008806228638, 0.7657666206359863, 1.0334831476211548),
(0.0, 0.0, 0.0, 1.0))), 'c_index1.l': Matrix(((-0.3491906523704529, -0.8236109614372253, 0.4469125270843506, 0.5233335494995117),
(-0.7358909249305725, -0.054232120513916016, -0.6749246716499329, -0.17774353921413422),
(0.5801123976707458, -0.5645565986633301, -0.5871503949165344, 0.999487578868866),
(0.0, 0.0, 0.0, 1.0))), 'c_index2.l': Matrix(((-0.29383838176727295, -0.25366896390914917, -0.9215805530548096, 0.4883497357368469),
(-0.9045541882514954, 0.38540804386138916, 0.18232452869415283, -0.18004705011844635),
(0.30893459916114807, 0.8871937394142151, -0.34270498156547546, 0.9755074977874756),
(0.0, 0.0, 0.0, 1.0))), 'c_index3.l': Matrix(((-0.09548855572938919, 0.9815612435340881, 0.1655871868133545, 0.48278629779815674),
(-0.9254596829414368, -0.1488049030303955, 0.3483980894088745, -0.17159435153007507),
(0.36661431193351746, -0.11997638642787933, 0.9226047396659851, 0.9949652552604675),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb1_base.l': Matrix(((-0.742074191570282, 0.16673608124256134, -0.6492496132850647, 0.47226616740226746),
(0.06269612163305283, -0.9470593333244324, -0.3148776590824127, -0.1471620798110962),
(-0.6673793196678162, -0.2743678689002991, 0.6923345923423767, 1.02974534034729),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb1.l': Matrix(((-0.916347861289978, 0.030809633433818817, -0.3991962671279907, 0.47226616740226746),
(0.18215209245681763, -0.8557999730110168, -0.4841769337654114, -0.1471620798110962),
(-0.3565494418144226, -0.5163886547088623, 0.778598427772522, 1.02974534034729),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb2.l': Matrix(((-0.9960366487503052, 0.006717206910252571, -0.08869118243455887, 0.47347909212112427),
(0.08558714389801025, -0.199024498462677, -0.9762499332427979, -0.1808519959449768),
(-0.024209430441260338, -0.9799714088439941, 0.19766077399253845, 1.009416937828064),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb3.l': Matrix(((-0.8867418766021729, 0.45290955901145935, -0.0925314724445343, 0.4736900329589844),
(0.4336596429347992, 0.7457115054130554, -0.5058196783065796, -0.18710407614707947),
(-0.16008882224559784, -0.4886585474014282, -0.8576620221138, 0.9786321520805359),
(0.0, 0.0, 0.0, 1.0)))}
matrix_coords_r = {'c_pinky1_base.r': Matrix(((-0.3588300347328186, -0.4242035448551178, -0.831439733505249, -0.49333256483078003),
(0.8281898498535156, -0.5555428266525269, -0.07398723065853119, -0.10675002634525299),
(-0.4305148422718048, -0.7151386141777039, 0.5506665706634521, 1.0162558555603027),
(0.0, 0.0, 0.0, 1.0))), 'c_pinky1.r': Matrix(((-0.05721384286880493, 0.9644578099250793, -0.25796759128570557, -0.5156054496765137),
(0.8807926774024963, -0.07288983464241028, -0.4678589701652527, -0.13591890037059784),
(-0.4700336754322052, -0.2539840042591095, -0.8453166484832764, 0.9787073731422424),
(0.0, 0.0, 0.0, 1.0))), 'c_pinky2.r': Matrix(((0.03900822252035141, -0.06956491619348526, 0.9968144297599792, -0.47992897033691406),
(0.9197608232498169, 0.3923851549625397, -0.008609358221292496, -0.1386151909828186),
(-0.3905361294746399, 0.9171666502952576, 0.07928939163684845, 0.9693121910095215),
(0.0, 0.0, 0.0, 1.0))), 'c_pinky3.r': Matrix(((-0.012232892215251923, -0.9586328864097595, -0.28438252210617065, -0.48120447993278503),
(0.92388516664505, -0.11962135881185532, 0.3634931743144989, -0.13142070174217224),
(-0.3824746608734131, -0.25829029083251953, 0.8871298432350159, 0.9861287474632263),
(0.0, 0.0, 0.0, 1.0))), 'c_ring1_base.r': Matrix(((-0.39991509914398193, -0.4759686291217804, -0.7832764387130737, -0.49740028381347656),
(0.8209203481674194, -0.5660758018493652, -0.07515129446983337, -0.11789508163928986),
(-0.4076239764690399, -0.673061728477478, 0.617114782333374, 1.0223617553710938),
(0.0, 0.0, 0.0, 1.0))), 'c_ring1.r': Matrix(((-0.1441354602575302, 0.9160371422767639, -0.37430238723754883, -0.5230216979980469),
(0.802708089351654, -0.11296850442886353, -0.5855749249458313, -0.14836692810058594),
(-0.5786920189857483, -0.38485804200172424, -0.7190274596214294, 0.9861308336257935),
(0.0, 0.0, 0.0, 1.0))), 'c_ring2.r': Matrix(((-0.10325005650520325, 0.19098877906799316, 0.976146936416626, -0.4808262884616852),
(0.8736380934715271, 0.4865686595439911, -0.00279245525598526, -0.15357057750225067),
(-0.4754956364631653, 0.8525108695030212, -0.2170931100845337, 0.968403160572052),
(0.0, 0.0, 0.0, 1.0))), 'c_ring3.r': Matrix(((-0.28642746806144714, -0.9244990348815918, -0.25151753425598145, -0.4763032793998718),
(0.8480921983718872, -0.3667834401130676, 0.38237419724464417, -0.14204759895801544),
(-0.445756733417511, -0.10378794372081757, 0.8891172409057617, 0.9885924458503723),
(0.0, 0.0, 0.0, 1.0))), 'c_middle1_base.r': Matrix(((-0.49470436573028564, -0.5256195068359375, -0.6920923590660095, -0.4984886348247528),
(0.792552649974823, -0.5995886921882629, -0.11114684492349625, -0.12799014151096344),
(-0.3565496802330017, -0.6035044193267822, 0.7132005095481873, 1.0285948514938354),
(0.0, 0.0, 0.0, 1.0))), 'c_middle1.r': Matrix(((-0.18281537294387817, 0.8775213360786438, -0.4433228373527527, -0.5293797850608826),
(0.8266454935073853, -0.10689447820186615, -0.5524771809577942, -0.16322852671146393),
(-0.5321993827819824, -0.4674723744392395, -0.7058568000793457, 0.993126392364502),
(0.0, 0.0, 0.0, 1.0))), 'c_middle2.r': Matrix(((-0.09921589493751526, 0.19851899147033691, 0.9750621318817139, -0.4884335994720459),
(0.8331380486488342, 0.5523715019226074, -0.027686096727848053, -0.16821634769439697),
(-0.5440930128097534, 0.8096147179603577, -0.2201976180076599, 0.9713135957717896),
(0.0, 0.0, 0.0, 1.0))), 'c_middle3.r': Matrix(((-0.4529270827770233, -0.8585991263389587, -0.24013493955135345, -0.4828752875328064),
(0.7653987407684326, -0.5125921964645386, 0.3891199827194214, -0.15275052189826965),
(-0.45718950033187866, -0.007556170225143433, 0.8893375396728516, 0.9939819574356079),
(0.0, 0.0, 0.0, 1.0))), 'c_index1_base.r': Matrix(((-0.5358662605285645, -0.5562516450881958, -0.6351625323295593, -0.4885374903678894),
(0.7715515494346619, -0.6281226873397827, -0.10084672272205353, -0.13851310312747955),
(-0.3428637981414795, -0.5441008806228638, 0.7657667398452759, 1.033432960510254),
(0.0, 0.0, 0.0, 1.0))), 'c_index1.r': Matrix(((-0.3491906523704529, 0.8236109018325806, -0.44691264629364014, -0.5232923030853271),
(0.7358911037445068, -0.0542321503162384, -0.6749247312545776, -0.17775849997997284),
(-0.5801124572753906, -0.564556360244751, -0.5871505737304688, 0.9994373321533203),
(0.0, 0.0, 0.0, 1.0))), 'c_index2.r': Matrix(((-0.29383835196495056, 0.25366878509521484, 0.9215806722640991, -0.4883083999156952),
(0.9045540690422058, 0.3854084312915802, 0.18232448399066925, -0.18006213009357452),
(-0.3089349865913391, 0.8871937394142151, -0.342704713344574, 0.9754570126533508),
(0.0, 0.0, 0.0, 1.0))), 'c_index3.r': Matrix(((-0.09548826515674591, -0.9815613031387329, -0.1655874252319336, -0.48274490237236023),
(0.9254595637321472, -0.14880481362342834, 0.3483985364437103, -0.17160934209823608),
(-0.36661475896835327, -0.11997681856155396, 0.9226045608520508, 0.9949148893356323),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb1_base.r': Matrix(((-0.7420739531517029, -0.16673603653907776, 0.6492499709129333, -0.47222471237182617),
(-0.0626964196562767, -0.947059154510498, -0.31487780809402466, -0.14717699587345123),
(0.6673798561096191, -0.2743680477142334, 0.6923341155052185, 1.0296955108642578),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb1.r': Matrix(((-0.9163479208946228, -0.03080972284078598, 0.3991967439651489, -0.4722246825695038),
(-0.18215233087539673, -0.855799674987793, -0.4841769337654114, -0.14717702567577362),
(0.35655027627944946, -0.5163888931274414, 0.7785980105400085, 1.0296955108642578),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb2.r': Matrix(((-0.9960367679595947, -0.0067172907292842865, 0.08869123458862305, -0.4734375476837158),
(-0.08558713644742966, -0.19902411103248596, -0.9762501120567322, -0.18086694180965424),
(0.024209478870034218, -0.9799717664718628, 0.19766059517860413, 1.0093668699264526),
(0.0, 0.0, 0.0, 1.0))), 'c_thumb3.r': Matrix(((-0.8867420554161072, -0.45290952920913696, 0.09253138303756714, -0.4736485183238983),
(-0.43365973234176636, 0.7457118630409241, -0.5058193206787109, -0.18711905181407928),
(0.16008873283863068, -0.48865842819213867, -0.8576623797416687, 0.978581964969635),
(0.0, 0.0, 0.0, 1.0)))}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
from mathutils import Vector, Euler, Matrix
coords={'pelvis': Vector((1.1648752433757181e-06, -0.03546452522277832, 9.705429077148438)), 'spine_01': Vector((2.2931205876375316e-06, -0.6347151398658752, 19.094528198242188)), 'spine_02': Vector((1.598883045517141e-06, 0.6980798244476318, 13.395744323730469)), 'spine_03': Vector((1.6606641111138742e-06, 1.0556304454803467, 13.992294311523438)), 'clavicle_l': Vector((14.954872131347656, 4.510254859924316, -2.2741546630859375)), 'upperarm_l': Vector((15.369140625, -0.2687036991119385, -1.2218170166015625)), 'lowerarm_l': Vector((20.141151428222656, -3.486912250518799, -1.384307861328125)), 'hand_l': Vector((10.576087951660156, -1.987648606300354, -0.7958221435546875)), 'index_01_l': Vector((4.158714294433594, -0.7536659240722656, -0.720947265625)), 'index_02_l': Vector((3.249267578125, -0.3236865997314453, -0.9248504638671875)), 'index_03_l': Vector((3.1864013671875, -0.4611630439758301, -1.073333740234375)), 'middle_01_l': Vector((4.579429626464844, -0.4014453887939453, -0.6331024169921875)), 'middle_02_l': Vector((3.5030288696289062, -0.391385555267334, -0.943206787109375)), 'middle_03_l': Vector((3.4647369384765625, -0.42128419876098633, -1.06402587890625)), 'pinky_01_l': Vector((3.498687744140625, 0.6987745761871338, -0.15118408203125)), 'pinky_02_l': Vector((2.8414535522460938, 0.5827560424804688, -0.70745849609375)), 'pinky_03_l': Vector((2.8664474487304688, 0.37882447242736816, -0.744293212890625)), 'ring_01_l': Vector((4.408515930175781, 0.2745676636695862, -0.3408203125)), 'ring_02_l': Vector((3.3953475952148438, 0.27488279342651367, -0.695098876953125)), 'ring_03_l': Vector((3.3896865844726562, 0.14387929439544678, -0.75921630859375)), 'thumb_01_l': Vector((2.0802688598632812, -2.6089048385620117, -1.9597320556640625)), 'thumb_02_l': Vector((2.58538818359375, -1.9712257385253906, -2.4354095458984375)), 'thumb_03_l': Vector((2.8783111572265625, -1.8077516555786133, -2.2245330810546875)), 'clavicle_r': Vector((-14.95479679107666, 4.510228633880615, -2.274139404296875)), 'upperarm_r': Vector((-15.369194030761719, -0.26871156692504883, -1.2218170166015625)), 'lowerarm_r': Vector((-20.141193389892578, -3.486935615539551, -1.384307861328125)), 'hand_r': Vector((-10.576080322265625, -1.987655758857727, -0.7958221435546875)), 'index_01_r': Vector((-4.158897399902344, -0.7537021636962891, -0.720977783203125)), 'index_02_r': Vector((-3.249267578125, -0.32368993759155273, -0.9248504638671875)), 'index_03_r': Vector((-3.1864089965820312, -0.4611630439758301, -1.0733184814453125)), 'middle_01_r': Vector((-4.579620361328125, -0.40146565437316895, -0.6331329345703125)), 'middle_02_r': Vector((-3.5030975341796875, -0.39139294624328613, -0.9432220458984375)), 'middle_03_r': Vector((-3.4648056030273438, -0.42128705978393555, -1.06402587890625)), 'pinky_01_r': Vector((-3.4987640380859375, 0.6987893581390381, -0.1511993408203125)), 'pinky_02_r': Vector((-2.84124755859375, 0.5827126502990723, -0.7073974609375)), 'pinky_03_r': Vector((-2.8662338256835938, 0.37879395484924316, -0.744232177734375)), 'ring_01_r': Vector((-4.408203125, 0.2745439410209656, -0.3408050537109375)), 'ring_02_r': Vector((-3.3953475952148438, 0.27488067746162415, -0.695098876953125)), 'ring_03_r': Vector((-3.3897171020507812, 0.14389744400978088, -0.7591552734375)), 'thumb_01_r': Vector((-2.0802078247070312, -2.608829975128174, -1.9596710205078125)), 'thumb_02_r': Vector((-2.58538818359375, -1.9712257385253906, -2.4354248046875)), 'thumb_03_r': Vector((-2.878326416015625, -1.8077640533447266, -2.2245025634765625)), 'neck_01': Vector((1.083126335288398e-06, -2.8042922019958496, 8.857421875)), 'head': Vector((1.1140700735268183e-06, 0.1489097774028778, 16.57891845703125)), 'thigh_l': Vector((3.5867919921875, -0.9371733665466309, -32.11991882324219)), 'calf_l': Vector((1.8110084533691406, 1.1467314958572388, -30.26090431213379)), 'foot_l': Vector((0.8927364349365234, -19.544645309448242, -1.1506776809692383)), 'thigh_r': Vector((-3.586806297302246, -0.9371740818023682, -32.120018005371094)), 'calf_r': Vector((-1.8110074996948242, 1.1467384099960327, -30.261043548583984)), 'foot_r': Vector((-0.8925895690917969, -19.54458999633789, -1.1506767272949219))}
File diff suppressed because it is too large Load Diff