268 lines
8.3 KiB
Python
268 lines
8.3 KiB
Python
#!/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 |