#!/usr/bin/env python # -*- coding: utf-8 -*- """ Geometry 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 #========================================== FUNCTIONS ======================================== # 底部工具面板功能 # 导入相关功能 #========================================== LOD相关功能 ======================================== def clean(): """ 删除当前选中的LOD """ print("删除LOD") try: # 获取当前选中的LOD标签页 tab_widget = cmds.getParent("geometryTabWidget") if tab_widget: current_index = cmds.tabLayout(tab_widget, q=True, selectTabIndex=True) - 1 lod_name = f"LOD{current_index}" # 确认删除 result = cmds.confirmDialog( title=TEXT("confirm_delete", "确认删除"), message=TEXT("delete_lod_confirm", f"确定要删除LOD吗?"), button=[TEXT("yes", "是"), TEXT("no", "否")], defaultButton=TEXT("no", "否"), cancelButton=TEXT("no", "否"), dismissString=TEXT("no", "否") ) if result == TEXT("yes", "是"): # 删除与该LOD相关的所有模型 nodes_to_delete = cmds.ls(f"{lod_name}_*") if nodes_to_delete: cmds.delete(nodes_to_delete) print(f"{lod_name}已删除") return True except Exception as e: print(f"删除LOD时出错: {e}") return False def load_model_for_lod(lod_name, part_name): """ 为指定LOD的身体部位加载模型 参数: lod_name: LOD名称,如"LOD0" part_name: 身体部位名称,如"头部" """ print(f"为{lod_name}的{part_name}加载模型") try: # 打开文件选择对话框 file_path = cmds.fileDialog2( fileFilter="Maya Files (*.ma *.mb);;OBJ Files (*.obj);;FBX Files (*.fbx);;All Files (*.*)", dialogStyle=2, fileMode=1, caption=f"选择{lod_name}的{part_name}模型" ) if file_path and len(file_path) > 0: # 根据文件类型导入模型 file_ext = os.path.splitext(file_path[0])[1].lower() if file_ext in [".ma", ".mb"]: # 导入Maya文件 cmds.file(file_path[0], i=True, type="mayaAscii" if file_ext == ".ma" else "mayaBinary", ignoreVersion=True, mergeNamespacesOnClash=False, namespace=f"{lod_name}_{part_name}") elif file_ext == ".obj": # 导入OBJ文件 cmds.file(file_path[0], i=True, type="OBJ", ignoreVersion=True, options="mo=1", namespace=f"{lod_name}_{part_name}") elif file_ext == ".fbx": # 导入FBX文件 mel.eval(f'FBXImport -file "{file_path[0]}";') # 重命名导入的模型 imported_nodes = cmds.ls(sl=True) if imported_nodes: for node in imported_nodes: new_name = f"{lod_name}_{part_name}_{os.path.basename(node)}" cmds.rename(node, new_name) print(f"成功为{lod_name}的{part_name}加载模型: {file_path[0]}") return True except Exception as e: print(f"加载模型时出错: {e}") return False # 模型工具相关功能 def standardize_names(): """ 标准化命名 """ print("标准化命名") # 这里实现标准化命名的功能 return True def auto_group(): """ 自动分组 """ print("自动分组") # 这里实现自动分组的功能 return True #========================================== 底部功能按钮 ======================================== # 拓扑结构相关功能 def update_topology(index): """ 更新拓扑结构 参数: index: 拓扑结构下拉框的索引 """ print(f"更新拓扑结构: {index}") try: # 获取选择的拓扑结构 topology_types = ["MetaHuman"] selected_topology = topology_types[index] if index < len(topology_types) else topology_types[0] print(f"已选择拓扑结构: {selected_topology}") return True except Exception as e: print(f"更新拓扑结构时出错: {e}") return False # LOD选择相关功能 def select_lod(index): """ 选择LOD 参数: index: LOD下拉框的索引 """ print(f"选择LOD: {index}") try: # 获取选择的LOD lod_name = f"LOD{index}" # 显示对应LOD的模型,隐藏其他LOD的模型 for i in range(9): # LOD0~LOD8 current_lod = f"LOD{i}" lod_models = cmds.ls(f"{current_lod}_*") if lod_models: visibility = (current_lod == lod_name) for model in lod_models: cmds.setAttr(f"{model}.visibility", visibility) print(f"已选择LOD: {lod_name}") return True except Exception as e: print(f"选择LOD时出错: {e}") return False # 创建LOD相关功能 def create_lod(): """ 创建LOD 根据当前选择的LOD创建新的LOD """ print("创建LOD") try: # 获取当前选择的LOD selected_lod_index = cmds.optionMenu("lodCombo", query=True, select=True) - 1 selected_lod = f"LOD{selected_lod_index}" # 创建新的LOD new_lod_index = selected_lod_index + 1 new_lod = f"LOD{new_lod_index}" # 复制当前选择的LOD模型 lod_models = cmds.ls(f"{selected_lod}_*") if lod_models: for model in lod_models: # 复制模型 new_model = cmds.duplicate(model, name=model.replace(selected_lod, new_lod))[0] # 减少模型的多边形数量(简化模型) cmds.polyReduce(new_model, percentage=80, triangulate=False) print(f"已创建{new_lod}") return True except Exception as e: print(f"创建LOD时出错: {e}") return False # 模型工具相关功能 def separate_model(): """ 模型分离 将选中的模型分离成多个独立的组件 """ print("模型分离") try: # 获取当前选中的模型 selected_models = cmds.ls(selection=True, type="transform") if not selected_models: cmds.warning(TEXT("no_model_selected", "未选中模型")) return False # 对每个选中的模型进行分离 for model in selected_models: # 分离模型的各个部分 cmds.polySeparate(model, name=f"{model}_separated") print("模型分离完成") return True except Exception as e: print(f"模型分离时出错: {e}") return False def generate_face_components(): """ 生成面部配件 如眉毛、睡毛、泪腺等 """ print("生成面部配件") try: # 获取当前选中的模型 selected_models = cmds.ls(selection=True, type="transform") if not selected_models: cmds.warning(TEXT("no_model_selected", "未选中模型")) return False # 生成眉毛 # 这里只是示例代码,实际实现需要更复杂的逻辑 eyebrow_curve = cmds.curve(d=3, p=[(0,0,0), (0.5,0.1,0), (1,0,0)]) cmds.rename(eyebrow_curve, "eyebrow_curve") # 生成睫毛 eyelash_curve = cmds.curve(d=3, p=[(0,0,0), (0.5,-0.1,0), (1,0,0)]) cmds.rename(eyelash_curve, "eyelash_curve") print("面部配件生成完成") return True except Exception as e: print(f"生成面部配件时出错: {e}") return False def fix_normals(): """ 修复法线 修复选中模型的法线方向 """ print("修复法线") try: # 获取当前选中的模型 selected_models = cmds.ls(selection=True, type="transform") if not selected_models: cmds.warning(TEXT("no_model_selected", "未选中模型")) return False # 对每个选中的模型修复法线 for model in selected_models: # 先解锁法线 cmds.polyNormalPerVertex(model, unFreezeNormal=True) # 然后计算法线 cmds.polyNormal(model, normalMode=0, userNormalMode=0, ch=1) # 最后让法线平滑 cmds.polySoftEdge(model, angle=30, ch=1) print("法线修复完成") return True except Exception as e: print(f"修复法线时出错: {e}") return False def fix_vertex_order(): """ 修复点序 """ print("修复点序") # 这里实现修复点序的功能 return True def fix_seams(): """ 修复接缝 """ print("修复接缝") # 这里实现修复接缝的功能 return True def optimize_scene(): """ 优化场景 清理场景中的未使用节点和历史记录 """ print("优化场景") try: # 清理未使用的节点 mel.eval('MLdeleteUnused;') # 删除所有历史记录 cmds.delete(constructionHistory=True, all=True) # 优化场景大小 cmds.file(cleanReference=True) cmds.file(removeReference=True) cmds.file(optimizeReference=True) # 清理未使用的材质和纹理 mel.eval('hyperShadePanelMenuCommand("hyperShadePanel1", "deleteUnusedNodes");') print("场景优化完成") return True except Exception as e: print(f"优化场景时出错: {e}") return False