#!/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 Qt import QtWidgets, QtCore, QtGui from 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 LANG = localization.LANG #========================================== FUNCTIONS ======================================== # 左侧面板功能 # LOD相关功能 def lod_selected(item): """ LOD项目被选中 """ print(f"LOD项目被选中: {item.text()}") # 这里实现LOD项目选中的功能 return True def update_lod_selection(): """ 更新LOD选择 """ print("更新LOD选择") # 这里实现更新LOD选择的功能 return True def add_lod(): """ 添加LOD """ print("添加LOD") # 这里实现添加LOD的功能 return True def remove_lod(): """ 移除LOD """ print("移除LOD") # 这里实现移除LOD的功能 return True def duplicate_lod(): """ 复制LOD """ print("复制LOD") # 这里实现复制LOD的功能 return True # 模型相关功能 def model_selected(item): """ 模型项目被选中 """ print(f"模型项目被选中: {item.text()}") # 这里实现模型项目选中的功能 return True def update_model_selection(): """ 更新模型选择 """ print("更新模型选择") # 这里实现更新模型选择的功能 return True def add_model(): """ 添加模型 """ print("添加模型") # 这里实现添加模型的功能 return True def remove_model(): """ 移除模型 """ print("移除模型") # 这里实现移除模型的功能 return True def duplicate_model(): """ 复制模型 """ print("复制模型") # 这里实现复制模型的功能 return True # 右侧面板功能 # 模型属性相关功能 def update_model_name(text): """ 更新模型名称 """ print(f"更新模型名称: {text}") # 这里实现更新模型名称的功能 return True def update_model_type(index): """ 更新模型类型 """ print(f"更新模型类型: {index}") # 这里实现更新模型类型的功能 return True def toggle_model_visibility(state): """ 切换模型可见性 """ print(f"切换模型可见性: {state}") # 这里实现切换模型可见性的功能 return True def apply_properties(): """ 应用属性 """ print("应用属性") # 这里实现应用属性的功能 return True def reset_properties(): """ 重置属性 """ print("重置属性") # 这里实现重置属性的功能 return True # 模型工具相关功能 def standardize_names(): """ 标准化命名 """ print("标准化命名") # 这里实现标准化命名的功能 return True def auto_group(): """ 自动分组 """ print("自动分组") # 这里实现自动分组的功能 return True def generate_accessories(): """ 生成配件 """ print("生成配件") # 这里实现生成配件的功能 return True def fix_seams(): """ 修复接缝 """ print("修复接缝") # 这里实现修复接缝的功能 return True def fix_vertex_order(): """ 修复点序 """ print("修复点序") # 这里实现修复点序的功能 return True # 底部工具面板功能 # 导入相关功能 def import_model(): """ 导入模型 """ print("导入模型") try: file_path = cmds.fileDialog2( fileFilter="Maya Files (*.ma *.mb);;All Files (*.*)", dialogStyle=2, fileMode=1 ) if file_path: # 这里实现导入模型的功能 print(f"将从{file_path[0]}导入模型") except Exception as e: print(f"导入模型时出错: {e}") return True def import_fbx(): """ 导入FBX """ print("导入FBX") try: file_path = cmds.fileDialog2( fileFilter="FBX Files (*.fbx);;All Files (*.*)", dialogStyle=2, fileMode=1 ) if file_path: # 这里实现导入FBX的功能 print(f"将从{file_path[0]}导入FBX") mel.eval(f'FBXImport -file "{file_path[0]}";') except Exception as e: print(f"导入FBX时出错: {e}") return True def import_obj(): """ 导入OBJ """ print("导入OBJ") try: file_path = cmds.fileDialog2( fileFilter="OBJ Files (*.obj);;All Files (*.*)", dialogStyle=2, fileMode=1 ) if file_path: # 这里实现导入OBJ的功能 print(f"将从{file_path[0]}导入OBJ") cmds.file(file_path[0], i=True, type="OBJ", ignoreVersion=True, options="mo=1") except Exception as e: print(f"导入OBJ时出错: {e}") return True # 导出相关功能 def export_model(): """ 导出模型 """ print("导出模型") try: file_path = cmds.fileDialog2( fileFilter="Maya Files (*.ma *.mb);;All Files (*.*)", dialogStyle=2, fileMode=2 ) if file_path: # 这里实现导出模型的功能 print(f"将模型导出到{file_path[0]}") except Exception as e: print(f"导出模型时出错: {e}") return True def export_fbx(): """ 导出 FBX """ print("导出 FBX") try: file_path = cmds.fileDialog2( fileFilter="FBX Files (*.fbx);;All Files (*.*)", dialogStyle=2, fileMode=2 ) if file_path: # 这里实现导出 FBX的功能 print(f"将模型导出到{file_path[0]}") mel.eval('FBXExportSmoothingGroups -v true;') mel.eval('FBXExportHardEdges -v false;') mel.eval('FBXExportTangents -v false;') mel.eval('FBXExportSmoothMesh -v true;') mel.eval('FBXExportInstances -v false;') mel.eval('FBXExportReferencedContainersContent -v false;') mel.eval('FBXExportBakeComplexAnimation -v false;') mel.eval('FBXExportUseSceneName -v false;') mel.eval('FBXExportQuaternion -v euler;') mel.eval('FBXExportShapes -v true;') mel.eval('FBXExportSkins -v true;') mel.eval('FBXExportConstraints -v false;') mel.eval('FBXExportCameras -v false;') mel.eval('FBXExportLights -v false;') mel.eval('FBXExportEmbeddedTextures -v false;') mel.eval('FBXExportInputConnections -v false;') mel.eval('FBXExportUpAxis y;') mel.eval(f'FBXExport -f "{file_path[0]}" -s;') except Exception as e: print(f"导出 FBX时出错: {e}") return True def export_obj(): """ 导出 OBJ """ print("导出 OBJ") try: file_path = cmds.fileDialog2( fileFilter="OBJ Files (*.obj);;All Files (*.*)", dialogStyle=2, fileMode=2 ) if file_path: # 这里实现导出 OBJ的功能 print(f"将模型导出到{file_path[0]}") cmds.file(file_path[0], force=True, options="groups=1;ptgroups=1;materials=1;smoothing=1;normals=1", type="OBJexport", pr=True, exportSelected=True) except Exception as e: print(f"导出 OBJ时出错: {e}") return True # 工具相关功能 def check_model(): """ 检查模型 """ print("检查模型") # 这里实现检查模型的功能 return True def optimize_model(): """ 优化模型 """ print("优化模型") # 这里实现优化模型的功能 return True def clean_model(): """ 清理模型 """ print("清理模型") # 这里实现清理模型的功能 return True def uv_tools(): """ UV工具 """ print("UV工具") # 这里实现UV工具的功能 return True #========================================== LOD相关功能 ======================================== 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 delete(): """ 删除当前选中的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=LANG.get("confirm_delete", "确认删除"), message=LANG.get("delete_lod_confirm", f"确定要删除{lod_name}吗?"), button=[LANG.get("yes", "是"), LANG.get("no", "否")], defaultButton=LANG.get("no", "否"), cancelButton=LANG.get("no", "否"), dismissString=LANG.get("no", "否") ) if result == LANG.get("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 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(LANG.get("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 fix_normals(): """ 修复法线 修复选中模型的法线方向 """ print("修复法线") try: # 获取当前选中的模型 selected_models = cmds.ls(selection=True, type="transform") if not selected_models: cmds.warning(LANG.get("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 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 def modify_topology(): """ 修改模型拓扑 """ print("修改模型拓扑") try: # 获取当前选中的模型 selected_models = cmds.ls(selection=True, type="transform") if not selected_models: cmds.warning(LANG.get("no_model_selected", "未选中模型")) return False # 切换到多边形编辑模式 cmds.selectMode(component=True) cmds.selectType(polymesh=True) 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(LANG.get("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 generate_uvs(): """ 生成模型UV """ print("生成模型UV") try: # 获取当前选中的模型 selected_models = cmds.ls(selection=True, type="transform") if not selected_models: cmds.warning(LANG.get("no_model_selected", "未选中模型")) return False # 为每个选中的模型生成UV for model in selected_models: # 使用Maya的自动UV展开功能 cmds.polyAutoProjection(model, constructionHistory=True, layout=2, optimize=1, percentageSpace=0.2) print("UV生成完成") return True except Exception as e: print(f"生成UV时出错: {e}") return False # 保留原来的函数作为兼容性考虑 def geometry_temp_utils_function(): """ Placeholder function for geometry module This function will be replaced with actual functionality in future updates """ print("Geometry module initialized with placeholder function") return True