Files
MetaFusion/scripts/utils/utils_rigging.py
2025-05-09 01:21:24 +08:00

268 lines
8.3 KiB
Python
Raw 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 -*-
"""
Rigging function module
绑定系统功能模块 - 提供绑定系统相关的功能函数
"""
#===================================== IMPORT MODULES =====================================
import maya.cmds as cmds
import pymel.core as pm
import maya.mel as mel
from maya import OpenMayaUI as omui
from scripts.ui.Qt import QtWidgets, QtCore, QtGui
from scripts.ui.Qt.QtCompat import wrapInstance
import webbrowser
import subprocess
import importlib
import traceback
import sys
import os
# 导入配置
import config
TOOL_NAME = config.TOOL_NAME
TOOL_VERSION = config.TOOL_VERSION
TOOL_AUTHOR = config.TOOL_AUTHOR
TOOL_YEAR = config.TOOL_YEAR
TOOL_MOD_FILENAME = config.TOOL_MOD_FILENAME
TOOL_LANG = config.TOOL_LANG
TOOL_WSCL_NAME = config.TOOL_WSCL_NAME
TOOL_HELP_URL = config.TOOL_HELP_URL
TOOL_PATH = config.TOOL_PATH
SCRIPTS_PATH = config.SCRIPTS_PATH
TOOL_MAIN_SCRIPT = config.TOOL_MAIN_SCRIPT
UI_PATH = config.UI_PATH
STYLE_FILE = config.STYLE_FILE
ICONS_PATH = config.ICONS_PATH
TOOL_ICON = config.TOOL_ICON
ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
# Localization
from scripts.ui import localization
TEXT = localization.TEXT
#========================================== GLOBALS ========================================
# 存储当前选中的关节和控制器信息
selected_joint = None
selected_controller = None
# 存储关节和控制器的原始属性,用于撤销操作
original_joint_properties = {}
original_controller_properties = {}
#========================================== FUNCTIONS ========================================
#------------------------------------ UI UTILITIES ------------------------------------
def browse_file(parent_widget, title, line_edit, file_type=None):
"""
浏览文件对话框
Args:
parent_widget: 父窗口
title: 对话框标题
line_edit: 要更新的文本框
file_type: 文件类型筛选
"""
try:
# 设置文件过滤器
if file_type == "dna":
file_filter = "DNA文件 (*.dna);;所有文件 (*.*)"
elif file_type == "fbx":
file_filter = "FBX文件 (*.fbx);;所有文件 (*.*)"
else:
file_filter = "所有文件 (*.*)"
# 打开文件选择对话框
file_path = cmds.fileDialog2(
fileFilter=file_filter,
dialogStyle=2,
caption=TEXT(title, title)
)
if file_path:
# 更新文本框
line_edit.setText(file_path[0])
return file_path[0]
return None
except Exception as e:
print(f"浏览文件失败: {str(e)}")
return None
#------------------------------------ DNA OPERATIONS ------------------------------------
def import_dna():
"""
导入DNA文件
从文件中导入DNA数据
"""
try:
# 打开文件选择对话框
file_path = cmds.fileDialog2(
fileFilter="DNA文件 (*.dna);;所有文件 (*.*)",
dialogStyle=2,
caption="选择DNA文件"
)
if not file_path:
print("未选择文件")
return False
print(f"导入DNA文件: {file_path[0]}")
# 实现DNA导入逻辑
return True
except Exception as e:
print(f"导入DNA文件失败: {str(e)}")
return False
def export_dna():
"""
导出DNA文件
将当前绑定数据导出为DNA文件
"""
try:
# 打开文件保存对话框
file_path = cmds.fileDialog2(
fileFilter="DNA文件 (*.dna);;所有文件 (*.*)",
dialogStyle=2,
caption="保存DNA文件",
fileMode=0
)
if not file_path:
print("未选择保存位置")
return False
# 确保文件扩展名为.dna
if not file_path[0].lower().endswith('.dna'):
file_path[0] += '.dna'
print(f"导出DNA文件: {file_path[0]}")
# 实现DNA导出逻辑
return True
except Exception as e:
print(f"导出DNA文件失败: {str(e)}")
return False
#------------------------------------ Rigging ------------------------------------
def remove_all(*args):
"""移除所有绑定组件
从场景中删除所有绑定相关的组件,包括关节、控制器等
"""
try:
# 获取所有关节
all_joints = cmds.ls(type="joint")
# 获取所有可能的控制器(这里简化为所有曲线)
all_controllers = cmds.ls(type="nurbsCurve")
all_controller_transforms = []
# 获取控制器的变换节点
for ctrl in all_controllers:
parent = cmds.listRelatives(ctrl, parent=True)
if parent:
all_controller_transforms.extend(parent)
# 确认删除
result = cmds.confirmDialog(
title=TEXT("confirm_delete", "确认删除"),
message=TEXT("delete_all_confirm", "确定要删除所有绑定组件吗?"),
button=[TEXT("yes", ""), TEXT("no", "")],
defaultButton=TEXT("no", ""),
cancelButton=TEXT("no", ""),
dismissString=TEXT("no", "")
)
if result == TEXT("yes", ""):
# 删除所有控制器
if all_controller_transforms:
cmds.delete(all_controller_transforms)
# 删除所有关节
if all_joints:
cmds.delete(all_joints)
print("成功删除所有绑定组件")
return True
else:
print("取消删除操作")
return False
except Exception as e:
print(f"删除绑定组件失败: {str(e)}")
return False
def import_skeleton(*args):
"""导入骨骼
从文件中导入骨骼结构
"""
try:
# 打开文件选择对话框
file_path = cmds.fileDialog2(
fileFilter="Maya文件 (*.ma *.mb);;FBX文件 (*.fbx);;所有文件 (*.*)",
dialogStyle=2,
caption="选择骨骼文件"
)
if not file_path:
print("未选择文件")
return False
file_path = file_path[0] # fileDialog2返回的是列表
# 根据文件类型选择导入方法
if file_path.lower().endswith(('.ma', '.mb')):
# 导入Maya文件
cmds.file(file_path, i=True, type="mayaAscii" if file_path.lower().endswith('.ma') else "mayaBinary",
ignoreVersion=True, mergeNamespacesOnClash=False, namespace="skeleton")
elif file_path.lower().endswith('.fbx'):
# 导入FBX文件
cmds.file(file_path, i=True, type="FBX", ignoreVersion=True)
else:
print(f"不支持的文件类型: {file_path}")
return False
print(f"成功导入骨骼: {file_path}")
return True
except Exception as e:
print(f"导入骨骼失败: {str(e)}")
return False
def build_rigging(*args):
"""构建绑定系统
基于当前场景中的骨骼构建完整的绑定系统
"""
try:
# 获取场景中的所有关节
all_joints = cmds.ls(type="joint")
if not all_joints:
print("场景中没有骨骼,无法构建绑定")
return False
# 为每个关节创建控制器
for joint in all_joints:
# 获取关节位置
pos = cmds.xform(joint, query=True, translation=True, worldSpace=True)
# 创建控制器这里简化为创建一个NURBS圆环
ctrl = cmds.circle(name=f"{joint}_ctrl", normal=[1, 0, 0], radius=1)[0]
# 移动控制器到关节位置
cmds.move(pos[0], pos[1], pos[2], ctrl)
# 创建约束
cmds.parentConstraint(ctrl, joint, maintainOffset=True)
print("成功构建绑定系统")
return True
except Exception as e:
print(f"构建绑定系统失败: {str(e)}")
return False