Files
MetaFusion/scripts/utils/utils_geometry.py

352 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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