麻烦帮我优化这段代码,需要保留完整功能和逻辑from PySide2 import QtGui
from PySide2 import QtWidgets, QtCore
from shiboken2 import wrapInstance
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
from maya import OpenMayaUI as omui
import maya.OpenMaya as om
import maya.cmds as cmds
import maya.mel as mel
import os, platform, subprocess, sys
import xml.dom.minidom as xml # 用于XML设置
DEBUG = False
PRNT_STRING = "<RizomUV桥接工具>" # 日志前缀汉化
try:
# Python 3
from . import scriptlist
except ImportError:
import scriptlist
class Settings():
def __init__(self):
config_path = os.path.join('%s/RizomBridge' % os.getenv('APPDATA'))
config_file = 'uisettings.xml'
# 脚本路径设置
rizom_script_path = os.path.join(config_path, "MayaRizomBridgeScript.lua")
self.rizom_script_path = rizom_script_path.replace('\\', '/')
self.apppath = None
self.config_file_path = os.path.join(config_path, config_file)
self.check_config_exists(config_path)
try:
self.read_settings()
except:
print("<RizomUV桥接工具> 读取设置失败,正在创建新的xml文件。") # 日志汉化
self.set_defaults()
self.save_xml()
self.read_settings()
self.exportFile = os.path.join(config_path, self.objname)
self.exportFile = self.exportFile.replace('\\', '/')
def read_settings(self):
doc = xml.parse(self.config_file_path)
ui_app = doc.getElementsByTagName("Application")[0]
self.apppath = ui_app.getAttribute("apppath")
self.objname = ui_app.getAttribute("objname")
self.upaxis = ui_app.getAttribute("upaxis")
self.loaduvs = self.str_to_bool(ui_app.getAttribute("loaduvs"))
self.useuvlink = self.str_to_bool(ui_app.getAttribute("useuvlink"))
self.fixuvnames = self.str_to_bool(ui_app.getAttribute("fixuvnames"))
ui_packer = doc.getElementsByTagName("Packer")[0]
self.quality = int(ui_packer.getAttribute("quality"))
self.mutations = int(ui_packer.getAttribute("mutations"))
self.margin = int(ui_packer.getAttribute("margin"))
self.spacing = int(ui_packer.getAttribute("spacing"))
self.resolution = int(ui_packer.getAttribute("resolution"))
self.initscaleavg = self.str_to_bool(ui_packer.getAttribute("initscaleavg"))
self.autofit = self.str_to_bool(ui_packer.getAttribute("autofit"))
def set_defaults(self):
self.objname = "MayaRizomExport.fbx"
self.upaxis = "Y"
self.loaduvs = True
self.useuvlink = True
self.fixuvnames = False
self.quality = 2
self.mutations = 256
self.margin = 2
self.spacing = 2
self.resolution = 1024
self.initscaleavg = True
self.autofit = True
self.apppath = self.findall_rizom_installs()[-1]
def save_xml(self):
print(PRNT_STRING, f"将设置保存到磁盘: {self.config_file_path}") # 日志汉化
doc = xml.Document()
root_element = doc.createElement("RizomBridge")
element_application = doc.createElement("Application")
element_application.setAttribute("apppath", str(self.apppath))
element_application.setAttribute("objname", str(self.objname))
element_application.setAttribute("upaxis", str(self.upaxis))
element_application.setAttribute("loaduvs", str(self.loaduvs))
element_application.setAttribute("useuvlink", str(self.useuvlink))
element_application.setAttribute("fixuvnames", str(self.fixuvnames))
element_packer = doc.createElement("Packer")
element_packer.setAttribute("quality", str(self.quality))
element_packer.setAttribute("mutations", str(self.mutations))
element_packer.setAttribute("margin", str(self.margin))
element_packer.setAttribute("spacing", str(self.spacing))
element_packer.setAttribute("resolution", str(self.resolution))
element_packer.setAttribute("initscaleavg", str(self.initscaleavg))
element_packer.setAttribute("autofit", str(self.autofit))
doc.appendChild(root_element)
root_element.appendChild(element_application)
root_element.appendChild(element_packer)
doc.writexml(open(self.config_file_path, 'w'), indent=" ", addindent=" ", newl='\n')
def check_config_exists(self, config_path):
print(PRNT_STRING, f"检查配置文件: {self.config_file_path}") # 日志汉化
if not os.path.exists(config_path):
os.makedirs(config_path)
if not os.path.exists(self.config_file_path):
print(PRNT_STRING, "未找到配置文件。") # 日志汉化
self.set_defaults()
self.save_xml()
else:
print(PRNT_STRING, "找到配置文件。") # 日志汉化
def findall_rizom_installs(self):
""" 返回注册表中所有存在的rizom.exe路径 """
try:
import winreg
except:
self.manual_locate_rizom()
return [self.apppath]
key_path = "SOFTWARE\\Rizom Lab\\"
parent_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path)
installs = []
i = 0
while i < 1000:
try:
key = winreg.EnumKey(parent_key, i)
try:
exe_path = winreg.QueryValue(winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path + key),
"rizomuv.exe") + ".exe"
except FileNotFoundError:
exe_path = ""
if os.path.exists(exe_path):
installs.append(exe_path.replace("\\", "/"))
except WindowsError:
break
i += 1
print(PRNT_STRING, "在Windows注册表中找到RizomUV安装路径:") # 日志汉化
for inst in sorted(installs):
print(" ..", inst)
return sorted(installs)
def manual_locate_rizom(self):
om.MGlobal.displayWarning(f"无法定位rizomuv.exe: {self.apppath}") # 提示汉化
fname = QtWidgets.QFileDialog.getOpenFileName(None, '定位rizomuv.exe', 'C:/Program Files/Rizom Lab/', "可执行文件 (*.exe)") # 对话框标题及过滤器汉化
self.apppath = fname[0]
self.appver = self.get_version()
self.save_xml()
def get_version(self):
""" 从文件夹名称读取版本号,返回列表形式,例如 [2022, 2] """ # 注释汉化
return os.path.dirname(self.apppath).split()[-1].split('.')
def check_lua_path(self):
if not os.path.exists(self.rizom_script_path):
with open(self.rizom_script_path, 'w') as f:
if DEBUG: print(PRNT_STRING, f"在{self.rizom_script_path}创建空白lua文件") # 日志汉化
f.write('')
def str_to_bool(self, s):
if s == 'True':
return True
else:
# 其他情况均返回False,否则可能返回None导致崩溃
return False
class RizomUVBridgeWindow(MayaQWidgetDockableMixin, QtWidgets.QDialog):
def __init__(self, rootWidget=None, *args, **kwargs):
super(RizomUVBridgeWindow, self).__init__()
self.conf = Settings()
self.conf.check_lua_path()
print("Rizom路径:", self.conf.apppath) # 日志汉化
self.setWindowTitle("桥接工具") # 窗口标题汉化
self.sj_num_selchange = cmds.scriptJob(parent=self.objectName(), event=['SelectionChanged', self.ui_update_uvchannels])
self.resize(250, 300)
self.create_widgets()
self.create_layouts()
self.create_connections()
self.exported_objects = []
self.ui_update_uvchannels()
self.cleanse_namespaces()
self.config_updated = False
if not os.path.exists(self.conf.apppath):
self.conf.manual_locate_rizom()
v = self.conf.get_version()
self.btn_run.setText(f'运行RizomUV {v[0]}.{v[1]}') # 按钮文本汉化
rizom_link_path = os.path.dirname(self.conf.apppath) + '/RizomUVLink'
self.port = None
if rizom_link_path not in sys.path:
sys.path.append(os.path.dirname(self.conf.apppath) + '/RizomUVLink')
try:
from RizomUVLink import CRizomUVLink
self.link = CRizomUVLink()
except:
self.link = None
def create_widgets(self):
v = ".".join(self.conf.get_version())
self.btn_run = QtWidgets.QPushButton(f'运行RizomUV {v}', self) # 按钮文本汉化
self.btn_export = QtWidgets.QPushButton('导出', self) # 按钮文本汉化
self.btn_import = QtWidgets.QPushButton('导入', self) # 按钮文本汉化
self.cbx_use_link = QtWidgets.QCheckBox('尝试使用CRizomUVLink连接', self) # 复选框文本汉化
self.cbx_use_link.setChecked(self.conf.useuvlink)
self.cbx_fix_set_names = QtWidgets.QCheckBox('导入时修复UV集名称', self) # 复选框文本汉化
self.cbx_fix_set_names.setChecked(self.conf.fixuvnames)
if not os.path.exists(self.conf.exportFile):
self.btn_import.setEnabled(False)
# 导出设置组件
self.radioAxisY = QtWidgets.QRadioButton('Y轴') # 单选按钮文本汉化
self.radioAxisZ = QtWidgets.QRadioButton('Z轴') # 单选按钮文本汉化
if self.conf.upaxis == 'Y':
self.radioAxisY.setChecked(True)
else:
self.radioAxisZ.setChecked(True)
self.cbx_keepuv = QtWidgets.QCheckBox('加载现有UV', self) # 复选框文本汉化
self.cbx_keepuv.setChecked(self.conf.loaduvs)
self.combo_scripts = QtWidgets.QComboBox(self)
self.combo_scripts.addItem("无脚本") # 下拉框文本汉化
for s in scriptlist.scripts:
self.combo_scripts.addItem(s[1])
# 工具组件
self.btn_fix_shell_normals = QtWidgets.QPushButton('将UV边界边设为硬边') # 按钮文本汉化
self.btn_edit_settings = QtWidgets.QPushButton('打开设置文件夹') # 按钮文本汉化
self.combo_pack_uvset = QtWidgets.QComboBox(self)
self.btn_pack = QtWidgets.QPushButton('导出并打包UV', self) # 按钮文本汉化
self.combo_pack_quality = QtWidgets.QComboBox(self)
self.combo_pack_quality.addItems(['低', '普通', '高', '较高', '极高']) # 下拉框文本汉化
self.combo_pack_quality.setCurrentIndex(self.conf.quality)
self.slider_pack_uvmap = QtWidgets.QSlider(QtCore.Qt.Orientation(1))
self.slider_pack_uvmap.setMinimum(0)
self.slider_pack_uvmap.setMaximum(5)
self.slider_pack_uvmap.setValue(1)
self.label_uvmap = QtWidgets.QLabel("1")
self.dspin_pack_mutations = QtWidgets.QSpinBox()
self.dspin_pack_mutations.setSingleStep(1)
self.dspin_pack_mutations.setRange(1, 1000)
self.dspin_pack_mutations.setWrapping(False)
self.dspin_pack_mutations.setValue(self.conf.mutations)
self.dspin_pack_resolution = QtWidgets.QSpinBox()
self.dspin_pack_resolution.setSingleStep(8)
self.dspin_pack_resolution.setRange(8, 8192)
self.dspin_pack_resolution.setWrapping(False)
self.dspin_pack_resolution.setValue(self.conf.resolution)
self.dspin_pack_margin = QtWidgets.QSpinBox()
self.dspin_pack_margin.setSingleStep(1)
self.dspin_pack_margin.setWrapping(False)
self.dspin_pack_margin.setValue(self.conf.margin)
self.dspin_pack_spacing = QtWidgets.QSpinBox()
self.dspin_pack_spacing.setSingleStep(1)
self.dspin_pack_spacing.setWrapping(False)
self.dspin_pack_spacing.setValue(self.conf.spacing)
self.cbx_initial_scale_avg = QtWidgets.QCheckBox("使用'平均'初始缩放", self) # 复选框文本汉化
self.cbx_initial_scale_avg.setChecked(self.conf.initscaleavg)
self.cbx_layout_scaling = QtWidgets.QCheckBox("布局:自动适配", self) # 复选框文本汉化
self.cbx_layout_scaling.setChecked(self.conf.autofit)
def create_layouts(self):
main_layout = QtWidgets.QVBoxLayout(self)
grp_layout = QtWidgets.QVBoxLayout()
grp_layout.addWidget(self.btn_run)
grp_layout.addWidget(self.btn_export)
grp_layout.addWidget(self.btn_import)
grp_layout.addWidget(self.cbx_use_link)
grp_layout.addWidget(self.cbx_fix_set_names)
grp = QtWidgets.QGroupBox("UV操作") # 组框标题汉化
grp.setLayout(grp_layout)
main_layout.addWidget(grp)
# 导出设置
grp_layout = QtWidgets.QVBoxLayout()
grp_layout.addWidget(self.cbx_keepuv)
hor_layout = QtWidgets.QHBoxLayout()
hor_layout.addWidget(QtWidgets.QLabel("脚本")) # 标签文本汉化
hor_layout.addWidget(self.combo_scripts)
grp_layout.addLayout(hor_layout)
hor_layout = QtWidgets.QHBoxLayout()
hor_layout.addWidget(QtWidgets.QLabel("上方向轴")) # 标签文本汉化
hor_layout.addWidget(self.radioAxisY)
hor_layout.addWidget(self.radioAxisZ)
grp_layout.addLayout(hor_layout)
grp = QtWidgets.QGroupBox("导出设置") # 组框标题汉化
grp.setLayout(grp_layout)
main_layout.addWidget(grp)
# 工具
grp_layout = QtWidgets.QVBoxLayout()
grp_layout.addWidget(self.btn_fix_shell_normals)
grp_layout.addWidget(self.btn_edit_settings)
grp = QtWidgets.QGroupBox("工具") # 组框标题汉化
grp.setLayout(grp_layout)
main_layout.addWidget(grp)
# UV打包
grp_layout = QtWidgets.QVBoxLayout()
grp = QtWidgets.QGroupBox("UV打包") # 组框标题汉化
hor_layout = QtWidgets.QHBoxLayout()
hor_layout.addWidget(self.btn_pack)
grp_layout.addLayout(hor_layout)
hor_layout = QtWidgets.QHBoxLayout()
hor_layout.addWidget(QtWidgets.QLabel("要打包的UV集")) # 标签文本汉化
hor_layout.addWidget(self.combo_pack_uvset)
grp_layout.addLayout(hor_layout)
hor_layout = QtWidgets.QHBoxLayout()
hor_layout.addWidget(QtWidgets.QLabel("打包质量")) # 标签文本汉化
hor_layout.addWidget(self.combo_pack_quality)
grp_layout.addLayout(hor_layout)
hor_layout = QtWidgets.QHBoxLayout()
hor_layout.addWidget(QtWidgets.QLabel("迭代次数")) # 标签文本汉化
hor_layout.addWidget(self.dspin_pack_mutations)
hor_layout.addWidget(QtWidgets.QLabel("分辨率")) # 标签文本汉化
hor_layout.addWidget(self.dspin_pack_resolution)
grp_layout.addLayout(hor_layout)
hor_layout = QtWidgets.QHBoxLayout()
hor_layout.addWidget(QtWidgets.QLabel("边距")) # 标签文本汉化
hor_layout.addWidget(self.dspin_pack_margin)
hor_layout.addWidget(QtWidgets.QLabel("间距")) # 标签文本汉化
hor_layout.addWidget(self.dspin_pack_spacing)
grp_layout.addLayout(hor_layout)
hor_layout = QtWidgets.QHBoxLayout()
hor_layout.addWidget(self.cbx_initial_scale_avg)
grp_layout.addLayout(hor_layout)
hor_layout = QtWidgets.QHBoxLayout()
hor_layout.addWidget(self.cbx_layout_scaling)
grp_layout.addLayout(hor_layout)
grp_layout.addStretch()
grp.setLayout(grp_layout)
main_layout.addWidget(grp)
def create_connections(self):
self.btn_run.clicked.connect(self.riz_run)
self.btn_export.clicked.connect(self.riz_export)
self.btn_import.clicked.connect(self.riz_import)
self.btn_fix_shell_normals.clicked.connect(fix_shell_border_normals)
self.btn_edit_settings.clicked.connect(self.browse_settings_location)
self.cbx_keepuv.stateChanged.connect(self.set_config)
self.slider_pack_uvmap.valueChanged.connect(self.ui_pack_update_labels)
self.radioAxisY.clicked.connect(self.set_config)
self.radioAxisZ.clicked.connect(self.set_config)
self.dspin_pack_mutations.valueChanged.connect(self.set_config)
self.dspin_pack_resolution.valueChanged.connect(self.set_config)
self.dspin_pack_margin.valueChanged.connect(self.set_config)
self.dspin_pack_spacing.valueChanged.connect(self.set_config)
self.cbx_layout_scaling.stateChanged.connect(self.set_config)
self.cbx_use_link.stateChanged.connect(self.set_config)
self.cbx_fix_set_names.stateChanged.connect(self.set_config)
self.btn_pack.clicked.connect(self.riz_pack)
return
def riz_run(self):
# 确认应用程序路径
if not os.path.exists(self.conf.apppath):
self.manual_locate_rizom()
if self.link and self.cbx_use_link.isChecked():
self.port = self.link.RunRizomUV()
print(PRNT_STRING, f"RizomUV {self.link.RizomUVVersion()}链接已建立。正在TCP端口{self.port}监听命令") # 日志汉化
else:
# 链接不可用时使用原始方法
print(PRNT_STRING, "RizomUV链接不可用。将使用LUA脚本文件通信。") # 日志汉化
cmd = '"' + self.conf.apppath + '" -cf "' + self.conf.rizom_script_path + '"'
print(cmd)
if platform.system() == "Windows":
self.sp = subprocess.Popen(cmd, shell=True)
else:
self.sp = subprocess.Popen(["open", "-a", self.conf.apppath, "-cf", self.conf.rizom_script_path])
self.btn_export.setEnabled(True)
return
def riz_pack(self):
# 导出模型不加载脚本
current_uv = self.combo_pack_uvset.currentText()
exported = self.riz_export(False, False)
if not exported:
return
# 根据GUI选项构建LUA代码
cmd = ''
# 重复使用的文本块
cmd_properties_prefix = 'ZomIslandGroups({Mode="SetGroupsProperties", WorkingSet="Visible", MergingPolicyString="A_ADD|AIB_ADD_A_VALUE_B|B_CLONE", GroupPaths={ "RootGroup" }, '
# 加载模型
cmd += 'ZomLoad({File={Path="'+self.conf.exportFile+'", ImportGroups=true, XYZUVW=true, UVWProps=true}})\n'
cmd += 'ZomUvset({Mode="SetCurrent", Name="%s"})\n' % current_uv
cmd += cmd_properties_prefix + 'Properties={Pack={Rotate={Step=90}}}})\n'
cmd += cmd_properties_prefix + 'Properties={Pack={Rotate={Mode=0}}}})\n'
margin = float(self.dspin_pack_margin.value()) / self.dspin_pack_resolution.value()
spacing = float(self.dspin_pack_spacing.value()) / self.dspin_pack_resolution.value()
cmd += cmd_properties_prefix + 'Properties={Pack={SpacingSize=%f}}})\n' % spacing
cmd += cmd_properties_prefix + 'Properties={Pack={MarginSize=%f}}})\n' % margin
quality = [128, 256, 512, 1024, 2048]
cmd += cmd_properties_prefix + 'Properties={Pack={Resolution=%i}}})\n' % quality[self.combo_pack_quality.currentIndex()]
cmd += cmd_properties_prefix + 'Properties={Pack={MaxMutations=%i}}})\n' % self.dspin_pack_mutations.value()
init_scale = 0
if self.cbx_initial_scale_avg.isChecked():
init_scale = 2
layout_scale = 0
if self.cbx_layout_scaling.isChecked():
layout_scale = 2
cmd += 'ZomSet({Path="Prefs.PackOptions.__ScalingMode", Value=%i})\n' % init_scale
cmd += 'ZomSave({File={Path="c:/users/root/appdata/local/temp/MayaRizomExport.fbx", UVWProps=true}, __UpdateUIObjFileName=true})\n'
cmd += 'ZomIslandGroups({Mode="DistributeInTilesEvenly", WorkingSet="Visible&Flat", MergingPolicyString="A_ADD|AIB_ADD_A_VALUE_B|B_CLONE", UseTileLocks=true, UseIslandLocks=true})\n'
cmd += 'ZomPack({RootGroup="RootGroup", WorkingSet="Visible&Flat", ProcessTileSelection=false, RecursionDepth=1, Translate=true, LayoutScalingMode=%i, Scaling={Mode=%i}})\n' % (layout_scale, init_scale)
if DEBUG:
print(cmd)
self.write_to_lua_file(cmd)
return
def fbx_export(self):
# FBX导出
if not cmds.pluginInfo("fbxmaya", loaded=True, query=True):
cmds.loadPlugin("fbxmaya")
cmds.undoInfo(openChunk=True)
mel.eval('ConvertInstanceToObject;') # 这是唯一需要撤销的命令
mel.eval('FBXExportSmoothingGroups -v true;')
mel.eval('FBXExportTriangulate -v false;')
mel.eval('FBXExportSmoothMesh -v false;')
mel.eval('FBXExportUpAxis {};'.format(self.conf.upaxis))
print(f'FBXExport -s -f "{self.conf.exportFile}";')
mel.eval(f'FBXExport -s -f "{self.conf.exportFile}";')
try:
cmds.undo()
except RuntimeError as RE:
# 没有更多可撤销的命令
pass
# 结束FBX导出
return
def riz_export(self, use_script=True, load_model=True):
if self.link and self.cbx_use_link.isChecked():
print("Rizom链接版本", self.link.Version())
self.exported_objects = cmds.ls(selection=True, tr=True)
# FBX导出
self.fbx_export()
if not self.port:
self.riz_run()
params = {
"File.Path": self.conf.exportFile,
"File.XYZUVW": True, # 加载3D+UV数据(使用File.XYZ仅加载3D数据)
"File.UVWProps": True, # 加载UV属性(如固定、纹理密度设置等)
"File.ImportGroups": True, # 加载岛组层级
"__Focus": True, # 在视口中聚焦加载的网格
}
self.link.Load(params)
if DEBUG: print("<RIZOM> 导出至:", self.conf.exportFile) # 日志汉化
cmds.select(self.exported_objects)
# 启用导入按钮(如果之前禁用)
self.btn_import.setEnabled(True)
else:
# 传统方法 #
print("<RIZOM> 正在导出模型") # 日志汉化
self.cleanse_namespaces() # 强制清理
self.exported_objects = cmds.ls(selection=True, tr=True)
if not self.exported_objects:
return False
# 删除历史记录(防止存在空组被此操作移除)
# 否则导入时会被清除,导致对象计数不匹配
cmds.bakePartialHistory()
cmd = ''
# 导出选项
if load_model:
if self.cbx_keepuv.isChecked():
cmd += 'ZomLoad({File={Path="'+self.conf.exportFile+'", ImportGroups=true, XYZUVW=true, UVWProps=true}})\n'
else:
cmd += 'ZomLoad({File={Path="'+self.conf.exportFile+'", ImportGroups=true, XYZ=true}, NormalizeUVW=true})\n'
# 添加脚本
if self.combo_scripts.currentIndex() and use_script is True:
script_name = scriptlist.scripts[self.combo_scripts.currentIndex()-1][0]
script_path = os.path.join(os.path.dirname(__file__), 'lua_scripts/%s' % script_name)
with open(script_path, 'r') as lua_script:
cmd += lua_script.read()
if DEBUG: print("<RizomUV桥接工具>", cmd) # 日志汉化
# FBX导出
self.fbx_export()
if DEBUG: print("<RIZOM> 导出至:", self.conf.exportFile) # 日志汉化
cmds.select(self.exported_objects)
self.write_to_lua_file(cmd)
self.btn_import.setEnabled(True)
# 如果有显著更改,将配置写入磁盘
if self.config_updated == True:
self.conf.save_xml()
return True
def riz_import(self):
if not os.path.exists(self.conf.exportFile):
om.MGlobal.displayError(f"无法找到导出文件: {self.conf.exportFile}") # 错误提示汉化
self.btn_import.setEnabled(False)
if self.cbx_use_link.isChecked():
if self.link:
try:
self.link.Save({"File.Path": self.conf.exportFile})
except RuntimeError as RE:
cmds.error('无法通过Python链接向Rizom发送保存命令。导出时是否启用了链接?') # 错误提示汉化
# FBX导入
namespace = ':RIZOMUV'
if not cmds.namespace(ex=namespace):
cmds.namespace(add=namespace)
cmds.namespace(set=namespace)
mel.eval('FBXImportMode -v add;')
mel.eval(f'FBXImport -f "{self.conf.exportFile}";')
# 结束FBX导入
imported_objects = cmds.ls('RIZOMUV:*', long=True, type="transform")
original_matches = []
for riz_obj in imported_objects:
print("导入的对象名称", riz_obj) # 日志汉化
original = riz_obj.replace('RIZOMUV:', '')
original_matches.append(original)
# 列出每个项目的UV集,无UV集则跳过(排除组节点等对象)
original_uvsets = cmds.polyUVSet(original, allUVSets=True, q=True)
imported_uvsets = cmds.polyUVSet(riz_obj, allUVSets=True, q=True)
if original_uvsets:
# 检查名称 #
if self.cbx_fix_set_names.isChecked():
for i in range(len(original_uvsets)):
try:
if not original_uvsets[i] == imported_uvsets[i]:
cmds.polyUVSet(riz_obj, rename=True, uvSet=imported_uvsets[i], newUVSet=original_uvsets[i])
except IndexError:
# 可能因Maya奇怪的bug导致(polyUVSet命令列出的UV集数量与UV集编辑器中不同)
print("UV集索引错误。对象的UV集数量似乎不匹配") # 日志汉化
print("原始UV集:", original_uvsets, original)
print("导入的UV集:", imported_uvsets, riz_obj)
pass
try:
cmds.polyTransfer(original, ao=riz_obj, ch=False, uv=True)
except RuntimeError as rt:
print(f"<RizomUV桥接工具> 无法从{riz_obj}向{original}传输UV") # 日志汉化
if DEBUG: print(f"<RizomUV桥接工具> 从{riz_obj}向{original}传输UV") # 日志汉化
cmds.select(original_matches)
cmds.bakePartialHistory()
cmds.delete(':RIZOMUV:*')
cmds.namespace(rm=':RIZOMUV')
return
def browse_settings_location(self):
os.startfile(os.path.dirname(self.conf.config_file_path))
def ui_pack_update_labels(self):
print("值已更改") # 日志汉化
self.label_uvmap.setText(str(self.slider_pack_uvmap.value()))
def ui_update_uvchannels(self):
sel_obj = cmds.ls(sl=True, tr=True)
if not sel_obj:
self.combo_pack_uvset.clear()
return
uvsets = cmds.polyUVSet(sel_obj[0], allUVSets=True, q=True)
current_set = cmds.polyUVSet(sel_obj[0], cuv=True, q=True)
if uvsets:
print(uvsets)
self.combo_pack_uvset.clear()
self.combo_pack_uvset.addItems(uvsets)
self.combo_pack_uvset.setCurrentIndex(uvsets.index(current_set[0]))
def cleanse_namespaces(self):
""" 第一步 --
尝试删除所有现有RIZOMUV命名空间直到失败,这应该能清除像
RIZOMUV:RIZOMUV:RIZOMUV这样的嵌套实例
"""
fail = False
while(not fail):
try:
cmds.namespace(rm=':RIZOMUV', mnr=True)
except:
fail = True
""" 第二步 --
在某些场景中,命名空间可能被重命名为RIZOMUV1、RIZOMUV2、RIZOMUV3等
因此需要查找并删除这些残留
"""
""" 第二步(更新)--
尝试删除除RIZOM之外的其他命名空间,因为存在其他命名空间可能导致导入失败
"""
c = cmds.listRelatives(ad=True)
if not c:
return
for obj in c:
ns_split = obj.split(':')
if len(ns_split)>1:
result = cmds.confirmDialog(
m="选中的对象已分配命名空间。\n" # 对话框文本汉化
"使用工具前必须移除这些命名空间\n"
"是否为您删除?(包括未选中的对象)\n\n"
"对象上的命名空间:\n%s" % ns_split[0],
button=['移除', '取消'] # 按钮文本汉化
)
if result == '移除':
cmds.namespace(rm=ns_split[0], mnr=True)
else:
return
return
def write_to_lua_file(self, command):
with open(self.conf.rizom_script_path, 'w') as f:
f.write(command)
print("<RIZOM> 已将命令写入lua文件:", self.conf.rizom_script_path) # 日志汉化
for line in command.split("\n"):
print("\t", line)
return
def set_config(self):
self.conf.loaduvs = self.cbx_keepuv.isChecked()
if self.radioAxisY.isChecked():
self.conf.upaxis = "Y"
else:
self.conf.upaxis = "Z"
self.conf.mutations = self.dspin_pack_mutations.value()
self.conf.resolution = self.dspin_pack_resolution.value()
self.conf.margin = self.dspin_pack_margin.value()
self.conf.spacing = self.dspin_pack_spacing.value()
self.conf.autofit = self.cbx_layout_scaling.isChecked()
self.conf.fixuvnames = self.cbx_fix_set_names.isChecked()
self.conf.useuvlink = self.cbx_use_link.isChecked()
self.config_updated = True
self.conf.save_xml()
def closeEvent(self, event):
self.conf.save_xml()
def GetMayaWidget():
"""
返回Maya主窗口部件的Python对象
""" # 注释汉化
main_window_ptr = omui.MQtUtil.mainWindow()
if sys.version_info.major >= 3:
return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
else:
return wrapInstance(long(main_window_ptr), QtWidgets.QWidget)
def fix_shell_border_normals():
obj_list = cmds.ls(sl=True, o=True)
all_final_borders = []
for sub_obj in obj_list:
cmds.select(sub_obj, r=True)
cmds.polyNormalPerVertex(ufn=True)
cmds.polySoftEdge(sub_obj, a=180, ch=1)
print("全部软化") # 日志汉化
# 选择对象UV
cmds.select(sub_obj + '.map[*]')
mel.eval('polySelectBorderShell 1;')
uv_border = cmds.polyListComponentConversion(te=True, internal=True)
uv_border = cmds.ls(uv_border, fl=True)
final_border = []
# 特殊过滤
for curEdge in uv_border:
edge_uvs = cmds.polyListComponentConversion(curEdge, tuv=True)
edge_uvs = cmds.ls(edge_uvs, fl=True)
if len(edge_uvs) > 2:
final_border.append(curEdge)
cmds.polySoftEdge(final_border, a=0, ch=1)
all_final_borders.append(final_border)
cmds.select(cl=True)
for sel_l in all_final_borders:
cmds.select(sel_l, add=True)
cmds.hilite(obj_list)
def run():
scriptJobs = cmds.scriptJob(listJobs=True)
for sj in scriptJobs:
if "RizomBridge" in sj:
print(PRNT_STRING, f"终止已存在的scriptJob: {sj}") # 日志汉化
cmds.scriptJob(kill=int(sj.split(':')[0]))
d = RizomUVBridgeWindow()
d.show(dockable=True)
if __name__ == "__main__":
run()