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
+11 -66
View File
@@ -1,70 +1,15 @@
[3.73.41]
[3.76.15]
New/improved:
Compatibility: Blender 4.3: FBX export
Export: New "Actions Linker" menu, to link an action to another one. Useful if exporting RigA animation, that depends on RigB through constraints (such as two characters holding each other's hands).\nIf animations are interdependent, it is necessary to declare these links for correct animation export.
Export: New "One File per Actions List" setting, when enabling both "As Multiple Files" and "Actions Manager". This allows to export one file per Action List, including all actions in the list.
Export: Godot: New setting to comply with Godot Root Motion axes
Export Godot: Removed the "Root Motion" setting that bakes the c_traj animation to armature object, not relevant for Godot Root Motion.
Export: Actions Lists: Filtered actions in the actions selector, so that actions that are already part of the current list are removed from the search list
Export: GLTF: Exposed the "Start from frame 0" setting, in order to force the first frame of each action to be zero if enabled
Export: UE: Removed the extra orientation tweaks applied to the spine bones when exporting with Mannequin axes. It was offsetting the angles by a few degrees to improve the match in case of straight spine bones, but can be misleading. It is best to align the spine bones manually. It can still be enabled in the "Legacy" menu though.
Export: Actions Manager: New "Batch" feature to add multiple actions at once to a list, with filtering
Export: UI: Added the "Rename Bones from File" checkbox in the properties panel too. Was confusing to have it only in the menu located in file export browser
Export: Avoid operator when removing shape keys for faster performances
Export: FBX: New "Force Shape Keys Keying" setting. If disabled (default), non-driven or non-animated shape keys are now exported as static, without animation data
Rig: Spline IK: Changed "Parent External Bone to" setting, was limited to the tip bone before. Now applies to all bones of the chain, and to Deforming or Twist bones instead of controllers. Can still be parented to controllers if necessary, by directly parenting Spline reference bones to them.
Rig: Spline IK: New "Add Tail Bone" option. This adds an extra bone at the tip of the chain, that is not part of the main IK chain. Useful when needing to parent other limb to the tip, that must not be stretched or rotated with the main chain, such as rigging a spine for quadrupeds.
Rig: Spline IK: New "Preserve Shape" option, to preserve the current shape when changing the amount of the spline bones.
Rig: Spine: New "Preserve Shape" option, to preserve the current spine shape when changing the amount of bones
Rig: Leg: New Limb Option to skip the foot IK offset alignment when Match to Rig, allowing custom positioning
Rig: Spine: New "Parent Fallback" parameter in Limb Options, to set the default spine parent in case the root reference bone is not parented. By default parented to "c_traj". No parent if set to None.
Rig: Tail: New "Preserve Shape" option, to preserve the current shape when changing the amount of the tail bones
Rig: Spine, Tail, Spline IK: Removed "Update Transform" option, was confusing. Now, automatically update the bones transforms if the count was changed, using grid align or preserve shape if enabled.
Rig: New Bulge setting for arm Joint Fans, allowing to add extra bulge deformation when the fans are spreading/folding. The created constraint can be manually edited afterwards.
Rig: Apply Pose as Rest Pose: New "Apply Deformed Shape Keys" toggle, can be useful to speed up baking time in case deformations are not affecting shape keys
Rig: New Bulge setting for leg Joints Fans too
Rig: New Ankle Joints Fans
Rig: Leg: New "Foot Roll Break" option. If enabled, the foot will start rotating from the ball, then from the tip toes, when moving the foot roll cursor. Settings can be found in "Rig Main Properties".
Skin: Warning message in case of negative mesh scale when binding
Picker: The picker camera is now set automatically when clicking "Add Picker"
Picker: Support reverse spine bones
Rig Tools: New "Extract Root Motion" function that tracks and bakes the "c_traj" controller to the pelvis position, while maintaining it on the floor, and preserving the rig animation
Rig Tools: Extract Root Motion now supports Location Z with initial offset applied
Rig Tools: Improved Extract Root Motion performances, around 4x faster (before: 24 sec for 400 frames, after: 6 sec)
Rig Tools: Extract Root Motion: Added X and Y offsets
Remap: Added "Extract Root Motion" as an option when retargetting
Smart: Facial features are now togglable: Eyebrows, Eyes, Ears, Nose, Lips, Tongue, Teeth, Chin
Smart: Auto filter mesh objects when clicking "Get Selected Objects". This avoids throwing an error if non-mesh objects are selected.
Smart: UI: Moved the "Fingers" checkbox in the header of the Fingers tab to minimize redundancy
Version: Now supports the Blender 5 API, while maintaining backward-compatibility with older Blender versions. Rig, Remap, Export and Picker modules updated
Export: New "Reset Transforms" setting to reset controllers transforms between each actions when baking. Useful if some bones are not keyframes, avoiding persistent offsets shared across actions.
Rig: Eyebrows: Support of arbitrary amount of eyebrow bones
Rig: Eyebrows: Preserve shape when changing amount
Rig: Tongue: Support arbitrary amount of tongue bones
Remap: New "Bake Only Existing Keyframes" setting
Fixed:
Rig: The horse rig was containing unwanted actions data leftovers
Rig: Removed the "body_mid" collections coming from old armature presets
Rig: Facial: Error when disabling the mouth bones
Rig: Bottom bones had accidentally their Deform property turned off
Rig: Apply Pose as Rest Pose crashing in case of numerous shape keys
Rig: The Tail limb could have incorrect parent. No uses the default parent_retarget function
Rig: The Dog preset had a "Connected" second spine bone, leading ot issue
Rig: Thigh Joint Fans incorrectly parented to 3 Bones Leg Type 2
Rig: Custom shapes of reversed spine controllers could be offset
Rig: Kilt: When changing the amount of bones, the Z axis could be incorrectly oriented. Now calculates the curve normal vectors to align Z axes on.
Rig: Changed the arm, leg Stretch-Length minimum value to 0.1
Rig: Fixed the new Ankle Joint Fans bulge, was not evaluating the foot angle correctly, new helper foot bone added
Rig: Apply Pose as Rest Pose: Arm and leg stretch controller was not supported
Rig: Error when setting up secondary bones color group in pre-Blender 4 versions
Rig: Disabled "Extrapolate" from the c_foot_heel bone and cleared out of range mapping to avoid accidental rotation flip
Skin: Error when restoring current vertices weights if a temp object was deleted (Apply Shape Keys)
Export: Skinned objects having same name as export skeleton were clashing. It now throws a warning before exporting.
Export: Add batch actions to Action Manager could generate an error if an action was removed from file
Export: GLTF: Disabled the "export_apply" argument from the actual export, was preventing shape keys to export. Modifiers are pre-applied by AutoRigPro functions anyway, so no changes on the user side.
Export: Error when exporting actions list containing removed actions
Export: The exported skeleton was incorrectly named with an "_arpbob" suffix when the rig export name was the same as the armature name
Export: Apply Shape Keys was failing if the basis shape key was not named "Basis". Now, always refer to the shape key at index 0 as Basis
Export: Humanoid: Invalid fingers export when exporting more than 2 arms
Export: Shape keys could not apply and export correctly under certain circumstances, was missing "from_mix" set to false
Export: GLTF: Apply Modifiers was failing
Export: Displace modifiers were not scaled when exporting with Units x100
Rig Tools: Extract Root Motion quat_prev error
Smart: The arm/leg reference bones roll was not calculated. Was expecting the Match to Rig function to do it, but now that "Auto IK Roll" can be disabled, the roll must be aligned
Remap: Added further checks to evaluate if the source and target armature are really Armature objects, to avoid user error
QuickRig Compatibility: "Auto IK Roll" not applied in limb options
Rig: Lips corners mini not removed when disable the whole head
Export: Kilt: Master controller was not exported anymore
Unknown compatibility breakage: flush() function not recognized, likely due to a custom python environment
@@ -15,15 +15,13 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ***** END GPL LICENCE BLOCK *****
import os, shutil, bpy
# ***** END GPL LICENCE BLOCK *****
bl_info = {
"name": "Auto-Rig Pro",
"author": "Artell",
"version": (3, 73, 41),
"version": (3, 76, 15),
"blender": (4, 2, 0),
"location": "3D View > Properties> Auto-Rig Pro",
"description": "Automatic rig generation based on reference bones and various tools",
@@ -33,7 +31,7 @@ bl_info = {
}
import bpy
import bpy, shutil
from bpy.app.handlers import persistent
from .src import auto_rig_prefs
from .src import rig_functions
@@ -49,14 +47,22 @@ from .src import utils
# gltf export specials
if bpy.app.version >= (4, 4, 0):
from .src.lib.animation import get_action_slot_idx
class glTF2ExportUserExtension:
export_action_only = ''
def __init__(self):
self.action = None
self.base_action = None
self.base_action_slot_idx = 0
def gather_actions_hook(self, blender_object, params, export_settings):
def gather_actions_hook(self, blender_object, params, export_settings):
# This hook collects exportable baked actions
# Filter actions
# Only filter ARP rigs
if not 'arp_rig_name' in blender_object:
@@ -65,6 +71,7 @@ class glTF2ExportUserExtension:
# convert string list with fancy separators to list
export_actions_names = []
sep = '|%%|'
print('export_action_only', self.export_action_only)
if sep in self.export_action_only:
for actname in self.export_action_only.split(sep):
export_actions_names.append(actname)
@@ -73,50 +80,103 @@ class glTF2ExportUserExtension:
print('Actions:', export_actions_names)
# collection actions
act_list = []
for act in params.blender_actions:
if len(act.keys()):
if "arp_baked_action" in act.keys():
if self.export_action_only == 'all_actions':# all
act_list.append(act)
elif self.export_action_only == act.name:# single action export
act_list.append(act)
elif len(export_actions_names):# actions list export
if act.name in export_actions_names:
act_list = []
if bpy.app.version >= (4, 4, 0):
# With version 4.4.0 and higher, params is an object that contains all needed data
for action_id in list(params.actions.keys()):
for act_id, act in enumerate(params.actions[action_id][:]):
if "arp_baked_action" in act.action.keys():
if self.export_action_only == 'all_actions':
pass # Do nothing, all actions are exported
elif len(export_actions_names) == 0 and self.export_action_only == params.actions[action_id][act_id].action.name:# single action
pass
elif len(export_actions_names) and params.actions[action_id][act_id].action.name in export_actions_names:# multiple actions
pass # Do nothing, this action is exported
else:
# We are going to remove this action from the list
params.actions[action_id].remove(act)
else:
params.actions[action_id].remove(act)
else:
# With version previous to 4.4.0, params has 3 fields: blender_actions, blender_tracks, action_on_type
for act in params.blender_actions:
if len(act.keys()):
if "arp_baked_action" in act.keys():
if self.export_action_only == 'all_actions':# all
act_list.append(act)
elif self.export_action_only == act.name:# single action export
act_list.append(act)
elif len(export_actions_names):# actions list export
if act.name in export_actions_names:
act_list.append(act)
params.blender_actions = act_list
for (k, v) in params.blender_tracks.items():
print('k', k)
print('v', v)
params.blender_tracks = {k:v for (k, v) in params.blender_tracks.items() if k in [act.name for act in params.blender_actions]}
params.action_on_type = {k:v for (k, v) in params.action_on_type.items() if k in [act.name for act in params.blender_actions]}
def animation_switch_loop_hook(self, blender_object, post, export_settings):
# Before looping on actions to export
# Store used action of original rig
# This hook ensures that the original rig active action is conserved after exporting, since it is switched during the export
# Store active action of original rig before looping through actions
if 'arp_rig_name' in blender_object and post is False:
original_rig = bpy.data.objects[blender_object['arp_rig_name']]
if original_rig.animation_data and original_rig.animation_data.action:
self.action = original_rig.animation_data.action
# save action
self.base_action = original_rig.animation_data.action
# save slot
if bpy.app.version >= (4,4,0):# backward-compatibility
self.base_action_slot_idx = get_action_slot_idx(self.base_action, original_rig.animation_data.action_slot)
# Restore initial action of the original rig
# After looping on actions to export
if 'arp_rig_name' in blender_object and post is True:
original_rig = bpy.data.objects[blender_object['arp_rig_name']]
if original_rig.animation_data:
original_rig.animation_data.action = self.action
self.action = None
# assign action
original_rig.animation_data.action = self.base_action
# assign slot
if bpy.app.version >= (4, 4, 0):
original_rig.animation_data.action_slot = self.base_action.slots[self.base_action_slot_idx]
self.base_action = None
self.base_action_slot_idx = 0
def post_animation_switch_hook(self, blender_object, blender_action, track_name, on_type, export_settings):
# When switching the exported rig, also switch the original rig (same action + "_%temp")
def post_animation_switch_hook(self, *args, **kwargs):
# This hook is necessary for shape keys export
# (if shape keys are driven by bones, then contained in the action data)
# When switching the exported rig action, make sure to also switch the action of the original rig
# the original action name and slot index are now stored in the 'arp_baked_action' property located on the baked action:
# ["arp_baked_action"] = action.name+'|||'+str(slot_idx)
# to be fetched with:
# act_name, slot_idx = blender_action["arp_baked_action"].split('|||')
if bpy.app.version >= (4, 4, 0):
blender_object, blender_action, slot, track_name, on_type, export_settings = args
else:
blender_object, blender_action, track_name, on_type, export_settings = args
if 'arp_rig_name' in blender_object:
original_rig = bpy.data.objects[blender_object['arp_rig_name']]
if original_rig.animation_data:
original_rig.animation_data.action = bpy.data.actions[blender_action.name + "_%temp"]
act_name, slot_idx = blender_action["arp_baked_action"].split('|||')
# assign action
original_rig.animation_data.action = bpy.data.actions[act_name]
# assign slot
if bpy.app.version >= (4, 4, 0):
original_rig.animation_data.action_slot = \
original_rig.animation_data.action.slots[int(slot_idx)]
def menu_func_export(self, context):
@@ -1,6 +1,6 @@
schema_version = "1.0.0"
id = "auto_rig_pro"
version = "3.73.41"
version = "3.76.15"
name = "Auto-Rig Pro"
tagline = "Automatic rig generation based on reference bones and various tools"
maintainer = "Artell <elartblender@gmail.com>"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,870 @@
c_foot_ik.l%False%RELATIVE_CHAIN%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Ankle_L
False
True
c_leg_pole.l
c_toes_ik.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Toes_L
False
False
c_foot_ik.r%False%RELATIVE_CHAIN%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Ankle_R
False
True
c_leg_pole.r
c_toes_ik.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Toes_R
False
False
c_shoulder.l%True%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Scapula_L
False
False
c_hand_fk.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Wrist_L
False
False
c_arm_fk.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Shoulder_L
False
False
c_forearm_fk.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Elbow_L
False
False
c_shoulder.r%True%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Scapula_R
False
False
c_hand_fk.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Wrist_R
False
False
c_arm_fk.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Shoulder_R
False
False
c_forearm_fk.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Elbow_R
False
False
c_root_master.x%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Root_M
True
False
c_spine_01.x%True%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
RootPart1_M
False
False
c_spine_02.x%True%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
RootPart2_M
False
False
c_spine_03.x%True%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Spine1_M
False
False
c_spine_04.x%True%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Spine1Part1_M
False
False
c_spine_05.x%True%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Spine1Part2_M
False
False
c_spine_06.x%True%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Chest_M
False
False
c_subneck_1.x%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Neck_M
False
False
c_subneck_2.x%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
NeckPart1_M
False
False
c_neck.x%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
NeckPart2_M
False
False
c_head.x%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Head_M
False
False
c_thumb1.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
ThumbFinger1_L
False
False
c_thumb2.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
ThumbFinger2_L
False
False
c_thumb3.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
ThumbFinger3_L
False
False
c_index1.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
IndexFinger1_L
False
False
c_index2.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
IndexFinger2_L
False
False
c_index3.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
IndexFinger3_L
False
False
c_middle1.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
MiddleFinger1_L
False
False
c_middle2.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
MiddleFinger2_L
False
False
c_middle3.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
MiddleFinger3_L
False
False
c_ring1.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
RingFinger1_L
False
False
c_ring2.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
RingFinger2_L
False
False
c_ring3.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
RingFinger3_L
False
False
c_pinky1.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
PinkyFinger1_L
False
False
c_pinky2.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
PinkyFinger2_L
False
False
c_pinky3.l%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
PinkyFinger3_L
False
False
c_thumb1.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
ThumbFinger1_R
False
False
c_thumb2.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
ThumbFinger2_R
False
False
c_thumb3.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
ThumbFinger3_R
False
False
c_index1.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
IndexFinger1_R
False
False
c_index2.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
IndexFinger2_R
False
False
c_index3.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
IndexFinger3_R
False
False
c_middle1.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
MiddleFinger1_R
False
False
c_middle2.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
MiddleFinger2_R
False
False
c_middle3.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
MiddleFinger3_R
False
False
c_ring1.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
RingFinger1_R
False
False
c_ring2.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
RingFinger2_R
False
False
c_ring3.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
RingFinger3_R
False
False
c_pinky1.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
PinkyFinger1_R
False
False
c_pinky2.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
PinkyFinger2_R
False
False
c_pinky3.r%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
PinkyFinger3_R
False
False
MiddleFinger4_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
MiddleFinger4_L
False
True
Cup_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Cup_L
False
True
PinkyFinger4_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
PinkyFinger4_L
False
True
RingFinger4_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
RingFinger4_L
False
True
IndexFinger4_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
IndexFinger4_L
False
True
ThumbFinger4_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
ThumbFinger4_L
False
True
MiddleFinger4_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
MiddleFinger4_R
False
True
Cup_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Cup_R
False
True
PinkyFinger4_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
PinkyFinger4_R
False
True
RingFinger4_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
RingFinger4_R
False
True
IndexFinger4_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
IndexFinger4_R
False
True
ThumbFinger4_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
ThumbFinger4_R
False
True
HipPart2_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
HipPart2_L
False
True
HipPart2_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
HipPart2_R
False
True
ShoulderPart2_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
ShoulderPart2_L
False
True
ShoulderPart2_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
ShoulderPart2_R
False
True
FaceJoint_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
FaceJoint_M
False
True
EyeJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
EyeJoint_R
False
True
IrisJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
IrisJoint_R
False
True
PupilJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
PupilJoint_R
False
True
EyeJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
EyeJoint_L
False
True
IrisJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
IrisJoint_L
False
True
PupilJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
PupilJoint_L
False
True
upperLidMain0_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain0_R
False
True
upperLidMain1_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain1_R
False
True
upperLidMain2_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain2_R
False
True
upperLidMain3_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain3_R
False
True
upperLidMain4_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain4_R
False
True
upperLidMain5_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain5_R
False
True
upperLidMain6_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain6_R
False
True
upperLidMain7_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain7_R
False
True
upperLidMain8_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain8_R
False
True
upperLidMain9_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain9_R
False
True
upperLidMain10_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain10_R
False
True
upperLidMain11_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain11_R
False
True
upperLidMain12_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain12_R
False
True
upperLidMain13_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain13_R
False
True
upperLidMain14_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain14_R
False
True
upperLidBaseJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidBaseJoint_R
False
True
lowerLidMain1_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain1_R
False
True
lowerLidMain2_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain2_R
False
True
lowerLidMain3_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain3_R
False
True
lowerLidMain4_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain4_R
False
True
lowerLidMain5_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain5_R
False
True
lowerLidMain6_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain6_R
False
True
lowerLidMain7_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain7_R
False
True
lowerLidMain8_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain8_R
False
True
lowerLidMain9_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain9_R
False
True
lowerLidMain10_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain10_R
False
True
lowerLidMain11_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain11_R
False
True
lowerLidMain12_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain12_R
False
True
lowerLidMain13_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain13_R
False
True
upperLidMain0_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain0_L
False
True
upperLidMain1_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain1_L
False
True
upperLidMain2_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain2_L
False
True
upperLidMain3_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain3_L
False
True
upperLidMain4_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain4_L
False
True
upperLidMain5_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain5_L
False
True
upperLidMain6_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain6_L
False
True
upperLidMain7_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain7_L
False
True
upperLidMain8_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain8_L
False
True
upperLidMain9_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain9_L
False
True
upperLidMain10_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain10_L
False
True
upperLidMain11_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain11_L
False
True
upperLidMain12_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain12_L
False
True
upperLidMain13_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain13_L
False
True
upperLidMain14_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidMain14_L
False
True
upperLidBaseJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLidBaseJoint_L
False
True
lowerLidMain1_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain1_L
False
True
lowerLidMain2_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain2_L
False
True
lowerLidMain3_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain3_L
False
True
lowerLidMain4_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain4_L
False
True
lowerLidMain5_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain5_L
False
True
lowerLidMain6_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain6_L
False
True
lowerLidMain7_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain7_L
False
True
lowerLidMain8_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain8_L
False
True
lowerLidMain9_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain9_L
False
True
lowerLidMain10_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain10_L
False
True
lowerLidMain11_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain11_L
False
True
lowerLidMain12_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain12_L
False
True
lowerLidMain13_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLidMain13_L
False
True
EyeBrowInnerJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
EyeBrowInnerJoint_R
False
True
EyeBrowOuterJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
EyeBrowOuterJoint_R
False
True
EyeBrowCenterJoint_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
EyeBrowCenterJoint_M
False
True
EyeBrowInnerJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
EyeBrowInnerJoint_L
False
True
EyeBrowOuterJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
EyeBrowOuterJoint_L
False
True
LipJoints_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
LipJoints_M
False
True
upperLipJoint0_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLipJoint0_M
False
True
upperLipJoint1_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLipJoint1_R
False
True
upperLipJoint1_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLipJoint1_L
False
True
upperLipJoint2_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLipJoint2_R
False
True
upperLipJoint2_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLipJoint2_L
False
True
upperLipJoint3_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLipJoint3_R
False
True
upperLipJoint3_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperLipJoint3_L
False
True
LipJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
LipJoint_R
False
True
LipJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
LipJoint_L
False
True
lowerLipJoint0_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLipJoint0_M
False
True
lowerLipJoint1_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLipJoint1_R
False
True
lowerLipJoint1_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLipJoint1_L
False
True
lowerLipJoint2_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLipJoint2_R
False
True
lowerLipJoint2_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLipJoint2_L
False
True
lowerLipJoint3_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLipJoint3_R
False
True
lowerLipJoint3_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerLipJoint3_L
False
True
SmileBulgeJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
SmileBulgeJoint_R
False
True
FrownBulgeJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
FrownBulgeJoint_R
False
True
SmileBulgeJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
SmileBulgeJoint_L
False
True
FrownBulgeJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
FrownBulgeJoint_L
False
True
CheekRaiserJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
CheekRaiserJoint_R
False
True
CheekRaiserJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
CheekRaiserJoint_L
False
True
CheekJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
CheekJoint_R
False
True
CheekJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
CheekJoint_L
False
True
JawJoint_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
JawJoint_M
False
True
upperTeethJoint_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
upperTeethJoint_M
False
True
lowerTeethJoint_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
lowerTeethJoint_M
False
True
NoseJoint_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
NoseJoint_M
False
True
NoseCornerJoint_R%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
NoseCornerJoint_R
False
True
NoseCornerJoint_L%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
NoseCornerJoint_L
False
True
Tongue0Joint_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Tongue0Joint_M
False
True
Tongue1Joint_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Tongue1Joint_M
False
True
Tongue2Joint_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Tongue2Joint_M
False
True
Tongue3Joint_M%False%ABSOLUTE%0.0,0.0,0.0%0.0,0.0,0.0%1.0%False%False%Y%
Tongue3Joint_M
False
True
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