MetaFusion/scripts/utils/EnableJointOrient.py

674 lines
20 KiB
Python
Raw Permalink Normal View History

2025-02-07 05:10:30 +08:00
#!/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]