diff --git a/python-scripts/gt_auto_biped_rigger.py b/python-scripts/gt_auto_biped_rigger.py
index 6c0cb9a3..0c9940b2 100644
--- a/python-scripts/gt_auto_biped_rigger.py
+++ b/python-scripts/gt_auto_biped_rigger.py
@@ -155,6 +155,9 @@
1.7.16 - 2021-11-08
Big update to custom rig interface
+
+ 1.7.17 - 2021-11-08
+ Fixed an issue where IK fingers wouldn't follow the correct prefered angle
TODO:
Make scale system and breathing system optional
@@ -193,7 +196,7 @@
script_name = 'GT Auto Biped Rigger'
# Version:
-script_version = '1.7.16'
+script_version = '1.7.17'
# Python Version
python_version = sys.version_info.major
@@ -5946,7 +5949,7 @@ def lock_hide_default_attr(obj, translate=True, rotate=True, scale=True, visibil
for index in range(len(ik_chain)):
change_viewport_color(ik_chain[index][0], ik_jnt_color)
cmds.setAttr(ik_chain[index][0] + '.radius', (cmds.getAttr(ik_chain[index][0] + '.radius')*.5))
- cmds.setAttr(ik_chain[index][0] + '.preferredAngleZ', -0.01)
+ cmds.setAttr(ik_chain[index][0] + '.preferredAngleZ', -30)
if index == 0:
cmds.parent(ik_chain[index][0], left_ik_wrist_switch)
ik_chain_start = ik_chain[index][0]
@@ -6421,7 +6424,7 @@ def lock_hide_default_attr(obj, translate=True, rotate=True, scale=True, visibil
for index in range(len(ik_chain)):
change_viewport_color(ik_chain[index][0], ik_jnt_color)
cmds.setAttr(ik_chain[index][0] + '.radius', (cmds.getAttr(ik_chain[index][0] + '.radius')*.5))
- cmds.setAttr(ik_chain[index][0] + '.preferredAngleZ', -0.01)
+ cmds.setAttr(ik_chain[index][0] + '.preferredAngleZ', -30)
if index == 0:
cmds.parent(ik_chain[index][0], right_ik_wrist_switch)
ik_chain_start = ik_chain[index][0]
@@ -9291,7 +9294,7 @@ def add_rig_interface_button():
Create a button for a custom rig interface to the current shelf. It contains seamless FK/IK swtichers and pose management tools.
'''
- create_shelf_button("\"\"\"\n Custom Rig Interface for GT Auto Biped Rigger.\n @Guilherme Trevisan - TrevisanGMW@gmail.com - 2021-01-05\n github.com/TrevisanGMW/gt-tools\n\n 1.0 - 2021-01-05\n Initial Release\n\n 1.1 - 2021-05-11\n Made script compatible with Python 3.0 (Maya 2022)\n\n 1.2 - 2021-10-28\n Added mirror IK functions\n Added reset pose function\n Changed it to accept namespaces with or without \":\"\n \n 1.3 - 2021-10-29\n Changed the name from \"Seamless IK/FK Switch\" to \"Custom Rig Interface\"\n Added functions to mirror and reset FK controls\n Added center controls to reset pose function\n Added custom rig name (if not empty, it will display a message describing unique rig target)\n Added system to get and set persistent settings to store the namespace input\n Added warning message reminding user to check their namespace in case elements are not found\n \n 1.3.1 - 2021-11-01\n Changed versioning system to semantic to account for patches\n Fixed some typos in the \"locked\" message for when trying to mirror\n Added scale mirroring functions (fixes finger abduction pose)\n Included curl controls in the mirroring list\n \n 1.3.2 - 2021-11-03\n Added IK fingers to mirroring functions\n \n 1.3.3 - 2021-11-04\n Added animation import/export functions. (\".anim\" with \".json\" data)\n Changed the pose file extension to \".pose\" instead of \".json\" to avoid confusion\n Added animation mirroring functions\n \n 1.3.4 - 2021-11-08\n Add settings menu\n Made UI aware of FK/IK state\n Recreated part of the UI to use Tabs\n Improved switch functions with a mechanism to auto create keyframes (sparse or bake)\n Added inView feedback explaining switch information\n Fixed issue where the arm pole vector wouldn't mirror properly\n Added option to reset persistent settings\n \n TODO:\n Convert GUI to QT\n Extract keyframe tangents\n Add Flip options to animation and pose (instead of just mirror)\n Add Namespace picker (button to the right of the namespace textfield)\n Option to save pose thumbnail when exporting it \n Option to show or not feedback messages\n \n\"\"\"\ntry:\n from shiboken2 import wrapInstance\nexcept ImportError:\n from shiboken import wrapInstance\n \ntry:\n from PySide2.QtGui import QIcon\n from PySide2.QtWidgets import QWidget\nexcept ImportError:\n from PySide.QtGui import QIcon, QWidget\n\nfrom maya import OpenMayaUI as omui\nimport maya.cmds as cmds\nimport random\nimport json\nimport copy\nimport os\n\n\n# Script Name\nscript_name = 'GT Custom Rig Interface'\nunique_rig = '' # If provided, it will be used in the window title\n\n# Version:\nscript_version = \"1.3.4\"\n\n# Python Version\npython_version = sys.version_info.major\n\n# FK/IK Swticher Elements\nleft_arm_seamless_dict = { 'switch_ctrl' : 'left_arm_switch_ctrl', # Switch Ctrl\n 'end_ik_ctrl' : 'left_wrist_ik_ctrl', # IK Elements\n 'pvec_ik_ctrl' : 'left_elbow_ik_ctrl',\n 'base_ik_jnt' : 'left_shoulder_ik_jnt',\n 'mid_ik_jnt' : 'left_elbow_ik_jnt',\n 'end_ik_jnt' : 'left_wrist_ik_jnt',\n 'base_fk_ctrl' : 'left_shoulder_ctrl', # FK Elements\n 'mid_fk_ctrl' : 'left_elbow_ctrl',\n 'end_fk_ctrl' : 'left_wrist_ctrl' ,\n 'base_fk_jnt' : 'left_shoulder_fk_jnt',\n 'mid_fk_jnt' : 'left_elbow_fk_jnt',\n 'end_fk_jnt' : 'left_wrist_fk_jnt',\n 'mid_ik_reference' : 'left_elbowSwitch_loc',\n 'end_ik_reference' : ''\n }\n\nright_arm_seamless_dict = { 'switch_ctrl' : 'right_arm_switch_ctrl', # Switch Ctrl\n 'end_ik_ctrl' : 'right_wrist_ik_ctrl', # IK Elements\n 'pvec_ik_ctrl' : 'right_elbow_ik_ctrl',\n 'base_ik_jnt' : 'right_shoulder_ik_jnt',\n 'mid_ik_jnt' : 'right_elbow_ik_jnt',\n 'end_ik_jnt' : 'right_wrist_ik_jnt',\n 'base_fk_ctrl' : 'right_shoulder_ctrl', # FK Elements\n 'mid_fk_ctrl' : 'right_elbow_ctrl',\n 'end_fk_ctrl' : 'right_wrist_ctrl' ,\n 'base_fk_jnt' : 'right_shoulder_fk_jnt',\n 'mid_fk_jnt' : 'right_elbow_fk_jnt',\n 'end_fk_jnt' : 'right_wrist_fk_jnt',\n 'mid_ik_reference' : 'right_elbowSwitch_loc',\n 'end_ik_reference' : ''\n }\n \nleft_leg_seamless_dict = { 'switch_ctrl' : 'left_leg_switch_ctrl', # Switch Ctrl\n 'end_ik_ctrl' : 'left_foot_ik_ctrl', # IK Elements\n 'pvec_ik_ctrl' : 'left_knee_ik_ctrl',\n 'base_ik_jnt' : 'left_hip_ik_jnt',\n 'mid_ik_jnt' : 'left_knee_ik_jnt',\n 'end_ik_jnt' : 'left_ankle_ik_jnt',\n 'base_fk_ctrl' : 'left_hip_ctrl', # FK Elements\n 'mid_fk_ctrl' : 'left_knee_ctrl',\n 'end_fk_ctrl' : 'left_ankle_ctrl' ,\n 'base_fk_jnt' : 'left_hip_fk_jnt',\n 'mid_fk_jnt' : 'left_knee_fk_jnt',\n 'end_fk_jnt' : 'left_ankle_fk_jnt',\n 'mid_ik_reference' : 'left_kneeSwitch_loc',\n 'end_ik_reference' : 'left_ankleSwitch_loc'\n }\n \nright_leg_seamless_dict = { 'switch_ctrl' : 'right_leg_switch_ctrl', # Switch Ctrl\n 'end_ik_ctrl' : 'right_foot_ik_ctrl', # IK Elements\n 'pvec_ik_ctrl' : 'right_knee_ik_ctrl',\n 'base_ik_jnt' : 'right_hip_ik_jnt',\n 'mid_ik_jnt' : 'right_knee_ik_jnt',\n 'end_ik_jnt' : 'right_ankle_ik_jnt',\n 'base_fk_ctrl' : 'right_hip_ctrl', # FK Elements\n 'mid_fk_ctrl' : 'right_knee_ctrl',\n 'end_fk_ctrl' : 'right_ankle_ctrl' ,\n 'base_fk_jnt' : 'right_hip_fk_jnt',\n 'mid_fk_jnt' : 'right_knee_fk_jnt',\n 'end_fk_jnt' : 'right_ankle_fk_jnt',\n 'mid_ik_reference' : 'right_kneeSwitch_loc',\n 'end_ik_reference' : 'right_ankleSwitch_loc'\n }\n \n# Mirror Elements\nnamespace_separator = ':'\nleft_prefix = 'left'\nright_prefix = 'right'\nnot_inverted = (False, False, False)\ninvert_x = (True, False, False)\ninvert_y = (False, True, False)\ninvert_z = (False, False, True)\ninvert_yz = (False, True, True)\ninvert_all = (True, True, True)\n\n# Dictionary Pattern:\n# Key: Control name (if not in the center, remove prefix)\n# Value: A list with two tuples. [(Is Translate XYZ inverted?), (Is Rotate XYZ inverted?), Is mirroring scale?]\n# Value Example: '_fingers_ctrl': [not_inverted, not_inverted, True] = Not inverting Translate XYZ. Not inverting Rotate XYZ. Yes, mirroring scale.\ngt_ab_general_ctrls = {# Fingers Automation\n '_fingers_ctrl': [not_inverted, not_inverted, True],\n '_thumbCurl_ctrl': [not_inverted, not_inverted],\n '_indexCurl_ctrl': [not_inverted, not_inverted],\n '_middleCurl_ctrl': [not_inverted, not_inverted],\n '_ringCurl_ctrl': [not_inverted, not_inverted],\n '_pinkyCurl_ctrl': [not_inverted, not_inverted],\n \n # Fingers FK\n '_thumb03_ctrl': [not_inverted, not_inverted],\n '_thumb02_ctrl': [not_inverted, not_inverted],\n '_thumb01_ctrl': [not_inverted, not_inverted],\n '_index01_ctrl': [not_inverted, not_inverted],\n '_middle02_ctrl': [not_inverted, not_inverted],\n '_middle01_ctrl': [not_inverted, not_inverted],\n '_index03_ctrl': [not_inverted, not_inverted],\n '_index02_ctrl': [not_inverted, not_inverted],\n '_ring03_ctrl': [not_inverted, not_inverted],\n '_ring02_ctrl': [not_inverted, not_inverted],\n '_ring01_ctrl': [not_inverted, not_inverted],\n '_middle03_ctrl': [not_inverted, not_inverted],\n '_pinky03_ctrl': [not_inverted, not_inverted],\n '_pinky02_ctrl': [not_inverted, not_inverted],\n '_pinky01_ctrl': [not_inverted, not_inverted],\n \n # Finger IK\n '_thumb_ik_ctrl': [invert_z, invert_x],\n '_index_ik_ctrl': [invert_z, invert_x],\n '_middle_ik_ctrl': [invert_z, invert_x],\n '_ring_ik_ctrl': [invert_z, invert_x],\n '_pinky_ik_ctrl': [invert_z, invert_x],\n # Clavicle\n '_clavicle_ctrl': [not_inverted, not_inverted],\n # Eyes\n '_eye_ctrl': [invert_x, not_inverted],\n } \n\ngt_ab_ik_ctrls = { # Arm\n '_elbow_ik_ctrl': [invert_x, not_inverted], \n '_wrist_ik_ctrl': [invert_all, not_inverted],\n # Leg\n '_heelRoll_ctrl': [invert_x, not_inverted],\n '_ballRoll_ctrl': [invert_x, not_inverted],\n '_toeRoll_ctrl': [invert_x, not_inverted],\n '_toe_upDown_ctrl': [invert_x, not_inverted],\n '_foot_ik_ctrl': [invert_x, invert_yz],\n '_knee_ik_ctrl': [invert_x, not_inverted],\n }\n \ngt_ab_fk_ctrls = {# Arm\n '_shoulder_ctrl': [invert_all, not_inverted],\n '_elbow_ctrl': [invert_all, not_inverted],\n '_wrist_ctrl': [invert_all, not_inverted],\n # Leg\n '_hip_ctrl': [invert_x, invert_yz],\n '_knee_ctrl': [invert_all, not_inverted],\n '_ankle_ctrl': [invert_all, not_inverted],\n '_ball_ctrl': [invert_all, not_inverted],\n }\n \ngt_ab_center_ctrls = ['cog_ctrl', \n 'hip_ctrl', \n 'spine01_ctrl', \n 'spine02_ctrl', \n 'spine03_ctrl', \n 'spine04_ctrl', \n 'cog_ribbon_ctrl', \n 'spine_ribbon_ctrl', \n 'chest_ribbon_ctrl',\n 'neckBase_ctrl',\n 'neckMid_ctrl',\n 'head_ctrl',\n 'jaw_ctrl',\n 'main_eye_ctrl',\n 'left_eye_ctrl',\n 'right_eye_ctrl',\n ] \n\ngt_ab_interface_settings = {\n 'namespace' : '',\n 'auto_key_switch' : True,\n 'auto_key_method_bake' : True,\n 'auto_key_start_frame' : 1,\n 'auto_key_end_frame' : 10,\n 'pose_export_thumbnail' : True,\n }\n \ngt_ab_interface_settings_default = copy.deepcopy(gt_ab_interface_settings)\n\n\n# Manage Persistent Settings\ndef get_persistent_settings_auto_biped_rig_interface():\n ''' \n Checks if persistant settings for GT Auto Biped Rig Interface exists and loads it if this is the case.\n It assumes that persistent settings were stored using the cmds.optionVar function.\n '''\n # Check if there is anything stored\n stored_setup_exists = cmds.optionVar(exists=(\"gt_auto_biped_rig_interface_setup\"))\n \n if stored_setup_exists:\n stored_settings = {}\n try:\n stored_settings = eval(str(cmds.optionVar(q=(\"gt_auto_biped_rig_interface_setup\"))))\n for stored_item in stored_settings:\n for item in gt_ab_interface_settings:\n if stored_item == item:\n gt_ab_interface_settings[item] = stored_settings.get(stored_item)\n except:\n print('Couldn\\'t load persistent settings, try resetting it in the help menu.')\n\n\ndef set_persistent_settings_auto_biped_rig_interface():\n ''' \n Stores persistant settings for GT Auto Biped Rig Interface.\n It converts the dictionary into a list for easy storage. (The get function converts it back to a dictionary)\n It assumes that persistent settings were stored using the cmds.optionVar function.\n '''\n cmds.optionVar( sv=('gt_auto_biped_rig_interface_setup', str(gt_ab_interface_settings)))\n\n\ndef reset_persistent_settings_auto_biped_rig_interface():\n ''' Resets persistant settings for GT Auto Biped Rig Interface '''\n cmds.optionVar( remove='gt_auto_biped_rig_interface_setup' )\n gt_ab_interface_settings = gt_ab_interface_settings_default\n cmds.optionVar( sv=('gt_auto_biped_rig_interface_setup', str(gt_ab_interface_settings_default)))\n cmds.warning('Persistent settings for ' + script_name + ' were cleared.')\n try:\n cmds.evalDeferred('build_gui_custom_rig_interface()')\n except:\n try:\n build_gui_custom_rig_interface()\n except:\n try:\n cmds.evalDeferred('gt_biped_rig_interface.build_gui_custom_rig_interface()')\n except:\n pass\n\n \n# Main Window ============================================================================\ndef build_gui_custom_rig_interface():\n rig_interface_window_name = 'build_gui_custom_rig_interface'\n if cmds.window(rig_interface_window_name, exists =True):\n cmds.deleteUI(rig_interface_window_name) \n\n # Main GUI Start Here =================================================================================\n def update_fk_ik_buttons():\n '''\n Updates the background color of the FK/IK buttons according to the value of the current influenceSwitch attribute.\n This attempts to make the UI \"aware\" of the current state of the controls.\n '''\n active_color = (.6,.6,.6)\n inactive_color = (.36,.36,.36)\n ctrl_btn_lists = [\n [right_arm_seamless_dict, right_arm_fk_btn, right_arm_ik_btn],\n [left_arm_seamless_dict, left_arm_fk_btn, left_arm_ik_btn],\n [right_leg_seamless_dict, right_leg_fk_btn, right_leg_ik_btn],\n [left_leg_seamless_dict, left_leg_fk_btn, left_leg_ik_btn]\n ]\n for ctrl_btns in ctrl_btn_lists:\n if cmds.objExists(gt_ab_interface_settings.get('namespace') + namespace_separator + ctrl_btns[0].get('switch_ctrl')):\n try:\n current_system = cmds.getAttr(gt_ab_interface_settings.get('namespace') + namespace_separator + ctrl_btns[0].get('switch_ctrl') + '.influenceSwitch')\n if current_system < 0.5:\n cmds.button(ctrl_btns[1], e=True, bgc=active_color) # FK Button\n cmds.button(ctrl_btns[2], e=True, bgc=inactive_color) # IK Button\n else:\n cmds.button(ctrl_btns[2], e=True, bgc=active_color) # FK Button\n cmds.button(ctrl_btns[1], e=True, bgc=inactive_color) # IK Button\n except:\n pass\n else:\n cmds.button(ctrl_btns[2], e=True, bgc=inactive_color) # FK Button\n cmds.button(ctrl_btns[1], e=True, bgc=inactive_color) # IK Button\n \n\n def update_stored_settings():\n '''\n Extracts the namespace used and stores it as a persistent variable\n This function also calls \"update_fk_ik_buttons()\" so it updates the UI\n '''\n gt_ab_interface_settings['namespace'] = cmds.textField(namespace_txt, q=True, text=True)\n gt_ab_interface_settings['auto_key_switch'] = cmds.checkBox(auto_key_switch_chk, q=True, value=True)\n gt_ab_interface_settings['auto_key_switch'] = cmds.checkBox(auto_key_switch_chk, q=True, value=True)\n gt_ab_interface_settings['auto_key_method_bake'] = cmds.radioButton(auto_key_method_rb1, query=True, select=True)\n gt_ab_interface_settings['auto_key_start_frame'] = cmds.intField(auto_key_start_int_field, q=True, value=0)\n gt_ab_interface_settings['auto_key_end_frame'] = cmds.intField(auto_key_end_int_field, q=True, value=0)\n\n\n if gt_ab_interface_settings.get('auto_key_switch'):\n cmds.radioButton(auto_key_method_rb1, e=True, en=True)\n cmds.radioButton(auto_key_method_rb2, e=True, en=True)\n cmds.rowColumnLayout(switch_range_column, e=True, en=True)\n else:\n cmds.radioButton(auto_key_method_rb1, e=True, en=False)\n cmds.radioButton(auto_key_method_rb2, e=True, en=False)\n cmds.rowColumnLayout(switch_range_column, e=True, en=False)\n \n set_persistent_settings_auto_biped_rig_interface()\n update_fk_ik_buttons()\n\n\n def update_switch(ik_fk_dict, direction='ik_to_fk', is_auto_switch=False):\n '''\n Runs the switch function using the parameters provided in the UI\n Also updates the UI to keep track of the FK/IK state.\n \n Parameters:\n ik_fk_dict (dict): A dicitionary containg the elements that are part of the system you want to switch\n direction (optinal, string): Either \"fk_to_ik\" or \"ik_to_fk\". It determines what is the source and what is the target.\n '''\n method = 'bake' if gt_ab_interface_settings.get('auto_key_method_bake') else 'sparse' \n \n if is_auto_switch:\n gt_ab_seamless_fk_ik_switch_auto(ik_fk_dict, \n namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator,\n keyframe=gt_ab_interface_settings.get('auto_key_switch'),\n start_time=int(gt_ab_interface_settings.get('auto_key_start_frame')), \n end_time=int(gt_ab_interface_settings.get('auto_key_end_frame')), \n method=method\n )\n\n else:\n gt_ab_seamless_fk_ik_switch(ik_fk_dict, \n direction, \n namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator,\n keyframe=gt_ab_interface_settings.get('auto_key_switch'),\n start_time=int(gt_ab_interface_settings.get('auto_key_start_frame')), \n end_time=int(gt_ab_interface_settings.get('auto_key_end_frame')), \n method=method\n )\n\n update_fk_ik_buttons()\n\n\n \n def get_auto_key_current_frame(target_integer_field='start'):\n '''\n Gets the current frame and auto fills an integer field.\n \n Parameters:\n target_integer_field (optional, string) : Gets the current timeline frame and feeds it into the start or end integer field.\n Can only be \"start\" or \"end\". Anything else will be understood as \"end\".\n \n '''\n current_time = cmds.currentTime(q=True)\n if target_integer_field == 'start':\n cmds.intField(auto_key_start_int_field, e=True, value=current_time)\n else:\n cmds.intField(auto_key_end_int_field, e=True, value=current_time)\n update_stored_settings()\n \n \n def mirror_fk_ik_pose(source_side='right'):\n '''\n Runs a full pose mirror function.\n \n Parameters:\n source_side (optinal, string): Either \"right\" or \"left\". It determines what is the source and what is the target of the mirror.\n '''\n \n gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_ik_ctrls, gt_ab_fk_ctrls], source_side, namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator)\n \n def build_custom_help_window(input_text, help_title=''):\n ''' \n Creates a help window to display the provided text\n\n Parameters:\n input_text (string): Text used as help, this is displayed in a scroll fields.\n help_title (optinal, string)\n '''\n window_name = help_title.replace(\" \",\"_\").replace(\"-\",\"_\").lower().strip() + \"_help_window\"\n if cmds.window(window_name, exists=True):\n cmds.deleteUI(window_name, window=True)\n\n cmds.window(window_name, title= help_title + \" Help\", mnb=False, mxb=False, s=True)\n cmds.window(window_name, e=True, s=True, wh=[1,1])\n\n main_column = cmds.columnLayout(p= window_name)\n \n # Title Text\n cmds.separator(h=12, style='none') # Empty Space\n cmds.rowColumnLayout(nc=1, cw=[(1, 310)], cs=[(1, 10)], p=main_column) # Window Size Adjustment\n cmds.rowColumnLayout(nc=1, cw=[(1, 300)], cs=[(1, 10)], p=main_column) # Title Column\n cmds.text(help_title + ' Help', bgc=(.4, .4, .4), fn='boldLabelFont', align='center')\n cmds.separator(h=10, style='none', p=main_column) # Empty Space\n\n # Body ==================== \n cmds.rowColumnLayout(nc=1, cw=[(1, 300)], cs=[(1,10)], p=main_column)\n \n help_scroll_field = cmds.scrollField(editable=False, wordWrap=True, fn='smallPlainLabelFont')\n \n cmds.scrollField(help_scroll_field, e=True, ip=0, it=input_text)\n cmds.scrollField(help_scroll_field, e=True, ip=1, it='') # Bring Back to the Top\n \n # Close Button \n cmds.rowColumnLayout(nc=1, cw=[(1, 300)], cs=[(1,10)], p=main_column)\n cmds.separator(h=10, style='none')\n cmds.button(l='OK', h=30, c=lambda args: close_help_gui())\n cmds.separator(h=8, style='none')\n \n # Show and Lock Window\n cmds.showWindow(window_name)\n cmds.window(window_name, e=True, s=False)\n \n # Set Window Icon\n qw = omui.MQtUtil.findWindow(window_name)\n if python_version == 3:\n widget = wrapInstance(int(qw), QWidget)\n else:\n widget = wrapInstance(long(qw), QWidget)\n icon = QIcon(':/question.png')\n widget.setWindowIcon(icon)\n \n def close_help_gui():\n ''' Closes help windows '''\n if cmds.window(window_name, exists=True):\n cmds.deleteUI(window_name, window=True)\n # Custom Help Dialog Ends Here =================================================================================\n \n # Retrieve Persistent Settings\n get_persistent_settings_auto_biped_rig_interface()\n\n # Build UI\n script_title = script_name\n if unique_rig != '':\n script_title = 'GT - Rig Interface for ' + unique_rig\n \n build_gui_custom_rig_interface = cmds.window(rig_interface_window_name, title=script_title + ' (v' + script_version + ')',\\\n titleBar=True, mnb=False, mxb=False, sizeable =True)\n\n cmds.window(rig_interface_window_name, e=True, s=True, wh=[1,1])\n\n content_main = cmds.columnLayout(adj = True)\n\n # Title Text\n title_bgc_color = (.4, .4, .4)\n cmds.separator(h=10, style='none') # Empty Space\n cmds.rowColumnLayout(nc=1, cw=[(1, 270)], cs=[(1, 10)], p=content_main) # Window Size Adjustment\n cmds.rowColumnLayout(nc=3, cw=[(1, 10), (2, 200), (3, 50)], cs=[(1, 10), (2, 0), (3, 0)], p=content_main) # Title Column\n cmds.text(\" \", bgc=title_bgc_color) # Tiny Empty Green Space\n cmds.text(script_title, bgc=title_bgc_color, fn=\"boldLabelFont\", align=\"left\")\n cmds.button( l =\"Help\", bgc=title_bgc_color, c=lambda x:open_gt_tools_documentation())\n cmds.separator(h=5, style='none') # Empty Space\n \n # Body ====================\n body_column = cmds.rowColumnLayout(nc=1, cw=[(1, 260)], cs=[(1,10)], p=content_main)\n \n cmds.text('Namespace:')\n namespace_txt = cmds.textField(text=gt_ab_interface_settings.get('namespace'), pht='Namespace:: (Optional)', cc=lambda x:update_stored_settings())\n \n cmds.separator(h=10, style='none') # Empty Space\n \n form = cmds.formLayout()\n tabs = cmds.tabLayout(innerMarginWidth=5, innerMarginHeight=5)\n cmds.formLayout(form, edit=True, attachForm=((tabs, 'top', 0), (tabs, 'left', 0), (tabs, 'bottom', 0), (tabs, 'right', 0)) )\n\n ############# FK/IK Switch Tab #############\n btn_margin = 5\n fk_ik_switch_tab = cmds.rowColumnLayout(nc=1, cw=[(1, 246)], cs=[(1,0)], p=tabs)\n\n fk_ik_btn_width = 59\n cw_fk_ik_states = [(1, fk_ik_btn_width),(2, fk_ik_btn_width),(3, fk_ik_btn_width),(4, fk_ik_btn_width)]\n cs_fk_ik_states = [(1,2), (2,2), (3,3), (4,2)]\n \n switch_btn_width = 120\n cw_fk_ik_switches = [(1, switch_btn_width),(2, switch_btn_width)]\n cs_fk_ik_switches = [(1,2), (2,3)]\n \n arms_text = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=fk_ik_switch_tab)\n cmds.separator(h=2, style='none') # Empty Space\n cmds.separator(h=2, style='none') # Empty Space\n cmds.text('Right Arm:', p=arms_text) #R\n cmds.text('Left Arm:', p=arms_text) #L\n \n arms_switch_state_column = cmds.rowColumnLayout(nc=4, cw=cw_fk_ik_states, cs=cs_fk_ik_states, p=fk_ik_switch_tab)\n right_arm_fk_btn = cmds.button(l =\"FK\", c=lambda x:update_switch(right_arm_seamless_dict, 'ik_to_fk'), p=arms_switch_state_column) #R\n right_arm_ik_btn = cmds.button(l =\"IK\", c=lambda x:update_switch(right_arm_seamless_dict, 'fk_to_ik'), p=arms_switch_state_column) #L\n left_arm_fk_btn = cmds.button(l =\"FK\", c=lambda x:update_switch(left_arm_seamless_dict, 'ik_to_fk'), p=arms_switch_state_column) #R\n left_arm_ik_btn = cmds.button(l =\"IK\", c=lambda x:update_switch(left_arm_seamless_dict, 'fk_to_ik'), p=arms_switch_state_column) #L\n \n arms_switch_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=fk_ik_switch_tab)\n cmds.button(l =\"Switch\", c=lambda x:update_switch(right_arm_seamless_dict, is_auto_switch=True), p=arms_switch_column) #R\n cmds.button(l =\"Switch\", c=lambda x:update_switch(left_arm_seamless_dict, is_auto_switch=True), p=arms_switch_column) #L\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.text('Right Leg:', p=arms_switch_column) #R\n cmds.text('Left Leg:', p=arms_switch_column) #L\n\n legs_switch_state_column = cmds.rowColumnLayout(nc=4, cw=cw_fk_ik_states, cs=cs_fk_ik_states, p=fk_ik_switch_tab)\n right_leg_fk_btn = cmds.button(l =\"FK\", c=lambda x:update_switch(right_leg_seamless_dict, 'ik_to_fk'), p=legs_switch_state_column) #R\n right_leg_ik_btn = cmds.button(l =\"IK\", c=lambda x:update_switch(right_leg_seamless_dict, 'fk_to_ik'), p=legs_switch_state_column) #L\n left_leg_fk_btn = cmds.button(l =\"FK\", c=lambda x:update_switch(left_arm_seamless_dict, 'ik_to_fk'), p=legs_switch_state_column) #R\n left_leg_ik_btn = cmds.button(l =\"IK\", c=lambda x:update_switch(left_arm_seamless_dict, 'fk_to_ik'), p=legs_switch_state_column) #L\n \n legs_switch_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=fk_ik_switch_tab)\n cmds.button(l =\"Switch\", c=lambda x:update_switch(right_leg_seamless_dict, is_auto_switch=True), p=legs_switch_column) #R\n cmds.button(l =\"Switch\", c=lambda x:update_switch(left_leg_seamless_dict, is_auto_switch=True), p=legs_switch_column) #L\n \n # Auto Key Settings (Switch Settings)\n switch_settings_column = cmds.rowColumnLayout(nc=1, cw=[(1, 245)], cs=[(1, 6)], p=fk_ik_switch_tab)\n cmds.separator(h=15) # Empty Space\n switch_auto_key_column = cmds.rowColumnLayout(nc=3, cw=[(1, 80),(2, 130),(3, 60)], cs=[(1, 25)], p=fk_ik_switch_tab)\n auto_key_switch_chk = cmds.checkBox( label='Auto Key', value=gt_ab_interface_settings.get('auto_key_switch'), cc=lambda x:update_stored_settings())\n \n method_container = cmds.rowColumnLayout( p=switch_auto_key_column, numberOfRows=1)\n auto_key_method_rc = cmds.radioCollection()\n auto_key_method_rb1 = cmds.radioButton( p=method_container, label=' Bake ', sl=gt_ab_interface_settings.get('auto_key_method_bake'), cc=lambda x:update_stored_settings())\n auto_key_method_rb2 = cmds.radioButton( p=method_container, label=' Sparse ', sl=(not gt_ab_interface_settings.get('auto_key_method_bake')), cc=lambda x:update_stored_settings())\n cmds.separator(h=5, style='none', p=fk_ik_switch_tab) # Empty Space\n \n switch_range_column = cmds.rowColumnLayout(nc=6, cw=[(1, 40),(2, 40),(3, 30),(4, 30),(5, 40),(6, 30)], cs=[(1, 10), (4, 10)], p=fk_ik_switch_tab)\n cmds.text('Start:', p=switch_range_column)\n auto_key_start_int_field = cmds.intField(value=int(gt_ab_interface_settings.get('auto_key_start_frame')), p=switch_range_column, cc=lambda x:update_stored_settings())\n cmds.button(l =\"Get\", c=lambda x:get_auto_key_current_frame(), p=switch_range_column, h=5) #L\n cmds.text('End:', p=switch_range_column)\n auto_key_end_int_field = cmds.intField(value=int(gt_ab_interface_settings.get('auto_key_end_frame')),p=switch_range_column, cc=lambda x:update_stored_settings())\n cmds.button(l =\"Get\", c=lambda x:get_auto_key_current_frame('end'), p=switch_range_column, h=5) #L\n cmds.separator(h=10, style='none', p=fk_ik_switch_tab) # Empty Space\n \n\n ############# Pose Management Tab #############\n pose_management_tab = cmds.rowColumnLayout(nc=1, cw=[(1, 246)], cs=[(1,0)], p=tabs)\n\n btn_margin = 2\n \n cmds.separator(h=5, style='none') # Empty Space\n pose_title_column = cmds.rowColumnLayout(nc=1, cw=[(1, 245)], cs=cs_fk_ik_switches, p=pose_management_tab)\n cmds.text('Mirror Pose:', p=pose_title_column)\n cmds.separator(h=5, style='none', p=pose_title_column) # Empty Space\n \n \n mirror_pose_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=pose_management_tab)\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n \n cmds.text('Right to Left:', p=mirror_pose_column) #R\n cmds.text('Left to Right:', p=mirror_pose_column) #L\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n \n cmds.button(l =\"Mirror ->\", c=lambda x:mirror_fk_ik_pose('right'), p=mirror_pose_column) #R\n cmds.button(l =\"<- Mirror\", c=lambda x:mirror_fk_ik_pose('left'), p=mirror_pose_column) #L\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n \n pose_mirror_ik_fk_column = cmds.rowColumnLayout(nc=4, cw=cw_fk_ik_states, cs=cs_fk_ik_states, p=pose_management_tab)\n \n # IK Pose Mirror\n cmds.button(l =\"IK Only >\", c=lambda x:gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_ik_ctrls], 'right', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=pose_mirror_ik_fk_column) #R\n cmds.button(l =\"FK Only >\", c=lambda x:gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_fk_ctrls], 'right', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=pose_mirror_ik_fk_column) #R\n \n \n # FK Pose Mirror\n cmds.button(l =\"< IK Only\", c=lambda x:gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_ik_ctrls], 'left', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=pose_mirror_ik_fk_column) #L\n cmds.button(l =\"< FK Only\", c=lambda x:gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_fk_ctrls], 'left', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=pose_mirror_ik_fk_column) #L\n \n\n # Reset Pose\n pose_management_column = cmds.rowColumnLayout(nc=1, cw=[(1, 245)], cs=cs_fk_ik_switches, p=pose_management_tab)\n cmds.separator(h=15, style='none', p=pose_management_column) # Empty Space\n cmds.text('Reset Pose:', p=pose_management_column) #R\n cmds.separator(h=btn_margin, style='none', p=pose_management_column) # Empty Space\n cmds.button(l =\"Reset Back to Default Pose\", c=lambda x:gt_ab_reset_pose(gt_ab_ik_ctrls, gt_ab_fk_ctrls, gt_ab_center_ctrls, namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=pose_management_column)\n\n # Export Import Pose\n cmds.separator(h=btn_margin, style='none', p=pose_management_column) # Empty Space\n cmds.separator(h=15, style='none', p=pose_management_column) # Empty Space\n cmds.text('Import/Export Poses:', p=pose_management_column) \n \n import_export_pose_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=pose_management_tab)\n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.button(l =\"Import Current Pose\", c=lambda x:gt_import_rig_pose(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=import_export_pose_column)\n cmds.button(l =\"Export Current Pose\", c=lambda x:gt_export_rig_pose(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=import_export_pose_column) \n\n\n ############# Animation Management Tab #############\n \n anim_management_tab = cmds.rowColumnLayout(nc=1, cw=[(1, 246)], cs=[(1,0)], p=tabs)\n \n cmds.separator(h=5, style='none') # Empty Space\n anim_title_column = cmds.rowColumnLayout(nc=1, cw=[(1, 245)], cs=cs_fk_ik_switches, p=anim_management_tab)\n cmds.text('Mirror Animation:', p=anim_title_column)\n cmds.separator(h=5, style='none', p=anim_title_column) # Empty Space\n \n mirror_anim_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=anim_management_tab)\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n \n cmds.text('Right to Left:', p=mirror_anim_column) #R\n cmds.text('Left to Right:', p=mirror_anim_column) #L\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n \n cmds.button(l =\"Mirror ->\", c=lambda x:gt_ab_mirror_anim([gt_ab_general_ctrls, gt_ab_ik_ctrls, gt_ab_fk_ctrls], 'right', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=mirror_anim_column) #R\n cmds.button(l =\"<- Mirror\", c=lambda x:gt_ab_mirror_anim([gt_ab_general_ctrls, gt_ab_ik_ctrls, gt_ab_fk_ctrls], 'left', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=mirror_anim_column) #L\n \n # Reset Animation\n anim_management_column = cmds.rowColumnLayout(nc=1, cw=[(1, 245)], cs=cs_fk_ik_switches, p=anim_management_tab)\n cmds.separator(h=15, style='none', p=anim_management_column) # Empty Space\n cmds.text('Reset Animation:', p=anim_management_column) #R\n cmds.separator(h=btn_margin, style='none', p=anim_management_column) # Empty Space\n cmds.button(l =\"Reset Animation (Delete Keyframes)\", c=lambda x:gt_reset_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=anim_management_column)\n cmds.separator(h=btn_margin, style='none', p=anim_management_column) # Empty Space\n cmds.button(l =\"Reset Animation and Pose\", c=lambda x:gt_reset_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=anim_management_column)\n \n\n # Export Import Pose\n cmds.separator(h=17, style='none', p=anim_management_column) # Empty Space\n cmds.text('Import/Export Animation:', p=anim_management_column) \n\n import_export_pose_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=anim_management_tab)\n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.button(l =\"Import Animation\", c=lambda x:gt_import_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=import_export_pose_column)\n cmds.button(l =\"Export Animation\", c=lambda x:gt_export_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=import_export_pose_column) \n \n\n ############# Settings Tab #############\n settings_tab = cmds.rowColumnLayout(nc=1, cw=[(1, 240)], cs=[(1,0)], p=tabs)\n # settings_column = cmds.rowColumnLayout(nc=1, cw=[(1, 240)], cs=[(1, 0)], p=settings_tab)\n \n # General Settings\n enabled_bgc_color = (.4, .4, .4)\n disabled_bgc_color = (.3,.3,.3)\n cmds.separator(h=5, style='none') # Empty Space\n cmds.text('General Settings:', font='boldLabelFont')\n cmds.separator(h=5, style='none') # Empty Space\n cmds.rowColumnLayout(nc=3, cw=[(1, 10), (2, 200), (3, 20)], cs=[(1,10)]) \n \n # Export Thumbnail With Pose\n is_option_enabled = False\n cmds.text(' ', bgc=(enabled_bgc_color if is_option_enabled else disabled_bgc_color), h=20) # Tiny Empty Spac\n # cmds.text(' ', bgc=disabled_color, h=20) # Tiny Empty Space\n cmds.checkBox( label=' Export Thumbnail with Pose', value=gt_ab_interface_settings.get('pose_export_thumbnail'), ebg=True, cc=lambda x:invert_stored_setting('pose_export_thumbnail'), en=is_option_enabled) \n\n export_pose_thumbnail_help_message = 'Exports a thumbnail \".jpg\" file together with your \".pose\" file.\\nThis extra thumbnail file can be used to quickly undestand what you pose looks like before importing it.\\n\\nThe thumbnail is a screenshot of you active viewport at the moment of exporting the pose. If necessary, export it again to generate another thumbnail.'\n export_pose_thumbnail_help_title = 'Export Thumbnail with Pose'\n cmds.button(l ='?', bgc=enabled_bgc_color, c=lambda x:build_custom_help_window(export_pose_thumbnail_help_message, export_pose_thumbnail_help_title))\n \n # Reset Persistent Settings\n cmds.separator(h=btn_margin, style='none', p=settings_tab) # Empty Space\n \n settings_buttons_column = cmds.rowColumnLayout(nc=1, cw=[(1, 240)], cs=[(1,10)], p=settings_tab) \n cmds.button(l =\"Reset Persistent Settings\", c=lambda x:reset_persistent_settings_auto_biped_rig_interface(), p=settings_buttons_column)\n \n \n \n ################# END TABS #################\n cmds.tabLayout( tabs, edit=True, tabLabel=((fk_ik_switch_tab, ' FK/IK '), (pose_management_tab, ' Pose '), (anim_management_tab, 'Animation'), (settings_tab, ' Settings ')))\n # Outside Margin\n cmds.separator(h=10, style='none', p=content_main) # Empty Space\n \n # Show and Lock Window\n cmds.showWindow(build_gui_custom_rig_interface)\n cmds.window(rig_interface_window_name, e=True, s=False)\n \n # Set Window Icon\n qw = omui.MQtUtil.findWindow(rig_interface_window_name)\n if python_version == 3:\n widget = wrapInstance(int(qw), QWidget)\n else:\n widget = wrapInstance(long(qw), QWidget)\n icon = QIcon(':/ikSCsolver.svg')\n widget.setWindowIcon(icon)\n\n # Update FK/IK States and Settings for the first run time\n update_fk_ik_buttons()\n update_stored_settings()\n\n # Remove the focus from the textfield and give it to the window\n cmds.setFocus(rig_interface_window_name)\n\n # Main GUI Ends Here =================================================================================\n \n\n\ndef gt_ab_seamless_fk_ik_switch(ik_fk_dict, direction='fk_to_ik', namespace='', keyframe=False, start_time=0, end_time=0, method='sparse'):\n '''\n Transfer the position of the FK to IK or IK to FK systems in a seamless way, so the animator can easily switch between one and the other\n \n Parameters:\n ik_fk_dict (dict): A dicitionary containg the elements that are part of the system you want to switch\n direction (optinal, string): Either \"fk_to_ik\" or \"ik_to_fk\". It determines what is the source and what is the target.\n namespace (optinal, string): In case the rig has a namespace, it will be used to properly select the controls.\n \n \n keyframe (optinal, bool): If active it will created a keyframe at the current frame, move to the\n start_time (optinal, int): Where to create the first keyframe\n end_time (optinal, int): Where to create the last keyframe\n method (optinal, string): Method used for creating the keyframes. Either 'sparse' or 'bake'.\n '''\n def switch(match_only=False):\n '''\n Performs the switch operation.\n Commands were wrapped into a function to be used during the bake operation.\n \n Parameters:\n match_only (optional, bool) If active (True) it will only match the pose, but not switch\n \n Returns:\n attr_value (float): Value which the influence attribute was set to. Either 1 (fk_to_ik) or 0 (ik_to_fk).\n This value is returned only if \"match_only\" is False. Otherwise, expect None.\n '''\n try:\n ik_fk_ns_dict = {}\n for obj in ik_fk_dict:\n ik_fk_ns_dict[obj] = namespace + ik_fk_dict.get(obj)\n \n fk_pairs = [[ik_fk_ns_dict.get('base_ik_jnt'), ik_fk_ns_dict.get('base_fk_ctrl')],\n [ik_fk_ns_dict.get('mid_ik_jnt'), ik_fk_ns_dict.get('mid_fk_ctrl')],\n [ik_fk_ns_dict.get('end_ik_jnt'), ik_fk_ns_dict.get('end_fk_ctrl')]] \n \n if direction == 'fk_to_ik':\n if ik_fk_dict.get('end_ik_reference') != '':\n cmds.matchTransform(ik_fk_ns_dict.get('end_ik_ctrl'), ik_fk_ns_dict.get('end_ik_reference'), pos=1, rot=1)\n else:\n cmds.matchTransform(ik_fk_ns_dict.get('end_ik_ctrl'), ik_fk_ns_dict.get('end_fk_jnt'), pos=1, rot=1)\n \n cmds.matchTransform(ik_fk_ns_dict.get('pvec_ik_ctrl'), ik_fk_ns_dict.get('mid_ik_reference'), pos=1, rot=1)\n if not match_only:\n cmds.setAttr(ik_fk_ns_dict.get('switch_ctrl') + '.influenceSwitch', 1)\n return 1\n if direction == 'ik_to_fk':\n for pair in fk_pairs:\n cmds.matchTransform(pair[1], pair[0], pos=1, rot=1)\n if not match_only:\n cmds.setAttr(ik_fk_ns_dict.get('switch_ctrl') + '.influenceSwitch', 0)\n return 0\n except Exception as e:\n cmds.warning('An error occurred. Please check if a namespace is necessary or if a control was deleted. Error: ' + str(e))\n \n \n def print_inview_feedback():\n '''\n Prints feedback using inView messages so the user knows what operation was executed.\n '''\n \n\n # namespace='', keyframe=False, start_time=0, end_time=0, method='sparse'\n \n is_valid_message = True\n message_target = 'IK' if direction == 'fk_to_ik' else 'FK'\n \n # Try to figure it out system:\n message_direction = ''\n pvec_ik_ctrl = ik_fk_dict.get(next(iter(ik_fk_dict)))\n if pvec_ik_ctrl.startswith('right_'):\n message_direction = 'right'\n elif pvec_ik_ctrl.startswith('left_'):\n message_direction = 'left'\n else:\n is_valid_message = False\n \n message_limb = ''\n if 'knee' in pvec_ik_ctrl:\n message_limb = 'leg'\n elif 'elbow' in pvec_ik_ctrl:\n message_limb = 'arm'\n else:\n is_valid_message = False\n \n message_range = ''\n if keyframe:\n message_range = '(Start: ' + str(start_time) + ' End: ' + str(end_time) + ' Method: ' + method.capitalize() + ' )'\n \n\n if is_valid_message:\n # Print Feedback\n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Switched ' + message_direction + ' ' + message_limb + ' to ' + message_target +' ' + message_range, pos='botLeft', fade=True, alpha=.9)\n \n\n\n # Find Available Controls\n available_ctrls = []\n\n for key in ik_fk_dict:\n if cmds.objExists(namespace + ik_fk_dict.get(key)):\n available_ctrls.append(ik_fk_dict.get(key))\n if cmds.objExists(namespace + key):\n available_ctrls.append(key)\n \n # No Controls were found\n if len(available_ctrls) == 0:\n is_valid=False\n cmds.warning('No controls were found. Make sure you are using the correct namespace.')\n\n else:\n if keyframe:\n if method.lower() == 'sparse': # Only Influence Switch\n original_time = cmds.currentTime(q=True)\n cmds.currentTime(start_time)\n cmds.setKeyframe(namespace + ik_fk_dict.get('switch_ctrl'), time=start_time, attribute='influenceSwitch')\n cmds.currentTime(end_time)\n switch()\n cmds.setKeyframe(namespace + ik_fk_dict.get('switch_ctrl'), time=end_time, attribute='influenceSwitch')\n cmds.currentTime(original_time)\n print_inview_feedback()\n elif method.lower() == 'bake':\n if start_time >= end_time:\n cmds.warning('Invalid range. Please review the stard and end frame and try again.')\n else:\n original_time = cmds.currentTime(q=True)\n cmds.currentTime(start_time)\n current_time = cmds.currentTime(q=True)\n cmds.setKeyframe(namespace + ik_fk_dict.get('switch_ctrl'), time=current_time, attribute='influenceSwitch') # Start Switch\n for index in range(end_time - start_time):\n cmds.currentTime(current_time)\n switch(match_only=True)\n if direction == 'fk_to_ik':\n for channel in ['t','r']:\n for dimension in ['x', 'y', 'z']:\n cmds.setKeyframe(namespace + ik_fk_dict.get('end_ik_ctrl'), time=current_time, attribute=channel+dimension) # Wrist IK Ctrl\n cmds.setKeyframe(namespace + ik_fk_dict.get('pvec_ik_ctrl'), time=current_time, attribute=channel+dimension) # PVec Elbow IK Ctrl\n\n if direction == 'ik_to_fk':\n for channel in ['t','r']:\n for dimension in ['x', 'y', 'z']:\n cmds.setKeyframe(namespace + ik_fk_dict.get('base_fk_ctrl'), time=current_time, attribute=channel+dimension) # Shoulder FK Ctrl\n cmds.setKeyframe(namespace + ik_fk_dict.get('end_fk_ctrl'), time=current_time, attribute=channel+dimension) # Wrist FK Ctrl\n cmds.setKeyframe(namespace + ik_fk_dict.get('mid_fk_ctrl'), time=current_time, attribute=channel+dimension) # Elbow FK Ctrl\n current_time += 1\n switch()\n cmds.setKeyframe(namespace + ik_fk_dict.get('switch_ctrl'), time=current_time, attribute='influenceSwitch') # End Switch\n cmds.currentTime(original_time)\n print_inview_feedback()\n else:\n cmds.warning('Invalid method was provided. Must be either \"sparse\" or \"bake\", but got ' + method)\n else:\n switch()\n print_inview_feedback()\n\ndef gt_ab_seamless_fk_ik_switch_auto(ik_fk_dict, namespace='', keyframe=False, start_time=0, end_time=0, method='sparse'):\n ''' \n Calls gt_ab_seamless_fk_ik_switch, but switches (toggles) between FK and IK based on the current influence number. \n It automatically checks the influenceSwitch value attribute and determines what direction to take it. \"0-0.5\":IK and \"0.5-1\":FK\n \n Parameters:\n ik_fk_dict (dictionary): A dicitionary containg the elements that are part of the system you want to switch\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n keyframe (optinal, bool): If active it will created a keyframe at the current frame, move to the\n start_time (optinal, int): Where to create the first keyframe\n end_time (optinal, int): Where to create the last keyframe\n method (optinal, string): Method used for creating the keyframes. Either 'sparse' or 'bake'. \n '''\n try:\n if cmds.objExists(namespace + ik_fk_dict.get('switch_ctrl')):\n current_system = cmds.getAttr(namespace + ik_fk_dict.get('switch_ctrl') + '.influenceSwitch')\n if current_system < 0.5:\n gt_ab_seamless_fk_ik_switch(ik_fk_dict, direction='fk_to_ik', namespace=namespace, keyframe=keyframe, start_time=start_time, end_time=end_time, method=method)\n else:\n gt_ab_seamless_fk_ik_switch(ik_fk_dict, direction='ik_to_fk', namespace=namespace, keyframe=keyframe, start_time=start_time, end_time=end_time, method=method)\n else:\n cmds.warning('Switch control was not found. Please check if a namespace is necessary.')\n except Exception as e:\n cmds.warning('An error occurred. Please check if a namespace is necessary. Error: ' + str(e))\n\n\ndef open_gt_tools_documentation():\n ''' Opens a web browser with the the auto rigger docs '''\n cmds.showHelp ('https://github.com/TrevisanGMW/gt-tools/tree/release/docs#-gt-auto-biped-rigger-', absolute=True) \n\n\ndef gt_ab_mirror_pose(gt_ab_ctrls, source_side, namespace=''):\n '''\n Mirrors the character pose from one side to the other\n\n Parameters:\n gt_ab_ctrls (dict) : A list of dictionaries of controls without their side prefix (e.g. \"_wrist_ctrl\")\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n '''\n # Merge Dictionaries\n gt_ab_ctrls_dict = {}\n for ctrl_dict in gt_ab_ctrls:\n gt_ab_ctrls_dict.update(ctrl_dict)\n \n # Find available Ctrls\n available_ctrls = []\n for obj in gt_ab_ctrls_dict:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n # Start Mirroring\n if len(available_ctrls) != 0:\n \n errors = []\n \n right_side_objects = []\n left_side_objects = []\n\n for obj in available_ctrls: \n if right_prefix in obj:\n right_side_objects.append(obj)\n \n for obj in available_ctrls: \n if left_prefix in obj:\n left_side_objects.append(obj)\n \n for left_obj in left_side_objects:\n for right_obj in right_side_objects:\n remove_side_tag_left = left_obj.replace(left_prefix,'')\n remove_side_tag_right = right_obj.replace(right_prefix,'')\n if remove_side_tag_left == remove_side_tag_right:\n # print(right_obj + ' was paired with ' + left_obj)\n \n key = gt_ab_ctrls_dict.get(remove_side_tag_right) # TR = [(ivnerted?,ivnerted?,ivnerted?),(ivnerted?,ivnerted?,ivnerted?)]\n transforms = []\n\n # Mirroring Transform?, Inverting it? (X,Y,Z), Transform name.\n transforms.append([True, key[0][0], 'tx']) \n transforms.append([True, key[0][1], 'ty'])\n transforms.append([True, key[0][2], 'tz'])\n transforms.append([True, key[1][0], 'rx'])\n transforms.append([True, key[1][1], 'ry'])\n transforms.append([True, key[1][2], 'rz'])\n \n if len(key) > 2: # Mirroring Scale?\n transforms.append([True, False, 'sx'])\n transforms.append([True, False, 'sy'])\n transforms.append([True, False, 'sz'])\n \n # Transfer Right to Left\n if source_side is 'right':\n for transform in transforms:\n if transform[0]: # Using Transform?\n if transform[1]: # Inverted?\n source_transform = (cmds.getAttr(namespace + right_obj + '.' + transform[2]) * -1)\n else:\n source_transform = cmds.getAttr(namespace + right_obj + '.' + transform[2])\n\n if not cmds.getAttr(namespace + left_obj + '.' + transform[2], lock=True):\n cmds.setAttr(namespace + left_obj + '.' + transform[2], source_transform)\n else:\n errors.append(namespace + left_obj + ' \"' + transform[2]+'\" is locked.' )\n \n # Transfer Left to Right\n if source_side is 'left':\n for transform in transforms:\n if transform[0]: # Using Transform?\n if transform[1]: # Inverted?\n source_transform = (cmds.getAttr(namespace + left_obj + '.' + transform[2]) * -1)\n else:\n source_transform = cmds.getAttr(namespace + left_obj + '.' + transform[2])\n \n if not cmds.getAttr(namespace + right_obj + '.' + transform[2], lock=True):\n cmds.setAttr(namespace + right_obj + '.' + transform[2], source_transform)\n else:\n errors.append(namespace + right_obj + ' \"' + transform[2]+'\" is locked.' )\n \n # Print Feedback\n unique_message = '<' + str(random.random()) + '>'\n source_message = '(Left to Right)'\n if source_side == 'right':\n source_message = '(Right to Left)'\n cmds.inViewMessage(amg=unique_message + 'Pose mirrored! ' + source_message, pos='botLeft', fade=True, alpha=.9)\n \n \n if len(errors) != 0:\n unique_message = '<' + str(random.random()) + '>'\n if len(errors) == 1:\n is_plural = 'attribute was'\n else:\n is_plural = 'attributes were'\n for error in errors:\n print(str(error))\n sys.stdout.write(str(len(errors)) + ' locked '+ is_plural + ' ignored. (Open Script Editor to see a list)\\n')\n else:\n cmds.warning('No controls were found. Please check if a namespace is necessary.')\n cmds.setFocus(\"MayaWindow\")\n \ndef gt_ab_reset_pose(gt_ab_ik_ctrls, gt_ab_fk_ctrls, gt_ab_center_ctrls, namespace=''):\n '''\n Reset transforms list of controls back to 0 Transalte and Rotate values. \n\n Parameters:\n gt_ab_ik_ctrls (dict, list) : A list or dictionary of IK controls without their side prefix (e.g. \"_wrist_ctrl\")\n gt_ab_fk_ctrls (dict, list) : A list or dictionary of FK controls without their side prefix (e.g. \"_wrist_ctrl\")\n gt_ab_center_ctrls (dict, list) : A list or dictionary of center controls (full names) (e.g. \"spine01_ctrl\")\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n '''\n available_ctrls = []\n for obj in gt_ab_ik_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_fk_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_general_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_center_ctrls:\n if cmds.objExists(namespace + obj):\n available_ctrls.append(obj)\n \n if len(available_ctrls) == 0:\n cmds.warning('No controls were found. Please check if a namespace is necessary.')\n else:\n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Pose Reset!', pos='botLeft', fade=True, alpha=.9)\n \n for ctrl in available_ctrls:\n dimensions = ['x','y','z']\n transforms = ['t', 'r', 's']\n for transform in transforms:\n for dimension in dimensions:\n try:\n if cmds.getAttr(namespace + ctrl + '.' + transform + dimension, lock=True) is False:\n cmds.setAttr(namespace + ctrl + '.' + transform + dimension, 0)\n except:\n pass\n \n # Special Cases\n special_case_ctrls = ['left_fingers_ctrl', 'right_fingers_ctrl']\n for ctrl in special_case_ctrls:\n if cmds.objExists(namespace + ctrl):\n if cmds.getAttr(namespace + ctrl + '.' + 'sz', lock=True) is False:\n cmds.setAttr(namespace + ctrl + '.' + 'sz', 2)\n\n\ndef gt_export_rig_pose(namespace =''):\n ''' \n Exports a Pose (JSON) file containing the translate, rotate and scale data from the rig controls (used to export a pose)\n Added a variable called \"gt_auto_biped_export_method\" after v1.3, so the extraction method can be stored.\n \n Parameters:\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n ''' \n # Validate Operation and Write file\n is_valid = True\n successfully_created_file = False\n\n # Find Available Controls\n available_ctrls = []\n for obj in gt_ab_ik_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_fk_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_general_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_center_ctrls:\n if cmds.objExists(namespace + obj):\n available_ctrls.append(obj)\n \n # No Controls were found\n if len(available_ctrls) == 0:\n is_valid=False\n cmds.warning('No controls were found. Make sure you are using the correct namespace.')\n\n\n if is_valid:\n file_name = cmds.fileDialog2(fileFilter=script_name + \" - POSE File (*.pose)\", dialogStyle=2, okCaption= 'Export', caption= 'Exporting Rig Pose for \"' + script_name + '\"') or []\n if len(file_name) > 0:\n pose_file = file_name[0]\n successfully_created_file = True\n \n\n if successfully_created_file and is_valid:\n export_dict = {'gt_interface_version' : script_version, 'gt_export_method' : 'object-space'}\n for obj in available_ctrls:\n translate = cmds.getAttr(obj + '.translate')[0]\n rotate = cmds.getAttr(obj + '.rotate')[0]\n scale = cmds.getAttr(obj + '.scale')[0]\n to_save = [obj, translate, rotate, scale]\n export_dict[obj] = to_save\n \n try: \n with open(pose_file, 'w') as outfile:\n json.dump(export_dict, outfile, indent=4)\n\n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Current Pose exported to ' + os.path.basename(file_name[0]) +'.', pos='botLeft', fade=True, alpha=.9)\n sys.stdout.write('Pose exported to the file \"' + pose_file + '\".')\n except Exception as e:\n print (e)\n successfully_created_file = False\n cmds.warning('Couldn\\'t write to file. Please make sure the exporting directory is accessible.')\n\n\ndef gt_import_rig_pose(debugging=False, debugging_path='', namespace=''):\n ''' \n Imports a POSE (JSON) file containing the translate, rotate and scale data for the rig controls (exported using the \"gt_export_rig_pose\" function)\n Uses the imported data to set the translate, rotate and scale position of every control curve\n \n Parameters:\n debugging (bool): If debugging, the function will attempt to auto load the file provided in the \"debugging_path\" parameter\n debugging_path (string): Debugging path for the import function\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n TODO\n Check import method to use the proper method when setting attributes.\n Exporting using the export button uses \"setAttr\", extract functions will use \"xform\" instead.\n \n ''' \n def set_unlocked_os_attr(target, attr, value):\n ''' \n Sets an attribute to the provided value in case it's not locked (Uses \"cmds.setAttr\" function so object space)\n \n Parameters:\n target (string): Name of the target object (object that will receive transforms)\n attr (string): Name of the attribute to apply (no need to add \".\", e.g. \"rx\" would be enough)\n value (float): Value used to set attribute. e.g. 1.5, 2, 5...\n \n '''\n try:\n if not cmds.getAttr(target + '.' + attr, lock=True):\n cmds.setAttr(target + '.' + attr, value)\n except:\n pass\n \n def set_unlocked_ws_attr(target, attr, value_tuple):\n ''' \n Sets an attribute to the provided value in case it's not locked (Uses \"cmds.xform\" function with world space)\n \n Parameters:\n target (string): Name of the target object (object that will receive transforms)\n attr (string): Name of the attribute to apply (no need to add \".\", e.g. \"rx\" would be enough)\n value_tuple (tuple): A tuple with three (3) floats used to set attributes. e.g. (1.5, 2, 5)\n \n '''\n try:\n if attr == 'translate':\n cmds.xform(target, ws=True, t=value_tuple)\n if attr == 'rotate':\n cmds.xform(target, ws=True, ro=value_tuple)\n if attr == 'scale':\n cmds.xform(target, ws=True, s=value_tuple)\n except:\n pass\n \n \n # Find Available Controls\n available_ctrls = []\n for obj in gt_ab_ik_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_fk_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_general_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_center_ctrls:\n if cmds.objExists(namespace + obj):\n available_ctrls.append(obj)\n \n # Track Current State\n import_version = 0.0\n import_method = 'object-space'\n \n if not debugging:\n file_name = cmds.fileDialog2(fileFilter=script_name + \" - POSE File (*.pose)\", dialogStyle=2, fileMode= 1, okCaption= 'Import', caption= 'Importing Proxy Pose for \"' + script_name + '\"') or []\n else:\n file_name = [debugging_path]\n \n if len(file_name) > 0:\n pose_file = file_name[0]\n file_exists = True\n else:\n file_exists = False\n \n if file_exists:\n try: \n with open(pose_file) as json_file:\n data = json.load(json_file)\n try:\n is_valid_file = True\n is_operation_valid = True\n\n if not data.get('gt_interface_version'):\n is_valid_file = False\n cmds.warning('Imported file doesn\\'t seem to be compatible or is missing data.')\n else: \n import_version = float(re.sub(\"[^0-9]\", \"\", str(data.get('gt_interface_version'))))\n \n if data.get('gt_export_method'):\n import_method = data.get('gt_export_method')\n \n if len(available_ctrls) == 0:\n cmds.warning('No controls were found. Please check if a namespace is necessary.')\n is_operation_valid = False\n \n if is_operation_valid:\n # Object-Space\n for ctrl in data:\n if ctrl != 'gt_interface_version' and ctrl != 'gt_export_method':\n curent_object = data.get(ctrl) # Name, T, R, S\n if cmds.objExists(namespace + curent_object[0]):\n set_unlocked_os_attr(namespace + curent_object[0], 'tx', curent_object[1][0])\n set_unlocked_os_attr(namespace + curent_object[0], 'ty', curent_object[1][1])\n set_unlocked_os_attr(namespace + curent_object[0], 'tz', curent_object[1][2])\n set_unlocked_os_attr(namespace + curent_object[0], 'rx', curent_object[2][0])\n set_unlocked_os_attr(namespace + curent_object[0], 'ry', curent_object[2][1])\n set_unlocked_os_attr(namespace + curent_object[0], 'rz', curent_object[2][2])\n set_unlocked_os_attr(namespace + curent_object[0], 'sx', curent_object[3][0])\n set_unlocked_os_attr(namespace + curent_object[0], 'sy', curent_object[3][1])\n set_unlocked_os_attr(namespace + curent_object[0], 'sz', curent_object[3][2])\n \n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Pose imported from ' + os.path.basename(pose_file) +'.', pos='botLeft', fade=True, alpha=.9)\n sys.stdout.write('Pose imported from the file \"' + pose_file + '\".')\n \n except Exception as e:\n print(e)\n cmds.warning('An error occured when importing the pose. Make sure you imported a valid POSE file.')\n except:\n file_exists = False\n cmds.warning('Couldn\\'t read the file. Please make sure the selected file is accessible.')\n\n\ndef gt_ab_mirror_anim(gt_ab_ctrls, source_side, namespace=''):\n '''\n Mirrors the character animation from one side to the other\n\n Parameters:\n gt_ab_ctrls (dict) : A list of dictionaries of controls without their side prefix (e.g. \"_wrist_ctrl\")\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n '''\n # Merge Dictionaries\n gt_ab_ctrls_dict = {}\n for ctrl_dict in gt_ab_ctrls:\n gt_ab_ctrls_dict.update(ctrl_dict)\n \n # Find available Ctrls\n available_ctrls = []\n for obj in gt_ab_ctrls_dict:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n # Start Mirroring\n if len(available_ctrls) != 0:\n \n errors = []\n \n right_side_objects = []\n left_side_objects = []\n\n for obj in available_ctrls: \n if right_prefix in obj:\n right_side_objects.append(obj)\n \n for obj in available_ctrls: \n if left_prefix in obj:\n left_side_objects.append(obj)\n \n for left_obj in left_side_objects:\n for right_obj in right_side_objects:\n remove_side_tag_left = left_obj.replace(left_prefix,'')\n remove_side_tag_right = right_obj.replace(right_prefix,'')\n if remove_side_tag_left == remove_side_tag_right:\n # print(right_obj + ' was paired with ' + left_obj)\n \n key = gt_ab_ctrls_dict.get(remove_side_tag_right) # TR = [(ivnerted?,ivnerted?,ivnerted?),(ivnerted?,ivnerted?,ivnerted?)]\n transforms = []\n\n # Mirroring Transform?, Inverting it? (X,Y,Z), Transform name.\n transforms.append([True, key[0][0], 'tx']) \n transforms.append([True, key[0][1], 'ty'])\n transforms.append([True, key[0][2], 'tz'])\n transforms.append([True, key[1][0], 'rx'])\n transforms.append([True, key[1][1], 'ry'])\n transforms.append([True, key[1][2], 'rz'])\n \n if len(key) > 2: # Mirroring Scale?\n transforms.append([True, False, 'sx'])\n transforms.append([True, False, 'sy'])\n transforms.append([True, False, 'sz'])\n \n # Transfer Right to Left \n if source_side is 'right':\n for transform in transforms:\n if transform[0]: # Using Transform?\n # Get Keys/Values\n frames = cmds.keyframe(namespace + right_obj, q=1, at=transform[2])\n values = cmds.keyframe(namespace + right_obj, q=1, at=transform[2], valueChange=True)\n \n if transform[1]: # Inverted?\n inverted_values = []\n for val in values:\n inverted_values.append(val* -1)\n values = inverted_values\n \n # Set Keys/Values\n for index in range(len(values)):\n cmds.setKeyframe(namespace + left_obj, time=frames[index], attribute=transform[2], value=values[index])\n\n # Other Attributes\n attributes = cmds.listAnimatable(namespace + right_obj)\n default_channels = ['translateX', 'translateY', 'translateZ', 'rotateX', 'rotateY', 'rotateZ']\n for attr in attributes:\n try:\n short_attr = attr.split('.')[-1]\n if short_attr not in default_channels:\n # Get Keys/Values\n frames = cmds.keyframe(namespace + right_obj, q=1, at=short_attr)\n values = cmds.keyframe(namespace + right_obj, q=1, at=short_attr, valueChange=True)\n\n # Set Keys/Values\n for index in range(len(values)):\n cmds.setKeyframe(namespace + left_obj, time=frames[index], attribute=short_attr, value=values[index])\n except:\n pass # 0 keyframes\n \n\n # Transfer Left to Right\n if source_side is 'left':\n for transform in transforms:\n if transform[0]: # Using Transform?\n \n try:\n # Get Keys/Values\n frames = cmds.keyframe(namespace + left_obj, q=1, at=transform[2])\n values = cmds.keyframe(namespace + left_obj, q=1, at=transform[2], valueChange=True)\n \n if transform[1]: # Inverted?\n inverted_values = []\n for val in values:\n inverted_values.append(val* -1)\n values = inverted_values\n \n # Set Keys/Values\n for index in range(len(values)):\n cmds.setKeyframe(namespace + right_obj, time=frames[index], attribute=transform[2], value=values[index])\n except:\n pass # 0 keyframes\n\n # Other Attributes\n attributes = cmds.listAnimatable(namespace + left_obj)\n default_channels = ['translateX', 'translateY', 'translateZ', 'rotateX', 'rotateY', 'rotateZ']\n for attr in attributes:\n try:\n short_attr = attr.split('.')[-1]\n if short_attr not in default_channels:\n # Get Keys/Values\n frames = cmds.keyframe(namespace + left_obj, q=1, at=short_attr)\n values = cmds.keyframe(namespace + left_obj, q=1, at=short_attr, valueChange=True)\n\n # Set Keys/Values\n for index in range(len(values)):\n cmds.setKeyframe(namespace + right_obj, time=frames[index], attribute=short_attr, value=values[index])\n except:\n pass # 0 keyframes\n\n # Print Feedback\n unique_message = '<' + str(random.random()) + '>'\n source_message = '(Left to Right)'\n if source_side == 'right':\n source_message = '(Right to Left)'\n cmds.inViewMessage(amg=unique_message + 'Animation mirrored! ' + source_message, pos='botLeft', fade=True, alpha=.9)\n \n if len(errors) != 0:\n unique_message = '<' + str(random.random()) + '>'\n if len(errors) == 1:\n is_plural = ' error '\n else:\n is_plural = ' errors '\n for error in errors:\n print(str(error))\n sys.stdout.write(str(len(errors)) + is_plural + 'occurred. (Open Script Editor to see a list)\\n')\n else:\n cmds.warning('No controls were found. Please check if a namespace is necessary.')\n cmds.setFocus(\"MayaWindow\")\n\n\n\ndef gt_export_rig_animation(namespace =''):\n ''' \n Exports an ANIM (JSON) file containing the translate, rotate and scale keyframe (animation) data from the rig controls.\n\n Parameters:\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n ''' \n # Validate Operation and Write file\n is_valid = True\n successfully_created_file = False\n\n # Find Available Controls\n available_ctrls = []\n for obj in gt_ab_ik_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_fk_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_general_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_center_ctrls:\n if cmds.objExists(namespace + obj):\n available_ctrls.append(obj)\n \n \n # No Controls were found\n if len(available_ctrls) == 0:\n is_valid=False\n cmds.warning('No controls were found. Make sure you are using the correct namespace.')\n\n\n if is_valid:\n file_name = cmds.fileDialog2(fileFilter=script_name + \" - ANIM File (*.anim)\", dialogStyle=2, okCaption= 'Export', caption= 'Exporting Rig Animation for \"' + script_name + '\"') or []\n if len(file_name) > 0:\n pose_file = file_name[0]\n successfully_created_file = True\n \n\n if successfully_created_file and is_valid:\n export_dict = {'gt_interface_version' : script_version, 'gt_export_method' : 'object-space'}\n \n # Extract Keyframes:\n for obj in available_ctrls:\n attributes = cmds.listAnimatable(namespace + obj)\n for attr in attributes:\n try:\n short_attr = attr.split('.')[-1]\n # all the time value\n frames = cmds.keyframe(namespace + obj, q=1, at=short_attr)\n # all the attribute value associated\n values = cmds.keyframe(namespace + obj, q=1, at=short_attr, valueChange=True)\n # store it in the dictionary with key 'objectName.attribute' and value\n export_dict['{}.{}'.format(obj, short_attr)]=zip(frames, values)\n except:\n pass # 0 keyframes\n\n try: \n with open(pose_file, 'w') as outfile:\n json.dump(export_dict, outfile, indent=4)\n \n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Current Animation exported to ' + os.path.basename(file_name[0]) +'.', pos='botLeft', fade=True, alpha=.9)\n sys.stdout.write('Animation exported to the file \"' + pose_file + '\".')\n except Exception as e:\n print (e)\n successfully_created_file = False\n cmds.warning('Couldn\\'t write to file. Please make sure the exporting directory is accessible.')\n\n\ndef gt_import_rig_animation(debugging=False, debugging_path='', namespace=''):\n ''' \n Imports an ANIM (JSON) file containing the translate, rotate and scale keyframe data for the rig controls (exported using the \"gt_export_rig_animation\" function)\n Uses the imported data to set the translate, rotate and scale position of every control curve\n \n Parameters:\n debugging (bool): If debugging, the function will attempt to auto load the file provided in the \"debugging_path\" parameter\n debugging_path (string): Debugging path for the import function\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls. \n ''' \n # Find Available Controls\n available_ctrls = []\n for obj in gt_ab_ik_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_fk_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_general_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_center_ctrls:\n if cmds.objExists(namespace + obj):\n available_ctrls.append(obj)\n \n # Track Current State\n import_version = 0.0\n import_method = 'object-space'\n \n if not debugging:\n file_name = cmds.fileDialog2(fileFilter=script_name + \" - ANIM File (*.anim)\", dialogStyle=2, fileMode= 1, okCaption= 'Import', caption= 'Importing Proxy Pose for \"' + script_name + '\"') or []\n else:\n file_name = [debugging_path]\n \n if len(file_name) > 0:\n anim_file = file_name[0]\n file_exists = True\n else:\n file_exists = False\n \n if file_exists:\n try: \n with open(anim_file) as json_file:\n data = json.load(json_file)\n try:\n is_valid_file = True\n is_operation_valid = True\n\n if not data.get('gt_interface_version'):\n is_valid_file = False\n cmds.warning('Imported file doesn\\'t seem to be compatible or is missing data.')\n else: \n import_version = float(re.sub(\"[^0-9]\", \"\", str(data.get('gt_interface_version'))))\n \n if data.get('gt_export_method'):\n import_method = data.get('gt_export_method')\n \n if len(available_ctrls) == 0:\n cmds.warning('No controls were found. Please check if a namespace is necessary.')\n is_operation_valid = False\n \n if is_operation_valid:\n # Object-Space\n for key, value in data.iteritems():\n if key != 'gt_interface_version' and key != 'gt_export_method':\n for time, attr_value in value:\n obj, attr = key.split('.')\n cmds.setKeyframe(namespace + obj, time=time, attribute=attr, value=attr_value)\n\n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Animation imported from ' + os.path.basename(anim_file) +'.', pos='botLeft', fade=True, alpha=.9)\n sys.stdout.write('Animation imported from the file \"' + anim_file + '\".')\n \n except Exception as e:\n print(e)\n cmds.warning('An error occured when importing the pose. Make sure you imported a valid ANIM file.')\n except:\n file_exists = False\n cmds.warning('Couldn\\'t read the file. Please make sure the selected file is accessible.')\n \n \ndef gt_reset_rig_animation(namespace=''):\n '''\n Deletes all keyframes and resets pose (Doesn't include Set Driven Keys)\n \n Parameters:\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls. \n ''' \n function_name = 'GT Reset Rig Animation'\n cmds.undoInfo(openChunk=True, chunkName=function_name)\n try:\n keys_ta = cmds.ls(type='animCurveTA')\n keys_tl = cmds.ls(type='animCurveTL')\n keys_tt = cmds.ls(type='animCurveTT')\n keys_tu = cmds.ls(type='animCurveTU')\n deleted_counter = 0\n all_keyframes = keys_ta + keys_tl + keys_tt + keys_tu\n for key in all_keyframes:\n try:\n key_target_namespace = cmds.listConnections(key, destination=True)[0].split(':')[0]\n if key_target_namespace == namespace.replace(':', '') or len(cmds.listConnections(key, destination=True)[0].split(':')) == 1:\n cmds.delete(key)\n deleted_counter += 1\n except:\n pass \n message = '' + str(deleted_counter) + ' '\n is_plural = 'keyframe nodes were'\n if deleted_counter == 1:\n is_plural = 'keyframe node was'\n message += is_plural + ' deleted.'\n \n cmds.inViewMessage(amg=message, pos='botLeft', fade=True, alpha=.9)\n \n # gt_ab_reset_pose(gt_ab_ik_ctrls, gt_ab_fk_ctrls, gt_ab_center_ctrls, namespace) # Add as an option?\n \n except Exception as e:\n cmds.warning(str(e))\n finally:\n cmds.undoInfo(closeChunk=True, chunkName=function_name)\n \n#Build UI\nif __name__ == '__main__':\n build_gui_custom_rig_interface()" ,
+ create_shelf_button("\"\"\"\n Custom Rig Interface for GT Auto Biped Rigger.\n @Guilherme Trevisan - TrevisanGMW@gmail.com - 2021-01-05\n github.com/TrevisanGMW/gt-tools\n\n 1.0 - 2021-01-05\n Initial Release\n\n 1.1 - 2021-05-11\n Made script compatible with Python 3.0 (Maya 2022)\n\n 1.2 - 2021-10-28\n Added mirror IK functions\n Added reset pose function\n Changed it to accept namespaces with or without \":\"\n \n 1.3 - 2021-10-29\n Changed the name from \"Seamless IK/FK Switch\" to \"Custom Rig Interface\"\n Added functions to mirror and reset FK controls\n Added center controls to reset pose function\n Added custom rig name (if not empty, it will display a message describing unique rig target)\n Added system to get and set persistent settings to store the namespace input\n Added warning message reminding user to check their namespace in case elements are not found\n \n 1.3.1 - 2021-11-01\n Changed versioning system to semantic to account for patches\n Fixed some typos in the \"locked\" message for when trying to mirror\n Added scale mirroring functions (fixes finger abduction pose)\n Included curl controls in the mirroring list\n \n 1.3.2 - 2021-11-03\n Added IK fingers to mirroring functions\n \n 1.3.3 - 2021-11-04\n Added animation import/export functions. (\".anim\" with \".json\" data)\n Changed the pose file extension to \".pose\" instead of \".json\" to avoid confusion\n Added animation mirroring functions\n \n 1.3.4 - 2021-11-08\n Add settings menu\n Made UI aware of FK/IK state\n Recreated part of the UI to use Tabs\n Improved switch functions with a mechanism to auto create keyframes (sparse or bake)\n Added inView feedback explaining switch information\n Fixed issue where the arm pole vector wouldn't mirror properly\n Added option to reset persistent settings\n \n 1.3.5 - 2021-11-10\n Added animation and pose reset\n Updates animation functions to account for tangents and other key properties\n \n TODO:\n Convert GUI to QT\n Add Flip options\n Overwrite keys for animation functions\n Add Namespace picker (button to the right of the namespace textfield)\n Option to save pose thumbnail when exporting it \n Add option to open multiple instances\n \n\"\"\"\ntry:\n from shiboken2 import wrapInstance\nexcept ImportError:\n from shiboken import wrapInstance\n \ntry:\n from PySide2.QtGui import QIcon\n from PySide2.QtWidgets import QWidget\nexcept ImportError:\n from PySide.QtGui import QIcon, QWidget\n\nfrom maya import OpenMayaUI as omui\nimport maya.cmds as cmds\nimport random\nimport json\nimport copy\nimport os\n\n\n# Script Name\nscript_name = 'GT Custom Rig Interface'\nunique_rig = '' # If provided, it will be used in the window title\n\n# Version:\nscript_version = \"1.3.5\"\n\n# Python Version\npython_version = sys.version_info.major\n\n# FK/IK Swticher Elements\nleft_arm_seamless_dict = { 'switch_ctrl' : 'left_arm_switch_ctrl', # Switch Ctrl\n 'end_ik_ctrl' : 'left_wrist_ik_ctrl', # IK Elements\n 'pvec_ik_ctrl' : 'left_elbow_ik_ctrl',\n 'base_ik_jnt' : 'left_shoulder_ik_jnt',\n 'mid_ik_jnt' : 'left_elbow_ik_jnt',\n 'end_ik_jnt' : 'left_wrist_ik_jnt',\n 'base_fk_ctrl' : 'left_shoulder_ctrl', # FK Elements\n 'mid_fk_ctrl' : 'left_elbow_ctrl',\n 'end_fk_ctrl' : 'left_wrist_ctrl' ,\n 'base_fk_jnt' : 'left_shoulder_fk_jnt',\n 'mid_fk_jnt' : 'left_elbow_fk_jnt',\n 'end_fk_jnt' : 'left_wrist_fk_jnt',\n 'mid_ik_reference' : 'left_elbowSwitch_loc',\n 'end_ik_reference' : ''\n }\n\nright_arm_seamless_dict = { 'switch_ctrl' : 'right_arm_switch_ctrl', # Switch Ctrl\n 'end_ik_ctrl' : 'right_wrist_ik_ctrl', # IK Elements\n 'pvec_ik_ctrl' : 'right_elbow_ik_ctrl',\n 'base_ik_jnt' : 'right_shoulder_ik_jnt',\n 'mid_ik_jnt' : 'right_elbow_ik_jnt',\n 'end_ik_jnt' : 'right_wrist_ik_jnt',\n 'base_fk_ctrl' : 'right_shoulder_ctrl', # FK Elements\n 'mid_fk_ctrl' : 'right_elbow_ctrl',\n 'end_fk_ctrl' : 'right_wrist_ctrl' ,\n 'base_fk_jnt' : 'right_shoulder_fk_jnt',\n 'mid_fk_jnt' : 'right_elbow_fk_jnt',\n 'end_fk_jnt' : 'right_wrist_fk_jnt',\n 'mid_ik_reference' : 'right_elbowSwitch_loc',\n 'end_ik_reference' : ''\n }\n \nleft_leg_seamless_dict = { 'switch_ctrl' : 'left_leg_switch_ctrl', # Switch Ctrl\n 'end_ik_ctrl' : 'left_foot_ik_ctrl', # IK Elements\n 'pvec_ik_ctrl' : 'left_knee_ik_ctrl',\n 'base_ik_jnt' : 'left_hip_ik_jnt',\n 'mid_ik_jnt' : 'left_knee_ik_jnt',\n 'end_ik_jnt' : 'left_ankle_ik_jnt',\n 'base_fk_ctrl' : 'left_hip_ctrl', # FK Elements\n 'mid_fk_ctrl' : 'left_knee_ctrl',\n 'end_fk_ctrl' : 'left_ankle_ctrl' ,\n 'base_fk_jnt' : 'left_hip_fk_jnt',\n 'mid_fk_jnt' : 'left_knee_fk_jnt',\n 'end_fk_jnt' : 'left_ankle_fk_jnt',\n 'mid_ik_reference' : 'left_kneeSwitch_loc',\n 'end_ik_reference' : 'left_ankleSwitch_loc'\n }\n \nright_leg_seamless_dict = { 'switch_ctrl' : 'right_leg_switch_ctrl', # Switch Ctrl\n 'end_ik_ctrl' : 'right_foot_ik_ctrl', # IK Elements\n 'pvec_ik_ctrl' : 'right_knee_ik_ctrl',\n 'base_ik_jnt' : 'right_hip_ik_jnt',\n 'mid_ik_jnt' : 'right_knee_ik_jnt',\n 'end_ik_jnt' : 'right_ankle_ik_jnt',\n 'base_fk_ctrl' : 'right_hip_ctrl', # FK Elements\n 'mid_fk_ctrl' : 'right_knee_ctrl',\n 'end_fk_ctrl' : 'right_ankle_ctrl' ,\n 'base_fk_jnt' : 'right_hip_fk_jnt',\n 'mid_fk_jnt' : 'right_knee_fk_jnt',\n 'end_fk_jnt' : 'right_ankle_fk_jnt',\n 'mid_ik_reference' : 'right_kneeSwitch_loc',\n 'end_ik_reference' : 'right_ankleSwitch_loc'\n }\n \n# Mirror Elements\nnamespace_separator = ':'\nleft_prefix = 'left'\nright_prefix = 'right'\nnot_inverted = (False, False, False)\ninvert_x = (True, False, False)\ninvert_y = (False, True, False)\ninvert_z = (False, False, True)\ninvert_yz = (False, True, True)\ninvert_all = (True, True, True)\n\n# Dictionary Pattern:\n# Key: Control name (if not in the center, remove prefix)\n# Value: A list with two tuples. [(Is Translate XYZ inverted?), (Is Rotate XYZ inverted?), Is mirroring scale?]\n# Value Example: '_fingers_ctrl': [not_inverted, not_inverted, True] = Not inverting Translate XYZ. Not inverting Rotate XYZ. Yes, mirroring scale.\ngt_ab_general_ctrls = {# Fingers Automation\n '_fingers_ctrl': [not_inverted, not_inverted, True],\n '_thumbCurl_ctrl': [not_inverted, not_inverted],\n '_indexCurl_ctrl': [not_inverted, not_inverted],\n '_middleCurl_ctrl': [not_inverted, not_inverted],\n '_ringCurl_ctrl': [not_inverted, not_inverted],\n '_pinkyCurl_ctrl': [not_inverted, not_inverted],\n \n # Fingers FK\n '_thumb03_ctrl': [not_inverted, not_inverted],\n '_thumb02_ctrl': [not_inverted, not_inverted],\n '_thumb01_ctrl': [not_inverted, not_inverted],\n '_index01_ctrl': [not_inverted, not_inverted],\n '_middle02_ctrl': [not_inverted, not_inverted],\n '_middle01_ctrl': [not_inverted, not_inverted],\n '_index03_ctrl': [not_inverted, not_inverted],\n '_index02_ctrl': [not_inverted, not_inverted],\n '_ring03_ctrl': [not_inverted, not_inverted],\n '_ring02_ctrl': [not_inverted, not_inverted],\n '_ring01_ctrl': [not_inverted, not_inverted],\n '_middle03_ctrl': [not_inverted, not_inverted],\n '_pinky03_ctrl': [not_inverted, not_inverted],\n '_pinky02_ctrl': [not_inverted, not_inverted],\n '_pinky01_ctrl': [not_inverted, not_inverted],\n \n # Finger IK\n '_thumb_ik_ctrl': [invert_z, invert_x],\n '_index_ik_ctrl': [invert_z, invert_x],\n '_middle_ik_ctrl': [invert_z, invert_x],\n '_ring_ik_ctrl': [invert_z, invert_x],\n '_pinky_ik_ctrl': [invert_z, invert_x],\n # Clavicle\n '_clavicle_ctrl': [not_inverted, not_inverted],\n # Eyes\n '_eye_ctrl': [invert_x, not_inverted],\n } \n\ngt_ab_ik_ctrls = { # Arm\n '_elbow_ik_ctrl': [invert_x, not_inverted], \n '_wrist_ik_ctrl': [invert_all, not_inverted],\n # Leg\n '_heelRoll_ctrl': [invert_x, not_inverted],\n '_ballRoll_ctrl': [invert_x, not_inverted],\n '_toeRoll_ctrl': [invert_x, not_inverted],\n '_toe_upDown_ctrl': [invert_x, not_inverted],\n '_foot_ik_ctrl': [invert_x, invert_yz],\n '_knee_ik_ctrl': [invert_x, not_inverted],\n }\n \ngt_ab_fk_ctrls = {# Arm\n '_shoulder_ctrl': [invert_all, not_inverted],\n '_elbow_ctrl': [invert_all, not_inverted],\n '_wrist_ctrl': [invert_all, not_inverted],\n # Leg\n '_hip_ctrl': [invert_x, invert_yz],\n '_knee_ctrl': [invert_all, not_inverted],\n '_ankle_ctrl': [invert_all, not_inverted],\n '_ball_ctrl': [invert_all, not_inverted],\n }\n \ngt_ab_center_ctrls = ['cog_ctrl', \n 'hip_ctrl', \n 'spine01_ctrl', \n 'spine02_ctrl', \n 'spine03_ctrl', \n 'spine04_ctrl', \n 'cog_ribbon_ctrl', \n 'spine_ribbon_ctrl', \n 'chest_ribbon_ctrl',\n 'neckBase_ctrl',\n 'neckMid_ctrl',\n 'head_ctrl',\n 'jaw_ctrl',\n 'main_eye_ctrl',\n 'left_eye_ctrl',\n 'right_eye_ctrl',\n ] \n\ngt_ab_interface_settings = {\n 'namespace' : '',\n 'auto_key_switch' : True,\n 'auto_key_method_bake' : True,\n 'auto_key_start_frame' : 1,\n 'auto_key_end_frame' : 10,\n 'pose_export_thumbnail' : False,\n 'allow_multiple_instances' : False,\n }\n \ngt_ab_interface_settings_default = copy.deepcopy(gt_ab_interface_settings)\n\n\n# Manage Persistent Settings\ndef get_persistent_settings_auto_biped_rig_interface():\n ''' \n Checks if persistant settings for GT Auto Biped Rig Interface exists and loads it if this is the case.\n It assumes that persistent settings were stored using the cmds.optionVar function.\n '''\n # Check if there is anything stored\n stored_setup_exists = cmds.optionVar(exists=(\"gt_auto_biped_rig_interface_setup\"))\n \n if stored_setup_exists:\n stored_settings = {}\n try:\n stored_settings = eval(str(cmds.optionVar(q=(\"gt_auto_biped_rig_interface_setup\"))))\n for stored_item in stored_settings:\n for item in gt_ab_interface_settings:\n if stored_item == item:\n gt_ab_interface_settings[item] = stored_settings.get(stored_item)\n except:\n print('Couldn\\'t load persistent settings, try resetting it in the help menu.')\n\n\ndef set_persistent_settings_auto_biped_rig_interface():\n ''' \n Stores persistant settings for GT Auto Biped Rig Interface.\n It converts the dictionary into a list for easy storage. (The get function converts it back to a dictionary)\n It assumes that persistent settings were stored using the cmds.optionVar function.\n '''\n cmds.optionVar( sv=('gt_auto_biped_rig_interface_setup', str(gt_ab_interface_settings)))\n\n\ndef reset_persistent_settings_auto_biped_rig_interface():\n ''' Resets persistant settings for GT Auto Biped Rig Interface '''\n cmds.optionVar( remove='gt_auto_biped_rig_interface_setup' )\n gt_ab_interface_settings = gt_ab_interface_settings_default\n cmds.optionVar( sv=('gt_auto_biped_rig_interface_setup', str(gt_ab_interface_settings_default)))\n cmds.warning('Persistent settings for ' + script_name + ' were cleared.')\n try:\n cmds.evalDeferred('build_gui_custom_rig_interface()')\n except:\n try:\n build_gui_custom_rig_interface()\n except:\n try:\n cmds.evalDeferred('gt_biped_rig_interface.build_gui_custom_rig_interface()')\n except:\n pass\n\n \n# Main Window ============================================================================\ndef build_gui_custom_rig_interface():\n rig_interface_window_name = 'build_gui_custom_rig_interface'\n if cmds.window(rig_interface_window_name, exists =True):\n cmds.deleteUI(rig_interface_window_name) \n\n # Main GUI Start Here =================================================================================\n def update_fk_ik_buttons():\n '''\n Updates the background color of the FK/IK buttons according to the value of the current influenceSwitch attribute.\n This attempts to make the UI \"aware\" of the current state of the controls.\n '''\n active_color = (.6,.6,.6)\n inactive_color = (.36,.36,.36)\n ctrl_btn_lists = [\n [right_arm_seamless_dict, right_arm_fk_btn, right_arm_ik_btn],\n [left_arm_seamless_dict, left_arm_fk_btn, left_arm_ik_btn],\n [right_leg_seamless_dict, right_leg_fk_btn, right_leg_ik_btn],\n [left_leg_seamless_dict, left_leg_fk_btn, left_leg_ik_btn]\n ]\n for ctrl_btns in ctrl_btn_lists:\n if cmds.objExists(gt_ab_interface_settings.get('namespace') + namespace_separator + ctrl_btns[0].get('switch_ctrl')):\n try:\n current_system = cmds.getAttr(gt_ab_interface_settings.get('namespace') + namespace_separator + ctrl_btns[0].get('switch_ctrl') + '.influenceSwitch')\n if current_system < 0.5:\n cmds.button(ctrl_btns[1], e=True, bgc=active_color) # FK Button\n cmds.button(ctrl_btns[2], e=True, bgc=inactive_color) # IK Button\n else:\n cmds.button(ctrl_btns[2], e=True, bgc=active_color) # FK Button\n cmds.button(ctrl_btns[1], e=True, bgc=inactive_color) # IK Button\n except:\n pass\n else:\n cmds.button(ctrl_btns[2], e=True, bgc=inactive_color) # FK Button\n cmds.button(ctrl_btns[1], e=True, bgc=inactive_color) # IK Button\n \n\n def update_stored_settings():\n '''\n Extracts the namespace used and stores it as a persistent variable\n This function also calls \"update_fk_ik_buttons()\" so it updates the UI\n '''\n gt_ab_interface_settings['namespace'] = cmds.textField(namespace_txt, q=True, text=True)\n gt_ab_interface_settings['auto_key_switch'] = cmds.checkBox(auto_key_switch_chk, q=True, value=True)\n gt_ab_interface_settings['auto_key_switch'] = cmds.checkBox(auto_key_switch_chk, q=True, value=True)\n gt_ab_interface_settings['auto_key_method_bake'] = cmds.radioButton(auto_key_method_rb1, query=True, select=True)\n gt_ab_interface_settings['auto_key_start_frame'] = cmds.intField(auto_key_start_int_field, q=True, value=0)\n gt_ab_interface_settings['auto_key_end_frame'] = cmds.intField(auto_key_end_int_field, q=True, value=0)\n\n\n if gt_ab_interface_settings.get('auto_key_switch'):\n cmds.radioButton(auto_key_method_rb1, e=True, en=True)\n cmds.radioButton(auto_key_method_rb2, e=True, en=True)\n cmds.rowColumnLayout(switch_range_column, e=True, en=True)\n else:\n cmds.radioButton(auto_key_method_rb1, e=True, en=False)\n cmds.radioButton(auto_key_method_rb2, e=True, en=False)\n cmds.rowColumnLayout(switch_range_column, e=True, en=False)\n \n set_persistent_settings_auto_biped_rig_interface()\n update_fk_ik_buttons()\n\n\n def update_switch(ik_fk_dict, direction='ik_to_fk', is_auto_switch=False):\n '''\n Runs the switch function using the parameters provided in the UI\n Also updates the UI to keep track of the FK/IK state.\n \n Parameters:\n ik_fk_dict (dict): A dicitionary containg the elements that are part of the system you want to switch\n direction (optinal, string): Either \"fk_to_ik\" or \"ik_to_fk\". It determines what is the source and what is the target.\n '''\n method = 'bake' if gt_ab_interface_settings.get('auto_key_method_bake') else 'sparse' \n \n if is_auto_switch:\n gt_ab_seamless_fk_ik_switch_auto(ik_fk_dict, \n namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator,\n keyframe=gt_ab_interface_settings.get('auto_key_switch'),\n start_time=int(gt_ab_interface_settings.get('auto_key_start_frame')), \n end_time=int(gt_ab_interface_settings.get('auto_key_end_frame')), \n method=method\n )\n\n else:\n gt_ab_seamless_fk_ik_switch(ik_fk_dict, \n direction, \n namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator,\n keyframe=gt_ab_interface_settings.get('auto_key_switch'),\n start_time=int(gt_ab_interface_settings.get('auto_key_start_frame')), \n end_time=int(gt_ab_interface_settings.get('auto_key_end_frame')), \n method=method\n )\n\n update_fk_ik_buttons()\n\n \n def get_auto_key_current_frame(target_integer_field='start'):\n '''\n Gets the current frame and auto fills an integer field.\n \n Parameters:\n target_integer_field (optional, string) : Gets the current timeline frame and feeds it into the start or end integer field.\n Can only be \"start\" or \"end\". Anything else will be understood as \"end\".\n \n '''\n current_time = cmds.currentTime(q=True)\n if target_integer_field == 'start':\n cmds.intField(auto_key_start_int_field, e=True, value=current_time)\n else:\n cmds.intField(auto_key_end_int_field, e=True, value=current_time)\n update_stored_settings()\n \n \n def mirror_fk_ik_pose(source_side='right'):\n '''\n Runs a full pose mirror function.\n \n Parameters:\n source_side (optinal, string): Either \"right\" or \"left\". It determines what is the source and what is the target of the mirror.\n '''\n \n gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_ik_ctrls, gt_ab_fk_ctrls], source_side, namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator)\n \n \n def reset_animation_and_pose():\n '''\n Deletes Keyframes and Resets pose back to default\n '''\n gt_reset_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator)\n gt_ab_reset_pose(gt_ab_ik_ctrls, gt_ab_fk_ctrls, gt_ab_center_ctrls, namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator)\n \n def build_custom_help_window(input_text, help_title=''):\n ''' \n Creates a help window to display the provided text\n\n Parameters:\n input_text (string): Text used as help, this is displayed in a scroll fields.\n help_title (optinal, string)\n '''\n window_name = help_title.replace(\" \",\"_\").replace(\"-\",\"_\").lower().strip() + \"_help_window\"\n if cmds.window(window_name, exists=True):\n cmds.deleteUI(window_name, window=True)\n\n cmds.window(window_name, title= help_title + \" Help\", mnb=False, mxb=False, s=True)\n cmds.window(window_name, e=True, s=True, wh=[1,1])\n\n main_column = cmds.columnLayout(p= window_name)\n \n # Title Text\n cmds.separator(h=12, style='none') # Empty Space\n cmds.rowColumnLayout(nc=1, cw=[(1, 310)], cs=[(1, 10)], p=main_column) # Window Size Adjustment\n cmds.rowColumnLayout(nc=1, cw=[(1, 300)], cs=[(1, 10)], p=main_column) # Title Column\n cmds.text(help_title + ' Help', bgc=(.4, .4, .4), fn='boldLabelFont', align='center')\n cmds.separator(h=10, style='none', p=main_column) # Empty Space\n\n # Body ==================== \n cmds.rowColumnLayout(nc=1, cw=[(1, 300)], cs=[(1,10)], p=main_column)\n \n help_scroll_field = cmds.scrollField(editable=False, wordWrap=True, fn='smallPlainLabelFont')\n \n cmds.scrollField(help_scroll_field, e=True, ip=0, it=input_text)\n cmds.scrollField(help_scroll_field, e=True, ip=1, it='') # Bring Back to the Top\n \n # Close Button \n cmds.rowColumnLayout(nc=1, cw=[(1, 300)], cs=[(1,10)], p=main_column)\n cmds.separator(h=10, style='none')\n cmds.button(l='OK', h=30, c=lambda args: close_help_gui())\n cmds.separator(h=8, style='none')\n \n # Show and Lock Window\n cmds.showWindow(window_name)\n cmds.window(window_name, e=True, s=False)\n \n # Set Window Icon\n qw = omui.MQtUtil.findWindow(window_name)\n if python_version == 3:\n widget = wrapInstance(int(qw), QWidget)\n else:\n widget = wrapInstance(long(qw), QWidget)\n icon = QIcon(':/question.png')\n widget.setWindowIcon(icon)\n \n def close_help_gui():\n ''' Closes help windows '''\n if cmds.window(window_name, exists=True):\n cmds.deleteUI(window_name, window=True)\n # Custom Help Dialog Ends Here =================================================================================\n \n # Retrieve Persistent Settings\n get_persistent_settings_auto_biped_rig_interface()\n\n # Build UI\n script_title = script_name\n if unique_rig != '':\n script_title = 'GT - Rig Interface for ' + unique_rig\n \n build_gui_custom_rig_interface = cmds.window(rig_interface_window_name, title=script_title + ' (v' + script_version + ')',\\\n titleBar=True, mnb=False, mxb=False, sizeable =True)\n\n cmds.window(rig_interface_window_name, e=True, s=True, wh=[1,1])\n\n content_main = cmds.columnLayout(adj = True)\n\n # Title Text\n title_bgc_color = (.4, .4, .4)\n cmds.separator(h=10, style='none') # Empty Space\n cmds.rowColumnLayout(nc=1, cw=[(1, 270)], cs=[(1, 10)], p=content_main) # Window Size Adjustment\n cmds.rowColumnLayout(nc=3, cw=[(1, 10), (2, 200), (3, 50)], cs=[(1, 10), (2, 0), (3, 0)], p=content_main) # Title Column\n cmds.text(\" \", bgc=title_bgc_color) # Tiny Empty Green Space\n cmds.text(script_title, bgc=title_bgc_color, fn=\"boldLabelFont\", align=\"left\")\n cmds.button( l =\"Help\", bgc=title_bgc_color, c=lambda x:open_gt_tools_documentation())\n cmds.separator(h=5, style='none') # Empty Space\n \n # Body ====================\n body_column = cmds.rowColumnLayout(nc=1, cw=[(1, 260)], cs=[(1,10)], p=content_main)\n \n cmds.text('Namespace:')\n namespace_txt = cmds.textField(text=gt_ab_interface_settings.get('namespace'), pht='Namespace:: (Optional)', cc=lambda x:update_stored_settings())\n \n cmds.separator(h=10, style='none') # Empty Space\n \n form = cmds.formLayout()\n tabs = cmds.tabLayout(innerMarginWidth=5, innerMarginHeight=5)\n cmds.formLayout(form, edit=True, attachForm=((tabs, 'top', 0), (tabs, 'left', 0), (tabs, 'bottom', 0), (tabs, 'right', 0)) )\n\n ############# FK/IK Switch Tab #############\n btn_margin = 5\n fk_ik_switch_tab = cmds.rowColumnLayout(nc=1, cw=[(1, 246)], cs=[(1,0)], p=tabs)\n\n fk_ik_btn_width = 59\n cw_fk_ik_states = [(1, fk_ik_btn_width),(2, fk_ik_btn_width),(3, fk_ik_btn_width),(4, fk_ik_btn_width)]\n cs_fk_ik_states = [(1,2), (2,2), (3,3), (4,2)]\n \n switch_btn_width = 120\n cw_fk_ik_switches = [(1, switch_btn_width),(2, switch_btn_width)]\n cs_fk_ik_switches = [(1,2), (2,3)]\n \n arms_text = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=fk_ik_switch_tab)\n cmds.separator(h=2, style='none') # Empty Space\n cmds.separator(h=2, style='none') # Empty Space\n cmds.text('Right Arm:', p=arms_text) #R\n cmds.text('Left Arm:', p=arms_text) #L\n \n arms_switch_state_column = cmds.rowColumnLayout(nc=4, cw=cw_fk_ik_states, cs=cs_fk_ik_states, p=fk_ik_switch_tab)\n right_arm_fk_btn = cmds.button(l =\"FK\", c=lambda x:update_switch(right_arm_seamless_dict, 'ik_to_fk'), p=arms_switch_state_column) #R\n right_arm_ik_btn = cmds.button(l =\"IK\", c=lambda x:update_switch(right_arm_seamless_dict, 'fk_to_ik'), p=arms_switch_state_column) #L\n left_arm_fk_btn = cmds.button(l =\"FK\", c=lambda x:update_switch(left_arm_seamless_dict, 'ik_to_fk'), p=arms_switch_state_column) #R\n left_arm_ik_btn = cmds.button(l =\"IK\", c=lambda x:update_switch(left_arm_seamless_dict, 'fk_to_ik'), p=arms_switch_state_column) #L\n \n arms_switch_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=fk_ik_switch_tab)\n cmds.button(l =\"Switch\", c=lambda x:update_switch(right_arm_seamless_dict, is_auto_switch=True), p=arms_switch_column) #R\n cmds.button(l =\"Switch\", c=lambda x:update_switch(left_arm_seamless_dict, is_auto_switch=True), p=arms_switch_column) #L\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.text('Right Leg:', p=arms_switch_column) #R\n cmds.text('Left Leg:', p=arms_switch_column) #L\n\n legs_switch_state_column = cmds.rowColumnLayout(nc=4, cw=cw_fk_ik_states, cs=cs_fk_ik_states, p=fk_ik_switch_tab)\n right_leg_fk_btn = cmds.button(l =\"FK\", c=lambda x:update_switch(right_leg_seamless_dict, 'ik_to_fk'), p=legs_switch_state_column) #R\n right_leg_ik_btn = cmds.button(l =\"IK\", c=lambda x:update_switch(right_leg_seamless_dict, 'fk_to_ik'), p=legs_switch_state_column) #L\n left_leg_fk_btn = cmds.button(l =\"FK\", c=lambda x:update_switch(left_arm_seamless_dict, 'ik_to_fk'), p=legs_switch_state_column) #R\n left_leg_ik_btn = cmds.button(l =\"IK\", c=lambda x:update_switch(left_arm_seamless_dict, 'fk_to_ik'), p=legs_switch_state_column) #L\n \n legs_switch_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=fk_ik_switch_tab)\n cmds.button(l =\"Switch\", c=lambda x:update_switch(right_leg_seamless_dict, is_auto_switch=True), p=legs_switch_column) #R\n cmds.button(l =\"Switch\", c=lambda x:update_switch(left_leg_seamless_dict, is_auto_switch=True), p=legs_switch_column) #L\n \n # Auto Key Settings (Switch Settings)\n switch_settings_column = cmds.rowColumnLayout(nc=1, cw=[(1, 245)], cs=[(1, 6)], p=fk_ik_switch_tab)\n cmds.separator(h=15) # Empty Space\n switch_auto_key_column = cmds.rowColumnLayout(nc=3, cw=[(1, 80),(2, 130),(3, 60)], cs=[(1, 25)], p=fk_ik_switch_tab)\n auto_key_switch_chk = cmds.checkBox( label='Auto Key', value=gt_ab_interface_settings.get('auto_key_switch'), cc=lambda x:update_stored_settings())\n \n method_container = cmds.rowColumnLayout( p=switch_auto_key_column, numberOfRows=1)\n auto_key_method_rc = cmds.radioCollection()\n auto_key_method_rb1 = cmds.radioButton( p=method_container, label=' Bake ', sl=gt_ab_interface_settings.get('auto_key_method_bake'), cc=lambda x:update_stored_settings())\n auto_key_method_rb2 = cmds.radioButton( p=method_container, label=' Sparse ', sl=(not gt_ab_interface_settings.get('auto_key_method_bake')), cc=lambda x:update_stored_settings())\n cmds.separator(h=5, style='none', p=fk_ik_switch_tab) # Empty Space\n \n switch_range_column = cmds.rowColumnLayout(nc=6, cw=[(1, 40),(2, 40),(3, 30),(4, 30),(5, 40),(6, 30)], cs=[(1, 10), (4, 10)], p=fk_ik_switch_tab)\n cmds.text('Start:', p=switch_range_column)\n auto_key_start_int_field = cmds.intField(value=int(gt_ab_interface_settings.get('auto_key_start_frame')), p=switch_range_column, cc=lambda x:update_stored_settings())\n cmds.button(l =\"Get\", c=lambda x:get_auto_key_current_frame(), p=switch_range_column, h=5) #L\n cmds.text('End:', p=switch_range_column)\n auto_key_end_int_field = cmds.intField(value=int(gt_ab_interface_settings.get('auto_key_end_frame')),p=switch_range_column, cc=lambda x:update_stored_settings())\n cmds.button(l =\"Get\", c=lambda x:get_auto_key_current_frame('end'), p=switch_range_column, h=5) #L\n cmds.separator(h=10, style='none', p=fk_ik_switch_tab) # Empty Space\n \n\n ############# Pose Management Tab #############\n pose_management_tab = cmds.rowColumnLayout(nc=1, cw=[(1, 246)], cs=[(1,0)], p=tabs)\n\n btn_margin = 2\n \n cmds.separator(h=5, style='none') # Empty Space\n pose_title_column = cmds.rowColumnLayout(nc=1, cw=[(1, 245)], cs=cs_fk_ik_switches, p=pose_management_tab)\n cmds.text('Mirror Pose:', p=pose_title_column)\n cmds.separator(h=5, style='none', p=pose_title_column) # Empty Space\n \n \n mirror_pose_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=pose_management_tab)\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n \n cmds.text('Right to Left:', p=mirror_pose_column) #R\n cmds.text('Left to Right:', p=mirror_pose_column) #L\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n \n cmds.button(l =\"Mirror ->\", c=lambda x:mirror_fk_ik_pose('right'), p=mirror_pose_column) #R\n cmds.button(l =\"<- Mirror\", c=lambda x:mirror_fk_ik_pose('left'), p=mirror_pose_column) #L\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n \n pose_mirror_ik_fk_column = cmds.rowColumnLayout(nc=4, cw=cw_fk_ik_states, cs=cs_fk_ik_states, p=pose_management_tab)\n \n # IK Pose Mirror\n cmds.button(l =\"IK Only >\", c=lambda x:gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_ik_ctrls], 'right', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=pose_mirror_ik_fk_column) #R\n cmds.button(l =\"FK Only >\", c=lambda x:gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_fk_ctrls], 'right', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=pose_mirror_ik_fk_column) #R\n \n \n # FK Pose Mirror\n cmds.button(l =\"< IK Only\", c=lambda x:gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_ik_ctrls], 'left', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=pose_mirror_ik_fk_column) #L\n cmds.button(l =\"< FK Only\", c=lambda x:gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_fk_ctrls], 'left', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=pose_mirror_ik_fk_column) #L\n \n\n # Reset Pose\n pose_management_column = cmds.rowColumnLayout(nc=1, cw=[(1, 245)], cs=cs_fk_ik_switches, p=pose_management_tab)\n cmds.separator(h=15, style='none', p=pose_management_column) # Empty Space\n cmds.text('Reset Pose:', p=pose_management_column) #R\n cmds.separator(h=btn_margin, style='none', p=pose_management_column) # Empty Space\n cmds.button(l =\"Reset Back to Default Pose\", c=lambda x:gt_ab_reset_pose(gt_ab_ik_ctrls, gt_ab_fk_ctrls, gt_ab_center_ctrls, namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=pose_management_column)\n\n # Export Import Pose\n cmds.separator(h=btn_margin, style='none', p=pose_management_column) # Empty Space\n cmds.separator(h=15, style='none', p=pose_management_column) # Empty Space\n cmds.text('Import/Export Poses:', p=pose_management_column) \n \n import_export_pose_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=pose_management_tab)\n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.button(l =\"Import Current Pose\", c=lambda x:gt_import_rig_pose(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=import_export_pose_column)\n cmds.button(l =\"Export Current Pose\", c=lambda x:gt_export_rig_pose(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=import_export_pose_column) \n\n\n ############# Animation Management Tab #############\n \n anim_management_tab = cmds.rowColumnLayout(nc=1, cw=[(1, 246)], cs=[(1,0)], p=tabs)\n \n cmds.separator(h=5, style='none') # Empty Space\n anim_title_column = cmds.rowColumnLayout(nc=1, cw=[(1, 245)], cs=cs_fk_ik_switches, p=anim_management_tab)\n cmds.text('Mirror Animation:', p=anim_title_column)\n cmds.separator(h=5, style='none', p=anim_title_column) # Empty Space\n \n mirror_anim_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=anim_management_tab)\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n \n cmds.text('Right to Left:', p=mirror_anim_column) #R\n cmds.text('Left to Right:', p=mirror_anim_column) #L\n \n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n \n cmds.button(l =\"Mirror ->\", c=lambda x:gt_ab_mirror_anim([gt_ab_general_ctrls, gt_ab_ik_ctrls, gt_ab_fk_ctrls], 'right', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=mirror_anim_column) #R\n cmds.button(l =\"<- Mirror\", c=lambda x:gt_ab_mirror_anim([gt_ab_general_ctrls, gt_ab_ik_ctrls, gt_ab_fk_ctrls], 'left', namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=mirror_anim_column) #L\n \n # Reset Animation\n anim_management_column = cmds.rowColumnLayout(nc=1, cw=[(1, 245)], cs=cs_fk_ik_switches, p=anim_management_tab)\n cmds.separator(h=15, style='none', p=anim_management_column) # Empty Space\n cmds.text('Reset Animation:', p=anim_management_column) #R\n cmds.separator(h=btn_margin, style='none', p=anim_management_column) # Empty Space\n cmds.button(l =\"Reset Animation (Delete Keyframes)\", c=lambda x:gt_reset_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=anim_management_column)\n cmds.separator(h=btn_margin, style='none', p=anim_management_column) # Empty Space\n cmds.button(l =\"Reset Animation and Pose\", c=lambda x:reset_animation_and_pose(), p=anim_management_column)\n \n\n # Export Import Pose\n cmds.separator(h=17, style='none', p=anim_management_column) # Empty Space\n cmds.text('Import/Export Animation:', p=anim_management_column) \n\n import_export_pose_column = cmds.rowColumnLayout(nc=2, cw=cw_fk_ik_switches, cs=cs_fk_ik_switches, p=anim_management_tab)\n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.separator(h=btn_margin, style='none') # Empty Space\n cmds.button(l =\"Import Animation\", c=lambda x:gt_import_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=import_export_pose_column)\n cmds.button(l =\"Export Animation\", c=lambda x:gt_export_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=import_export_pose_column) \n \n \n ############# Settings Tab #############\n settings_tab = cmds.rowColumnLayout(nc=1, cw=[(1, 240)], cs=[(1,0)], p=tabs)\n \n # General Settings\n enabled_bgc_color = (.4, .4, .4)\n disabled_bgc_color = (.3,.3,.3)\n cmds.separator(h=5, style='none') # Empty Space\n cmds.text('General Settings:', font='boldLabelFont')\n cmds.separator(h=5, style='none') # Empty Space\n cmds.rowColumnLayout(nc=3, cw=[(1, 10), (2, 200), (3, 20)], cs=[(1,10)]) \n \n # Allow Multiple Instances\n is_option_enabled = False\n cmds.text(' ', bgc=(enabled_bgc_color if is_option_enabled else disabled_bgc_color), h=20) # Tiny Empty Spac\n cmds.checkBox( label=' Allow Multiple Instances', value=gt_ab_interface_settings.get('allow_multiple_instances'), ebg=True, cc=lambda x:invert_stored_setting('allow_multiple_instances'), en=is_option_enabled) \n\n export_pose_thumbnail_help_message = 'Allow Multiple Instances Help Placeholder.'\n export_pose_thumbnail_help_title = 'Allow Multiple Instances'\n cmds.button(l ='?', bgc=enabled_bgc_color, c=lambda x:build_custom_help_window(export_pose_thumbnail_help_message, export_pose_thumbnail_help_title))\n \n # Export Thumbnail With Pose\n is_option_enabled = False\n cmds.text(' ', bgc=(enabled_bgc_color if is_option_enabled else disabled_bgc_color), h=20) # Tiny Empty Spac\n cmds.checkBox( label=' Export Thumbnail with Pose', value=gt_ab_interface_settings.get('pose_export_thumbnail'), ebg=True, cc=lambda x:invert_stored_setting('pose_export_thumbnail'), en=is_option_enabled) \n\n export_pose_thumbnail_help_message = 'Exports a thumbnail \".jpg\" file together with your \".pose\" file.\\nThis extra thumbnail file can be used to quickly undestand what you pose looks like before importing it.\\n\\nThe thumbnail is a screenshot of you active viewport at the moment of exporting the pose. If necessary, export it again to generate another thumbnail.'\n export_pose_thumbnail_help_title = 'Export Thumbnail with Pose'\n cmds.button(l ='?', bgc=enabled_bgc_color, c=lambda x:build_custom_help_window(export_pose_thumbnail_help_message, export_pose_thumbnail_help_title))\n \n # Reset Persistent Settings\n cmds.separator(h=btn_margin, style='none', p=settings_tab) # Empty Space\n settings_buttons_column = cmds.rowColumnLayout(nc=1, cw=[(1, 240)], cs=[(1,10)], p=settings_tab) \n cmds.button(l =\"Reset Persistent Settings\", c=lambda x:reset_persistent_settings_auto_biped_rig_interface(), p=settings_buttons_column)\n \n \n \n ################# END TABS #################\n cmds.tabLayout( tabs, edit=True, tabLabel=((fk_ik_switch_tab, ' FK/IK '), (pose_management_tab, ' Pose '), (anim_management_tab, 'Animation'), (settings_tab, ' Settings ')))\n # Outside Margin\n cmds.separator(h=10, style='none', p=content_main) # Empty Space\n \n # Show and Lock Window\n cmds.showWindow(build_gui_custom_rig_interface)\n cmds.window(rig_interface_window_name, e=True, s=False)\n \n # Set Window Icon\n qw = omui.MQtUtil.findWindow(rig_interface_window_name)\n if python_version == 3:\n widget = wrapInstance(int(qw), QWidget)\n else:\n widget = wrapInstance(long(qw), QWidget)\n icon = QIcon(':/ikSCsolver.svg')\n widget.setWindowIcon(icon)\n\n # Update FK/IK States and Settings for the first run time\n update_fk_ik_buttons()\n update_stored_settings()\n\n # Remove the focus from the textfield and give it to the window\n cmds.setFocus(rig_interface_window_name)\n\n # Main GUI Ends Here =================================================================================\n \n\n\ndef gt_ab_seamless_fk_ik_switch(ik_fk_dict, direction='fk_to_ik', namespace='', keyframe=False, start_time=0, end_time=0, method='sparse'):\n '''\n Transfer the position of the FK to IK or IK to FK systems in a seamless way, so the animator can easily switch between one and the other\n \n Parameters:\n ik_fk_dict (dict): A dicitionary containg the elements that are part of the system you want to switch\n direction (optinal, string): Either \"fk_to_ik\" or \"ik_to_fk\". It determines what is the source and what is the target.\n namespace (optinal, string): In case the rig has a namespace, it will be used to properly select the controls.\n \n \n keyframe (optinal, bool): If active it will created a keyframe at the current frame, move to the\n start_time (optinal, int): Where to create the first keyframe\n end_time (optinal, int): Where to create the last keyframe\n method (optinal, string): Method used for creating the keyframes. Either 'sparse' or 'bake'.\n '''\n def switch(match_only=False):\n '''\n Performs the switch operation.\n Commands were wrapped into a function to be used during the bake operation.\n \n Parameters:\n match_only (optional, bool) If active (True) it will only match the pose, but not switch\n \n Returns:\n attr_value (float): Value which the influence attribute was set to. Either 1 (fk_to_ik) or 0 (ik_to_fk).\n This value is returned only if \"match_only\" is False. Otherwise, expect None.\n '''\n try:\n ik_fk_ns_dict = {}\n for obj in ik_fk_dict:\n ik_fk_ns_dict[obj] = namespace + ik_fk_dict.get(obj)\n \n fk_pairs = [[ik_fk_ns_dict.get('base_ik_jnt'), ik_fk_ns_dict.get('base_fk_ctrl')],\n [ik_fk_ns_dict.get('mid_ik_jnt'), ik_fk_ns_dict.get('mid_fk_ctrl')],\n [ik_fk_ns_dict.get('end_ik_jnt'), ik_fk_ns_dict.get('end_fk_ctrl')]] \n \n if direction == 'fk_to_ik':\n if ik_fk_dict.get('end_ik_reference') != '':\n cmds.matchTransform(ik_fk_ns_dict.get('end_ik_ctrl'), ik_fk_ns_dict.get('end_ik_reference'), pos=1, rot=1)\n else:\n cmds.matchTransform(ik_fk_ns_dict.get('end_ik_ctrl'), ik_fk_ns_dict.get('end_fk_jnt'), pos=1, rot=1)\n \n cmds.matchTransform(ik_fk_ns_dict.get('pvec_ik_ctrl'), ik_fk_ns_dict.get('mid_ik_reference'), pos=1, rot=1)\n if not match_only:\n cmds.setAttr(ik_fk_ns_dict.get('switch_ctrl') + '.influenceSwitch', 1)\n return 1\n if direction == 'ik_to_fk':\n for pair in fk_pairs:\n cmds.matchTransform(pair[1], pair[0], pos=1, rot=1)\n if not match_only:\n cmds.setAttr(ik_fk_ns_dict.get('switch_ctrl') + '.influenceSwitch', 0)\n return 0\n except Exception as e:\n cmds.warning('An error occurred. Please check if a namespace is necessary or if a control was deleted. Error: ' + str(e))\n \n \n def print_inview_feedback():\n '''\n Prints feedback using inView messages so the user knows what operation was executed.\n '''\n \n\n # namespace='', keyframe=False, start_time=0, end_time=0, method='sparse'\n \n is_valid_message = True\n message_target = 'IK' if direction == 'fk_to_ik' else 'FK'\n \n # Try to figure it out system:\n message_direction = ''\n pvec_ik_ctrl = ik_fk_dict.get(next(iter(ik_fk_dict)))\n if pvec_ik_ctrl.startswith('right_'):\n message_direction = 'right'\n elif pvec_ik_ctrl.startswith('left_'):\n message_direction = 'left'\n else:\n is_valid_message = False\n \n message_limb = ''\n if 'knee' in pvec_ik_ctrl:\n message_limb = 'leg'\n elif 'elbow' in pvec_ik_ctrl:\n message_limb = 'arm'\n else:\n is_valid_message = False\n \n message_range = ''\n if keyframe:\n message_range = '(Start: ' + str(start_time) + ' End: ' + str(end_time) + ' Method: ' + method.capitalize() + ' )'\n \n\n if is_valid_message:\n # Print Feedback\n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Switched ' + message_direction + ' ' + message_limb + ' to ' + message_target +' ' + message_range, pos='botLeft', fade=True, alpha=.9)\n \n\n\n # Find Available Controls\n available_ctrls = []\n\n for key in ik_fk_dict:\n if cmds.objExists(namespace + ik_fk_dict.get(key)):\n available_ctrls.append(ik_fk_dict.get(key))\n if cmds.objExists(namespace + key):\n available_ctrls.append(key)\n \n # No Controls were found\n if len(available_ctrls) == 0:\n is_valid=False\n cmds.warning('No controls were found. Make sure you are using the correct namespace.')\n\n else:\n if keyframe:\n if method.lower() == 'sparse': # Only Influence Switch\n original_time = cmds.currentTime(q=True)\n cmds.currentTime(start_time)\n cmds.setKeyframe(namespace + ik_fk_dict.get('switch_ctrl'), time=start_time, attribute='influenceSwitch')\n cmds.currentTime(end_time)\n switch()\n cmds.setKeyframe(namespace + ik_fk_dict.get('switch_ctrl'), time=end_time, attribute='influenceSwitch')\n cmds.currentTime(original_time)\n print_inview_feedback()\n elif method.lower() == 'bake':\n if start_time >= end_time:\n cmds.warning('Invalid range. Please review the stard and end frame and try again.')\n else:\n original_time = cmds.currentTime(q=True)\n cmds.currentTime(start_time)\n current_time = cmds.currentTime(q=True)\n cmds.setKeyframe(namespace + ik_fk_dict.get('switch_ctrl'), time=current_time, attribute='influenceSwitch') # Start Switch\n for index in range(end_time - start_time):\n cmds.currentTime(current_time)\n switch(match_only=True)\n if direction == 'fk_to_ik':\n for channel in ['t','r']:\n for dimension in ['x', 'y', 'z']:\n cmds.setKeyframe(namespace + ik_fk_dict.get('end_ik_ctrl'), time=current_time, attribute=channel+dimension) # Wrist IK Ctrl\n cmds.setKeyframe(namespace + ik_fk_dict.get('pvec_ik_ctrl'), time=current_time, attribute=channel+dimension) # PVec Elbow IK Ctrl\n\n if direction == 'ik_to_fk':\n for channel in ['t','r']:\n for dimension in ['x', 'y', 'z']:\n cmds.setKeyframe(namespace + ik_fk_dict.get('base_fk_ctrl'), time=current_time, attribute=channel+dimension) # Shoulder FK Ctrl\n cmds.setKeyframe(namespace + ik_fk_dict.get('end_fk_ctrl'), time=current_time, attribute=channel+dimension) # Wrist FK Ctrl\n cmds.setKeyframe(namespace + ik_fk_dict.get('mid_fk_ctrl'), time=current_time, attribute=channel+dimension) # Elbow FK Ctrl\n current_time += 1\n switch()\n cmds.setKeyframe(namespace + ik_fk_dict.get('switch_ctrl'), time=current_time, attribute='influenceSwitch') # End Switch\n cmds.currentTime(original_time)\n print_inview_feedback()\n else:\n cmds.warning('Invalid method was provided. Must be either \"sparse\" or \"bake\", but got ' + method)\n else:\n switch()\n print_inview_feedback()\n\ndef gt_ab_seamless_fk_ik_switch_auto(ik_fk_dict, namespace='', keyframe=False, start_time=0, end_time=0, method='sparse'):\n ''' \n Calls gt_ab_seamless_fk_ik_switch, but switches (toggles) between FK and IK based on the current influence number. \n It automatically checks the influenceSwitch value attribute and determines what direction to take it. \"0-0.5\":IK and \"0.5-1\":FK\n \n Parameters:\n ik_fk_dict (dictionary): A dicitionary containg the elements that are part of the system you want to switch\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n keyframe (optinal, bool): If active it will created a keyframe at the current frame, move to the\n start_time (optinal, int): Where to create the first keyframe\n end_time (optinal, int): Where to create the last keyframe\n method (optinal, string): Method used for creating the keyframes. Either 'sparse' or 'bake'. \n '''\n try:\n if cmds.objExists(namespace + ik_fk_dict.get('switch_ctrl')):\n current_system = cmds.getAttr(namespace + ik_fk_dict.get('switch_ctrl') + '.influenceSwitch')\n if current_system < 0.5:\n gt_ab_seamless_fk_ik_switch(ik_fk_dict, direction='fk_to_ik', namespace=namespace, keyframe=keyframe, start_time=start_time, end_time=end_time, method=method)\n else:\n gt_ab_seamless_fk_ik_switch(ik_fk_dict, direction='ik_to_fk', namespace=namespace, keyframe=keyframe, start_time=start_time, end_time=end_time, method=method)\n else:\n cmds.warning('Switch control was not found. Please check if a namespace is necessary.')\n except Exception as e:\n cmds.warning('An error occurred. Please check if a namespace is necessary. Error: ' + str(e))\n\n\ndef open_gt_tools_documentation():\n ''' Opens a web browser with the the auto rigger docs '''\n cmds.showHelp ('https://github.com/TrevisanGMW/gt-tools/tree/release/docs#-gt-auto-biped-rigger-', absolute=True) \n\n\ndef gt_ab_mirror_pose(gt_ab_ctrls, source_side, namespace=''):\n '''\n Mirrors the character pose from one side to the other\n\n Parameters:\n gt_ab_ctrls (dict) : A list of dictionaries of controls without their side prefix (e.g. \"_wrist_ctrl\")\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n '''\n # Merge Dictionaries\n gt_ab_ctrls_dict = {}\n for ctrl_dict in gt_ab_ctrls:\n gt_ab_ctrls_dict.update(ctrl_dict)\n \n # Find available Ctrls\n available_ctrls = []\n for obj in gt_ab_ctrls_dict:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n # Start Mirroring\n if len(available_ctrls) != 0:\n \n errors = []\n \n right_side_objects = []\n left_side_objects = []\n\n for obj in available_ctrls: \n if right_prefix in obj:\n right_side_objects.append(obj)\n \n for obj in available_ctrls: \n if left_prefix in obj:\n left_side_objects.append(obj)\n \n for left_obj in left_side_objects:\n for right_obj in right_side_objects:\n remove_side_tag_left = left_obj.replace(left_prefix,'')\n remove_side_tag_right = right_obj.replace(right_prefix,'')\n if remove_side_tag_left == remove_side_tag_right:\n # print(right_obj + ' was paired with ' + left_obj)\n \n key = gt_ab_ctrls_dict.get(remove_side_tag_right) # TR = [(ivnerted?,ivnerted?,ivnerted?),(ivnerted?,ivnerted?,ivnerted?)]\n transforms = []\n\n # Mirroring Transform?, Inverting it? (X,Y,Z), Transform name.\n transforms.append([True, key[0][0], 'tx']) \n transforms.append([True, key[0][1], 'ty'])\n transforms.append([True, key[0][2], 'tz'])\n transforms.append([True, key[1][0], 'rx'])\n transforms.append([True, key[1][1], 'ry'])\n transforms.append([True, key[1][2], 'rz'])\n \n if len(key) > 2: # Mirroring Scale?\n transforms.append([True, False, 'sx'])\n transforms.append([True, False, 'sy'])\n transforms.append([True, False, 'sz'])\n \n # Transfer Right to Left\n if source_side is 'right':\n for transform in transforms:\n if transform[0]: # Using Transform?\n if transform[1]: # Inverted?\n source_transform = (cmds.getAttr(namespace + right_obj + '.' + transform[2]) * -1)\n else:\n source_transform = cmds.getAttr(namespace + right_obj + '.' + transform[2])\n\n if not cmds.getAttr(namespace + left_obj + '.' + transform[2], lock=True):\n cmds.setAttr(namespace + left_obj + '.' + transform[2], source_transform)\n else:\n errors.append(namespace + left_obj + ' \"' + transform[2]+'\" is locked.' )\n \n # Transfer Left to Right\n if source_side is 'left':\n for transform in transforms:\n if transform[0]: # Using Transform?\n if transform[1]: # Inverted?\n source_transform = (cmds.getAttr(namespace + left_obj + '.' + transform[2]) * -1)\n else:\n source_transform = cmds.getAttr(namespace + left_obj + '.' + transform[2])\n \n if not cmds.getAttr(namespace + right_obj + '.' + transform[2], lock=True):\n cmds.setAttr(namespace + right_obj + '.' + transform[2], source_transform)\n else:\n errors.append(namespace + right_obj + ' \"' + transform[2]+'\" is locked.' )\n \n # Print Feedback\n unique_message = '<' + str(random.random()) + '>'\n source_message = '(Left to Right)'\n if source_side == 'right':\n source_message = '(Right to Left)'\n cmds.inViewMessage(amg=unique_message + 'Pose mirrored! ' + source_message, pos='botLeft', fade=True, alpha=.9)\n \n \n if len(errors) != 0:\n unique_message = '<' + str(random.random()) + '>'\n if len(errors) == 1:\n is_plural = 'attribute was'\n else:\n is_plural = 'attributes were'\n for error in errors:\n print(str(error))\n sys.stdout.write(str(len(errors)) + ' locked '+ is_plural + ' ignored. (Open Script Editor to see a list)\\n')\n else:\n cmds.warning('No controls were found. Please check if a namespace is necessary.')\n cmds.setFocus(\"MayaWindow\")\n \ndef gt_ab_reset_pose(gt_ab_ik_ctrls, gt_ab_fk_ctrls, gt_ab_center_ctrls, namespace=''):\n '''\n Reset transforms list of controls back to 0 Transalte and Rotate values. \n\n Parameters:\n gt_ab_ik_ctrls (dict, list) : A list or dictionary of IK controls without their side prefix (e.g. \"_wrist_ctrl\")\n gt_ab_fk_ctrls (dict, list) : A list or dictionary of FK controls without their side prefix (e.g. \"_wrist_ctrl\")\n gt_ab_center_ctrls (dict, list) : A list or dictionary of center controls (full names) (e.g. \"spine01_ctrl\")\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n '''\n available_ctrls = []\n for obj in gt_ab_ik_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_fk_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_general_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_center_ctrls:\n if cmds.objExists(namespace + obj):\n available_ctrls.append(obj)\n \n if len(available_ctrls) == 0:\n cmds.warning('No controls were found. Please check if a namespace is necessary.')\n else:\n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Pose Reset!', pos='botLeft', fade=True, alpha=.9)\n \n for ctrl in available_ctrls:\n dimensions = ['x','y','z']\n transforms = ['t', 'r', 's']\n for transform in transforms:\n for dimension in dimensions:\n try:\n if cmds.getAttr(namespace + ctrl + '.' + transform + dimension, lock=True) is False:\n cmds.setAttr(namespace + ctrl + '.' + transform + dimension, 0)\n except:\n pass\n \n # Special Cases\n special_case_ctrls = ['left_fingers_ctrl', 'right_fingers_ctrl']\n for ctrl in special_case_ctrls:\n if cmds.objExists(namespace + ctrl):\n if cmds.getAttr(namespace + ctrl + '.' + 'sz', lock=True) is False:\n cmds.setAttr(namespace + ctrl + '.' + 'sz', 2)\n\n\ndef gt_export_rig_pose(namespace =''):\n ''' \n Exports a Pose (JSON) file containing the translate, rotate and scale data from the rig controls (used to export a pose)\n Added a variable called \"gt_auto_biped_export_method\" after v1.3, so the extraction method can be stored.\n \n Parameters:\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n ''' \n # Validate Operation and Write file\n is_valid = True\n successfully_created_file = False\n\n # Find Available Controls\n available_ctrls = []\n for obj in gt_ab_ik_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_fk_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_general_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_center_ctrls:\n if cmds.objExists(namespace + obj):\n available_ctrls.append(obj)\n \n # No Controls were found\n if len(available_ctrls) == 0:\n is_valid=False\n cmds.warning('No controls were found. Make sure you are using the correct namespace.')\n\n\n if is_valid:\n file_name = cmds.fileDialog2(fileFilter=script_name + \" - POSE File (*.pose)\", dialogStyle=2, okCaption= 'Export', caption= 'Exporting Rig Pose for \"' + script_name + '\"') or []\n if len(file_name) > 0:\n pose_file = file_name[0]\n successfully_created_file = True\n \n\n if successfully_created_file and is_valid:\n export_dict = {'gt_interface_version' : script_version, 'gt_export_method' : 'object-space'}\n for obj in available_ctrls:\n translate = cmds.getAttr(obj + '.translate')[0]\n rotate = cmds.getAttr(obj + '.rotate')[0]\n scale = cmds.getAttr(obj + '.scale')[0]\n to_save = [obj, translate, rotate, scale]\n export_dict[obj] = to_save\n \n try: \n with open(pose_file, 'w') as outfile:\n json.dump(export_dict, outfile, indent=4)\n\n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Current Pose exported to ' + os.path.basename(file_name[0]) +'.', pos='botLeft', fade=True, alpha=.9)\n sys.stdout.write('Pose exported to the file \"' + pose_file + '\".')\n except Exception as e:\n print (e)\n successfully_created_file = False\n cmds.warning('Couldn\\'t write to file. Please make sure the exporting directory is accessible.')\n\n\ndef gt_import_rig_pose(debugging=False, debugging_path='', namespace=''):\n ''' \n Imports a POSE (JSON) file containing the translate, rotate and scale data for the rig controls (exported using the \"gt_export_rig_pose\" function)\n Uses the imported data to set the translate, rotate and scale position of every control curve\n \n Parameters:\n debugging (bool): If debugging, the function will attempt to auto load the file provided in the \"debugging_path\" parameter\n debugging_path (string): Debugging path for the import function\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n TODO\n Check import method to use the proper method when setting attributes.\n Exporting using the export button uses \"setAttr\", extract functions will use \"xform\" instead.\n \n ''' \n def set_unlocked_os_attr(target, attr, value):\n ''' \n Sets an attribute to the provided value in case it's not locked (Uses \"cmds.setAttr\" function so object space)\n \n Parameters:\n target (string): Name of the target object (object that will receive transforms)\n attr (string): Name of the attribute to apply (no need to add \".\", e.g. \"rx\" would be enough)\n value (float): Value used to set attribute. e.g. 1.5, 2, 5...\n \n '''\n try:\n if not cmds.getAttr(target + '.' + attr, lock=True):\n cmds.setAttr(target + '.' + attr, value)\n except:\n pass\n \n def set_unlocked_ws_attr(target, attr, value_tuple):\n ''' \n Sets an attribute to the provided value in case it's not locked (Uses \"cmds.xform\" function with world space)\n \n Parameters:\n target (string): Name of the target object (object that will receive transforms)\n attr (string): Name of the attribute to apply (no need to add \".\", e.g. \"rx\" would be enough)\n value_tuple (tuple): A tuple with three (3) floats used to set attributes. e.g. (1.5, 2, 5)\n \n '''\n try:\n if attr == 'translate':\n cmds.xform(target, ws=True, t=value_tuple)\n if attr == 'rotate':\n cmds.xform(target, ws=True, ro=value_tuple)\n if attr == 'scale':\n cmds.xform(target, ws=True, s=value_tuple)\n except:\n pass\n \n \n # Find Available Controls\n available_ctrls = []\n for obj in gt_ab_ik_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_fk_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_general_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_center_ctrls:\n if cmds.objExists(namespace + obj):\n available_ctrls.append(obj)\n \n # Track Current State\n import_version = 0.0\n import_method = 'object-space'\n \n if not debugging:\n file_name = cmds.fileDialog2(fileFilter=script_name + \" - POSE File (*.pose)\", dialogStyle=2, fileMode= 1, okCaption= 'Import', caption= 'Importing Proxy Pose for \"' + script_name + '\"') or []\n else:\n file_name = [debugging_path]\n \n if len(file_name) > 0:\n pose_file = file_name[0]\n file_exists = True\n else:\n file_exists = False\n \n if file_exists:\n try: \n with open(pose_file) as json_file:\n data = json.load(json_file)\n try:\n is_valid_file = True\n is_operation_valid = True\n\n if not data.get('gt_interface_version'):\n is_valid_file = False\n cmds.warning('Imported file doesn\\'t seem to be compatible or is missing data.')\n else: \n import_version = float(re.sub(\"[^0-9]\", \"\", str(data.get('gt_interface_version'))))\n \n if data.get('gt_export_method'):\n import_method = data.get('gt_export_method')\n \n if len(available_ctrls) == 0:\n cmds.warning('No controls were found. Please check if a namespace is necessary.')\n is_operation_valid = False\n \n if is_operation_valid:\n # Object-Space\n for ctrl in data:\n if ctrl != 'gt_interface_version' and ctrl != 'gt_export_method':\n curent_object = data.get(ctrl) # Name, T, R, S\n if cmds.objExists(namespace + curent_object[0]):\n set_unlocked_os_attr(namespace + curent_object[0], 'tx', curent_object[1][0])\n set_unlocked_os_attr(namespace + curent_object[0], 'ty', curent_object[1][1])\n set_unlocked_os_attr(namespace + curent_object[0], 'tz', curent_object[1][2])\n set_unlocked_os_attr(namespace + curent_object[0], 'rx', curent_object[2][0])\n set_unlocked_os_attr(namespace + curent_object[0], 'ry', curent_object[2][1])\n set_unlocked_os_attr(namespace + curent_object[0], 'rz', curent_object[2][2])\n set_unlocked_os_attr(namespace + curent_object[0], 'sx', curent_object[3][0])\n set_unlocked_os_attr(namespace + curent_object[0], 'sy', curent_object[3][1])\n set_unlocked_os_attr(namespace + curent_object[0], 'sz', curent_object[3][2])\n \n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Pose imported from ' + os.path.basename(pose_file) +'.', pos='botLeft', fade=True, alpha=.9)\n sys.stdout.write('Pose imported from the file \"' + pose_file + '\".')\n \n except Exception as e:\n print(e)\n cmds.warning('An error occured when importing the pose. Make sure you imported a valid POSE file.')\n except:\n file_exists = False\n cmds.warning('Couldn\\'t read the file. Please make sure the selected file is accessible.')\n\n\ndef gt_ab_mirror_anim(gt_ab_ctrls, source_side, namespace=''):\n '''\n Mirrors the character animation from one side to the other\n\n Parameters:\n gt_ab_ctrls (dict) : A list of dictionaries of controls without their side prefix (e.g. \"_wrist_ctrl\")\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n '''\n \n def invert_float_list_values(float_list):\n '''\n Returns a list where all the float values are inverted. For example, if the value is 5, it will then become -5.\n\n Parameters:\n float_list (list) : A list of floats.\n \n Returns:\n inverted_float_list (list): A list of floats with their values inverted\n \n '''\n\n inverted_values = []\n for val in float_list:\n inverted_values.append(val* -1)\n return inverted_values\n\n \n # Merge Dictionaries\n gt_ab_ctrls_dict = {}\n for ctrl_dict in gt_ab_ctrls:\n gt_ab_ctrls_dict.update(ctrl_dict)\n \n # Find available Ctrls\n available_ctrls = []\n for obj in gt_ab_ctrls_dict:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n # Start Mirroring\n if len(available_ctrls) != 0:\n \n errors = []\n \n right_side_objects = []\n left_side_objects = []\n\n for obj in available_ctrls: \n if right_prefix in obj:\n right_side_objects.append(obj)\n \n for obj in available_ctrls: \n if left_prefix in obj:\n left_side_objects.append(obj)\n \n for left_obj in left_side_objects:\n for right_obj in right_side_objects:\n remove_side_tag_left = left_obj.replace(left_prefix,'')\n remove_side_tag_right = right_obj.replace(right_prefix,'')\n if remove_side_tag_left == remove_side_tag_right:\n # print(right_obj + ' was paired with ' + left_obj)\n \n key = gt_ab_ctrls_dict.get(remove_side_tag_right) # TR = [(ivnerted?,ivnerted?,ivnerted?),(ivnerted?,ivnerted?,ivnerted?)]\n transforms = []\n\n # Mirroring Transform?, Inverting it? (X,Y,Z), Transform name.\n transforms.append([True, key[0][0], 'tx']) \n transforms.append([True, key[0][1], 'ty'])\n transforms.append([True, key[0][2], 'tz'])\n transforms.append([True, key[1][0], 'rx'])\n transforms.append([True, key[1][1], 'ry'])\n transforms.append([True, key[1][2], 'rz'])\n \n if len(key) > 2: # Mirroring Scale?\n transforms.append([True, False, 'sx'])\n transforms.append([True, False, 'sy'])\n transforms.append([True, False, 'sz'])\n \n # Transfer Right to Left \n if source_side is 'right':\n for transform in transforms:\n if transform[0]: # Using Transform? Inverted? Name of the Attr\n try:\n attr = transform[2]\n \n # Get Values\n frames = cmds.keyframe(namespace + right_obj, q=1, at=attr)\n values = cmds.keyframe(namespace + right_obj, q=1, at=attr, valueChange=True)\n \n in_angle_tangent = cmds.keyTangent(namespace + right_obj, at=attr, inAngle=True, query=True)\n out_angle_tanget = cmds.keyTangent(namespace + right_obj, at=attr, outAngle=True, query=True)\n is_locked = cmds.keyTangent(namespace + right_obj, at=attr, weightLock=True, query=True)\n in_weight = cmds.keyTangent(namespace + right_obj, at=attr, inWeight=True, query=True)\n out_weight = cmds.keyTangent(namespace + right_obj, at=attr, outWeight=True, query=True)\n in_tangent_type = cmds.keyTangent(namespace + right_obj, at=attr, inTangentType=True, query=True)\n out_tangent_type = cmds.keyTangent(namespace + right_obj, at=attr, outTangentType=True, query=True)\n \n if transform[1]: # Inverted?\n values = invert_float_list_values(values)\n in_angle_tangent = invert_float_list_values(in_angle_tangent)\n out_angle_tanget = invert_float_list_values(out_angle_tanget)\n in_weight = invert_float_list_values(in_weight)\n out_weight = invert_float_list_values(out_weight)\n\n\n # Set Keys/Values\n for index in range(len(values)):\n time = frames[index]\n cmds.setKeyframe(namespace + left_obj, time=time, attribute=attr, value=values[index])\n # Set Tangents\n cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), lock=is_locked[index], e=True)\n cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), inAngle=in_angle_tangent[index], e=True)\n cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), outAngle=out_angle_tanget[index], e=True)\n cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), inWeight=in_weight[index], e=True)\n cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), outWeight=out_weight[index], e=True)\n cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), inTangentType=in_tangent_type[index], e=True)\n cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), outTangentType=out_tangent_type[index], e=True)\n except:\n pass # 0 keyframes\n\n # Other Attributes\n attributes = cmds.listAnimatable(namespace + right_obj)\n default_channels = ['translateX', 'translateY', 'translateZ', 'rotateX', 'rotateY', 'rotateZ']\n for attr in attributes:\n try:\n short_attr = attr.split('.')[-1]\n if short_attr not in default_channels:\n # Get Keys/Values\n frames = cmds.keyframe(namespace + right_obj, q=1, at=short_attr)\n values = cmds.keyframe(namespace + right_obj, q=1, at=short_attr, valueChange=True)\n\n # Set Keys/Values\n for index in range(len(values)):\n cmds.setKeyframe(namespace + left_obj, time=frames[index], attribute=short_attr, value=values[index])\n except:\n pass # 0 keyframes\n \n # Transfer Left to Right\n if source_side is 'left':\n for transform in transforms:\n if transform[0]: # Using Transform? Inverted? Name of the Attr\n try:\n attr = transform[2]\n \n # Get Values\n frames = cmds.keyframe(namespace + left_obj, q=1, at=attr)\n values = cmds.keyframe(namespace + left_obj, q=1, at=attr, valueChange=True)\n \n in_angle_tangent = cmds.keyTangent(namespace + left_obj, at=attr, inAngle=True, query=True)\n out_angle_tanget = cmds.keyTangent(namespace + left_obj, at=attr, outAngle=True, query=True)\n is_locked = cmds.keyTangent(namespace + left_obj, at=attr, weightLock=True, query=True)\n in_weight = cmds.keyTangent(namespace + left_obj, at=attr, inWeight=True, query=True)\n out_weight = cmds.keyTangent(namespace + left_obj, at=attr, outWeight=True, query=True)\n in_tangent_type = cmds.keyTangent(namespace + left_obj, at=attr, inTangentType=True, query=True)\n out_tangent_type = cmds.keyTangent(namespace + left_obj, at=attr, outTangentType=True, query=True)\n \n if transform[1]: # Inverted?\n values = invert_float_list_values(values)\n in_angle_tangent = invert_float_list_values(in_angle_tangent)\n out_angle_tanget = invert_float_list_values(out_angle_tanget)\n in_weight = invert_float_list_values(in_weight)\n out_weight = invert_float_list_values(out_weight)\n\n\n # Set Keys/Values\n for index in range(len(values)):\n time = frames[index]\n cmds.setKeyframe(namespace + right_obj, time=time, attribute=attr, value=values[index])\n # Set Tangents\n cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), lock=is_locked[index], e=True)\n cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), inAngle=in_angle_tangent[index], e=True)\n cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), outAngle=out_angle_tanget[index], e=True)\n cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), inWeight=in_weight[index], e=True)\n cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), outWeight=out_weight[index], e=True)\n cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), inTangentType=in_tangent_type[index], e=True)\n cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), outTangentType=out_tangent_type[index], e=True)\n except:\n pass # 0 keyframes\n\n # Other Attributes\n attributes = cmds.listAnimatable(namespace + left_obj)\n default_channels = ['translateX', 'translateY', 'translateZ', 'rotateX', 'rotateY', 'rotateZ']\n for attr in attributes:\n try:\n short_attr = attr.split('.')[-1]\n if short_attr not in default_channels:\n # Get Keys/Values\n frames = cmds.keyframe(namespace + left_obj, q=1, at=short_attr)\n values = cmds.keyframe(namespace + left_obj, q=1, at=short_attr, valueChange=True)\n\n # Set Keys/Values\n for index in range(len(values)):\n cmds.setKeyframe(namespace + right_obj, time=frames[index], attribute=short_attr, value=values[index])\n except:\n pass # 0 keyframes\n\n # Print Feedback\n unique_message = '<' + str(random.random()) + '>'\n source_message = '(Left to Right)'\n if source_side == 'right':\n source_message = '(Right to Left)'\n cmds.inViewMessage(amg=unique_message + 'Animation mirrored! ' + source_message, pos='botLeft', fade=True, alpha=.9)\n \n if len(errors) != 0:\n unique_message = '<' + str(random.random()) + '>'\n if len(errors) == 1:\n is_plural = ' error '\n else:\n is_plural = ' errors '\n for error in errors:\n print(str(error))\n sys.stdout.write(str(len(errors)) + is_plural + 'occurred. (Open Script Editor to see a list)\\n')\n else:\n cmds.warning('No controls were found. Please check if a namespace is necessary.')\n cmds.setFocus(\"MayaWindow\")\n\n\n\ndef gt_export_rig_animation(namespace =''):\n ''' \n Exports an ANIM (JSON) file containing the translate, rotate and scale keyframe (animation) data from the rig controls.\n\n Parameters:\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls.\n \n ''' \n # Validate Operation and Write file\n is_valid = True\n successfully_created_file = False\n\n # Find Available Controls\n available_ctrls = []\n for obj in gt_ab_ik_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_fk_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_general_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_center_ctrls:\n if cmds.objExists(namespace + obj):\n available_ctrls.append(obj)\n \n \n # No Controls were found\n if len(available_ctrls) == 0:\n is_valid=False\n cmds.warning('No controls were found. Make sure you are using the correct namespace.')\n\n\n if is_valid:\n file_name = cmds.fileDialog2(fileFilter=script_name + \" - ANIM File (*.anim)\", dialogStyle=2, okCaption= 'Export', caption= 'Exporting Rig Animation for \"' + script_name + '\"') or []\n if len(file_name) > 0:\n pose_file = file_name[0]\n successfully_created_file = True\n \n\n if successfully_created_file and is_valid:\n export_dict = {'gt_interface_version' : script_version, 'gt_export_method' : 'object-space'}\n \n # Extract Keyframes:\n for obj in available_ctrls:\n attributes = cmds.listAnimatable(namespace + obj)\n for attr in attributes:\n try:\n short_attr = attr.split('.')[-1]\n frames = cmds.keyframe(namespace + obj, q=1, at=short_attr)\n values = cmds.keyframe(namespace + obj, q=1, at=short_attr, valueChange=True)\n in_angle_tangent = cmds.keyTangent(namespace + obj, at=short_attr, inAngle=True, query=True)\n out_angle_tanget = cmds.keyTangent(namespace + obj, at=short_attr, outAngle=True, query=True)\n is_locked = cmds.keyTangent(namespace + obj, at=short_attr, weightLock=True, query=True)\n in_weight = cmds.keyTangent(namespace + obj, at=short_attr, inWeight=True, query=True)\n out_weight = cmds.keyTangent(namespace + obj, at=short_attr, outWeight=True, query=True)\n in_tangent_type = cmds.keyTangent(namespace + obj, at=short_attr, inTangentType=True, query=True)\n out_tangent_type = cmds.keyTangent(namespace + obj, at=short_attr, outTangentType=True, query=True)\n export_dict['{}.{}'.format(obj, short_attr)] = zip(frames, values, in_angle_tangent, out_angle_tanget, is_locked, in_weight, out_weight, in_tangent_type, out_tangent_type)\n except:\n pass # 0 keyframes\n\n\n try: \n with open(pose_file, 'w') as outfile:\n json.dump(export_dict, outfile, indent=4)\n \n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Current Animation exported to ' + os.path.basename(file_name[0]) +'.', pos='botLeft', fade=True, alpha=.9)\n sys.stdout.write('Animation exported to the file \"' + pose_file + '\".')\n except Exception as e:\n print (e)\n successfully_created_file = False\n cmds.warning('Couldn\\'t write to file. Please make sure the exporting directory is accessible.')\n\n\ndef gt_import_rig_animation(debugging=False, debugging_path='', namespace=''):\n ''' \n Imports an ANIM (JSON) file containing the translate, rotate and scale keyframe data for the rig controls (exported using the \"gt_export_rig_animation\" function)\n Uses the imported data to set the translate, rotate and scale position of every control curve\n \n Parameters:\n debugging (bool): If debugging, the function will attempt to auto load the file provided in the \"debugging_path\" parameter\n debugging_path (string): Debugging path for the import function\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls. \n ''' \n # Find Available Controls\n available_ctrls = []\n for obj in gt_ab_ik_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_fk_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_general_ctrls:\n if cmds.objExists(namespace + left_prefix + obj):\n available_ctrls.append(left_prefix + obj)\n if cmds.objExists(namespace + right_prefix + obj):\n available_ctrls.append(right_prefix + obj)\n \n for obj in gt_ab_center_ctrls:\n if cmds.objExists(namespace + obj):\n available_ctrls.append(obj)\n \n # Track Current State\n import_version = 0.0\n import_method = 'object-space'\n \n if not debugging:\n file_name = cmds.fileDialog2(fileFilter=script_name + \" - ANIM File (*.anim)\", dialogStyle=2, fileMode= 1, okCaption= 'Import', caption= 'Importing Proxy Pose for \"' + script_name + '\"') or []\n else:\n file_name = [debugging_path]\n \n if len(file_name) > 0:\n anim_file = file_name[0]\n file_exists = True\n else:\n file_exists = False\n \n if file_exists:\n try: \n with open(anim_file) as json_file:\n data = json.load(json_file)\n try:\n is_valid_file = True\n is_operation_valid = True\n\n if not data.get('gt_interface_version'):\n is_valid_file = False\n cmds.warning('Imported file doesn\\'t seem to be compatible or is missing data.')\n else: \n import_version = float(re.sub(\"[^0-9]\", \"\", str(data.get('gt_interface_version'))))\n \n if data.get('gt_export_method'):\n import_method = data.get('gt_export_method')\n \n if len(available_ctrls) == 0:\n cmds.warning('No controls were found. Please check if a namespace is necessary.')\n is_operation_valid = False\n \n if is_operation_valid:\n # Object-Space\n for key, dict_value in data.iteritems():\n if key != 'gt_interface_version' and key != 'gt_export_method':\n for key_data in dict_value:\n # Unpack Data\n time = key_data[0]\n value = key_data[1]\n in_angle_tangent = key_data[2]\n out_angle_tanget = key_data[3] \n is_locked = key_data[4]\n in_weight = key_data[5]\n out_weight = key_data[6] \n in_tangent_type = key_data[7] \n out_tangent_type = key_data[8] \n \n try:\n obj, attr = key.split('.')\n cmds.setKeyframe(namespace + obj, time=time, attribute=attr, value=value)\n cmds.keyTangent(namespace + obj, at=attr, time=(time,time), lock=is_locked, e=True)\n cmds.keyTangent(namespace + obj, at=attr, time=(time,time), inAngle=in_angle_tangent, e=True)\n cmds.keyTangent(namespace + obj, at=attr, time=(time,time), outAngle=out_angle_tanget, e=True)\n cmds.keyTangent(namespace + obj, at=attr, time=(time,time), inWeight=in_weight, e=True)\n cmds.keyTangent(namespace + obj, at=attr, time=(time,time), outWeight=out_weight, e=True)\n cmds.keyTangent(namespace + obj, at=attr, time=(time,time), inTangentType=in_tangent_type, e=True)\n cmds.keyTangent(namespace + obj, at=attr, time=(time,time), outTangentType=out_tangent_type, e=True)\n except:\n pass\n\n unique_message = '<' + str(random.random()) + '>'\n cmds.inViewMessage(amg=unique_message + 'Animation imported from ' + os.path.basename(anim_file) +'.', pos='botLeft', fade=True, alpha=.9)\n sys.stdout.write('Animation imported from the file \"' + anim_file + '\".')\n \n except Exception as e:\n print(e)\n cmds.warning('An error occured when importing the pose. Make sure you imported a valid ANIM file.')\n except:\n file_exists = False\n cmds.warning('Couldn\\'t read the file. Please make sure the selected file is accessible.')\n \n \ndef gt_reset_rig_animation(namespace=''):\n '''\n Deletes all keyframes and resets pose (Doesn't include Set Driven Keys)\n \n Parameters:\n namespace (string): In case the rig has a namespace, it will be used to properly select the controls. \n ''' \n function_name = 'GT Reset Rig Animation'\n cmds.undoInfo(openChunk=True, chunkName=function_name)\n try:\n keys_ta = cmds.ls(type='animCurveTA')\n keys_tl = cmds.ls(type='animCurveTL')\n keys_tt = cmds.ls(type='animCurveTT')\n keys_tu = cmds.ls(type='animCurveTU')\n deleted_counter = 0\n all_keyframes = keys_ta + keys_tl + keys_tt + keys_tu\n for key in all_keyframes:\n try:\n key_target_namespace = cmds.listConnections(key, destination=True)[0].split(':')[0]\n if key_target_namespace == namespace.replace(':', '') or len(cmds.listConnections(key, destination=True)[0].split(':')) == 1:\n cmds.delete(key)\n deleted_counter += 1\n except:\n pass \n message = '' + str(deleted_counter) + ' '\n is_plural = 'keyframe nodes were'\n if deleted_counter == 1:\n is_plural = 'keyframe node was'\n message += is_plural + ' deleted.'\n \n cmds.inViewMessage(amg=message, pos='botLeft', fade=True, alpha=.9)\n \n # gt_ab_reset_pose(gt_ab_ik_ctrls, gt_ab_fk_ctrls, gt_ab_center_ctrls, namespace) # Add as an option?\n \n except Exception as e:\n cmds.warning(str(e))\n finally:\n cmds.undoInfo(closeChunk=True, chunkName=function_name)\n \n#Build UI\nif __name__ == '__main__':\n build_gui_custom_rig_interface()",
label='GTRig', tooltip='This button opens the Custom Rig Interface for GT Auto Biped Rigger.', image='out_timeEditorAnimSource.png', label_color=(1, 0.45, 0))
cmds.inViewMessage(amg='Custom Rig Interface button was added to your current shelf.', pos='botLeft', fade=True, alpha=.9)
diff --git a/python-scripts/gt_biped_rig_interface.py b/python-scripts/gt_biped_rig_interface.py
index f6a8a02e..6258d757 100644
--- a/python-scripts/gt_biped_rig_interface.py
+++ b/python-scripts/gt_biped_rig_interface.py
@@ -45,13 +45,16 @@
Fixed issue where the arm pole vector wouldn't mirror properly
Added option to reset persistent settings
+ 1.3.5 - 2021-11-10
+ Added animation and pose reset
+ Updates animation functions to account for tangents and other key properties
+
TODO:
Convert GUI to QT
- Extract keyframe tangents
- Add Flip options to animation and pose (instead of just mirror)
+ Add Flip options
+ Overwrite keys for animation functions
Add Namespace picker (button to the right of the namespace textfield)
Option to save pose thumbnail when exporting it
- Option to show or not feedback messages
Add option to open multiple instances
"""
@@ -79,7 +82,7 @@
unique_rig = '' # If provided, it will be used in the window title
# Version:
-script_version = "1.3.4"
+script_version = "1.3.5"
# Python Version
python_version = sys.version_info.major
@@ -248,7 +251,8 @@
'auto_key_method_bake' : True,
'auto_key_start_frame' : 1,
'auto_key_end_frame' : 10,
- 'pose_export_thumbnail' : True,
+ 'pose_export_thumbnail' : False,
+ 'allow_multiple_instances' : False,
}
gt_ab_interface_settings_default = copy.deepcopy(gt_ab_interface_settings)
@@ -397,7 +401,6 @@ def update_switch(ik_fk_dict, direction='ik_to_fk', is_auto_switch=False):
update_fk_ik_buttons()
-
def get_auto_key_current_frame(target_integer_field='start'):
'''
@@ -426,6 +429,14 @@ def mirror_fk_ik_pose(source_side='right'):
gt_ab_mirror_pose([gt_ab_general_ctrls, gt_ab_ik_ctrls, gt_ab_fk_ctrls], source_side, namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator)
+
+ def reset_animation_and_pose():
+ '''
+ Deletes Keyframes and Resets pose back to default
+ '''
+ gt_reset_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator)
+ gt_ab_reset_pose(gt_ab_ik_ctrls, gt_ab_fk_ctrls, gt_ab_center_ctrls, namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator)
+
def build_custom_help_window(input_text, help_title=''):
'''
Creates a help window to display the provided text
@@ -674,7 +685,7 @@ def close_help_gui():
cmds.separator(h=btn_margin, style='none', p=anim_management_column) # Empty Space
cmds.button(l ="Reset Animation (Delete Keyframes)", c=lambda x:gt_reset_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=anim_management_column)
cmds.separator(h=btn_margin, style='none', p=anim_management_column) # Empty Space
- cmds.button(l ="Reset Animation and Pose", c=lambda x:gt_reset_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=anim_management_column)
+ cmds.button(l ="Reset Animation and Pose", c=lambda x:reset_animation_and_pose(), p=anim_management_column)
# Export Import Pose
@@ -687,10 +698,9 @@ def close_help_gui():
cmds.button(l ="Import Animation", c=lambda x:gt_import_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=import_export_pose_column)
cmds.button(l ="Export Animation", c=lambda x:gt_export_rig_animation(namespace=cmds.textField(namespace_txt, q=True, text=True)+namespace_separator), p=import_export_pose_column)
-
+
############# Settings Tab #############
settings_tab = cmds.rowColumnLayout(nc=1, cw=[(1, 240)], cs=[(1,0)], p=tabs)
- # settings_column = cmds.rowColumnLayout(nc=1, cw=[(1, 240)], cs=[(1, 0)], p=settings_tab)
# General Settings
enabled_bgc_color = (.4, .4, .4)
@@ -700,19 +710,26 @@ def close_help_gui():
cmds.separator(h=5, style='none') # Empty Space
cmds.rowColumnLayout(nc=3, cw=[(1, 10), (2, 200), (3, 20)], cs=[(1,10)])
+ # Allow Multiple Instances
+ is_option_enabled = False
+ cmds.text(' ', bgc=(enabled_bgc_color if is_option_enabled else disabled_bgc_color), h=20) # Tiny Empty Spac
+ cmds.checkBox( label=' Allow Multiple Instances', value=gt_ab_interface_settings.get('allow_multiple_instances'), ebg=True, cc=lambda x:invert_stored_setting('allow_multiple_instances'), en=is_option_enabled)
+
+ multiple_instances_help_message = 'Allow Multiple Instances Help Placeholder.'
+ multiple_instances_help_title = 'Allow Multiple Instances'
+ cmds.button(l ='?', bgc=enabled_bgc_color, c=lambda x:build_custom_help_window(multiple_instances_help_message, multiple_instances_help_title))
+
# Export Thumbnail With Pose
is_option_enabled = False
cmds.text(' ', bgc=(enabled_bgc_color if is_option_enabled else disabled_bgc_color), h=20) # Tiny Empty Spac
- # cmds.text(' ', bgc=disabled_color, h=20) # Tiny Empty Space
cmds.checkBox( label=' Export Thumbnail with Pose', value=gt_ab_interface_settings.get('pose_export_thumbnail'), ebg=True, cc=lambda x:invert_stored_setting('pose_export_thumbnail'), en=is_option_enabled)
export_pose_thumbnail_help_message = 'Exports a thumbnail ".jpg" file together with your ".pose" file.\nThis extra thumbnail file can be used to quickly undestand what you pose looks like before importing it.\n\nThe thumbnail is a screenshot of you active viewport at the moment of exporting the pose. If necessary, export it again to generate another thumbnail.'
export_pose_thumbnail_help_title = 'Export Thumbnail with Pose'
cmds.button(l ='?', bgc=enabled_bgc_color, c=lambda x:build_custom_help_window(export_pose_thumbnail_help_message, export_pose_thumbnail_help_title))
-
+
# Reset Persistent Settings
cmds.separator(h=btn_margin, style='none', p=settings_tab) # Empty Space
-
settings_buttons_column = cmds.rowColumnLayout(nc=1, cw=[(1, 240)], cs=[(1,10)], p=settings_tab)
cmds.button(l ="Reset Persistent Settings", c=lambda x:reset_persistent_settings_auto_biped_rig_interface(), p=settings_buttons_column)
@@ -1325,6 +1342,25 @@ def gt_ab_mirror_anim(gt_ab_ctrls, source_side, namespace=''):
namespace (string): In case the rig has a namespace, it will be used to properly select the controls.
'''
+
+ def invert_float_list_values(float_list):
+ '''
+ Returns a list where all the float values are inverted. For example, if the value is 5, it will then become -5.
+
+ Parameters:
+ float_list (list) : A list of floats.
+
+ Returns:
+ inverted_float_list (list): A list of floats with their values inverted
+
+ '''
+
+ inverted_values = []
+ for val in float_list:
+ inverted_values.append(val* -1)
+ return inverted_values
+
+
# Merge Dictionaries
gt_ab_ctrls_dict = {}
for ctrl_dict in gt_ab_ctrls:
@@ -1380,20 +1416,44 @@ def gt_ab_mirror_anim(gt_ab_ctrls, source_side, namespace=''):
# Transfer Right to Left
if source_side is 'right':
for transform in transforms:
- if transform[0]: # Using Transform?
- # Get Keys/Values
- frames = cmds.keyframe(namespace + right_obj, q=1, at=transform[2])
- values = cmds.keyframe(namespace + right_obj, q=1, at=transform[2], valueChange=True)
-
- if transform[1]: # Inverted?
- inverted_values = []
- for val in values:
- inverted_values.append(val* -1)
- values = inverted_values
-
- # Set Keys/Values
- for index in range(len(values)):
- cmds.setKeyframe(namespace + left_obj, time=frames[index], attribute=transform[2], value=values[index])
+ if transform[0]: # Using Transform? Inverted? Name of the Attr
+ try:
+ attr = transform[2]
+
+ # Get Values
+ frames = cmds.keyframe(namespace + right_obj, q=1, at=attr)
+ values = cmds.keyframe(namespace + right_obj, q=1, at=attr, valueChange=True)
+
+ in_angle_tangent = cmds.keyTangent(namespace + right_obj, at=attr, inAngle=True, query=True)
+ out_angle_tanget = cmds.keyTangent(namespace + right_obj, at=attr, outAngle=True, query=True)
+ is_locked = cmds.keyTangent(namespace + right_obj, at=attr, weightLock=True, query=True)
+ in_weight = cmds.keyTangent(namespace + right_obj, at=attr, inWeight=True, query=True)
+ out_weight = cmds.keyTangent(namespace + right_obj, at=attr, outWeight=True, query=True)
+ in_tangent_type = cmds.keyTangent(namespace + right_obj, at=attr, inTangentType=True, query=True)
+ out_tangent_type = cmds.keyTangent(namespace + right_obj, at=attr, outTangentType=True, query=True)
+
+ if transform[1]: # Inverted?
+ values = invert_float_list_values(values)
+ in_angle_tangent = invert_float_list_values(in_angle_tangent)
+ out_angle_tanget = invert_float_list_values(out_angle_tanget)
+ in_weight = invert_float_list_values(in_weight)
+ out_weight = invert_float_list_values(out_weight)
+
+
+ # Set Keys/Values
+ for index in range(len(values)):
+ time = frames[index]
+ cmds.setKeyframe(namespace + left_obj, time=time, attribute=attr, value=values[index])
+ # Set Tangents
+ cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), lock=is_locked[index], e=True)
+ cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), inAngle=in_angle_tangent[index], e=True)
+ cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), outAngle=out_angle_tanget[index], e=True)
+ cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), inWeight=in_weight[index], e=True)
+ cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), outWeight=out_weight[index], e=True)
+ cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), inTangentType=in_tangent_type[index], e=True)
+ cmds.keyTangent(namespace + left_obj, at=attr, time=(time,time), outTangentType=out_tangent_type[index], e=True)
+ except:
+ pass # 0 keyframes
# Other Attributes
attributes = cmds.listAnimatable(namespace + right_obj)
@@ -1412,26 +1472,45 @@ def gt_ab_mirror_anim(gt_ab_ctrls, source_side, namespace=''):
except:
pass # 0 keyframes
-
# Transfer Left to Right
if source_side is 'left':
for transform in transforms:
- if transform[0]: # Using Transform?
-
+ if transform[0]: # Using Transform? Inverted? Name of the Attr
try:
- # Get Keys/Values
- frames = cmds.keyframe(namespace + left_obj, q=1, at=transform[2])
- values = cmds.keyframe(namespace + left_obj, q=1, at=transform[2], valueChange=True)
+ attr = transform[2]
- if transform[1]: # Inverted?
- inverted_values = []
- for val in values:
- inverted_values.append(val* -1)
- values = inverted_values
+ # Get Values
+ frames = cmds.keyframe(namespace + left_obj, q=1, at=attr)
+ values = cmds.keyframe(namespace + left_obj, q=1, at=attr, valueChange=True)
+
+ in_angle_tangent = cmds.keyTangent(namespace + left_obj, at=attr, inAngle=True, query=True)
+ out_angle_tanget = cmds.keyTangent(namespace + left_obj, at=attr, outAngle=True, query=True)
+ is_locked = cmds.keyTangent(namespace + left_obj, at=attr, weightLock=True, query=True)
+ in_weight = cmds.keyTangent(namespace + left_obj, at=attr, inWeight=True, query=True)
+ out_weight = cmds.keyTangent(namespace + left_obj, at=attr, outWeight=True, query=True)
+ in_tangent_type = cmds.keyTangent(namespace + left_obj, at=attr, inTangentType=True, query=True)
+ out_tangent_type = cmds.keyTangent(namespace + left_obj, at=attr, outTangentType=True, query=True)
+ if transform[1]: # Inverted?
+ values = invert_float_list_values(values)
+ in_angle_tangent = invert_float_list_values(in_angle_tangent)
+ out_angle_tanget = invert_float_list_values(out_angle_tanget)
+ in_weight = invert_float_list_values(in_weight)
+ out_weight = invert_float_list_values(out_weight)
+
+
# Set Keys/Values
for index in range(len(values)):
- cmds.setKeyframe(namespace + right_obj, time=frames[index], attribute=transform[2], value=values[index])
+ time = frames[index]
+ cmds.setKeyframe(namespace + right_obj, time=time, attribute=attr, value=values[index])
+ # Set Tangents
+ cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), lock=is_locked[index], e=True)
+ cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), inAngle=in_angle_tangent[index], e=True)
+ cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), outAngle=out_angle_tanget[index], e=True)
+ cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), inWeight=in_weight[index], e=True)
+ cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), outWeight=out_weight[index], e=True)
+ cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), inTangentType=in_tangent_type[index], e=True)
+ cmds.keyTangent(namespace + right_obj, at=attr, time=(time,time), outTangentType=out_tangent_type[index], e=True)
except:
pass # 0 keyframes
@@ -1533,15 +1612,20 @@ def gt_export_rig_animation(namespace =''):
for attr in attributes:
try:
short_attr = attr.split('.')[-1]
- # all the time value
frames = cmds.keyframe(namespace + obj, q=1, at=short_attr)
- # all the attribute value associated
values = cmds.keyframe(namespace + obj, q=1, at=short_attr, valueChange=True)
- # store it in the dictionary with key 'objectName.attribute' and value
- export_dict['{}.{}'.format(obj, short_attr)]=zip(frames, values)
+ in_angle_tangent = cmds.keyTangent(namespace + obj, at=short_attr, inAngle=True, query=True)
+ out_angle_tanget = cmds.keyTangent(namespace + obj, at=short_attr, outAngle=True, query=True)
+ is_locked = cmds.keyTangent(namespace + obj, at=short_attr, weightLock=True, query=True)
+ in_weight = cmds.keyTangent(namespace + obj, at=short_attr, inWeight=True, query=True)
+ out_weight = cmds.keyTangent(namespace + obj, at=short_attr, outWeight=True, query=True)
+ in_tangent_type = cmds.keyTangent(namespace + obj, at=short_attr, inTangentType=True, query=True)
+ out_tangent_type = cmds.keyTangent(namespace + obj, at=short_attr, outTangentType=True, query=True)
+ export_dict['{}.{}'.format(obj, short_attr)] = zip(frames, values, in_angle_tangent, out_angle_tanget, is_locked, in_weight, out_weight, in_tangent_type, out_tangent_type)
except:
pass # 0 keyframes
+
try:
with open(pose_file, 'w') as outfile:
json.dump(export_dict, outfile, indent=4)
@@ -1627,16 +1711,37 @@ def gt_import_rig_animation(debugging=False, debugging_path='', namespace=''):
if is_operation_valid:
# Object-Space
- for key, value in data.iteritems():
+ for key, dict_value in data.iteritems():
if key != 'gt_interface_version' and key != 'gt_export_method':
- for time, attr_value in value:
- obj, attr = key.split('.')
- cmds.setKeyframe(namespace + obj, time=time, attribute=attr, value=attr_value)
+ for key_data in dict_value:
+ # Unpack Data
+ time = key_data[0]
+ value = key_data[1]
+ in_angle_tangent = key_data[2]
+ out_angle_tanget = key_data[3]
+ is_locked = key_data[4]
+ in_weight = key_data[5]
+ out_weight = key_data[6]
+ in_tangent_type = key_data[7]
+ out_tangent_type = key_data[8]
+
+ try:
+ obj, attr = key.split('.')
+ cmds.setKeyframe(namespace + obj, time=time, attribute=attr, value=value)
+ cmds.keyTangent(namespace + obj, at=attr, time=(time,time), lock=is_locked, e=True)
+ cmds.keyTangent(namespace + obj, at=attr, time=(time,time), inAngle=in_angle_tangent, e=True)
+ cmds.keyTangent(namespace + obj, at=attr, time=(time,time), outAngle=out_angle_tanget, e=True)
+ cmds.keyTangent(namespace + obj, at=attr, time=(time,time), inWeight=in_weight, e=True)
+ cmds.keyTangent(namespace + obj, at=attr, time=(time,time), outWeight=out_weight, e=True)
+ cmds.keyTangent(namespace + obj, at=attr, time=(time,time), inTangentType=in_tangent_type, e=True)
+ cmds.keyTangent(namespace + obj, at=attr, time=(time,time), outTangentType=out_tangent_type, e=True)
+ except:
+ pass
unique_message = '<' + str(random.random()) + '>'
cmds.inViewMessage(amg=unique_message + 'Animation imported from ' + os.path.basename(anim_file) +'.', pos='botLeft', fade=True, alpha=.9)
sys.stdout.write('Animation imported from the file "' + anim_file + '".')
-
+
except Exception as e:
print(e)
cmds.warning('An error occured when importing the pose. Make sure you imported a valid ANIM file.')