Files
MetaFusion/scripts/utils/utils_geometry.py
2025-05-08 23:57:22 +08:00

700 lines
20 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 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 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_name}吗?"),
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 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 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 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(TEXT("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(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 generate_uvs():
"""
生成模型UV
"""
print("生成模型UV")
try:
# 获取当前选中的模型
selected_models = cmds.ls(selection=True, type="transform")
if not selected_models:
cmds.warning(TEXT("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