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