#!/usr/bin/env python # -*- coding: utf-8 -*- """ Rigging function module """ #===================================== IMPORT MODULES ===================================== import maya.cmds as cmds import pymel.core as pm import importlib import sys import os import random from PySide2 import QtWidgets, QtCore, QtGui from functools import partial #========================================== GLOBALS ======================================== # 存储当前选中的关节和控制器信息 selected_joint = None selected_controller = None # 存储关节和控制器的原始属性,用于撤销操作 original_joint_properties = {} original_controller_properties = {} #========================================== FUNCTIONS ======================================== #------------------------------------ UI UTILITIES ------------------------------------ def update_splitter_position(splitter): """ 更新分割器位置 Args: splitter: 分割器对象 """ if not splitter: return # 记录当前分割器位置 sizes = splitter.sizes() print(f"Splitter sizes updated: {sizes}") #------------------------------------ JOINT OPERATIONS ------------------------------------ def add_joint(skeleton_list): """ 添加关节到骨骼列表 Args: skeleton_list: 骨骼列表控件 """ if not skeleton_list: return # 生成唯一的关节名称 joint_count = skeleton_list.count() joint_name = f"Joint_{joint_count}" # 添加到列表 item = QtWidgets.QListWidgetItem(joint_name) skeleton_list.addItem(item) # 更新组标题中的计数 update_group_count(skeleton_list, "Skeleton") # 在Maya中创建关节 try: cmds.select(clear=True) joint = cmds.joint(name=joint_name, position=(0, 0, 0)) print(f"Created joint: {joint}") except Exception as e: print(f"Error creating joint: {e}") def remove_joint(skeleton_list): """ 从骨骼列表中移除选中的关节 Args: skeleton_list: 骨骼列表控件 """ if not skeleton_list: return # 获取选中的项目 selected_items = skeleton_list.selectedItems() if not selected_items: return # 移除选中的项目 for item in selected_items: joint_name = item.text() row = skeleton_list.row(item) skeleton_list.takeItem(row) # 在Maya中删除关节 try: if cmds.objExists(joint_name): cmds.delete(joint_name) print(f"Deleted joint: {joint_name}") except Exception as e: print(f"Error deleting joint: {e}") # 更新组标题中的计数 update_group_count(skeleton_list, "Skeleton") def duplicate_joint(skeleton_list): """ 复制选中的关节 Args: skeleton_list: 骨骼列表控件 """ if not skeleton_list: return # 获取选中的项目 selected_items = skeleton_list.selectedItems() if not selected_items: return # 复制选中的项目 for item in selected_items: joint_name = item.text() new_joint_name = f"{joint_name}_copy" # 添加到列表 new_item = QtWidgets.QListWidgetItem(new_joint_name) skeleton_list.addItem(new_item) # 在Maya中复制关节 try: if cmds.objExists(joint_name): cmds.select(joint_name) new_joint = cmds.duplicate(name=new_joint_name)[0] print(f"Duplicated joint: {joint_name} -> {new_joint}") except Exception as e: print(f"Error duplicating joint: {e}") # 更新组标题中的计数 update_group_count(skeleton_list, "Skeleton") def update_joint_properties(skeleton_list, name_input, x_input, y_input, z_input, rx_input, ry_input, rz_input, sx_input, sy_input, sz_input): """ 更新关节属性面板 Args: skeleton_list: 骨骼列表控件 name_input: 名称输入框 x_input, y_input, z_input: 位置输入框 rx_input, ry_input, rz_input: 旋转输入框 sx_input, sy_input, sz_input: 缩放输入框 """ global selected_joint, original_joint_properties # 获取选中的项目 selected_items = skeleton_list.selectedItems() if not selected_items: # 清空输入框 name_input.setText("") x_input.setText("0.0") y_input.setText("0.0") z_input.setText("0.0") rx_input.setText("0.0") ry_input.setText("0.0") rz_input.setText("0.0") sx_input.setText("1.0") sy_input.setText("1.0") sz_input.setText("1.0") selected_joint = None original_joint_properties = {} return # 获取选中的关节 joint_name = selected_items[0].text() selected_joint = joint_name # 在Maya中获取关节属性 try: if cmds.objExists(joint_name): # 获取位置 pos = cmds.xform(joint_name, query=True, translation=True, worldSpace=True) # 获取旋转 rot = cmds.xform(joint_name, query=True, rotation=True, worldSpace=True) # 获取缩放 scale = cmds.xform(joint_name, query=True, scale=True, relative=True) # 更新输入框 name_input.setText(joint_name) x_input.setText(str(round(pos[0], 3))) y_input.setText(str(round(pos[1], 3))) z_input.setText(str(round(pos[2], 3))) rx_input.setText(str(round(rot[0], 3))) ry_input.setText(str(round(rot[1], 3))) rz_input.setText(str(round(rot[2], 3))) sx_input.setText(str(round(scale[0], 3))) sy_input.setText(str(round(scale[1], 3))) sz_input.setText(str(round(scale[2], 3))) # 存储原始属性用于撤销 original_joint_properties = { "name": joint_name, "position": pos, "rotation": rot, "scale": scale } print(f"Updated joint properties for: {joint_name}") else: print(f"Joint does not exist in scene: {joint_name}") except Exception as e: print(f"Error updating joint properties: {e}") def update_joint_name(skeleton_list, name_input): """ 更新关节名称 Args: skeleton_list: 骨骼列表控件 name_input: 名称输入框 """ global selected_joint if not selected_joint or not skeleton_list: return # 获取新名称 new_name = name_input.text() if not new_name or new_name == selected_joint: return # 更新列表项 selected_items = skeleton_list.selectedItems() if selected_items: selected_items[0].setText(new_name) # 在Maya中重命名关节 try: if cmds.objExists(selected_joint): cmds.rename(selected_joint, new_name) selected_joint = new_name print(f"重命名关节: {selected_joint} -> {new_name}") except Exception as e: print(f"重命名关节失败: {e}") # 恢复原名称 if selected_items: selected_items[0].setText(selected_joint) def update_joint_position(skeleton_list, axis, value_input): """ 更新关节位置 Args: skeleton_list: 骨骼列表控件 axis: 坐标轴 (x, y, z) value_input: 值输入框 """ global selected_joint if not selected_joint or not skeleton_list: return # 获取新值 try: value = float(value_input.text()) except ValueError: print(f"无效的数值输入: {value_input.text()}") return # 在Maya中更新关节位置 try: if cmds.objExists(selected_joint): # 获取当前位置 pos = cmds.xform(selected_joint, query=True, translation=True, worldSpace=True) # 更新指定轴的位置 if axis == "x": pos[0] = value elif axis == "y": pos[1] = value elif axis == "z": pos[2] = value # 应用新位置 cmds.xform(selected_joint, translation=pos, worldSpace=True) print(f"更新关节位置: {selected_joint}, {axis}={value}") except Exception as e: print(f"更新关节位置失败: {e}") def update_joint_rotation(skeleton_list, axis, value_input): """ 更新关节旋转 Args: skeleton_list: 骨骼列表控件 axis: 旋转轴 (x, y, z) value_input: 值输入框 """ global selected_joint if not selected_joint or not skeleton_list: return # 获取新值 try: value = float(value_input.text()) except ValueError: print(f"无效的数值输入: {value_input.text()}") return # 在Maya中更新关节旋转 try: if cmds.objExists(selected_joint): # 获取当前旋转 rot = cmds.xform(selected_joint, query=True, rotation=True, worldSpace=True) # 更新指定轴的旋转 if axis == "x": rot[0] = value elif axis == "y": rot[1] = value elif axis == "z": rot[2] = value # 应用新旋转 cmds.xform(selected_joint, rotation=rot, worldSpace=True) print(f"更新关节旋转: {selected_joint}, {axis}={value}") except Exception as e: print(f"更新关节旋转失败: {e}") def update_joint_scale(skeleton_list, axis, value_input): """ 更新关节缩放 Args: skeleton_list: 骨骼列表控件 axis: 缩放轴 (x, y, z) value_input: 值输入框 """ global selected_joint if not selected_joint or not skeleton_list: return # 获取新值 try: value = float(value_input.text()) except ValueError: print(f"无效的数值输入: {value_input.text()}") return # 在Maya中更新关节缩放 try: if cmds.objExists(selected_joint): # 获取当前缩放 scale = cmds.xform(selected_joint, query=True, scale=True, relative=True) # 更新指定轴的缩放 if axis == "x": scale[0] = value elif axis == "y": scale[1] = value elif axis == "z": scale[2] = value # 应用新缩放 cmds.xform(selected_joint, scale=scale, relative=True) print(f"更新关节缩放: {selected_joint}, {axis}={value}") except Exception as e: print(f"更新关节缩放失败: {e}") def apply_joint_properties(skeleton_list, name_input, x_input, y_input, z_input, rx_input, ry_input, rz_input, sx_input, sy_input, sz_input): """ 应用关节属性 Args: skeleton_list: 骨骼列表控件 name_input: 名称输入框 x_input, y_input, z_input: 位置输入框 rx_input, ry_input, rz_input: 旋转输入框 sx_input, sy_input, sz_input: 缩放输入框 """ global selected_joint if not selected_joint or not skeleton_list: return # 更新名称 update_joint_name(skeleton_list, name_input) # 更新位置 update_joint_position(skeleton_list, "x", x_input) update_joint_position(skeleton_list, "y", y_input) update_joint_position(skeleton_list, "z", z_input) # 更新旋转 update_joint_rotation(skeleton_list, "x", rx_input) update_joint_rotation(skeleton_list, "y", ry_input) update_joint_rotation(skeleton_list, "z", rz_input) # 更新缩放 update_joint_scale(skeleton_list, "x", sx_input) update_joint_scale(skeleton_list, "y", sy_input) update_joint_scale(skeleton_list, "z", sz_input) print(f"应用关节属性完成: {selected_joint}") def reset_joint_properties(skeleton_list, name_input, x_input, y_input, z_input, rx_input, ry_input, rz_input, sx_input, sy_input, sz_input): """ 重置关节属性 Args: skeleton_list: 骨骼列表控件 name_input: 名称输入框 x_input, y_input, z_input: 位置输入框 rx_input, ry_input, rz_input: 旋转输入框 sx_input, sy_input, sz_input: 缩放输入框 """ global selected_joint, original_joint_properties if not selected_joint or not original_joint_properties: return # 重置名称 name_input.setText(original_joint_properties["name"]) # 重置位置 pos = original_joint_properties["position"] x_input.setText(str(round(pos[0], 3))) y_input.setText(str(round(pos[1], 3))) z_input.setText(str(round(pos[2], 3))) # 重置旋转 rot = original_joint_properties["rotation"] rx_input.setText(str(round(rot[0], 3))) ry_input.setText(str(round(rot[1], 3))) rz_input.setText(str(round(rot[2], 3))) # 重置缩放 scale = original_joint_properties["scale"] sx_input.setText(str(round(scale[0], 3))) sy_input.setText(str(round(scale[1], 3))) sz_input.setText(str(round(scale[2], 3))) # 在Maya中重置关节属性 try: if cmds.objExists(selected_joint): cmds.xform(selected_joint, translation=pos, worldSpace=True) cmds.xform(selected_joint, rotation=rot, worldSpace=True) cmds.xform(selected_joint, scale=scale, relative=True) print(f"重置关节属性完成: {selected_joint}") except Exception as e: print(f"重置关节属性失败: {e}") #------------------------------------ 控制器操作 ------------------------------------ def add_controller(controller_list): """ 添加控制器到控制器列表 Args: controller_list: 控制器列表控件 """ if not controller_list: return # 生成唯一的控制器名称 controller_count = controller_list.count() controller_name = f"Controller_{controller_count}" # 添加到列表 item = QtWidgets.QListWidgetItem(controller_name) controller_list.addItem(item) # 更新组标题中的计数 update_group_count(controller_list, "Controller") # 在Maya中创建控制器 try: cmds.select(clear=True) # 创建一个简单的NURBS圆形控制器 circle = cmds.circle(name=controller_name, normal=[0, 1, 0], radius=1)[0] print(f"创建控制器: {circle}") except Exception as e: print(f"创建控制器失败: {e}") def remove_controller(controller_list): """ 从控制器列表中移除选中的控制器 Args: controller_list: 控制器列表控件 """ if not controller_list: return # 获取选中的项目 selected_items = controller_list.selectedItems() if not selected_items: return # 移除选中的项目 for item in selected_items: controller_name = item.text() row = controller_list.row(item) controller_list.takeItem(row) # 在Maya中删除控制器 try: if cmds.objExists(controller_name): cmds.delete(controller_name) print(f"删除控制器: {controller_name}") except Exception as e: print(f"删除控制器失败: {e}") # 更新组标题中的计数 update_group_count(controller_list, "Controller") def duplicate_controller(controller_list): """ 复制选中的控制器 Args: controller_list: 控制器列表控件 """ if not controller_list: return # 获取选中的项目 selected_items = controller_list.selectedItems() if not selected_items: return # 复制选中的项目 for item in selected_items: controller_name = item.text() new_controller_name = f"{controller_name}_copy" # 添加到列表 new_item = QtWidgets.QListWidgetItem(new_controller_name) controller_list.addItem(new_item) # 在Maya中复制控制器 try: if cmds.objExists(controller_name): cmds.select(controller_name) new_controller = cmds.duplicate(name=new_controller_name)[0] print(f"复制控制器: {controller_name} -> {new_controller}") except Exception as e: print(f"复制控制器失败: {e}") # 更新组标题中的计数 update_group_count(controller_list, "Controller") def update_controller_properties(controller_list): """ 更新控制器属性 Args: controller_list: 控制器列表控件 """ global selected_controller # 获取选中的项目 selected_items = controller_list.selectedItems() if not selected_items: selected_controller = None return # 获取选中的控制器 controller_name = selected_items[0].text() selected_controller = controller_name # 在Maya中选中控制器 try: if cmds.objExists(controller_name): cmds.select(controller_name) print(f"选中控制器: {controller_name}") except Exception as e: print(f"选中控制器失败: {e}") #------------------------------------ 工具函数 ------------------------------------ def update_group_count(list_widget, group_type): """ 更新组标题中的计数 Args: list_widget: 列表控件 group_type: 组类型 (Skeleton 或 Controller) """ if not list_widget: return count = list_widget.count() count_str = str(count).zfill(3) # 填充零使其成为3位数 # 查找父组件 parent = list_widget.parent() if parent and isinstance(parent, QtWidgets.QGroupBox): parent.setTitle(f"{group_type}s [{count_str}]") print(f"更新{group_type}组计数: {count}") else: print(f"找不到{group_type}组组件") #------------------------------------ DNA操作函数 ------------------------------------ def import_dna(): """ 导入DNA文件 """ # 打开文件选择对话框 file_path = cmds.fileDialog2( fileFilter="DNA Files (*.dna);;All Files (*.*)", dialogStyle=2, fileMode=1, caption="导入DNA文件" ) if not file_path: print("未选择文件") return file_path = file_path[0] print(f"尝试导入DNA文件: {file_path}") # 检查DNA校准库是否可用 if not HAS_DNA_CALIB: cmds.warning("无法导入DNA文件: DNA校准库未找到") return try: # 在实际应用中,这里将使用DNA校准库来读取DNA文件 # 这里仅为演示目的显示成功消息 print(f"成功导入DNA文件: {file_path}") cmds.inViewMessage(message=f"成功导入DNA文件", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"导入DNA文件失败: {e}") def export_dna(): """ 导出DNA文件 """ # 打开文件保存对话框 file_path = cmds.fileDialog2( fileFilter="DNA Files (*.dna);;All Files (*.*)", dialogStyle=2, fileMode=0, caption="导出DNA文件" ) if not file_path: print("未选择文件保存路径") return file_path = file_path[0] # 确保文件扩展名为.dna if not file_path.lower().endswith('.dna'): file_path += '.dna' print(f"尝试导出DNA文件: {file_path}") # 检查DNA校准库是否可用 if not HAS_DNA_CALIB: cmds.warning("无法导出DNA文件: DNA校准库未找到") return try: # 在实际应用中,这里将使用DNA校准库来写入DNA文件 # 这里仅为演示目的显示成功消息 print(f"成功导出DNA文件: {file_path}") cmds.inViewMessage(message=f"成功导出DNA文件", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"导出DNA文件失败: {e}") def calibrate_dna(): """ 校准DNA """ # 获取当前选中的对象 selection = cmds.ls(selection=True) if not selection: cmds.warning("请先选择要校准的模型") return # 检查DNA校准库是否可用 if not HAS_DNA_CALIB: cmds.warning("无法校准DNA: DNA校准库未找到") return try: # 在实际应用中,这里将使用DNA校准库来校准DNA # 这里仅为演示目的显示成功消息 print(f"正在校准DNA: {selection}") # 模拟校准过程 cmds.progressWindow( title="DNA校准", progress=0, status="正在准备...", isInterruptable=True ) for i in range(0, 101, 10): # 检查是否取消 if cmds.progressWindow(query=True, isCancelled=True): break # 更新进度 cmds.progressWindow(edit=True, progress=i, status=f"校准中... {i}%") cmds.pause(seconds=0.1) cmds.progressWindow(endProgress=1) print(f"成功校准DNA") cmds.inViewMessage(message=f"成功校准DNA", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"校准DNA失败: {e}") cmds.progressWindow(endProgress=1) #------------------------------------ 骨骼工具函数 ------------------------------------ def import_skeleton(): """ 导入骨骼 """ # 打开文件选择对话框 file_path = cmds.fileDialog2( fileFilter="FBX Files (*.fbx);;Maya Files (*.ma *.mb);;All Files (*.*)", dialogStyle=2, fileMode=1, caption="导入骨骼" ) if not file_path: print("未选择文件") return file_path = file_path[0] print(f"尝试导入骨骼: {file_path}") try: # 导入骨骼 if file_path.lower().endswith('.fbx'): # 导入FBX文件 cmds.file(file_path, i=True, type="FBX", ignoreVersion=True, ra=True, mergeNamespacesOnClash=False, namespace="skeleton") elif file_path.lower().endswith(('.ma', '.mb')): # 导入Maya文件 cmds.file(file_path, i=True, ignoreVersion=True, ra=True, mergeNamespacesOnClash=False, namespace="skeleton") else: cmds.warning(f"不支持的文件类型: {file_path}") return print(f"成功导入骨骼: {file_path}") cmds.inViewMessage(message=f"成功导入骨骼", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"导入骨骼失败: {e}") def export_skeleton(): """ 导出骨骼 """ # 获取当前选中的对象 selection = cmds.ls(selection=True, type="joint") if not selection: cmds.warning("请先选择要导出的骨骼") return # 打开文件保存对话框 file_path = cmds.fileDialog2( fileFilter="FBX Files (*.fbx);;Maya Files (*.ma);;All Files (*.*)", dialogStyle=2, fileMode=0, caption="导出骨骼" ) if not file_path: print("未选择文件保存路径") return file_path = file_path[0] print(f"尝试导出骨骼: {file_path}") try: # 选中骨骼 cmds.select(selection) # 导出骨骼 if file_path.lower().endswith('.fbx'): # 导出fbx文件 cmds.file(file_path, force=True, options="v=0;", type="FBX export", pr=True, ea=True) elif file_path.lower().endswith('.ma'): # 导出Maya ASCII文件 cmds.file(file_path, force=True, options="v=0;", type="mayaAscii", pr=True, ea=True) else: # 添加默认扩展名 if not file_path.lower().endswith(('.fbx', '.ma', '.mb')): file_path += '.fbx' cmds.file(file_path, force=True, options="v=0;", type="FBX export", pr=True, ea=True) print(f"成功导出骨骼: {file_path}") cmds.inViewMessage(message=f"成功导出骨骼", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"导出骨骼失败: {e}") def calibrate_skeleton(): """ 校准骨骼位置 """ # 获取当前选中的对象 selection = cmds.ls(selection=True, type="joint") if not selection: cmds.warning("请先选择要校准的骨骼") return try: # 模拟校准过程 print(f"正在校准骨骼位置: {selection}") cmds.progressWindow( title="骨骼校准", progress=0, status="正在准备...", isInterruptable=True ) for i in range(0, 101, 10): # 检查是否取消 if cmds.progressWindow(query=True, isCancelled=True): break # 更新进度 cmds.progressWindow(edit=True, progress=i, status=f"校准中... {i}%") cmds.pause(seconds=0.1) # 模拟对骨骼进行微调 if i > 0 and i % 20 == 0 and len(selection) > 0: # 随机选择一个骨骼进行微调 joint_index = random.randint(0, len(selection) - 1) joint = selection[joint_index] # 微调位置 pos = cmds.xform(joint, query=True, translation=True, worldSpace=True) pos = [p + (random.random() - 0.5) * 0.01 for p in pos] # 添加小的随机偏移 cmds.xform(joint, translation=pos, worldSpace=True) cmds.progressWindow(endProgress=1) print(f"成功校准骨骼位置") cmds.inViewMessage(message=f"成功校准骨骼位置", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"校准骨骼位置失败: {e}") cmds.progressWindow(endProgress=1) #------------------------------------ 绑定工具函数 ------------------------------------ def create_binding(): """ 创建绑定 """ # 获取当前选中的对象 joints = cmds.ls(selection=True, type="joint") meshes = cmds.ls(selection=True, type="mesh") transforms = cmds.ls(selection=True, type="transform") # 过滤出网格对象 mesh_transforms = [] for transform in transforms: shapes = cmds.listRelatives(transform, shapes=True, type="mesh") if shapes: mesh_transforms.append(transform) if not joints: cmds.warning("请选择至少一个骨骼") return if not (meshes or mesh_transforms): cmds.warning("请选择至少一个网格") return # 合并网格列表 all_meshes = meshes + mesh_transforms try: # 创建绑定 print(f"正在创建绑定: 骨骼={joints}, 网格={all_meshes}") # 模拟绑定过程 cmds.progressWindow( title="创建绑定", progress=0, status="正在准备...", isInterruptable=True ) for i in range(0, 101, 10): # 检查是否取消 if cmds.progressWindow(query=True, isCancelled=True): break # 更新进度 cmds.progressWindow(edit=True, progress=i, status=f"绑定中... {i}%") cmds.pause(seconds=0.1) # 实际创建蒙皮绑定 for mesh in all_meshes: # 选择网格和骨骼 cmds.select(mesh, joints[0], replace=True) # 创建蒙皮类型的绑定 skin_cluster = cmds.skinCluster(joints, mesh, bindMethod=0, skinMethod=0, normalizeWeights=1, maximumInfluences=4)[0] print(f"创建蒙皮类型的绑定: {skin_cluster}") cmds.progressWindow(endProgress=1) print(f"成功创建绑定") cmds.inViewMessage(message=f"成功创建绑定", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"创建绑定失败: {e}") cmds.progressWindow(endProgress=1) def copy_skin(): """ 复制蒙皮权重 """ # 获取当前选中的对象 selection = cmds.ls(selection=True) if len(selection) < 2: cmds.warning("请选择源网格和目标网格") return source = selection[0] targets = selection[1:] try: # 检查源对象是否有蒙皮类型的绑定 source_skin = cmds.listConnections(source, type="skinCluster") if not source_skin: cmds.warning(f"源对象没有蒙皮类型的绑定: {source}") return print(f"正在复制蒙皮权重: 从 {source} 到 {targets}") # 模拟复制过程 cmds.progressWindow( title="复制蒙皮", progress=0, status="正在准备...", isInterruptable=True ) total_targets = len(targets) for i, target in enumerate(targets): # 检查是否取消 if cmds.progressWindow(query=True, isCancelled=True): break # 更新进度 progress = int((i / total_targets) * 100) cmds.progressWindow(edit=True, progress=progress, status=f"复制中... {progress}%") # 复制蒙皮权重 try: # 在实际应用中使用copySkinWeights命令 cmds.select(source, target) cmds.copySkinWeights(noMirror=True, surfaceAssociation="closestPoint", influenceAssociation=["name", "oneToOne", "closestJoint"]) print(f"成功复制蒙皮权重到: {target}") except Exception as e: cmds.warning(f"复制蒙皮权重到 {target} 失败: {e}") cmds.progressWindow(endProgress=1) print(f"成功复制蒙皮权重") cmds.inViewMessage(message=f"成功复制蒙皮权重", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"复制蒙皮权重失败: {e}") cmds.progressWindow(endProgress=1) def mirror_skin(): """ 镜像蒙皮权重 """ # 获取当前选中的对象 selection = cmds.ls(selection=True) if not selection: cmds.warning("请选择要镜像的网格") return try: print(f"正在镜像蒙皮权重: {selection}") # 模拟镜像过程 cmds.progressWindow( title="镜像蒙皮", progress=0, status="正在准备...", isInterruptable=True ) for i in range(0, 101, 10): # 检查是否取消 if cmds.progressWindow(query=True, isCancelled=True): break # 更新进度 cmds.progressWindow(edit=True, progress=i, status=f"镜像中... {i}%") cmds.pause(seconds=0.1) # 实际镜像蒙皮权重 for obj in selection: # 检查是否有蒙皮类型的绑定 skin_clusters = cmds.listConnections(obj, type="skinCluster") if skin_clusters: # 在实际应用中使用copySkinWeights命令进行镜像 cmds.select(obj) cmds.copySkinWeights(mirrorMode="YZ", surfaceAssociation="closestPoint", influenceAssociation=["name", "closestJoint"]) print(f"成功镜像蒙皮权重: {obj}") else: cmds.warning(f"对象没有蒙皮类型的绑定: {obj}") cmds.progressWindow(endProgress=1) print(f"成功镜像蒙皮权重") cmds.inViewMessage(message=f"成功镜像蒙皮权重", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"镜像蒙皮权重失败: {e}") cmds.progressWindow(endProgress=1) def paint_weights(): """ 打开权重绘制工具 """ # 获取当前选中的对象 selection = cmds.ls(selection=True) if not selection: cmds.warning("请选择要绘制权重的网格") return try: # 检查是否有蒙皮类型的绑定 for obj in selection: skin_clusters = cmds.listConnections(obj, type="skinCluster") if skin_clusters: # 打开权重绘制工具 cmds.select(obj, replace=True) cmds.ArtPaintSkinWeightsTool() print(f"成功打开权重绘制工具: {obj}") return # 只处理第一个有效对象 else: cmds.warning(f"对象没有蒙皮类型的绑定: {obj}") cmds.warning("所选对象都没有蒙皮类型的绑定") except Exception as e: cmds.warning(f"打开权重绘制工具失败: {e}") def generate_controllers(): """ 生成控制器 """ # 获取当前选中的骨骼 joints = cmds.ls(selection=True, type="joint") if not joints: cmds.warning("请选择要生成控制器的骨骼") return try: print(f"正在为骨骼生成控制器: {joints}") # 模拟生成过程 cmds.progressWindow( title="生成控制器", progress=0, status="正在准备...", isInterruptable=True ) controllers = [] total_joints = len(joints) for i, joint in enumerate(joints): # 检查是否取消 if cmds.progressWindow(query=True, isCancelled=True): break # 更新进度 progress = int((i / total_joints) * 100) cmds.progressWindow(edit=True, progress=progress, status=f"生成中... {progress}%") # 为骨骼创建控制器 controller_name = f"{joint}_ctrl" # 根据骨骼类型选择不同的控制器形状 if "_spine_" in joint.lower() or "_back_" in joint.lower(): # 脚柄控制器 controller = cmds.circle(name=controller_name, normal=[0, 1, 0], radius=1.5)[0] elif "_shoulder_" in joint.lower() or "_clavicle_" in joint.lower(): # 半圆控制器 controller = cmds.circle(name=controller_name, normal=[1, 0, 0], radius=1.2, sweep=180)[0] elif "_finger_" in joint.lower() or "_toe_" in joint.lower(): # 小方形控制器 controller = cmds.curve(name=controller_name, d=1, p=[(-0.5, 0, -0.5), (0.5, 0, -0.5), (0.5, 0, 0.5), (-0.5, 0, 0.5), (-0.5, 0, -0.5)]) else: # 默认圆形控制器 controller = cmds.circle(name=controller_name, normal=[0, 1, 0], radius=1)[0] # 获取骨骼位置和旋转 pos = cmds.xform(joint, query=True, translation=True, worldSpace=True) rot = cmds.xform(joint, query=True, rotation=True, worldSpace=True) # 将控制器移动到骨骼位置 cmds.xform(controller, translation=pos, rotation=rot, worldSpace=True) # 添加到控制器列表 controllers.append(controller) print(f"为骨骼 {joint} 创建控制器: {controller}") cmds.progressWindow(endProgress=1) # 选中所有新创建的控制器 if controllers: cmds.select(controllers) print(f"成功生成 {len(controllers)} 个控制器") cmds.inViewMessage(message=f"成功生成 {len(controllers)} 个控制器", pos='midCenter', fade=True) else: cmds.warning("未生成任何控制器") except Exception as e: cmds.warning(f"生成控制器失败: {e}") cmds.progressWindow(endProgress=1) def generate_body(): """ 生成身体模型 """ try: print("正在生成身体模型...") # 模拟生成过程 cmds.progressWindow( title="生成身体模型", progress=0, status="正在准备...", isInterruptable=True ) for i in range(0, 101, 5): # 检查是否取消 if cmds.progressWindow(query=True, isCancelled=True): break # 更新进度 cmds.progressWindow(edit=True, progress=i, status=f"生成中... {i}%") cmds.pause(seconds=0.1) # 创建一个简单的身体模型 body = cmds.polyCube(name="body", width=1, height=2, depth=0.5)[0] head = cmds.polySphere(name="head", radius=0.4)[0] cmds.move(0, 1.2, 0, head) left_arm = cmds.polyCylinder(name="left_arm", radius=0.1, height=1)[0] cmds.move(-0.7, 0.7, 0, left_arm) cmds.rotate(0, 0, -90, left_arm) right_arm = cmds.polyCylinder(name="right_arm", radius=0.1, height=1)[0] cmds.move(0.7, 0.7, 0, right_arm) cmds.rotate(0, 0, 90, right_arm) left_leg = cmds.polyCylinder(name="left_leg", radius=0.15, height=1)[0] cmds.move(-0.3, -1, 0, left_leg) right_leg = cmds.polyCylinder(name="right_leg", radius=0.15, height=1)[0] cmds.move(0.3, -1, 0, right_leg) # 合并所有部分 body_parts = [body, head, left_arm, right_arm, left_leg, right_leg] cmds.select(body_parts) body_mesh = cmds.polyUnite(name="human_body")[0] cmds.progressWindow(endProgress=1) cmds.select(body_mesh) print(f"成功生成身体模型: {body_mesh}") cmds.inViewMessage(message=f"成功生成身体模型", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"生成身体模型失败: {e}") cmds.progressWindow(endProgress=1) def clean_rigging(): """ 清理绑定 """ # 获取当前选中的对象 selection = cmds.ls(selection=True) if not selection: cmds.warning("请选择要清理绑定的对象") return try: print(f"正在清理绑定: {selection}") # 模拟清理过程 cmds.progressWindow( title="清理绑定", progress=0, status="正在准备...", isInterruptable=True ) # 查找所有蒙皮类型的绑定 skin_clusters = [] for obj in selection: # 检查是否有蒙皮类型的绑定 obj_skin_clusters = cmds.listConnections(obj, type="skinCluster") if obj_skin_clusters: skin_clusters.extend(obj_skin_clusters) # 去除重复项 skin_clusters = list(set(skin_clusters)) if not skin_clusters: cmds.warning("所选对象没有蒙皮类型的绑定") cmds.progressWindow(endProgress=1) return total_clusters = len(skin_clusters) for i, cluster in enumerate(skin_clusters): # 检查是否取消 if cmds.progressWindow(query=True, isCancelled=True): break # 更新进度 progress = int((i / total_clusters) * 100) cmds.progressWindow(edit=True, progress=progress, status=f"清理中... {progress}%") # 清理蒙皮类型的绑定 try: # 删除蒙皮类型的绑定 cmds.skinCluster(cluster, edit=True, unbind=True) print(f"成功清理蒙皮类型的绑定: {cluster}") except Exception as e: cmds.warning(f"清理蒙皮类型的绑定 {cluster} 失败: {e}") cmds.progressWindow(endProgress=1) print(f"成功清理 {total_clusters} 个蒙皮类型的绑定") cmds.inViewMessage(message=f"成功清理绑定", pos='midCenter', fade=True) except Exception as e: cmds.warning(f"清理绑定失败: {e}") cmds.progressWindow(endProgress=1)