700 lines
20 KiB
Python
700 lines
20 KiB
Python
#!/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 |