#!/usr/bin/env python # -*- coding: utf-8 -*- import os import maya.cmds as cmds import maya.mel as mel from Core import ProgressBar, Descriptor, ReadJson, GetJoints, GetNeutralJointTranslations # 配置常量 JOINT_ORIENT_CONFIG = { 'DEFAULT_AIM_AXIS': [1, 0, 0], 'DEFAULT_UP_AXIS': [0, 1, 0], 'DEFAULT_UP_VECTOR': [0, 1, 0], 'UI_WIDTH': 310, 'UI_HEIGHT': 256 } def enable_joint_orient(): """启用关节方向编辑功能""" try: comet_joint_orient() except Exception as e: cmds.warning(f"Error in joint orient editor: {str(e)}") def comet_joint_orient(): """创建关节方向编辑窗口""" # 检查SuperRiggingEditor窗口是否存在 if not cmds.window('SuperRiggingEditor', exists=True): return # 获取语言设置 labels = get_ui_labels() # 如果窗口已存在则删除 if cmds.window('cometJointOrientWin', exists=True): cmds.deleteUI('cometJointOrientWin') # 创建窗口 cmds.window( 'cometJointOrientWin', title="JointOrient", width=JOINT_ORIENT_CONFIG['UI_WIDTH'], height=JOINT_ORIENT_CONFIG['UI_HEIGHT'], sizeable=True, toolbox=True, parent='SuperRiggingEditor' ) # 创建主布局 main_form = cmds.formLayout('mainForm') # 创建控件 sep0 = cmds.separator(style="in", height=3) btn_show = cmds.button( label=labels['ShowAxis'], align="center", command=lambda x: show_axis(1), annotation="Show Local Axis" ) btn_hide = cmds.button( label=labels['HideAxis'], align="center", command=lambda x: show_axis(0), annotation="Hide Local Axis" ) rbg_aim = cmds.radioButtonGrp( label=labels['AimAxis'], numberOfRadioButtons=3, labelArray3=['X', 'Y', 'Z'], select=2, columnWidth4=[80, 40, 40, 40] ) rbg_up = cmds.radioButtonGrp( label=labels['UpAxis'], numberOfRadioButtons=3, labelArray3=['X', 'Y', 'Z'], select=1, columnWidth4=[80, 40, 40, 40] ) cb_rev_aim = cmds.checkBox(label=labels['Reverse'], value=0) cb_rev_up = cmds.checkBox(label=labels['Reverse'], value=0) sep1 = cmds.separator(style="in", height=6) ffg_up_dir = cmds.floatFieldGrp( numberOfFields=3, label=labels['WorldUpDir'], value1=1.0, value2=0.0, value3=0.0, columnWidth4=[80, 50, 50, 50] ) btn_x = cmds.button( label="X", width=20, command=lambda x: cmds.floatFieldGrp(ffg_up_dir, edit=True, value1=1.0, value2=0.0, value3=0.0), annotation="Auto Set UpDir to X-Axis" ) btn_y = cmds.button( label="Y", width=20, command=lambda x: cmds.floatFieldGrp(ffg_up_dir, edit=True, value1=0.0, value2=1.0, value3=0.0), annotation="Auto Set UpDir to Y-Axis" ) btn_z = cmds.button( label="Z", width=20, command=lambda x: cmds.floatFieldGrp(ffg_up_dir, edit=True, value1=0.0, value2=0.0, value3=1.0), annotation="Auto Set UpDir to Z-Axis" ) cb_auto_dir = cmds.checkBox(label=labels['AutoGuessUpDirection'], value=0) btn_orient = cmds.button( label=labels['OrientJoints'], align="center", command=lambda x: orient_ui(), annotation="Orient selected joints based on settings above." ) sep_big = cmds.separator(style="in", height=6) ffg_tweak = cmds.floatFieldGrp( numberOfFields=3, label=labels['Tweak'], value1=0.0, value2=0.0, value3=0.0, columnWidth4=[80, 50, 50, 50] ) btn_zero = cmds.button( label=labels['Zero'], width=58, command=lambda x: cmds.floatFieldGrp(ffg_tweak, edit=True, value1=0.0, value2=0.0, value3=0.0), annotation="Zero's tweak values." ) popup = cmds.popupMenu(parent=ffg_tweak) axis_map = {'X': 1, 'Y': 2, 'Z': 3} for axis in ['X', 'Y', 'Z']: for value in [1, 5, 10]: cmds.menuItem( label=f"{axis}={value}", command=lambda axis=axis, value=value: cmds.floatFieldGrp(ffg_tweak, edit=True, value1=float(value) if axis == 'X' else None, value2=float(value) if axis == 'Y' else None, value3=float(value) if axis == 'Z' else None) ) if axis != 'Z': cmds.menuItem(divider=True) btn_tweak_p = cmds.button( label=labels['ManualAddRotTweak'], align="center", command=lambda x: tweak_ui(1.0), annotation="Manually rotates selected joints axis positive." ) btn_tweak_n = cmds.button( label=labels['ManualRedRotTweak'], align="center", command=lambda x: tweak_ui(-1.0), annotation="Manually rotates selected joints axis negative." ) sep2 = cmds.separator(style="in", height=6) btn_main_amend = cmds.button( label=labels['MainJointAmendAxis'], align="center", command=lambda x: main_amend_axis(), annotation="Main Amend Axis" ) btn_skin_amend = cmds.button( label=labels['SkinJointAmendAxis'], align="center", command=lambda x: skin_amend_axis(), annotation="Other Amend Axis" ) sep3 = cmds.separator(style="in", height=6) # 设置布局 cmds.formLayout( main_form, edit=True, attachForm=[ (sep0, 'top', 0), (sep0, 'left', 0), (sep0, 'right', 0), (btn_show, 'left', 5), (btn_hide, 'right', 5), (rbg_aim, 'left', 0), (rbg_up, 'left', 0), (sep1, 'left', 0), (sep1, 'right', 0), (ffg_up_dir, 'left', 0), (btn_orient, 'left', 5), (btn_orient, 'right', 5), (sep_big, 'left', 0), (sep_big, 'right', 0), (ffg_tweak, 'left', 0), (btn_zero, 'right', 5), (btn_tweak_p, 'left', 5), (btn_tweak_p, 'right', 5), (btn_tweak_n, 'left', 5), (btn_tweak_n, 'right', 5), (sep2, 'left', 0), (sep2, 'right', 0), (btn_main_amend, 'left', 5), (btn_main_amend, 'right', 5), (btn_skin_amend, 'left', 5), (btn_skin_amend, 'right', 5), (sep3, 'left', 0), (sep3, 'right', 0) ], attachControl=[ (btn_show, 'top', 3, sep0), (btn_hide, 'top', 3, sep0), (rbg_aim, 'top', 0, btn_show), (cb_rev_aim, 'top', 0, btn_show), (cb_rev_aim, 'left', 0, rbg_aim), (rbg_up, 'top', 0, rbg_aim), (cb_rev_up, 'top', 0, rbg_aim), (cb_rev_up, 'left', 0, rbg_up), (sep1, 'top', 0, rbg_up), (ffg_up_dir, 'top', 0, sep1), (btn_x, 'top', 0, sep1), (btn_x, 'left', 2, ffg_up_dir), (btn_y, 'top', 0, sep1), (btn_y, 'left', 2, btn_x), (btn_z, 'top', 0, sep1), (btn_z, 'left', 2, btn_y), (cb_auto_dir, 'top', 0, ffg_up_dir), (btn_orient, 'top', 5, cb_auto_dir), (sep_big, 'top', 5, btn_orient), (ffg_tweak, 'top', 0, sep_big), (btn_zero, 'top', 0, sep_big), (btn_zero, 'left', 2, ffg_tweak), (btn_tweak_p, 'top', 5, ffg_tweak), (btn_tweak_n, 'top', 5, btn_tweak_p), (sep2, 'top', 5, btn_tweak_n), (btn_main_amend, 'top', 5, sep2), (btn_skin_amend, 'top', 5, btn_main_amend), (sep3, 'top', 5, btn_skin_amend) ] ) # 显示窗口 cmds.showWindow('cometJointOrientWin') def get_ui_labels(): """ 根据语言设置获取UI标签 """ is_chinese = Descriptor(l=True) == "ZH" if is_chinese: return { 'ShowAxis': '显示坐标轴', 'HideAxis': '隐藏坐标轴', 'AimAxis': '目标轴:', 'UpAxis': '向上轴:', 'Reverse': '反向', 'WorldUpDir': '世界向上方向:', 'AutoGuessUpDirection': '自动识别向上方向', 'OrientJoints': '确定关节方向', 'Tweak': '调整:', 'Zero': '归零', 'ManualAddRotTweak': '手动旋转调整+', 'ManualRedRotTweak': '手动旋转调整-', 'MainJointAmendAxis': '主要关节轴向修正', 'SkinJointAmendAxis': '蒙皮关节轴向修正' } else: return { 'ShowAxis': 'Show Axis', 'HideAxis': 'Hide Axis', 'AimAxis': 'Aim Axis:', 'UpAxis': 'Up Axis:', 'Reverse': 'Reverse', 'WorldUpDir': 'World Up Dir:', 'AutoGuessUpDirection': 'Auto-Guess Up Direction', 'OrientJoints': 'Orient Joints', 'Tweak': 'Tweak:', 'Zero': 'Zero', 'ManualAddRotTweak': 'Manual + Rot Tweak', 'ManualRedRotTweak': 'Manual - Rot Tweak', 'MainJointAmendAxis': 'Main Joint Amend Axis', 'SkinJointAmendAxis': 'Skin Joint Amend Axis' } # 其他辅助函数... def main_amend_axis(): """ 主要关节轴向修正 """ # 获取所有关节 joints = body_joints() # 创建进度条 ProgressBar(sp=True) ProgressBar(max=len(joints)) ProgressBar(t="Amend Joint Axis...") # 处理每个关节 for joint in joints: ProgressBar(apr=1) # 获取关节类型 joint_type = ReadJson(d=joint, k="type", t="string") # 获取关节名称 joint_drv = f"{joint}_drv" joint_head = f"{joint}_head" joint_body = f"{joint}_body" # 处理驱动关节 if cmds.objExists(joint_drv): cmds.setAttr(f"{joint_drv}.jointOrient", 0, 0, 0) rot = cmds.getAttr(f"{joint_drv}.rotate")[0] cmds.setAttr(f"{joint_drv}.jointOrient", *rot) # 处理身体关节 if cmds.objExists(joint_body): cmds.setAttr(f"{joint_body}.jointOrient", 0, 0, 0) rot = cmds.getAttr(f"{joint_body}.rotate")[0] cmds.setAttr(f"{joint_body}.jointOrient", *rot) # 处理头部关节 if cmds.objExists(joint_head): cmds.setAttr(f"{joint_head}.jointOrient", 0, 0, 0) rot = cmds.getAttr(f"{joint_head}.rotate")[0] cmds.setAttr(f"{joint_head}.jointOrient", *rot) # 处理驱动关节的方向 if cmds.objExists(joint_drv): drv_rot = cmds.getAttr(f"{joint_drv}.jo")[0] if cmds.objExists(joint_head) and joint_type[0] not in ["half", "muscle"]: cmds.setAttr(f"{joint_head}.jo", *drv_rot) if cmds.objExists(joint_body) and joint_type[0] not in ["half", "muscle"]: cmds.setAttr(f"{joint_body}.jo", *drv_rot) # 结束进度条 ProgressBar(ep=True) def skin_amend_axis(): """ 蒙皮关节轴向修正 """ # 获取所有关节 joints = body_joints() # 创建进度条 ProgressBar(sp=True) ProgressBar(max=len(joints)) ProgressBar(t="Amend Skin Joint Axis...") # 处理每个关节 for joint in joints: ProgressBar(apr=1) # 获取关节名称 joint_drv = f"{joint}_drv" joint_head = f"{joint}_head" joint_body = f"{joint}_body" # 处理驱动关节 if cmds.objExists(joint_drv): cmds.joint(joint_drv, edit=True, orientJoint="none") # 处理身体关节 if cmds.objExists(joint_body): cmds.joint(joint_body, edit=True, orientJoint="none") # 处理头部关节 if cmds.objExists(joint_head): cmds.joint(joint_head, edit=True, orientJoint="none") # 结束进度条 ProgressBar(ep=True) def other_amend_axis(joints): """ 其他关节轴向修正 参数: joints (list): 关节索引列表 """ body_joints = body_joints() for joint_index in joints: drv = f"{body_joints[joint_index]}_drv" if cmds.objExists(drv): cmds.joint(drv, edit=True, orientJoint="none") def half_amend_axis(joints): """ 半身关节轴向修正 参数: joints (list): 关节索引列表 """ body_joints = body_joints() for joint_index in joints: drv = f"{body_joints[joint_index]}_drv" root_joint = drv.replace("_half", "") if cmds.objExists(drv): pos = cmds.getAttr(f"{root_joint}.jointOrient")[0] cmds.setAttr(f"{drv}.jointOrient", *pos) def hand_amend_axis(joints, rot): """ 手部关节轴向修正 参数: joints (list): 关节索引列表 rot (list): 旋转值列表 """ body_joints = body_joints() joint_list = [] for joint_index in joints: drv = f"{body_joints[joint_index]}_drv" if cmds.objExists(drv): cmds.joint(drv, edit=True, orientJoint="none") joint_list.append(drv) tweak(joint_list, rot) def index_to_name(joints): """ 将关节索引转换为名称 参数: joints (list): 关节索引列表 返回: list: 关节名称列表 """ body_joints = body_joints() joint_list = [] for joint_index in joints: drv = f"{body_joints[joint_index]}_drv" if cmds.objExists(drv): joint_list.append(drv) return joint_list def corrective_root(joints): """ 修正根关节 参数: joints (list): 关节索引列表 """ body_joints = body_joints() for joint_index in joints: drv = f"{body_joints[joint_index]}_drv" parent = f"{body_joints[joint_index]}_drv_parentConstraint1" orient = f"{body_joints[joint_index]}_orientConstraint1_drv" if cmds.objExists(drv): cmds.setAttr(f"{drv}.jointOrient", 0, 0, 0) if cmds.objExists(parent): cmds.setAttr(f"{parent}.cr", 0, 0, 0) if cmds.objExists(orient): cmds.setAttr(f"{orient}.cr", 0, 0, 0) def amend_orient(joints, aim_axis, up_axis, up_dir): """ 修正关节方向 参数: joints (list): 关节索引列表 aim_axis (list): 目标轴向 up_axis (list): 向上轴向 up_dir (list): 向上方向 """ body_joints = body_joints() joint_drv_list = [] for joint_index in joints: drv = f"{body_joints[joint_index]}_drv" if cmds.objExists(drv): joint_drv_list.append(drv) do_auto = 0 orient(joint_drv_list, aim_axis, up_axis, up_dir, do_auto) def tweak(joints, rot): """ 调整关节旋转 参数: joints (list): 关节列表 rot (list): 旋转值列表 """ for joint in joints: current_rot = cmds.getAttr(f"{joint}.jointOrient")[0] new_rot = [a + b for a, b in zip(current_rot, rot)] cmds.setAttr(f"{joint}.jointOrient", *new_rot) def orient(joints, aim_axis, up_axis, up_dir, do_auto): """ 设置关节方向 参数: joints (list): 关节列表 aim_axis (list): 目标轴向 up_axis (list): 向上轴向 up_dir (list): 向上方向 do_auto (bool): 是否自动处理 """ # 这个函数的具体实现需要根据原MEL脚本中的SGOrient函数来完成 pass def body_joints(): """ 获取身体关节列表 """ return GetJoints(bj=True) def body_joints_locator(): """ 创建身体关节定位器 """ # 获取JSON文件路径 json_path = os.path.join(os.environ.get('SG_PATH'), "files/data/BodyJoints.json") # 读取对象数据 objects = ReadJson(f=json_path, t="object") if not objects: return # 创建进度条 ProgressBar(sp=True) # 创建临时定位器 locator = "MetaHumanLocatorTemp" if not cmds.objExists(locator): cmds.spaceLocator(name=locator) # 设置进度条最大值 ProgressBar(max=len(objects)) ProgressBar(t="Set Body Joint Translation...") # 获取中性关节位置 positions = GetNeutralJointTranslations(b=True) # 检查数据数量 if len(objects) != len(positions) // 3: cmds.error("count error......") return # 处理每个关节 for i, obj in enumerate(objects): ProgressBar(apr=1) # 获取关节和类型信息 joints = ReadJson(d=obj, k="joint", t="string") joint_type = ReadJson(d=obj, k="type", t="string") joint_drv = f"{joints[0]}_drv" # 检查是否需要定位 locate = ReadJson(d=obj, k="locate", t="bool") if locate[0] and cmds.objExists(joint_drv): # 设置位置 x = 0.0 if joint_type[0] == "middle" else positions[i*3] y = positions[i*3+1] z = positions[i*3+2] cmds.setAttr(f"{locator}.t", x, y, z) cmds.delete(cmds.pointConstraint(locator, joint_drv, weight=1)) # 重置进度条 ProgressBar(pr=0) ProgressBar(ep=True) # 删除临时定位器 cmds.delete(locator) cmds.refresh() def check_orient_constraint_connection(joint): """ 检查关节的方向约束连接 参数: joint (str): 关节名称 返回: str: 方向约束节点名称,如果没有则返回空字符串 """ if not cmds.objExists(joint): return "" # 获取所有下游连接的节点 connected_nodes = cmds.listConnections(joint, source=False, destination=True) # 检查每个连接的节点 for node in connected_nodes or []: if cmds.nodeType(node) == "orientConstraint": return node return "" def orient_ui(): """处理关节方向设置UI的回调""" # 获取选中的关节 joints = cmds.ls(selection=True, type='joint') if not joints: cmds.warning("Please select joints to orient.") return # 获取UI设置 aim_axis = get_axis_vector('rbg_aim', 'cb_rev_aim') up_axis = get_axis_vector('rbg_up', 'cb_rev_up') up_dir = [ cmds.floatFieldGrp('ffg_up_dir', query=True, value1=True), cmds.floatFieldGrp('ffg_up_dir', query=True, value2=True), cmds.floatFieldGrp('ffg_up_dir', query=True, value3=True) ] do_auto = cmds.checkBox('cb_auto_dir', query=True, value=True) # 执行方向设置 orient(joints, aim_axis, up_axis, up_dir, do_auto) def tweak_ui(value): """ 处理关节旋转调整UI的回调 参数: value (float): 调整值 """ # 获取选中的关节 joints = cmds.ls(selection=True, type='joint') if not joints: cmds.warning("Please select joints to tweak.") return # 获取调整值 rot = [ cmds.floatFieldGrp('ffg_tweak', query=True, value1=True) * value, cmds.floatFieldGrp('ffg_tweak', query=True, value2=True) * value, cmds.floatFieldGrp('ffg_tweak', query=True, value3=True) * value ] # 执行调整 tweak(joints, rot) def show_axis(value): """ 显示/隐藏轴向 参数: value (int): 0为隐藏,1为显示 """ cmds.jointDisplayScale(value) def get_axis_vector(radio_grp, check_box): """ 获取轴向向量 参数: radio_grp (str): 单选按钮组名称 check_box (str): 复选框名称 返回: list: 轴向向量 """ axis_map = {1: [1,0,0], 2: [0,1,0], 3: [0,0,1]} selected = cmds.radioButtonGrp(radio_grp, query=True, select=True) reverse = cmds.checkBox(check_box, query=True, value=True) vector = axis_map[selected] return [-v if reverse else v for v in vector]