From 8a939f6702f198c7c45402201a5aad115d7a8ae1 Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Mon, 25 Jul 2022 11:47:08 -0700 Subject: [PATCH 01/15] Updated version --- python-scripts/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-scripts/__init__.py b/python-scripts/__init__.py index 016dd2a9..6cd1dc8a 100644 --- a/python-scripts/__init__.py +++ b/python-scripts/__init__.py @@ -6,7 +6,7 @@ import os # Global Vars -PACKAGE_VERSION = "2.2.1" +PACKAGE_VERSION = "2.2.2" # Initial Setup - Add path and initialize logger if __name__ != '__main__': From f534f01c6ac06348d619ffabd2bd40e88a7fb6fe Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Mon, 25 Jul 2022 11:47:19 -0700 Subject: [PATCH 02/15] PEP8 Cleanup --- python-scripts/gt_rigger_biped_logic.py | 429 +++++++++++------------- 1 file changed, 198 insertions(+), 231 deletions(-) diff --git a/python-scripts/gt_rigger_biped_logic.py b/python-scripts/gt_rigger_biped_logic.py index 5ed8fa19..0e3fd572 100644 --- a/python-scripts/gt_rigger_biped_logic.py +++ b/python-scripts/gt_rigger_biped_logic.py @@ -284,6 +284,9 @@ Added logging Some PEP8 Cleanup + 1.9.15 - 2022-07-25 + Some PEP8 Cleanup + TODO Biped Rigger: Transfer scale information from ik spine limit spine to spines Add option to leave all lock translation attributes off @@ -1479,176 +1482,157 @@ def create_proxy(data_biped): change_outliner_color(proxy_crv, (.8, .8, 0)) # Create Lines - line_list = [ - create_visualization_line(elements.get('cog_proxy_crv'), elements.get('hip_proxy_crv')), - ] - line_list.append( - create_visualization_line(elements.get('cog_proxy_crv'), elements.get('hip_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('cog_proxy_crv'), - elements.get('spine01_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('spine01_proxy_crv'), - elements.get('spine02_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('spine02_proxy_crv'), - elements.get('spine03_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('spine03_proxy_crv'), - elements.get('spine04_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('spine04_proxy_crv'), - elements.get('neck_base_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('neck_base_proxy_crv'), - elements.get('neck_mid_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('neck_mid_proxy_crv'), - elements.get('head_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('head_proxy_crv'), - elements.get('head_end_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('head_proxy_crv'), elements.get('jaw_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('jaw_proxy_crv'), - elements.get('jaw_end_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('head_proxy_crv'), - elements.get('left_eye_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('head_proxy_crv'), - elements.get('right_eye_proxy_crv'))) - # Left Side - line_list.append( - create_visualization_line(elements.get('hip_proxy_crv'), - elements.get('left_hip_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('left_hip_proxy_crv'), - elements.get('left_knee_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('left_knee_proxy_crv'), - elements.get('left_ankle_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('left_ankle_proxy_crv'), - elements.get('left_ball_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('left_ball_proxy_crv'), - elements.get('left_toe_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('spine04_proxy_crv'), - elements.get('left_clavicle_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_clavicle_proxy_crv'), - elements.get('left_shoulder_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_shoulder_proxy_crv'), - elements.get('left_elbow_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('left_elbow_proxy_crv'), - elements.get('left_wrist_proxy_crv'))) - # Left Fingers - line_list.append(create_visualization_line(elements.get('left_wrist_proxy_crv'), - elements.get('left_thumb01_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_thumb01_proxy_crv'), - elements.get('left_thumb02_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_thumb02_proxy_crv'), - elements.get('left_thumb03_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_thumb03_proxy_crv'), - elements.get('left_thumb04_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_wrist_proxy_crv'), - elements.get('left_index01_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_index01_proxy_crv'), - elements.get('left_index02_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_index02_proxy_crv'), - elements.get('left_index03_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_index03_proxy_crv'), - elements.get('left_index04_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_wrist_proxy_crv'), - elements.get('left_middle01_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_middle01_proxy_crv'), - elements.get('left_middle02_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_middle02_proxy_crv'), - elements.get('left_middle03_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_middle03_proxy_crv'), - elements.get('left_middle04_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_wrist_proxy_crv'), - elements.get('left_ring01_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_ring01_proxy_crv'), - elements.get('left_ring02_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_ring02_proxy_crv'), - elements.get('left_ring03_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_ring03_proxy_crv'), - elements.get('left_ring04_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_wrist_proxy_crv'), - elements.get('left_pinky01_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_pinky01_proxy_crv'), - elements.get('left_pinky02_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_pinky02_proxy_crv'), - elements.get('left_pinky03_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('left_pinky03_proxy_crv'), - elements.get('left_pinky04_proxy_crv'))) - # Right Side - line_list.append( - create_visualization_line(elements.get('hip_proxy_crv'), - elements.get('right_hip_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('right_hip_proxy_crv'), - elements.get('right_knee_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_knee_proxy_crv'), - elements.get('right_ankle_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_ankle_proxy_crv'), - elements.get('right_ball_proxy_crv'))) - line_list.append( - create_visualization_line(elements.get('right_ball_proxy_crv'), - elements.get('right_toe_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('spine04_proxy_crv'), - elements.get('right_clavicle_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_clavicle_proxy_crv'), - elements.get('right_shoulder_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_shoulder_proxy_crv'), - elements.get('right_elbow_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_elbow_proxy_crv'), - elements.get('right_wrist_proxy_crv'))) - # Right Fingers - line_list.append(create_visualization_line(elements.get('right_wrist_proxy_crv'), - elements.get('right_thumb01_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_thumb01_proxy_crv'), - elements.get('right_thumb02_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_thumb02_proxy_crv'), - elements.get('right_thumb03_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_thumb03_proxy_crv'), - elements.get('right_thumb04_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_wrist_proxy_crv'), - elements.get('right_index01_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_index01_proxy_crv'), - elements.get('right_index02_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_index02_proxy_crv'), - elements.get('right_index03_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_index03_proxy_crv'), - elements.get('right_index04_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_wrist_proxy_crv'), - elements.get('right_middle01_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_middle01_proxy_crv'), - elements.get('right_middle02_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_middle02_proxy_crv'), - elements.get('right_middle03_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_middle03_proxy_crv'), - elements.get('right_middle04_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_wrist_proxy_crv'), - elements.get('right_ring01_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_ring01_proxy_crv'), - elements.get('right_ring02_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_ring02_proxy_crv'), - elements.get('right_ring03_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_ring03_proxy_crv'), - elements.get('right_ring04_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_wrist_proxy_crv'), - elements.get('right_pinky01_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_pinky01_proxy_crv'), - elements.get('right_pinky02_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_pinky02_proxy_crv'), - elements.get('right_pinky03_proxy_crv'))) - line_list.append(create_visualization_line(elements.get('right_pinky03_proxy_crv'), - elements.get('right_pinky04_proxy_crv'))) + line_list = [create_visualization_line(elements.get('cog_proxy_crv'), + elements.get('hip_proxy_crv')), + create_visualization_line(elements.get('cog_proxy_crv'), + elements.get('hip_proxy_crv')), + create_visualization_line(elements.get('cog_proxy_crv'), + elements.get('spine01_proxy_crv')), + create_visualization_line(elements.get('spine01_proxy_crv'), + elements.get('spine02_proxy_crv')), + create_visualization_line(elements.get('spine02_proxy_crv'), + elements.get('spine03_proxy_crv')), + create_visualization_line(elements.get('spine03_proxy_crv'), + elements.get('spine04_proxy_crv')), + create_visualization_line(elements.get('spine04_proxy_crv'), + elements.get('neck_base_proxy_crv')), + create_visualization_line(elements.get('neck_base_proxy_crv'), + elements.get('neck_mid_proxy_crv')), + create_visualization_line(elements.get('neck_mid_proxy_crv'), + elements.get('head_proxy_crv')), + create_visualization_line(elements.get('head_proxy_crv'), + elements.get('head_end_proxy_crv')), + create_visualization_line(elements.get('head_proxy_crv'), + elements.get('jaw_proxy_crv')), + create_visualization_line(elements.get('jaw_proxy_crv'), + elements.get('jaw_end_proxy_crv')), + create_visualization_line(elements.get('head_proxy_crv'), + elements.get('left_eye_proxy_crv')), + create_visualization_line(elements.get('head_proxy_crv'), + elements.get('right_eye_proxy_crv')), + # Left Side + create_visualization_line(elements.get('hip_proxy_crv'), + elements.get('left_hip_proxy_crv')), + create_visualization_line(elements.get('left_hip_proxy_crv'), + elements.get('left_knee_proxy_crv')), + create_visualization_line(elements.get('left_knee_proxy_crv'), + elements.get('left_ankle_proxy_crv')), + create_visualization_line(elements.get('left_ankle_proxy_crv'), + elements.get('left_ball_proxy_crv')), + create_visualization_line(elements.get('left_ball_proxy_crv'), + elements.get('left_toe_proxy_crv')), + create_visualization_line(elements.get('spine04_proxy_crv'), + elements.get('left_clavicle_proxy_crv')), + create_visualization_line(elements.get('left_clavicle_proxy_crv'), + elements.get('left_shoulder_proxy_crv')), + create_visualization_line(elements.get('left_shoulder_proxy_crv'), + elements.get('left_elbow_proxy_crv')), + create_visualization_line(elements.get('left_elbow_proxy_crv'), + elements.get('left_wrist_proxy_crv')), + # Left Fingers + create_visualization_line(elements.get('left_wrist_proxy_crv'), + elements.get('left_thumb01_proxy_crv')), + create_visualization_line(elements.get('left_thumb01_proxy_crv'), + elements.get('left_thumb02_proxy_crv')), + create_visualization_line(elements.get('left_thumb02_proxy_crv'), + elements.get('left_thumb03_proxy_crv')), + create_visualization_line(elements.get('left_thumb03_proxy_crv'), + elements.get('left_thumb04_proxy_crv')), + create_visualization_line(elements.get('left_wrist_proxy_crv'), + elements.get('left_index01_proxy_crv')), + create_visualization_line(elements.get('left_index01_proxy_crv'), + elements.get('left_index02_proxy_crv')), + create_visualization_line(elements.get('left_index02_proxy_crv'), + elements.get('left_index03_proxy_crv')), + create_visualization_line(elements.get('left_index03_proxy_crv'), + elements.get('left_index04_proxy_crv')), + create_visualization_line(elements.get('left_wrist_proxy_crv'), + elements.get('left_middle01_proxy_crv')), + create_visualization_line(elements.get('left_middle01_proxy_crv'), + elements.get('left_middle02_proxy_crv')), + create_visualization_line(elements.get('left_middle02_proxy_crv'), + elements.get('left_middle03_proxy_crv')), + create_visualization_line(elements.get('left_middle03_proxy_crv'), + elements.get('left_middle04_proxy_crv')), + create_visualization_line(elements.get('left_wrist_proxy_crv'), + elements.get('left_ring01_proxy_crv')), + create_visualization_line(elements.get('left_ring01_proxy_crv'), + elements.get('left_ring02_proxy_crv')), + create_visualization_line(elements.get('left_ring02_proxy_crv'), + elements.get('left_ring03_proxy_crv')), + create_visualization_line(elements.get('left_ring03_proxy_crv'), + elements.get('left_ring04_proxy_crv')), + create_visualization_line(elements.get('left_wrist_proxy_crv'), + elements.get('left_pinky01_proxy_crv')), + create_visualization_line(elements.get('left_pinky01_proxy_crv'), + elements.get('left_pinky02_proxy_crv')), + create_visualization_line(elements.get('left_pinky02_proxy_crv'), + elements.get('left_pinky03_proxy_crv')), + create_visualization_line(elements.get('left_pinky03_proxy_crv'), + elements.get('left_pinky04_proxy_crv')), + # Right Side + + create_visualization_line(elements.get('hip_proxy_crv'), + elements.get('right_hip_proxy_crv')), + create_visualization_line(elements.get('right_hip_proxy_crv'), + elements.get('right_knee_proxy_crv')), + create_visualization_line(elements.get('right_knee_proxy_crv'), + elements.get('right_ankle_proxy_crv')), + create_visualization_line(elements.get('right_ankle_proxy_crv'), + elements.get('right_ball_proxy_crv')), + + create_visualization_line(elements.get('right_ball_proxy_crv'), + elements.get('right_toe_proxy_crv')), + create_visualization_line(elements.get('spine04_proxy_crv'), + elements.get('right_clavicle_proxy_crv')), + create_visualization_line(elements.get('right_clavicle_proxy_crv'), + elements.get('right_shoulder_proxy_crv')), + create_visualization_line(elements.get('right_shoulder_proxy_crv'), + elements.get('right_elbow_proxy_crv')), + create_visualization_line(elements.get('right_elbow_proxy_crv'), + elements.get('right_wrist_proxy_crv')), + # Right Fingers + create_visualization_line(elements.get('right_wrist_proxy_crv'), + elements.get('right_thumb01_proxy_crv')), + create_visualization_line(elements.get('right_thumb01_proxy_crv'), + elements.get('right_thumb02_proxy_crv')), + create_visualization_line(elements.get('right_thumb02_proxy_crv'), + elements.get('right_thumb03_proxy_crv')), + create_visualization_line(elements.get('right_thumb03_proxy_crv'), + elements.get('right_thumb04_proxy_crv')), + create_visualization_line(elements.get('right_wrist_proxy_crv'), + elements.get('right_index01_proxy_crv')), + create_visualization_line(elements.get('right_index01_proxy_crv'), + elements.get('right_index02_proxy_crv')), + create_visualization_line(elements.get('right_index02_proxy_crv'), + elements.get('right_index03_proxy_crv')), + create_visualization_line(elements.get('right_index03_proxy_crv'), + elements.get('right_index04_proxy_crv')), + create_visualization_line(elements.get('right_wrist_proxy_crv'), + elements.get('right_middle01_proxy_crv')), + create_visualization_line(elements.get('right_middle01_proxy_crv'), + elements.get('right_middle02_proxy_crv')), + create_visualization_line(elements.get('right_middle02_proxy_crv'), + elements.get('right_middle03_proxy_crv')), + create_visualization_line(elements.get('right_middle03_proxy_crv'), + elements.get('right_middle04_proxy_crv')), + create_visualization_line(elements.get('right_wrist_proxy_crv'), + elements.get('right_ring01_proxy_crv')), + create_visualization_line(elements.get('right_ring01_proxy_crv'), + elements.get('right_ring02_proxy_crv')), + create_visualization_line(elements.get('right_ring02_proxy_crv'), + elements.get('right_ring03_proxy_crv')), + create_visualization_line(elements.get('right_ring03_proxy_crv'), + elements.get('right_ring04_proxy_crv')), + create_visualization_line(elements.get('right_wrist_proxy_crv'), + elements.get('right_pinky01_proxy_crv')), + create_visualization_line(elements.get('right_pinky01_proxy_crv'), + elements.get('right_pinky02_proxy_crv')), + create_visualization_line(elements.get('right_pinky02_proxy_crv'), + elements.get('right_pinky03_proxy_crv')), + create_visualization_line(elements.get('right_pinky03_proxy_crv'), + elements.get('right_pinky04_proxy_crv')) + ] lines_grp = cmds.group(name='visualization_lines', empty=True, world=True) cmds.setAttr(lines_grp + '.overrideEnabled', 1) @@ -1690,38 +1674,38 @@ def rename_proxy(old_name): Replaces "proxy" with "jnt" Replaces "endProxy" with "endJnt" - Parameters: - old_name (string): Name of the proxy element + Args: + old_name (string): Name of the proxy element - Returns: - new_name (string): Name of the joint to be created out of the element + Returns: + new_name (string): Name of the joint to be created out of the element """ return old_name.replace(PROXY_SUFFIX, JNT_SUFFIX).replace('end' + PROXY_SUFFIX.capitalize(), 'end' + JNT_SUFFIX.capitalize()) - def orient_to_target(obj_name, target, orient_offset=(0, 0, 0), proxy_obj=None, + def orient_to_target(obj_name, target, orientation_offset=(0, 0, 0), proxy_obj=None, aim_vec=(1, 0, 0), up_vec=(0, -1, 0), brute_force=False): """ Orients an object based on a target object - Parameters: - obj_name (string): Name of the object to orient (usually a joint) - target (string): Name of the target object (usually the element that will be the child of "obj") - orient_offset (tuple): A tuple containing three 32b floats, used as a rotation offset to change the - result orientation. - proxy_obj (string): The name of the proxy element (used as extra rotation input) - aim_vec (tuple): A tuple of floats used for the aim vector of the aim constraint. Default: (1, 0, 0) - up_vec (tuple): A tuple of floats used for the up vector of the aim constraint. Default: (0, -1, 0) - brute_force (bool): Creates up and dir points to determine orientation (Uses proxy object) + Args: + obj_name (string): Name of the object to orient (usually a joint) + target (string): Name of the target object (usually the element that will be the child of "obj") + orientation_offset (tuple): A tuple containing three 32b floats, used as a rotation offset to change the + result orientation. + proxy_obj (string): The name of the proxy element (used as extra rotation input) + aim_vec (tuple): A tuple of floats used for the aim vector of the aim constraint. Default: (1, 0, 0) + up_vec (tuple): A tuple of floats used for the up vector of the aim constraint. Default: (0, -1, 0) + brute_force (bool): Creates up and dir points to determine orientation (Uses proxy object) """ if proxy_obj: cmds.delete(cmds.orientConstraint(proxy_obj, obj_name, offset=(0, 0, 0))) cmds.makeIdentity(obj_name, apply=True, rotate=True) - cmds.setAttr(obj_name + '.rotateX', orient_offset[0]) - cmds.setAttr(obj_name + '.rotateY', orient_offset[1]) - cmds.setAttr(obj_name + '.rotateZ', orient_offset[2]) + cmds.setAttr(obj_name + '.rotateX', orientation_offset[0]) + cmds.setAttr(obj_name + '.rotateY', orientation_offset[1]) + cmds.setAttr(obj_name + '.rotateZ', orientation_offset[2]) cmds.makeIdentity(obj_name, apply=True, rotate=True) cmds.delete( @@ -1745,32 +1729,31 @@ def orient_to_target(obj_name, target, orient_offset=(0, 0, 0), proxy_obj=None, cmds.makeIdentity(obj_name, apply=True, rotate=True) - def orient_offset(obj_name, orient_offset=(0, 0, 0), apply=True): + def orient_offset(obj_name, orientation_offset=(0, 0, 0), apply=True): """ Rotates the target then freezes its transformation (used to quickly re-orient an object) Args: obj_name (string): Name of the object to orient (usually a joint) - orient_offset (tuple): A tuple containing three 32b floats, used as a rotation offset to change + orientation_offset (tuple): A tuple containing three 32b floats, used as a rotation offset to change the result orientation. apply (optional, bool): Whether to execute the function """ if apply: - cmds.setAttr(obj_name + '.rotateX', orient_offset[0]) - cmds.setAttr(obj_name + '.rotateY', orient_offset[1]) - cmds.setAttr(obj_name + '.rotateZ', orient_offset[2]) + cmds.setAttr(obj_name + '.rotateX', orientation_offset[0]) + cmds.setAttr(obj_name + '.rotateY', orientation_offset[1]) + cmds.setAttr(obj_name + '.rotateZ', orientation_offset[2]) cmds.makeIdentity(obj_name, apply=True, rotate=True) - def remove_numbers(string): """ Removes all numbers (digits) from the provided string - Parameters: - string (string): input string (numbers will be removed from it) + Args: + string (string): input string (numbers will be removed from it) - Returns: - string (string): output string without numbers (digits) + Returns: + string (string): output string without numbers (digits) """ return ''.join([i for i in string if not i.isdigit()]) @@ -2055,7 +2038,7 @@ def remove_numbers(string): cmds.parent(rig_joints.get('right_elbow_jnt'), rig_joints.get('right_shoulder_jnt')) cmds.parent(rig_joints.get('right_wrist_jnt'), rig_joints.get('right_elbow_jnt')) - # Left Hand Hierarchy and Orients + # Left-Hand Hierarchy and Orients # Left Remove Fingers cmds.parent(rig_joints.get('left_wrist_jnt'), world=True) cmds.parent(rig_joints.get('left_thumb01_jnt'), world=True) @@ -2083,7 +2066,7 @@ def remove_numbers(string): cmds.parent(rig_joints.get('left_ring01_jnt'), rig_joints.get('left_wrist_jnt')) cmds.parent(rig_joints.get('left_pinky01_jnt'), rig_joints.get('left_wrist_jnt')) - # Right Hand Hierarchy and Orients + # Right-Hand Hierarchy and Orients # Right Remove Fingers cmds.parent(rig_joints.get('right_wrist_jnt'), world=True) cmds.parent(rig_joints.get('right_thumb01_jnt'), world=True) @@ -3020,7 +3003,7 @@ def remove_numbers(string): if settings.get('uniform_ctrl_orient'): cmds.rotate(-90, -90, 0, neck_base_ctrl_grp, os=True, relative=True) - # Neck Mid Control + # Neck-Mid Control neck_mid_ctrl = cmds.curve(name=rig_joints.get('neck_mid_jnt').replace(JNT_SUFFIX, '') + CTRL_SUFFIX, p=[[0.0, 0.0, 0.0], [0.0, -1.794, 0.0], [0.067, -1.803, 0.0], [0.128, -1.829, 0.0], [0.181, -1.869, 0.0], [0.222, -1.922, 0.0], [0.247, -1.984, 0.0], [0.256, -2.05, 0.0], @@ -4049,7 +4032,7 @@ def remove_numbers(string): niceName='Rotate Order') cmds.connectAttr(left_wrist_ik_ctrl + '.rotationOrder', left_wrist_ik_ctrl + '.rotateOrder', f=True) - # Left Hand Ctrl Visibility Type + # Left-Hand Ctrl Visibility Type setup_shape_switch(left_wrist_ik_ctrl) # Left Wrist In-Between Offset @@ -4396,7 +4379,7 @@ def remove_numbers(string): niceName='Rotate Order') cmds.connectAttr(right_wrist_ik_ctrl + '.rotationOrder', right_wrist_ik_ctrl + '.rotateOrder', f=True) - # Right Hand Ctrl Visibility Type + # Right-Hand Ctrl Visibility Type setup_shape_switch(right_wrist_ik_ctrl) # Right Wrist In-Between Offset @@ -4739,7 +4722,6 @@ def remove_numbers(string): # Left Toe Position and Visibility cmds.delete(cmds.pointConstraint(rig_joints.get('left_toe_jnt'), left_toe_roll_ctrl_grp, skip='y')) desired_rotation = cmds.xform(elements.get('left_ankle_proxy_crv'), q=True, ro=True) - desired_translation = cmds.xform(elements.get('left_ankle_proxy_crv'), q=True, t=True, ws=True) cmds.setAttr(left_toe_roll_ctrl_grp + '.ry', desired_rotation[1]) cmds.move(left_foot_scale_offset / 4, left_toe_roll_ctrl_grp, z=True, relative=True, objectSpace=True) @@ -4768,7 +4750,6 @@ def remove_numbers(string): # Left Toe Position and Visibility cmds.delete(cmds.pointConstraint(rig_joints.get('left_toe_jnt'), left_toe_up_down_ctrl_grp, skip='y')) desired_rotation = cmds.xform(elements.get('left_ankle_proxy_crv'), q=True, ro=True) - desired_translation = cmds.xform(elements.get('left_ankle_proxy_crv'), q=True, t=True, ws=True) cmds.setAttr(left_toe_up_down_ctrl_grp + '.ry', desired_rotation[1]) cmds.move(left_foot_scale_offset / 2.6, left_toe_up_down_ctrl_grp, z=True, relative=True, objectSpace=True) @@ -4823,7 +4804,6 @@ def remove_numbers(string): # Left Ball Position and Visibility cmds.delete(cmds.pointConstraint(rig_joints.get('left_ball_jnt'), left_ball_roll_ctrl_grp, skip='y')) desired_rotation = cmds.xform(elements.get('left_ankle_proxy_crv'), q=True, ro=True) - desired_translation = cmds.xform(elements.get('left_ankle_proxy_crv'), q=True, t=True, ws=True) cmds.setAttr(left_ball_roll_ctrl_grp + '.ry', desired_rotation[1]) cmds.move(left_foot_scale_offset / 3, left_ball_roll_ctrl_grp, x=True, relative=True, objectSpace=True) @@ -4878,7 +4858,6 @@ def remove_numbers(string): # Left Heel Position and Visibility cmds.delete(cmds.pointConstraint(rig_joints.get('left_ankle_jnt'), left_heel_roll_ctrl_grp, skip='y')) desired_rotation = cmds.xform(elements.get('left_ankle_proxy_crv'), q=True, ro=True) - desired_translation = cmds.xform(elements.get('left_ankle_proxy_crv'), q=True, t=True, ws=True) cmds.setAttr(left_heel_roll_ctrl_grp + '.ry', desired_rotation[1]) cmds.move(left_foot_scale_offset / 3.5 * -1, left_heel_roll_ctrl_grp, z=True, relative=True, objectSpace=True) @@ -4938,7 +4917,6 @@ def remove_numbers(string): # Right Toe Position and Visibility cmds.delete(cmds.pointConstraint(rig_joints.get('right_toe_jnt'), right_toe_roll_ctrl_grp, skip='y')) desired_rotation = cmds.xform(elements.get('right_ankle_proxy_crv'), q=True, ro=True) - desired_translation = cmds.xform(elements.get('right_ankle_proxy_crv'), q=True, t=True, ws=True) cmds.setAttr(right_toe_roll_ctrl_grp + '.ry', desired_rotation[1]) cmds.move(-right_foot_scale_offset / 4, right_toe_roll_ctrl_grp, z=True, relative=True, objectSpace=True) @@ -4967,7 +4945,6 @@ def remove_numbers(string): # Right Toe Position and Visibility cmds.delete(cmds.pointConstraint(rig_joints.get('right_toe_jnt'), right_toe_up_down_ctrl_grp, skip='y')) desired_rotation = cmds.xform(elements.get('right_ankle_proxy_crv'), q=True, ro=True) - desired_translation = cmds.xform(elements.get('right_ankle_proxy_crv'), q=True, t=True, ws=True) cmds.setAttr(right_toe_up_down_ctrl_grp + '.ry', desired_rotation[1]) cmds.move(-right_foot_scale_offset / 2.6, right_toe_up_down_ctrl_grp, z=True, relative=True, objectSpace=True) @@ -5026,7 +5003,6 @@ def remove_numbers(string): # Right Ball Position and Visibility cmds.delete(cmds.pointConstraint(rig_joints.get('right_ball_jnt'), right_ball_roll_ctrl_grp, skip='y')) desired_rotation = cmds.xform(elements.get('right_ankle_proxy_crv'), q=True, ro=True) - desired_translation = cmds.xform(elements.get('right_ankle_proxy_crv'), q=True, t=True, ws=True) cmds.setAttr(right_ball_roll_ctrl_grp + '.ry', desired_rotation[1]) cmds.move(right_foot_scale_offset / 3, right_ball_roll_ctrl_grp, x=True, relative=True, objectSpace=True) @@ -5085,7 +5061,6 @@ def remove_numbers(string): # Right Heel Position and Visibility cmds.delete(cmds.pointConstraint(rig_joints.get('right_ankle_jnt'), right_heel_roll_ctrl_grp, skip='y')) desired_rotation = cmds.xform(elements.get('right_ankle_proxy_crv'), q=True, ro=True) - desired_translation = cmds.xform(elements.get('right_ankle_proxy_crv'), q=True, t=True, ws=True) cmds.setAttr(right_heel_roll_ctrl_grp + '.ry', desired_rotation[1]) cmds.move(-right_foot_scale_offset / 3.5 * -1, right_heel_roll_ctrl_grp, z=True, relative=True, objectSpace=True) @@ -5481,12 +5456,7 @@ def remove_numbers(string): cmds.parent(ik_spine02_jnt, ik_spine01_jnt) cmds.parent(ik_spine03_jnt, ik_spine02_jnt) cmds.parent(ik_spine04_jnt, ik_spine03_jnt) - ik_spine_joints = [] - ik_spine_joints.append(ik_cog_jnt[0]) - ik_spine_joints.append(ik_spine01_jnt[0]) - ik_spine_joints.append(ik_spine02_jnt[0]) - ik_spine_joints.append(ik_spine03_jnt[0]) - ik_spine_joints.append(ik_spine04_jnt[0]) + ik_spine_joints = [ik_cog_jnt[0], ik_spine01_jnt[0], ik_spine02_jnt[0], ik_spine03_jnt[0], ik_spine04_jnt[0]] # Create Limit Jnt Chain cmds.duplicate(ik_cog_jnt, rc=True) @@ -6057,8 +6027,7 @@ def remove_numbers(string): cmds.addAttr(spine_ribbon_ctrl, ln=CUSTOM_ATTR_SEPARATOR, at='enum', en='-------------:', keyable=True) cmds.setAttr(spine_ribbon_ctrl + '.' + CUSTOM_ATTR_SEPARATOR, lock=True) - cmds.addAttr(spine_ribbon_ctrl, ln="followChestAndHip", at='double', k=True, maxValue=1, - minValue=0) # , niceName='Auto Rotate Neck Mid') + cmds.addAttr(spine_ribbon_ctrl, ln="followChestAndHip", at='double', k=True, maxValue=1, minValue=0) cmds.setAttr(spine_ribbon_ctrl + '.followChestAndHip', 1) spine_follow_reverse_node = cmds.createNode('reverse', name='spine_follow_reverse') @@ -6296,7 +6265,7 @@ def remove_numbers(string): cmds.parent(left_fingers_ik_grp, ik_solvers_grp) # Left Auto Fist/Splay Offset - # A list of tuples of tuples 1:[thumb, index...], 2:(f_01, f_02, f_03), 3:(finger_ctrl, ctrl_grp, ctrl_offset) + # A list of tuples of tuples 1:[thumb, index...], 2: f_01, f_02, f_03, 3: finger_ctrl, ctrl_grp, ctrl_offset for obj in left_fingers_list: finger_name = remove_numbers(obj[0][0].replace(CTRL_SUFFIX, '')) @@ -6420,7 +6389,7 @@ def remove_numbers(string): cmds.setAttr(left_fingers_ctrl + '.minScaleZLimitEnable', 1) cmds.setAttr(left_fingers_ctrl + '.maxScaleZLimitEnable', 1) - # A list of tuples of tuples 1:[thumb, index...], 2:(f_01, f_02, f_03), 3:(finger_ctrl, ctrl_grp, ctrl_offset) + # A list of tuples of tuples 1:[thumb, index...], 2: f_01, f_02, f_03, 3: finger_ctrl, ctrl_grp, ctrl_offset for obj in left_fingers_list: # Unpack Elements finger_name = remove_numbers(obj[0][0].replace(CTRL_SUFFIX, '')) @@ -10195,11 +10164,10 @@ def remove_numbers(string): change_viewport_color(annotate_x, (1, 0, 0)) change_viewport_color(annotate_y, (0, 1, 0)) change_viewport_color(annotate_z, (0, 0, 1)) - annotation_transforms = [] - annotation_transforms.append(cmds.listRelatives(annotate, parent=True)) - annotation_transforms.append(cmds.listRelatives(annotate_x, parent=True)) - annotation_transforms.append(cmds.listRelatives(annotate_y, parent=True)) - annotation_transforms.append(cmds.listRelatives(annotate_z, parent=True)) + annotation_transforms = [cmds.listRelatives(annotate, parent=True), + cmds.listRelatives(annotate_x, parent=True), + cmds.listRelatives(annotate_y, parent=True), + cmds.listRelatives(annotate_z, parent=True)] cmds.parent(ann_holder, obj) cmds.parent(annotate, ann_holder, s=True) cmds.parent(annotate_x, ann_holder, s=True) @@ -10232,11 +10200,10 @@ def remove_numbers(string): change_viewport_color(annotate_x, (1, 0, 0)) change_viewport_color(annotate_y, (0, 1, 0)) change_viewport_color(annotate_z, (0, 0, 1)) - annotation_transforms = [] - annotation_transforms.append(cmds.listRelatives(annotate, parent=True)) - annotation_transforms.append(cmds.listRelatives(annotate_x, parent=True)) - annotation_transforms.append(cmds.listRelatives(annotate_y, parent=True)) - annotation_transforms.append(cmds.listRelatives(annotate_z, parent=True)) + annotation_transforms = [cmds.listRelatives(annotate, parent=True), + cmds.listRelatives(annotate_x, parent=True), + cmds.listRelatives(annotate_y, parent=True), + cmds.listRelatives(annotate_z, parent=True)] cmds.parent(ann_holder, obj) cmds.parent(annotate, ann_holder, s=True) cmds.parent(annotate_x, ann_holder, s=True) @@ -10321,4 +10288,4 @@ def build_biped_rig(create_rig_ctrls=True, debugging=True): # Test it if __name__ == '__main__': - build_biped_rig(create_rig_ctrls=True, debugging=False) + build_biped_rig(create_rig_ctrls=False, debugging=False) From f977f55ee567bd6f7c49675651f541bd709a70f1 Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Mon, 25 Jul 2022 11:47:58 -0700 Subject: [PATCH 03/15] Fixed extension of the template pose --- python-scripts/gt_rigger_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python-scripts/gt_rigger_data.py b/python-scripts/gt_rigger_data.py index e4f7c4f9..688b0ec8 100644 --- a/python-scripts/gt_rigger_data.py +++ b/python-scripts/gt_rigger_data.py @@ -15,7 +15,7 @@ logger = logging.getLogger("gt_rigger_data") logger.setLevel(logging.INFO) -SCRIPT_VERSION_BASE = '1.9.14' +SCRIPT_VERSION_BASE = '1.9.15' SCRIPT_VERSION_FACIAL = '0.0.19' SCRIPT_VERSION_CORRECTIVE = '0.0.14' @@ -171,7 +171,7 @@ def __init__(self): debugging_force_new_scene = True # Forces new instance every time debugging_keep_cam_transforms = True # Keeps camera position debugging_import_proxy = True # Auto Imports Proxy - debugging_import_path = 'C:\\template.ppose' # Path to auto import + debugging_import_path = 'C:\\template.ppose_base' # Path to auto import debugging_bind_rig = False # Auto Binds Rig debugging_bind_geo = 'body_geo' # Name of the geo to bind debugging_bind_heatmap = False # If not using heatmap, then closest distance From b4439436b7e400049e8def55e5b055d523287ea5 Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 11:35:58 -0700 Subject: [PATCH 04/15] Added shelf and set functions --- python-scripts/gt_extract_bound_joints.py | 157 ++++++++++++++++++---- 1 file changed, 128 insertions(+), 29 deletions(-) diff --git a/python-scripts/gt_extract_bound_joints.py b/python-scripts/gt_extract_bound_joints.py index a6d01d65..13fa4230 100644 --- a/python-scripts/gt_extract_bound_joints.py +++ b/python-scripts/gt_extract_bound_joints.py @@ -20,6 +20,10 @@ 1.0.2 - 2022-07-22 Increased the size of the output window +1.1.0 - 2022-07-26 +Added "Save to Shelf" button +Added "Extract Bound Joints to Selection Sets" button + Todo: Add Transfer functions Add save as set button @@ -27,6 +31,7 @@ """ from maya import OpenMayaUI as OpenMayaUI import maya.cmds as cmds +import maya.mel as mel import logging try: @@ -49,7 +54,7 @@ script_name = "GT - Extract Bound Joints" # Version -script_version = "1.0.2" +script_version = "1.1.0" # Settings extract_joints_settings = {'filter_non_existent': True, @@ -98,9 +103,12 @@ def build_gui_extract_bound_joints(): filter_non_existent_chk = cmds.checkBox("Include Non-Existent Filter", value=True, cc=lambda x: _btn_update_settings()) include_mesh_chk = cmds.checkBox("Include Bound Mesh", value=True, cc=lambda x: _btn_update_settings()) - cmds.rowColumnLayout(nc=1, cw=[(1, 480)], cs=[(1, 15)], p=content_main) cmds.separator(h=15, style='none') # Empty Space - cmds.button(l="Extract Bound Joints", bgc=(.6, .6, .6), c=lambda x: _btn_extract_python_curve_shape()) + cmds.rowColumnLayout(nc=2, cw=[(1, 235), (2, 235)], cs=[(1, 15), (2, 10)], p=content_main) + cmds.button(l="Extract Bound Joints to Python", bgc=(.6, .6, .6), + c=lambda x: _btn_extract_bound_validation('python')) + cmds.button(l="Extract Bound Joints to Selection Sets", bgc=(.6, .6, .6), + c=lambda x: _btn_extract_bound_validation('set')) cmds.separator(h=10, style='none', p=content_main) # Empty Space cmds.separator(h=10, p=content_main) @@ -109,14 +117,39 @@ def build_gui_extract_bound_joints(): cmds.text(label='Output - Selection Command:') output_python = cmds.scrollField(editable=True, wordWrap=True, height=200) cmds.separator(h=10, style='none') # Empty Space + cmds.rowColumnLayout(nc=2, cw=[(1, 235), (2, 235)], cs=[(1, 15), (2, 15)], p=content_main) cmds.button(l="Run Code", c=lambda x: run_output_code(cmds.scrollField(output_python, query=True, text=True))) + cmds.button(l="Save to Shelf", c=lambda x: _btn_add_to_shelf()) cmds.separator(h=10, style='none') # Empty Space def _btn_update_settings(): extract_joints_settings['filter_non_existent'] = cmds.checkBox(filter_non_existent_chk, q=True, value=True) extract_joints_settings['include_mesh'] = cmds.checkBox(include_mesh_chk, q=True, value=True) - def _btn_extract_python_curve_shape(): + def _btn_add_to_shelf(): + command = cmds.scrollField(output_python, query=True, text=True) or '' + if command: + create_shelf_button(command, + label='bJnts', + tooltip='Extracted joints', + image="smoothSkin.png", # Default Python Icon + label_color=(.97, 0, 1.7), # Default Red + label_bgc_color=(0, 0, 0, 1), # Default Black + ) + cmds.inViewMessage(amg='Current Selection Command' + ' was added as a button to your current shelf.', + pos='botLeft', fade=True, alpha=.9) + else: + cmds.warning('Unable to save to shelf. "Output- Selection Command" is empty.') + + def _btn_extract_bound_validation(operation_target='python'): + """ + Validation before extracting python or set out of the bound mesh + Args: + operation_target (optional, string): "python" will output python code into the scrollField, + "set" will create selection sets + + """ selection = cmds.ls(selection=True) or [] if len(selection) == 0: @@ -130,40 +163,50 @@ def _btn_extract_python_curve_shape(): if cmds.objectType(shapes[0]) == 'mesh' or cmds.objectType(shapes[0]) == 'nurbsSurface': valid_nodes.append(sel) - commands = [] - for transform in valid_nodes: - message = '# Joint influences found in "' + transform + '":' - message += '\nbound_list = ' - bound_joints = get_bound_joints(transform) + if operation_target == 'python': + commands = [] + for transform in valid_nodes: + message = '# Joint influences found in "' + transform + '":' + message += '\nbound_list = ' + bound_joints = get_bound_joints(transform) + + if not bound_joints: + cmds.warning('Unable to find skinCluster for "' + transform + '".') + return - if not bound_joints: - cmds.warning('Unable to find skinCluster for "' + transform + '".') - return + if extract_joints_settings.get('include_mesh'): + bound_joints.insert(0, transform) - if extract_joints_settings.get('include_mesh'): - bound_joints.insert(0, transform) + message += str(bound_joints) - message += str(bound_joints) + if extract_joints_settings.get('filter_non_existent'): + message += '\nbound_list = [jnt for jnt in bound_list if cmds.objExists(jnt)]' - if extract_joints_settings.get('filter_non_existent'): - message += '\nbound_list = [jnt for jnt in bound_list if cmds.objExists(jnt)]' + message += '\ncmds.select(bound_list)' - message += '\ncmds.select(bound_list)' + commands.append(message) - commands.append(message) + cmds.scrollField(output_python, edit=True, wordWrap=True, text='', sl=True) + command = '' + for cmd in commands: + command += cmd + '\n\n' - cmds.scrollField(output_python, edit=True, wordWrap=True, text='', sl=True) - command = '' - for cmd in commands: - command += cmd + '\n\n' + print('#' * 80) + print(command) + print('#' * 80) - print('#' * 80) - print(command) - print('#' * 80) + cmds.scrollField(output_python, edit=True, wordWrap=True, text=command, sl=True) + cmds.scrollField(output_python, e=True, ip=1, it='') # Bring Back to the Top + cmds.setFocus(output_python) - cmds.scrollField(output_python, edit=True, wordWrap=True, text=command, sl=True) - cmds.scrollField(output_python, e=True, ip=1, it='') # Bring Back to the Top - cmds.setFocus(output_python) + if operation_target == 'set': + for transform in valid_nodes: + bound_joints = get_bound_joints(transform) + if extract_joints_settings.get('include_mesh'): + bound_joints.insert(0, transform) + new_set = cmds.sets(name=transform + "_jointSet", empty=True) + for jnt in bound_joints: + cmds.sets(jnt, add=new_set) # Show and Lock Window cmds.showWindow(window_gui_extract_bound_joints) @@ -274,5 +317,61 @@ def get_bound_joints(obj): return joints +def create_shelf_button(command, + label='', + name=None, + tooltip='', + image=None, # Default Python Icon + label_color=(1, 0, 0), # Default Red + label_bgc_color=(0, 0, 0, 1), # Default Black + bgc_color=None + ): + """ + Add a shelf button to the current shelf (according to the provided parameters) + + Args: + command (str): A string containing the code or command you want the button to run when clicking on it. + e.g. "print("Hello World")" + label (str): The label of the button. This is the text you see below it. + name (str): The name of the button as seen inside the shelf editor. + tooltip (str): The help message you get when hovering the button. + image (str): The image used for the button (defaults to Python icon if none) + label_color (tuple): A tuple containing three floats, + these are RGB 0 to 1 values to determine the color of the label. + label_bgc_color (tuple): A tuple containing four floats, + these are RGBA 0 to 1 values to determine the background of the label. + bgc_color (tuple): A tuple containing three floats, + these are RGB 0 to 1 values to determine the background of the icon + + """ + maya_version = int(cmds.about(v=True)) + + shelf_top_level = mel.eval('$temp=$gShelfTopLevel') + if not cmds.tabLayout(shelf_top_level, exists=True): + cmds.warning('Shelf is not visible') + return + + if not image: + image = 'pythonFamily.png' + + shelf_tab = cmds.shelfTabLayout(shelf_top_level, query=True, selectTab=True) + shelf_tab = shelf_top_level + '|' + shelf_tab + + # Populate extra arguments according to the current Maya version + kwargs = {} + if maya_version >= 2009: + kwargs['commandRepeatable'] = True + if maya_version >= 2011: + kwargs['overlayLabelColor'] = label_color + kwargs['overlayLabelBackColor'] = label_bgc_color + if bgc_color: + kwargs['enableBackground'] = bool(bgc_color) + kwargs['backgroundColor'] = bgc_color + + return cmds.shelfButton(parent=shelf_tab, label=label, command=command, + imageOverlayLabel=label, image=image, annotation=tooltip, + width=32, height=32, align='center', **kwargs) + + if __name__ == '__main__': build_gui_extract_bound_joints() From 02e2fbbcd39587ba955f4dc9f6062c5530c03c4e Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 11:36:15 -0700 Subject: [PATCH 05/15] Added shelf and set buttons --- docs/media/gt_extract_bound_joints.jpg | Bin 43113 -> 40434 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/media/gt_extract_bound_joints.jpg b/docs/media/gt_extract_bound_joints.jpg index b0a06b07696872f391768801aa54a0c50faeaff1..91ebc86dc641eeeb7df0d596b86a0c863255fbeb 100644 GIT binary patch literal 40434 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 literal 43113 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 OcDP;p0|=+(;(r5c-x?YK From acbaba2529e29c2fc0d84389a95c4ad42c87b405 Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 11:41:37 -0700 Subject: [PATCH 06/15] Updated help and tweaked UI --- python-scripts/gt_extract_bound_joints.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/python-scripts/gt_extract_bound_joints.py b/python-scripts/gt_extract_bound_joints.py index 13fa4230..fefbe23f 100644 --- a/python-scripts/gt_extract_bound_joints.py +++ b/python-scripts/gt_extract_bound_joints.py @@ -24,10 +24,12 @@ Added "Save to Shelf" button Added "Extract Bound Joints to Selection Sets" button +1.1.1 - 2022-07-26 +Updated help +Tweaked the UI spacing + Todo: Add Transfer functions - Add save as set button - Add save to shelf """ from maya import OpenMayaUI as OpenMayaUI import maya.cmds as cmds @@ -54,7 +56,7 @@ script_name = "GT - Extract Bound Joints" # Version -script_version = "1.1.0" +script_version = "1.1.1" # Settings extract_joints_settings = {'filter_non_existent': True, @@ -99,7 +101,7 @@ def build_gui_extract_bound_joints(): # Body ==================== cmds.rowColumnLayout(nc=1, cw=[(1, 500)], cs=[(1, 10)], p=content_main) cmds.separator(h=5, style='none') # Empty Space - cmds.rowColumnLayout(nc=2, cw=[(1, 200)], cs=[(1, 70), (2, 15)]) + cmds.rowColumnLayout(nc=2, cw=[(1, 200)], cs=[(1, 60), (2, 35)]) filter_non_existent_chk = cmds.checkBox("Include Non-Existent Filter", value=True, cc=lambda x: _btn_update_settings()) include_mesh_chk = cmds.checkBox("Include Bound Mesh", value=True, cc=lambda x: _btn_update_settings()) @@ -248,14 +250,22 @@ def build_gui_help_extract_bound_joints(): cmds.text(l='Adds a line of code that ignores objects not found in the scene.\n', align="left") cmds.text(l='Include Bound Mesh:', align="left", fn="boldLabelFont") cmds.text(l='Determines if the selected bound mesh will be included in the\nextracted list.\n', align="left") - cmds.text(l='"Extract Bound Joints" button:', align="left", fn="boldLabelFont") + cmds.text(l='"Extract Bound Joints to Python" button:', align="left", fn="boldLabelFont") cmds.text(l='Outputs the python code necessary to reselect the joints', align="left") cmds.text(l='inside the "Output Python Curve" box.', align="left") cmds.separator(h=15, style='none') # Empty Space + cmds.text(l='"Extract Bound Joints to Selection Sets" button:', align="left", fn="boldLabelFont") + cmds.text(l='Saves the bound joints as selection sets instead of Python.' + '\nOne set per mesh. (May or may not include mesh, according \nto checkbox settings.', align="left") + cmds.separator(h=15, style='none') # Empty Space cmds.text(l='Run Code:', align="left", fn="boldLabelFont") cmds.text(l='Attempts to run the code (or anything written) inside ', align="left") cmds.text(l='"Output Python Curve" box', align="left") cmds.separator(h=15, style='none') # Empty Space + cmds.text(l='Save to Shelf:', align="left", fn="boldLabelFont") + cmds.text(l='Attempts to save the code (or anything written) inside ', align="left") + cmds.text(l='"Output Python Curve" box to the shelf as a button.', align="left") + cmds.separator(h=15, style='none') # Empty Space cmds.rowColumnLayout(nc=2, cw=[(1, 140), (2, 140)], cs=[(1, 10), (2, 0)], p="main_column") cmds.text('Guilherme Trevisan ') cmds.text(l='TrevisanGMW@gmail.com', hl=True, highlightColor=[1, 1, 1]) From aa554fb570bf8cba8d31fd08334e1b86ca0b7946 Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 11:46:40 -0700 Subject: [PATCH 07/15] Updated help --- python-scripts/gt_extract_bound_joints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python-scripts/gt_extract_bound_joints.py b/python-scripts/gt_extract_bound_joints.py index fefbe23f..871e6de6 100644 --- a/python-scripts/gt_extract_bound_joints.py +++ b/python-scripts/gt_extract_bound_joints.py @@ -263,8 +263,8 @@ def build_gui_help_extract_bound_joints(): cmds.text(l='"Output Python Curve" box', align="left") cmds.separator(h=15, style='none') # Empty Space cmds.text(l='Save to Shelf:', align="left", fn="boldLabelFont") - cmds.text(l='Attempts to save the code (or anything written) inside ', align="left") - cmds.text(l='"Output Python Curve" box to the shelf as a button.', align="left") + cmds.text(l='Saves the code (or anything written) inside ', align="left") + cmds.text(l='"Output Python Curve" box to the current shelf as a button.', align="left") cmds.separator(h=15, style='none') # Empty Space cmds.rowColumnLayout(nc=2, cw=[(1, 140), (2, 140)], cs=[(1, 10), (2, 0)], p="main_column") cmds.text('Guilherme Trevisan ') From 49e69ce9cdb9a4c85a8015a4abbed2ca0f2a2eae Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 11:46:53 -0700 Subject: [PATCH 08/15] Updated extract bound joints --- docs/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 3e8fe3b9..e4b9f050 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1087,9 +1087,11 @@ If painting the skin weights with "ngSkinTools" (third party plugin) you might h
1. Select bound meshes or surfaces.
2. Click on the "Extract Bound Joints" button to generate the code.

