MetaFusion/scripts/utils/EnableJointOrient.py
2025-02-07 05:10:30 +08:00

674 lines
20 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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]