-

"Extract Bound Joints" button:
Outputs the python code necessary to reselect the joints into the "Output PYthon Curve" box.

+

"Extract Bound Joints to Python" button:
Outputs the python code necessary to reselect the joints into the "Output PYthon Curve" box.

+

"Extract Bound Joints to Selection Sets" button:
Saves the bound joints as selection sets instead of Python. One set per mesh. (May or may not include mesh, according to checkbox settings.

Run Code:
Attempts to run the code (or anything written) inside "Output Python Curve" box

+

Save to Shelf:
Saves the code (or anything written) inside "Output Python Curve" box as a shelf button.


From c514ae9a78b0f3f89389f8a1237d2fefaea62b73 Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 14:52:44 -0700 Subject: [PATCH 09/15] Added "create_cheek_nose_controls" function --- python-scripts/gt_rigger_utilities.py | 221 +++++++++++++++++++++++--- 1 file changed, 200 insertions(+), 21 deletions(-) diff --git a/python-scripts/gt_rigger_utilities.py b/python-scripts/gt_rigger_utilities.py index e383edf7..5c380125 100644 --- a/python-scripts/gt_rigger_utilities.py +++ b/python-scripts/gt_rigger_utilities.py @@ -8,6 +8,9 @@ 2022-03-22 Added "enforce_parent" function + 2022-07-26 + Added "create_cheek_nose_controls" function + """ from gt_utilities import make_flat_list from gt_rigger_data import * @@ -1793,8 +1796,8 @@ def create_slider_control(name, initial_position='middle', lock_unused_channels= return [ctrl, ctrl_grp] -def create_2d_slider_control(name, initial_position_y='middle', initial_position_x='middle', lock_unused_channels=True, - ignore_range=None): +def create_2d_slider_control(name, initial_position_y='middle', initial_position_x='middle', + lock_unused_channels=True, ignore_range=None): """ Args: @@ -1802,7 +1805,7 @@ def create_2d_slider_control(name, initial_position_y='middle', initial_position initial_position_y: "middle", "top" or "bottom" (string) initial_position_x: "middle", "right" or "left" (string) lock_unused_channels: locks and hides unused channels (TX, TZ, ROT...) - ignore_range: TODO + ignore_range: "right", "left", "bottom" or "up". 2D Area to be ignored and removed from the available range Returns: ctrl_elements: A list with the control name and control group name @@ -2227,6 +2230,179 @@ def create_eyebrow_controls(): return gui_grp, controls +def create_cheek_nose_controls(): + """ + Dependencies: + rescale() + create_slider_control() + create_2d_slider_control() + create_text() + force_center_pivot() + change_outliner_color() + change_viewport_color() + + Returns: + control_tuple: A tuple with the parent group name and a list with all generated controls. + E.g. ('eyebrow_gui_grp', ['ctrl_one', 'ctrl_two']) + + """ + # Containers + controls = [] + background = [] + + # Top Label + nose_cheek_crv = create_text('NOSE / CHEEK') + left_nose_crv = create_text('LEFT NOSE') + right_nose_crv = create_text('RIGHT NOSE') + left_cheek_in_out_crv = create_text('IN/OUT') + right_cheek_in_out_crv = create_text('IN/OUT') + force_center_pivot(nose_cheek_crv) + rescale(nose_cheek_crv, 1.75) + cmds.setAttr(nose_cheek_crv + '.ty', 7.3) + for ctrl in [nose_cheek_crv, left_nose_crv, right_nose_crv, left_cheek_in_out_crv, right_cheek_in_out_crv]: + cmds.setAttr(ctrl + '.overrideDisplayType', 2) + background.append(nose_cheek_crv) + background.append(left_nose_crv) + background.append(right_nose_crv) + background.append(left_cheek_in_out_crv) + background.append(right_cheek_in_out_crv) + + # 1D Controls + left_cheek_in_out_ctrl = create_slider_control('left_cheek_in_out_offset_ctrl') + right_cheek_in_out_ctrl = create_slider_control('right_cheek_in_out_offset_ctrl') + + # 2D Controls + left_cheek_ctrl = create_2d_slider_control('left_cheek_offset_ctrl') + right_cheek_ctrl = create_2d_slider_control('right_cheek_offset_ctrl') + left_nose_ctrl = create_2d_slider_control('left_nose_offset_ctrl') + right_nose_ctrl = create_2d_slider_control('right_nose_offset_ctrl') + + # Reposition / Rescale BG + left_nose_crv_tx = 0.05 + right_nose_crv_tx = -5.3 + nose_crv_ty = -3.2 + nose_crv_scale = .5 + cmds.setAttr(left_nose_crv + '.tx', left_nose_crv_tx) + cmds.setAttr(right_nose_crv + '.tx', right_nose_crv_tx) + cmds.setAttr(left_nose_crv + '.ty', nose_crv_ty) + cmds.setAttr(right_nose_crv + '.ty', nose_crv_ty) + rescale(left_nose_crv, nose_crv_scale, freeze=False) + rescale(right_nose_crv, nose_crv_scale, freeze=False) + + left_cheek_in_out_crv_tx = 5.35 + right_cheek_in_out_crv_tx = -8.65 + cheek_in_out_crv_ty = -5.5 + cheek_in_out_crv_scale = .55 + cmds.setAttr(left_cheek_in_out_crv + '.tx', left_cheek_in_out_crv_tx) + cmds.setAttr(right_cheek_in_out_crv + '.tx', right_cheek_in_out_crv_tx) + cmds.setAttr(left_cheek_in_out_crv + '.ty', cheek_in_out_crv_ty) + cmds.setAttr(right_cheek_in_out_crv + '.ty', cheek_in_out_crv_ty) + rescale(left_cheek_in_out_crv, cheek_in_out_crv_scale, freeze=False) + rescale(right_cheek_in_out_crv, cheek_in_out_crv_scale, freeze=False) + + # Reposition / Rescale Ctrls + cheek_tx = 13.5 + cheek_ty = -1 + cheek_scale = .75 + cmds.setAttr(left_cheek_ctrl[1] + '.tx', cheek_tx) + cmds.setAttr(right_cheek_ctrl[1] + '.tx', -cheek_tx) + cmds.setAttr(left_cheek_ctrl[1] + '.ty', cheek_ty) + cmds.setAttr(right_cheek_ctrl[1] + '.ty', cheek_ty) + rescale(left_cheek_ctrl[1], cheek_scale, freeze=False) + rescale(right_cheek_ctrl[1], cheek_scale, freeze=False) + + nose_tx = 2.5 + nose_ty = -.6 + nose_scale = .3 + cmds.setAttr(left_nose_ctrl[1] + '.tx', nose_tx) + cmds.setAttr(right_nose_ctrl[1] + '.tx', -nose_tx) + cmds.setAttr(left_nose_ctrl[1] + '.ty', nose_ty) + cmds.setAttr(right_nose_ctrl[1] + '.ty', nose_ty) + rescale(left_nose_ctrl[1], nose_scale, freeze=False) + rescale(right_nose_ctrl[1], nose_scale, freeze=False) + + cheek_in_out_tx = 7 + cheek_in_out_ty = -.1 + cheek_in_out_scale = cheek_scale*.8 + cmds.setAttr(left_cheek_in_out_ctrl[1] + '.tx', cheek_in_out_tx) + cmds.setAttr(right_cheek_in_out_ctrl[1] + '.tx', -cheek_in_out_tx) + cmds.setAttr(left_cheek_in_out_ctrl[1] + '.ty', cheek_in_out_ty) + cmds.setAttr(right_cheek_in_out_ctrl[1] + '.ty', cheek_in_out_ty) + rescale(left_cheek_in_out_ctrl[1], cheek_in_out_scale, freeze=False) + rescale(right_cheek_in_out_ctrl[1], cheek_in_out_scale, freeze=False) + + # Determine Grp Order + controls.append(left_cheek_ctrl) + controls.append(right_cheek_ctrl) + controls.append(left_nose_ctrl) + controls.append(right_nose_ctrl) + controls.append(left_cheek_in_out_ctrl) + controls.append(right_cheek_in_out_ctrl) + + # L and R Indicators + l_crv = cmds.curve(p=[[12.357, -0.616, 0], [11.643, -0.616, 0], [11.643, 0.616, 0], [11.807, 0.616, 0], + [11.807, -0.47, 0], [12.357, -0.47, 0], [12.357, -0.616, 0], [11.643, -0.616, 0], + [11.643, 0.616, 0]], d=1, + name='left_indicator_nose_cheek_crv') + r_crv_a = cmds.curve(p=[[-11.523, -0.616, 0], [-11.63, -0.616, 0], [-11.736, -0.616, 0], [-11.931, -0.371, 0], + [-12.126, -0.126, 0], [-12.22, -0.126, 0], [-12.313, -0.126, 0], [-12.313, -0.371, 0], + [-12.313, -0.616, 0], [-12.395, -0.616, 0], [-12.477, -0.616, 0], [-12.477, 0, 0], + [-12.477, 0.616, 0], [-12.318, 0.616, 0], [-12.159, 0.616, 0], [-12.053, 0.616, 0], + [-11.91, 0.592, 0], [-11.846, 0.55, 0], [-11.781, 0.509, 0], [-11.706, 0.378, 0], + [-11.706, 0.282, 0], [-11.706, 0.146, 0], [-11.843, -0.036, 0], [-11.962, -0.08, 0], + [-11.742, -0.348, 0], [-11.523, -0.616, 0]], d=1, + name='right_indicator_a_nose_cheek_crv') + r_crv_b = cmds.curve(p=[[-11.877, 0.269, 0], [-11.877, 0.323, 0], [-11.915, 0.406, 0], [-11.955, 0.433, 0], + [-11.99, 0.456, 0], [-12.082, 0.475, 0], [-12.151, 0.475, 0], [-12.232, 0.475, 0], + [-12.313, 0.475, 0], [-12.313, 0.243, 0], [-12.313, 0.01, 0], [-12.241, 0.01, 0], + [-12.169, 0.01, 0], [-12.099, 0.01, 0], [-11.986, 0.035, 0], [-11.947, 0.074, 0], + [-11.911, 0.109, 0], [-11.877, 0.205, 0], [-11.877, 0.269, 0]], d=1, + name='right_indicator_b_nose_cheek_crv') + + r_crv = combine_curves_list([r_crv_a, r_crv_b]) + cmds.setAttr(l_crv + '.overrideDisplayType', 2) + cmds.setAttr(r_crv + '.overrideDisplayType', 2) + cmds.setAttr(l_crv + '.ty', 7.3) + cmds.setAttr(r_crv + '.ty', 7.3) + cmds.setAttr(l_crv + '.tx', 3) + cmds.setAttr(r_crv + '.tx', -3) + background.append(l_crv) + background.append(r_crv) + + # Parent Groups + gui_grp = cmds.group(name='cheek_nose_gui_grp', world=True, empty=True) + bg_grp = cmds.group(name='cheek_nose_background_grp', world=True, empty=True) + + # General Background + eyebrow_bg_crv = cmds.curve(name='cheek_nose_bg_crv', p=[[-20.0, 10.0, 0.0], [-20.0, -8.0, 0.0], [20.0, -8.0, 0.0], + [20.0, 10.0, 0.0], [-20.0, 10.0, 0.0]], d=1) + + cmds.setAttr(eyebrow_bg_crv + '.overrideDisplayType', 1) + background.append(eyebrow_bg_crv) + + for obj in controls: + cmds.parent(obj[1], gui_grp) + if 'left_' in obj[0]: + change_viewport_color(obj[0], LEFT_CTRL_COLOR) + change_outliner_color(obj[1], (0.21, 0.59, 1)) # Soft Blue + elif 'right_' in obj[0]: + change_viewport_color(obj[0], RIGHT_CTRL_COLOR) + change_outliner_color(obj[1], RIGHT_CTRL_COLOR) + else: + change_viewport_color(obj[0], CENTER_CTRL_COLOR) + change_outliner_color(obj[1], CENTER_CTRL_COLOR) + + for obj in background: + cmds.parent(obj, bg_grp) + cmds.setAttr(obj + '.overrideEnabled', 1) + + # Background Group + cmds.parent(bg_grp, gui_grp) + change_outliner_color(bg_grp, (0, 0, 0)) + + return gui_grp, controls + + def create_eye_controls(): """ Dependencies: @@ -2381,7 +2557,8 @@ def create_eye_controls(): return gui_grp, controls -def create_facial_side_gui(): +def create_facial_side_gui(add_nose_cheeks=False): + selection = cmds.ls(selection=True) parent_grp = cmds.group(empty=True, world=True, name='facial_side_gui_grp') eyebrow_ctrls = create_eyebrow_controls() eye_ctrls = create_eye_controls() @@ -2391,6 +2568,13 @@ def create_facial_side_gui(): cmds.parent(eyebrow_ctrls[0], parent_grp) cmds.parent(eye_ctrls[0], parent_grp) cmds.parent(mouth_ctrls[0], parent_grp) + if add_nose_cheeks: + nose_cheek_ctrls = create_cheek_nose_controls() + cmds.parent(nose_cheek_ctrls[0], parent_grp) + cmds.move(22, nose_cheek_ctrls[0], moveY=True) + cmds.move(42, eye_ctrls[0], moveY=True) + cmds.move(62, eyebrow_ctrls[0], moveY=True) + cmds.select(selection) return parent_grp @@ -2744,7 +2928,7 @@ def store_proxy_as_string(target_obj, attr_name, data_obj, method='world-space') method (optional, string): which method is used to generate the data """ - # Store Facial Proxy + # Store Proxy if method == 'object-space': export_dict = {data_obj.proxy_storage_variables.get('script_source'): data_obj.script_version, data_obj.proxy_storage_variables.get('export_method'): method} @@ -2846,20 +3030,15 @@ def print_inview_message(part_a, part_b, color_a='FF0000', color_b='FFFFFF'): pass logger.setLevel(logging.DEBUG) output = '' - # create_slider_control('temp', initial_position='bottom', lock_unused_channels=True) - # enforce_parent('pSphere1', 'head_offsetCtrl') - # if cmds.objExists('slider_2dGrp'): - # cmds.delete('slider_2dGrp') - # create_slider_control('1d_slider', initial_position='bottom') - # output = create_2d_slider_control('slider_2d', - # initial_position_y='middle', - # initial_position_x='middle', - # ignore_range='top') - # print(output) - # toggle_rigging_attr() - # print_inview_message('Hello', ' World!') - # create_facial_side_gui() - data_facial = GTBipedRiggerFacialData() - # output = store_proxy_as_string('head_ctrl', 'facial_proxy_pose', data_facial) - # output = store_proxy_as_string('head_ctrl', 'facial_proxy_pose', data_facial, method='object-space') + # data_facial = GTBipedRiggerFacialData() + try: + cmds.delete('facial_side_gui_grp') + except: + pass + try: + cmds.delete('cheek_nose_gui_grp') + except: + pass + # create_cheek_nose_controls() + create_facial_side_gui(True) logger.debug(str(output)) From 7c24c2d44ef65838a54b60945ac97dcd7680cccf Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 17:22:05 -0700 Subject: [PATCH 10/15] Added save to shelf --- python-scripts/gt_attributes_to_python.py | 87 +++++++++++++++++++++-- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/python-scripts/gt_attributes_to_python.py b/python-scripts/gt_attributes_to_python.py index c04b902e..d7552a9c 100644 --- a/python-scripts/gt_attributes_to_python.py +++ b/python-scripts/gt_attributes_to_python.py @@ -17,12 +17,17 @@ Increased the size of the UI Added "Extract User-Defined Attributes" function + 0.0.6 - 2022-07-26 + Added save to shelf + Updated help + TODO: Add options """ from maya import OpenMayaUI as OpenMayaUI import maya.cmds as cmds +import maya.mel as mel import logging import sys @@ -186,14 +191,12 @@ def default_attr_to_python(obj_list, printing=True, use_loop=False, decimal_plac return output -def user_attr_to_python(obj_list, printing=True, decimal_place=2, strip_zeroes=True): +def user_attr_to_python(obj_list, printing=True): """ Returns a string Args: obj_list (list, none): List objects to extract the transform from (if empty, it will try to use selection) printing (optional, bool): If active, the function will print the values to the script editor - decimal_place (optional, int): How precise you want the extracted values to be (formats the float it gets) - strip_zeroes (optional, bool): If active, it will remove unnecessary zeroes (e.g. 0.0 -> 0) Returns: Python code with extracted transform values @@ -210,7 +213,6 @@ def user_attr_to_python(obj_list, printing=True, decimal_place=2, strip_zeroes=T for obj in obj_list: output += '\n# User-Defined Attribute Data for "' + obj + '":\n' - data = {} attributes = cmds.listAttr(obj, userDefined=True) or [] if not attributes: output += '# No user-defined attributes found on this object.\n' @@ -297,9 +299,25 @@ def build_gui_attr_to_python(): cmds.text(label='Output Python Code') output_python = cmds.scrollField(editable=True, wordWrap=True, height=200) cmds.separator(h=10, style='none') # Empty Space + cmds.rowColumnLayout(nc=2, cw=[(1, 235), (2, 235)], cs=[(1, 15), (2, 15)], p=content_main) cmds.button(l="Run Code", c=lambda x: run_output_code(cmds.scrollField(output_python, query=True, text=True))) + cmds.button(l="Save to Shelf", c=lambda x: _btn_add_to_shelf()) cmds.separator(h=10, style='none') # Empty Space + def _btn_add_to_shelf(): + command = cmds.scrollField(output_python, query=True, text=True) or '' + if command: + create_shelf_button(command, + label='setAttr', + tooltip='Extracted Attributes', + image="editRenderPass.png", + label_color=(0, .84, .81)) + cmds.inViewMessage(amg='Python Output Command' + ' was added as a button to your current shelf.', + pos='botLeft', fade=True, alpha=.9) + else: + cmds.warning('Unable to save to shelf. "Output Python Code" is empty.') + def _btn_extract_attr(attr_type='default'): selection = cmds.ls(selection=True) or [] @@ -311,8 +329,7 @@ def _btn_extract_attr(attr_type='default'): output_python_command = attr_to_list(selection, printing=False, decimal_place=2, separate_channels=False, strip_zeroes=True) elif attr_type == 'user': - output_python_command = user_attr_to_python(selection, printing=False, - decimal_place=2, strip_zeroes=True) + output_python_command = user_attr_to_python(selection, printing=False) else: output_python_command = default_attr_to_python(selection, printing=False, use_loop=False, decimal_place=2, strip_zeroes=True) @@ -377,6 +394,10 @@ def build_gui_help_attr_to_python(): cmds.text(l='Run Code:', align="left", fn="boldLabelFont") cmds.text(l='Attempts to run the code (or anything written) inside ', align="left") cmds.text(l='"Output Python Curve" box', align="left") + cmds.separator(h=10, style='none') # Empty Space + cmds.text(l='Save to Shelf:', align="left", fn="boldLabelFont") + cmds.text(l='Creates a shelf button with the code (or anything written) inside ', align="left") + cmds.text(l='"Output Python Code" box', align="left") cmds.separator(h=15, style='none') # Empty Space cmds.rowColumnLayout(nc=2, cw=[(1, 140), (2, 140)], cs=[(1, 10), (2, 0)], p="main_column") cmds.text('Guilherme Trevisan ') @@ -407,5 +428,59 @@ def close_help_gui(): cmds.deleteUI(window_name, window=True) +def create_shelf_button(command, + label='', + tooltip='', + image=None, # Default Python Icon + label_color=(1, 0, 0), # Default Red + label_bgc_color=(0, 0, 0, 1), # Default Black + bgc_color=None + ): + """ + Add a shelf button to the current shelf (according to the provided parameters) + + Args: + command (str): A string containing the code or command you want the button to run when clicking on it. + e.g. "print("Hello World")" + label (str): The label of the button. This is the text you see below it. + tooltip (str): The help message you get when hovering the button. + image (str): The image used for the button (defaults to Python icon if none) + label_color (tuple): A tuple containing three floats, + these are RGB 0 to 1 values to determine the color of the label. + label_bgc_color (tuple): A tuple containing four floats, + these are RGBA 0 to 1 values to determine the background of the label. + bgc_color (tuple): A tuple containing three floats, + these are RGB 0 to 1 values to determine the background of the icon + + """ + maya_version = int(cmds.about(v=True)) + + shelf_top_level = mel.eval('$temp=$gShelfTopLevel') + if not cmds.tabLayout(shelf_top_level, exists=True): + cmds.warning('Shelf is not visible') + return + + if not image: + image = 'pythonFamily.png' + + shelf_tab = cmds.shelfTabLayout(shelf_top_level, query=True, selectTab=True) + shelf_tab = shelf_top_level + '|' + shelf_tab + + # Populate extra arguments according to the current Maya version + kwargs = {} + if maya_version >= 2009: + kwargs['commandRepeatable'] = True + if maya_version >= 2011: + kwargs['overlayLabelColor'] = label_color + kwargs['overlayLabelBackColor'] = label_bgc_color + if bgc_color: + kwargs['enableBackground'] = bool(bgc_color) + kwargs['backgroundColor'] = bgc_color + + return cmds.shelfButton(parent=shelf_tab, label=label, command=command, + imageOverlayLabel=label, image=image, annotation=tooltip, + width=32, height=32, align='center', **kwargs) + + if __name__ == '__main__': build_gui_attr_to_python() From 7d7f449d5e7bc2aca11f2e1da5657b98b8608df1 Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 17:22:17 -0700 Subject: [PATCH 11/15] Fixed a typo --- python-scripts/gt_extract_bound_joints.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/python-scripts/gt_extract_bound_joints.py b/python-scripts/gt_extract_bound_joints.py index 871e6de6..3eb9f0f7 100644 --- a/python-scripts/gt_extract_bound_joints.py +++ b/python-scripts/gt_extract_bound_joints.py @@ -28,8 +28,13 @@ Updated help Tweaked the UI spacing +1.1.2 - 2022-07-26 +Fixed a typo +Removed unnecessary parameter + Todo: Add Transfer functions + Add option to include maya.cmds """ from maya import OpenMayaUI as OpenMayaUI import maya.cmds as cmds @@ -56,7 +61,7 @@ script_name = "GT - Extract Bound Joints" # Version -script_version = "1.1.1" +script_version = "1.1.2" # Settings extract_joints_settings = {'filter_non_existent': True, @@ -134,15 +139,13 @@ def _btn_add_to_shelf(): create_shelf_button(command, label='bJnts', tooltip='Extracted joints', - image="smoothSkin.png", # Default Python Icon - label_color=(.97, 0, 1.7), # Default Red - label_bgc_color=(0, 0, 0, 1), # Default Black - ) + image="smoothSkin.png", + label_color=(.97, 0, 1.7)) cmds.inViewMessage(amg='Current Selection Command' ' was added as a button to your current shelf.', pos='botLeft', fade=True, alpha=.9) else: - cmds.warning('Unable to save to shelf. "Output- Selection Command" is empty.') + cmds.warning('Unable to save to shelf. "Output - Selection Command" is empty.') def _btn_extract_bound_validation(operation_target='python'): """ @@ -329,7 +332,6 @@ def get_bound_joints(obj): def create_shelf_button(command, label='', - name=None, tooltip='', image=None, # Default Python Icon label_color=(1, 0, 0), # Default Red @@ -343,7 +345,6 @@ def create_shelf_button(command, command (str): A string containing the code or command you want the button to run when clicking on it. e.g. "print("Hello World")" label (str): The label of the button. This is the text you see below it. - name (str): The name of the button as seen inside the shelf editor. tooltip (str): The help message you get when hovering the button. image (str): The image used for the button (defaults to Python icon if none) label_color (tuple): A tuple containing three floats, From ebdabc5f05ca14d1351e3bcb6ac77988a5daaf01 Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 17:23:08 -0700 Subject: [PATCH 12/15] Added save to shelf --- python-scripts/gt_shape_curve_to_python.py | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/python-scripts/gt_shape_curve_to_python.py b/python-scripts/gt_shape_curve_to_python.py index e2eddbf9..df606e6c 100644 --- a/python-scripts/gt_shape_curve_to_python.py +++ b/python-scripts/gt_shape_curve_to_python.py @@ -33,6 +33,7 @@ 1.6.2 - 2022-07-14 Updated script name Increased the size of the output window + Updated help """ @@ -122,9 +123,27 @@ def build_gui_py_curve(): cmds.text(label='Output Python Curve') output_python = cmds.scrollField(editable=True, wordWrap=True, height=200) cmds.separator(h=10, style='none') # Empty Space + cmds.rowColumnLayout(nc=2, cw=[(1, 235), (2, 235)], cs=[(1, 15), (2, 15)], p=content_main) cmds.button(l="Run Code", c=lambda x: run_output_code(cmds.scrollField(output_python, query=True, text=True))) + cmds.button(l="Save to Shelf", c=lambda x: _btn_add_to_shelf()) cmds.separator(h=10, style='none') # Empty Space + def _btn_add_to_shelf(): + command = cmds.scrollField(output_python, query=True, text=True) or '' + if command: + create_shelf_button(command, + label='Crv', + tooltip='Extracted curve', + image="curveBezier.png", + label_color=(0, 0.84, 0.81), + label_bgc_color=(0, 0, 0, 1), + ) + cmds.inViewMessage(amg='Current Python Curve Command' + ' was added as a button to your current shelf.', + pos='botLeft', fade=True, alpha=.9) + else: + cmds.warning('Unable to save to shelf. "Output Python Curve" is empty.') + def generate_python_curve(): not_curve_error = "Please make sure you selected a Nurbs Curve or a Bezier Curve object before generating it" @@ -243,6 +262,10 @@ def build_gui_help_py_curve(): cmds.text(l='Attempts to run the code (or anything written) inside ', align="left") cmds.text(l='"Output Python Curve" box', align="left") cmds.separator(h=15, style='none') # Empty Space + cmds.text(l='Save To Shelf:', align="left", fn="boldLabelFont") + cmds.text(l='Saves to shelf as a button the code (or anything written) inside ', align="left") + cmds.text(l='"Output Python Curve" box', align="left") + cmds.separator(h=15, style='none') # Empty Space cmds.rowColumnLayout(nc=2, cw=[(1, 140), (2, 140)], cs=[(1, 10), (2, 0)], p="main_column") cmds.text('Guilherme Trevisan ') cmds.text(l='TrevisanGMW@gmail.com', hl=True, highlightColor=[1, 1, 1]) @@ -272,6 +295,60 @@ def close_help_gui(): cmds.deleteUI(window_name, window=True) +def create_shelf_button(command, + label='', + tooltip='', + image=None, # Default Python Icon + label_color=(1, 0, 0), # Default Red + label_bgc_color=(0, 0, 0, 1), # Default Black + bgc_color=None + ): + """ + Add a shelf button to the current shelf (according to the provided parameters) + + Args: + command (str): A string containing the code or command you want the button to run when clicking on it. + e.g. "print("Hello World")" + label (str): The label of the button. This is the text you see below it. + tooltip (str): The help message you get when hovering the button. + image (str): The image used for the button (defaults to Python icon if none) + label_color (tuple): A tuple containing three floats, + these are RGB 0 to 1 values to determine the color of the label. + label_bgc_color (tuple): A tuple containing four floats, + these are RGBA 0 to 1 values to determine the background of the label. + bgc_color (tuple): A tuple containing three floats, + these are RGB 0 to 1 values to determine the background of the icon + + """ + maya_version = int(cmds.about(v=True)) + + shelf_top_level = mel.eval('$temp=$gShelfTopLevel') + if not cmds.tabLayout(shelf_top_level, exists=True): + cmds.warning('Shelf is not visible') + return + + if not image: + image = 'pythonFamily.png' + + shelf_tab = cmds.shelfTabLayout(shelf_top_level, query=True, selectTab=True) + shelf_tab = shelf_top_level + '|' + shelf_tab + + # Populate extra arguments according to the current Maya version + kwargs = {} + if maya_version >= 2009: + kwargs['commandRepeatable'] = True + if maya_version >= 2011: + kwargs['overlayLabelColor'] = label_color + kwargs['overlayLabelBackColor'] = label_bgc_color + if bgc_color: + kwargs['enableBackground'] = bool(bgc_color) + kwargs['backgroundColor'] = bgc_color + + return cmds.shelfButton(parent=shelf_tab, label=label, command=command, + imageOverlayLabel=label, image=image, annotation=tooltip, + width=32, height=32, align='center', **kwargs) + + # Build UI if __name__ == '__main__': build_gui_py_curve() From 0a688704223fbb9b9872d107b0de5d91bcf56acf Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 17:29:43 -0700 Subject: [PATCH 13/15] Added save to shelf --- docs/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index e4b9f050..6a3ebd17 100644 --- a/docs/README.md +++ b/docs/README.md @@ -632,6 +632,7 @@ with the provided prefix "Left Side Tag".

"Generate" button:
Outputs the python code necessary to create the curve inside the "Output PYthon Curve" box.

Run Code:
Attempts to run the code (or anything written) inside "Output Python Curve" box

+

Save to Shelf:
Saves the code (or anything written) inside "Output Python Curve" box as a shelf button.


@@ -674,7 +675,8 @@ with the provided prefix "Left Side Tag".

"Extract State" button:
Outputs the python code necessary to recreate the current curve shape inside the "Output PYthon Curve" box.

-

Run Code:
Attempts to run the code (or anything written) inside "Output Python Curve" box

+

Run Code:
Attempts to run the code (or anything written) inside "Output Python Code" box

+

Save to Shelf:
Saves the code (or anything written) inside "Output Python Code" box as a shelf button.


@@ -1090,8 +1092,8 @@ If painting the skin weights with "ngSkinTools" (third party plugin) you might h

"Extract Bound Joints to Python" button:
Outputs the python code necessary to reselect the joints into the "Output PYthon Curve" box.

"Extract Bound Joints to Selection Sets" button:
Saves the bound joints as selection sets instead of Python. One set per mesh. (May or may not include mesh, according to checkbox settings.

-

Run Code:
Attempts to run the code (or anything written) inside "Output Python Curve" box

-

Save to Shelf:
Saves the code (or anything written) inside "Output Python Curve" box as a shelf button.

+

Run Code:
Attempts to run the code (or anything written) inside "Output Selection Command" box

+

Save to Shelf:
Saves the code (or anything written) inside "Output Selection Command" box as a shelf button.


From 612681a7762e720b60097db5aef1e4b69201f61c Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 17:29:51 -0700 Subject: [PATCH 14/15] Added save to shelf --- python-scripts/gt_shape_curve_to_python.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python-scripts/gt_shape_curve_to_python.py b/python-scripts/gt_shape_curve_to_python.py index df606e6c..bcf1fa49 100644 --- a/python-scripts/gt_shape_curve_to_python.py +++ b/python-scripts/gt_shape_curve_to_python.py @@ -35,6 +35,9 @@ Increased the size of the output window Updated help + 1.6.3 - 2022-07-26 + Added save to shelf + """ import maya.cmds as cmds @@ -62,7 +65,7 @@ script_name = "GT - Extract Python Curve" # Version: -script_version = "1.6.2" +script_version = "1.6.3" # Default Settings close_curve = False From 00ebfd0a93e394529c06310cffe1e620ed9a5328 Mon Sep 17 00:00:00 2001 From: TrevisanGMW Date: Tue, 26 Jul 2022 17:30:03 -0700 Subject: [PATCH 15/15] Added save to shelf and updated help --- python-scripts/gt_shape_extract_state.py | 81 +++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/python-scripts/gt_shape_extract_state.py b/python-scripts/gt_shape_extract_state.py index ab72bc16..0bf83af8 100644 --- a/python-scripts/gt_shape_extract_state.py +++ b/python-scripts/gt_shape_extract_state.py @@ -16,9 +16,14 @@ 1.2.1 - 2022-07-23 Increased the size of the main window + 1.2.2 - 2022-07-26 + Added save to shelf + Updated help + """ from maya import OpenMayaUI as OpenMayaUI import maya.cmds as cmds +import maya.mel as mel import logging import sys @@ -42,7 +47,7 @@ script_name = "GT - Extract Shape State" # Version -script_version = "1.2.1" +script_version = "1.2.2" def extract_python_curve_shape(curve_transforms, printing=False): @@ -143,9 +148,25 @@ def build_gui_curve_shape_state(): cmds.text(label='Output Python Curve') output_python = cmds.scrollField(editable=True, wordWrap=True, height=200) cmds.separator(h=10, style='none') # Empty Space + cmds.rowColumnLayout(nc=2, cw=[(1, 235), (2, 235)], cs=[(1, 15), (2, 15)], p=content_main) cmds.button(l="Run Code", c=lambda x: run_output_code(cmds.scrollField(output_python, query=True, text=True))) + cmds.button(l="Save to Shelf", c=lambda x: _btn_add_to_shelf()) cmds.separator(h=10, style='none') # Empty Space + def _btn_add_to_shelf(): + command = cmds.scrollField(output_python, query=True, text=True) or '' + if command: + create_shelf_button(command, + label='setCrv', + tooltip='Extracted Curve State', + image="editRenderPass.png", + label_color=(0, .84, .81)) + cmds.inViewMessage(amg='Python Output Curve' + ' was added as a button to your current shelf.', + pos='botLeft', fade=True, alpha=.9) + else: + cmds.warning('Unable to save to shelf. "Output Python Curve" is empty.') + def _btn_extract_python_curve_shape(): selection = cmds.ls(selection=True) or [] @@ -214,6 +235,10 @@ def build_gui_help_curve_shape_state(): cmds.text(l='Attempts to run the code (or anything written) inside ', align="left") cmds.text(l='"Output Python Curve" box', align="left") cmds.separator(h=15, style='none') # Empty Space + cmds.text(l='Save to Shelf:', align="left", fn="boldLabelFont") + cmds.text(l='Saves to shelf as a button the code (or anything written) inside ', align="left") + cmds.text(l='"Output Python Curve" box', align="left") + cmds.separator(h=15, style='none') # Empty Space cmds.rowColumnLayout(nc=2, cw=[(1, 140), (2, 140)], cs=[(1, 10), (2, 0)], p="main_column") cmds.text('Guilherme Trevisan ') cmds.text(l='TrevisanGMW@gmail.com', hl=True, highlightColor=[1, 1, 1]) @@ -243,5 +268,59 @@ def close_help_gui(): cmds.deleteUI(window_name, window=True) +def create_shelf_button(command, + label='', + tooltip='', + image=None, # Default Python Icon + label_color=(1, 0, 0), # Default Red + label_bgc_color=(0, 0, 0, 1), # Default Black + bgc_color=None + ): + """ + Add a shelf button to the current shelf (according to the provided parameters) + + Args: + command (str): A string containing the code or command you want the button to run when clicking on it. + e.g. "print("Hello World")" + label (str): The label of the button. This is the text you see below it. + tooltip (str): The help message you get when hovering the button. + image (str): The image used for the button (defaults to Python icon if none) + label_color (tuple): A tuple containing three floats, + these are RGB 0 to 1 values to determine the color of the label. + label_bgc_color (tuple): A tuple containing four floats, + these are RGBA 0 to 1 values to determine the background of the label. + bgc_color (tuple): A tuple containing three floats, + these are RGB 0 to 1 values to determine the background of the icon + + """ + maya_version = int(cmds.about(v=True)) + + shelf_top_level = mel.eval('$temp=$gShelfTopLevel') + if not cmds.tabLayout(shelf_top_level, exists=True): + cmds.warning('Shelf is not visible') + return + + if not image: + image = 'pythonFamily.png' + + shelf_tab = cmds.shelfTabLayout(shelf_top_level, query=True, selectTab=True) + shelf_tab = shelf_top_level + '|' + shelf_tab + + # Populate extra arguments according to the current Maya version + kwargs = {} + if maya_version >= 2009: + kwargs['commandRepeatable'] = True + if maya_version >= 2011: + kwargs['overlayLabelColor'] = label_color + kwargs['overlayLabelBackColor'] = label_bgc_color + if bgc_color: + kwargs['enableBackground'] = bool(bgc_color) + kwargs['backgroundColor'] = bgc_color + + return cmds.shelfButton(parent=shelf_tab, label=label, command=command, + imageOverlayLabel=label, image=image, annotation=tooltip, + width=32, height=32, align='center', **kwargs) + + if __name__ == '__main__': build_gui_curve_shape_state()