Updated
This commit is contained in:
869
Scripts/Modeling/UV/RizomBridge/RizomUVBridge.py
Normal file
869
Scripts/Modeling/UV/RizomBridge/RizomUVBridge.py
Normal file
@ -0,0 +1,869 @@
|
||||
import os, platform, subprocess, sys
|
||||
|
||||
if sys.version_info.minor < 11:
|
||||
# Maya 2024 and earlier
|
||||
from PySide2 import QtGui
|
||||
from PySide2 import QtWidgets, QtCore
|
||||
from shiboken2 import wrapInstance
|
||||
else:
|
||||
# Maya 2025
|
||||
from PySide6 import QtGui
|
||||
from PySide6 import QtWidgets, QtCore
|
||||
from shiboken6 import wrapInstance
|
||||
|
||||
if sys.version_info.major == 3:
|
||||
# Python 3
|
||||
from . import scriptlist
|
||||
elif sys.version_info.major == 2:
|
||||
# Python 2
|
||||
import scriptlist
|
||||
|
||||
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 xml.dom.minidom as xml # For XML settings
|
||||
|
||||
DEBUG = False
|
||||
PRNT_STRING = "<RizomUV Bridge>"
|
||||
|
||||
# om.MGlobal.displayError('ERROR!')
|
||||
|
||||
class Settings():
|
||||
def __init__(self):
|
||||
self.config_path = os.path.join('%s/RizomBridge' % os.getenv('APPDATA'))
|
||||
config_file = 'uisettings.xml'
|
||||
|
||||
# TODO: Create a unique id for the lua script tied to the Maya instance.
|
||||
# rizom_script_path = tempfile.gettempdir() + os.sep + "MayaRizomBridgeScript.lua"
|
||||
rizom_script_path = os.path.join(self.config_path, "MayaRizomBridgeScript.lua")
|
||||
self.rizom_script_path = rizom_script_path.replace('\\', '/')
|
||||
|
||||
self.apppath = None
|
||||
self.config_file_path = os.path.join(self.config_path, config_file)
|
||||
self.check_config_exists(self.config_path)
|
||||
|
||||
try:
|
||||
self.read_settings()
|
||||
except:
|
||||
print("<RizomUV Bridge> Failed to read settings, creating new xml file.")
|
||||
self.set_defaults()
|
||||
self.save_xml()
|
||||
self.read_settings()
|
||||
|
||||
self.reset_export_path()
|
||||
|
||||
def reset_export_path(self):
|
||||
self.exportFile = os.path.join(self.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"))
|
||||
self.usecustompath = self.str_to_bool(ui_app.getAttribute("usecustompath"))
|
||||
self.custompath = ui_app.getAttribute("custompath")
|
||||
|
||||
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.usecustompath = False
|
||||
self.custompath = ""
|
||||
|
||||
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]
|
||||
pass
|
||||
|
||||
def save_xml(self):
|
||||
print(PRNT_STRING, "Saving settings to disk: {}".format(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_application.setAttribute("usecustompath", str(self.usecustompath))
|
||||
element_application.setAttribute("custompath", str(self.custompath))
|
||||
|
||||
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, "Checking for config file:{}".format(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, "Config not found.")
|
||||
self.set_defaults()
|
||||
self.save_xml()
|
||||
else:
|
||||
print(PRNT_STRING, "Config found.")
|
||||
|
||||
def findall_rizom_installs(self):
|
||||
""" Returns list of all rizom.exe in registry that exist on disk """
|
||||
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, "Found RizomUV installs in Windows Registry:")
|
||||
for inst in sorted(installs):
|
||||
print(" ..", inst)
|
||||
return sorted(installs)
|
||||
|
||||
def manual_locate_rizom(self):
|
||||
om.MGlobal.displayWarning("Could not locate rizomuv.exe: %s" % self.apppath)
|
||||
fname = QtWidgets.QFileDialog.getOpenFileName(None, 'Locate rizomuv.exe', 'C:/Program Files/Rizom Lab/', "Executable (*.exe)")
|
||||
|
||||
self.apppath = fname[0]
|
||||
self.appver = self.get_version()
|
||||
self.save_xml()
|
||||
|
||||
def get_version(self):
|
||||
""" Return version number as list as read from the folder name, eg. [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, "Creating blank lua file at", self.rizom_script_path)
|
||||
f.write('')
|
||||
|
||||
def str_to_bool(self, s):
|
||||
if s == 'True':
|
||||
return True
|
||||
else:
|
||||
# Return False for everything else, otherwise it might return None which will cause a crash.
|
||||
return False
|
||||
|
||||
|
||||
class RizomUVBridgeWindow(MayaQWidgetDockableMixin, QtWidgets.QDialog):
|
||||
def __init__(self, rootWidget=None, *args, **kwargs):
|
||||
super(RizomUVBridgeWindow, self).__init__()
|
||||
|
||||
"""
|
||||
if not os.path.exists(config_path):
|
||||
reset_config()
|
||||
if DEBUG: print("<RizomUV Bridge>", config_path)
|
||||
Config.read(config_path)
|
||||
"""
|
||||
self.conf = Settings()
|
||||
self.conf.check_lua_path()
|
||||
|
||||
# self.rizomPath = self.conf.apppath
|
||||
print("Rizom Path:", self.conf.apppath)
|
||||
self.setWindowTitle("Bridge")
|
||||
|
||||
self.sj_num_selchange = cmds.scriptJob(parent=self.objectName(), event=['SelectionChanged', self.ui_update_uvchannels])
|
||||
|
||||
# self.setMinimumSize(200, 300)
|
||||
self.resize(250, 300)
|
||||
|
||||
self.create_widgets()
|
||||
self.create_layouts()
|
||||
self.create_connections()
|
||||
self.validate_export_path()
|
||||
self.exported_objects = []
|
||||
self.ui_update_uvchannels()
|
||||
self.cleanse_namespaces()
|
||||
|
||||
if not os.path.exists(self.conf.apppath):
|
||||
self.conf.manual_locate_rizom()
|
||||
v = self.conf.get_version()
|
||||
self.btn_run.setText('Run RizomUV {}.{}'.format(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
|
||||
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def create_widgets(self):
|
||||
v = ".".join(self.conf.get_version())
|
||||
self.btn_run = QtWidgets.QPushButton('Run RizomUV {}'.format(v), self)
|
||||
self.btn_export = QtWidgets.QPushButton('Export', self)
|
||||
self.btn_import = QtWidgets.QPushButton('Import', self)
|
||||
self.cbx_use_link = QtWidgets.QCheckBox('Attempt to use CRizomUVLink', self)
|
||||
self.cbx_use_link.setChecked(self.conf.useuvlink)
|
||||
self.cbx_fix_set_names = QtWidgets.QCheckBox('Fix UV set names on Import', self)
|
||||
self.cbx_fix_set_names.setChecked(self.conf.fixuvnames)
|
||||
self.cbx_custom_path = QtWidgets.QCheckBox('Use custom path for fbx', self)
|
||||
self.cbx_custom_path.setChecked(self.conf.usecustompath)
|
||||
self.field_custom_path = QtWidgets.QLineEdit("C:/")
|
||||
self.field_custom_path.setText(self.conf.custompath)
|
||||
|
||||
# Export settings widgets
|
||||
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('Load Existing UVs', self)
|
||||
self.cbx_keepuv.setChecked(self.conf.loaduvs)
|
||||
|
||||
self.combo_scripts = QtWidgets.QComboBox(self)
|
||||
self.combo_scripts.addItem("No Script")
|
||||
for s in scriptlist.scripts:
|
||||
self.combo_scripts.addItem(s[1])
|
||||
|
||||
# Utilities widgets
|
||||
self.btn_fix_shell_normals = QtWidgets.QPushButton('Set UV border edges to Hard')
|
||||
self.btn_edit_settings = QtWidgets.QPushButton('Open settings folder')
|
||||
|
||||
self.combo_pack_uvset = QtWidgets.QComboBox(self)
|
||||
#
|
||||
self.btn_pack = QtWidgets.QPushButton('Export and Pack UVs', self)
|
||||
self.combo_pack_quality = QtWidgets.QComboBox(self)
|
||||
self.combo_pack_quality.addItems(['Low', 'Normal', 'High', 'Higher', 'Ultra'])
|
||||
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("Use 'Avarage' Initial Scale", self)
|
||||
self.cbx_initial_scale_avg.setChecked(self.conf.initscaleavg)
|
||||
#
|
||||
self.cbx_layout_scaling = QtWidgets.QCheckBox("Layout: Auto Fit", self)
|
||||
self.cbx_layout_scaling.setChecked(self.conf.autofit)
|
||||
|
||||
pass
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
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)
|
||||
#hor_layout = QtWidgets.QHBoxLayout()
|
||||
grp_layout.addWidget(self.cbx_custom_path)
|
||||
grp_layout.addWidget(self.field_custom_path)
|
||||
#grp_layout.addLayout(hor_layout)
|
||||
|
||||
# grp_layout.addWidget(self.btn_edit)
|
||||
grp = QtWidgets.QGroupBox("UV Operations")
|
||||
grp.setLayout(grp_layout)
|
||||
main_layout.addWidget(grp)
|
||||
|
||||
# Export Settings
|
||||
grp_layout = QtWidgets.QVBoxLayout()
|
||||
grp_layout.addWidget(self.cbx_keepuv)
|
||||
|
||||
hor_layout = QtWidgets.QHBoxLayout()
|
||||
hor_layout.addWidget(QtWidgets.QLabel("Scripts"))
|
||||
hor_layout.addWidget(self.combo_scripts)
|
||||
grp_layout.addLayout(hor_layout)
|
||||
|
||||
hor_layout = QtWidgets.QHBoxLayout()
|
||||
hor_layout.addWidget(QtWidgets.QLabel("Up Axis"))
|
||||
hor_layout.addWidget(self.radioAxisY)
|
||||
hor_layout.addWidget(self.radioAxisZ)
|
||||
grp_layout.addLayout(hor_layout)
|
||||
|
||||
grp = QtWidgets.QGroupBox("Export Settings")
|
||||
grp.setLayout(grp_layout)
|
||||
main_layout.addWidget(grp)
|
||||
|
||||
# Utilites
|
||||
grp_layout = QtWidgets.QVBoxLayout()
|
||||
grp_layout.addWidget(self.btn_fix_shell_normals)
|
||||
grp_layout.addWidget(self.btn_edit_settings)
|
||||
# hor_layout = QtWidgets.QHBoxLayout()
|
||||
# hor_layout.addWidget(self.btn_uvl_edges)
|
||||
# grp_layout.addLayout(hor_layout)
|
||||
|
||||
grp = QtWidgets.QGroupBox("Utilities")
|
||||
grp.setLayout(grp_layout)
|
||||
main_layout.addWidget(grp)
|
||||
|
||||
# UV Packer
|
||||
grp_layout = QtWidgets.QVBoxLayout()
|
||||
grp = QtWidgets.QGroupBox("UV Packer")
|
||||
|
||||
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 Set to pack"))
|
||||
hor_layout.addWidget(self.combo_pack_uvset)
|
||||
grp_layout.addLayout(hor_layout)
|
||||
|
||||
hor_layout = QtWidgets.QHBoxLayout()
|
||||
hor_layout.addWidget(QtWidgets.QLabel("Packing Quality"))
|
||||
hor_layout.addWidget(self.combo_pack_quality)
|
||||
grp_layout.addLayout(hor_layout)
|
||||
|
||||
hor_layout = QtWidgets.QHBoxLayout()
|
||||
hor_layout.addWidget(QtWidgets.QLabel("Mutations"))
|
||||
hor_layout.addWidget(self.dspin_pack_mutations)
|
||||
hor_layout.addWidget(QtWidgets.QLabel("Resolution"))
|
||||
hor_layout.addWidget(self.dspin_pack_resolution)
|
||||
grp_layout.addLayout(hor_layout)
|
||||
|
||||
hor_layout = QtWidgets.QHBoxLayout()
|
||||
hor_layout.addWidget(QtWidgets.QLabel("Margin"))
|
||||
hor_layout.addWidget(self.dspin_pack_margin)
|
||||
hor_layout.addWidget(QtWidgets.QLabel("Spacing"))
|
||||
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)
|
||||
pass
|
||||
|
||||
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_custom_path.stateChanged.connect(self.validate_export_path)
|
||||
self.field_custom_path.textEdited.connect(self.validate_export_path)
|
||||
|
||||
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.combo_scripts.currentIndexChanged.connect(self.ui_toggle_roundtrip_option)
|
||||
|
||||
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):
|
||||
# Confirm application path
|
||||
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("{} RizomUV {} link established. Now listening to commands on TCP port: {}".format(PRNT_STRING,self.link.RizomUVVersion(),self.port))
|
||||
|
||||
# Enable UI relevant only for RizomUV Link
|
||||
# self.btn_uvl_edges.setEnabled(True)
|
||||
# self.btn_uvl_edges.setStyleSheet("background-color: {}; color: white".format("#ef4000"))
|
||||
else:
|
||||
# Use original method if link is not working.
|
||||
print(PRNT_STRING, "RizomUV link not available. Communicating using LUA script file.")
|
||||
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):
|
||||
# Export model with no script loaded,
|
||||
# Also tell Rizom not to load the model in the riz_export function
|
||||
# because it sometimes does not have time to load before we overwrite the lua file with these commands.
|
||||
current_uv = self.combo_pack_uvset.currentText()
|
||||
# current_uv_index = self.combo_pack_uvset.currentIndex()
|
||||
|
||||
exported = self.riz_export(False, False)
|
||||
if not exported:
|
||||
return
|
||||
|
||||
# Construct LUA code from GUI options
|
||||
cmd = ''
|
||||
# Common repeated text chunk
|
||||
cmd_properties_prefix = 'ZomIslandGroups({Mode="SetGroupsProperties", WorkingSet="Visible", MergingPolicyString="A_ADD|AIB_ADD_A_VALUE_B|B_CLONE", GroupPaths={ "RootGroup" }, '
|
||||
|
||||
# Load model
|
||||
cmd += 'ZomLoad({File={Path="'+self.conf.exportFile+'", ImportGroups=true, XYZUVW=true, UVWProps=true}})\n'
|
||||
|
||||
# cmd += 'ZomUvset({Mode="SetCurrent", Name="Channel%s"})\n' % str(self.label_uvmap.text())
|
||||
cmd += 'ZomUvset({Mode="SetCurrent", Name="%s"})\n' % current_uv
|
||||
# cmd += 'ZomSet({Path = "Vars.Viewport.ViewportUV.WorkingSet", Value = "Visible&Flat"})\n'
|
||||
|
||||
# cmd += 'ZomSet({Path="Prefs.PackOptions.MapResolution", Value=%i})\n' % self.dspin_pack_resolution.value()
|
||||
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 Export
|
||||
if not cmds.pluginInfo("fbxmaya", loaded=True, query=True):
|
||||
cmds.loadPlugin("fbxmaya")
|
||||
|
||||
cmds.undoInfo(openChunk=True)
|
||||
mel.eval('ConvertInstanceToObject;') # This is the only command that is important to undo.
|
||||
mel.eval('FBXExportSmoothingGroups -v true;')
|
||||
mel.eval('FBXExportTriangulate -v false;')
|
||||
mel.eval('FBXExportSmoothMesh -v false;')
|
||||
mel.eval('FBXExportUpAxis {};'.format(self.conf.upaxis))
|
||||
print('FBXExport -s -f "{}";'.format(self.conf.exportFile))
|
||||
mel.eval('FBXExport -s -f "{}";'.format(self.conf.exportFile))
|
||||
try:
|
||||
cmds.undo()
|
||||
except RuntimeError as RE:
|
||||
# There are no more commands to undo
|
||||
pass
|
||||
|
||||
# End FBX Export
|
||||
return
|
||||
|
||||
def riz_export(self, use_script=True, load_model=True):
|
||||
# displaySmoothness -divisionsU 0
|
||||
self.exported_objects = cmds.ls(selection=True, tr=True)
|
||||
|
||||
# Exit preview smooth, because the FBXExportSmoothMesh option is not always respected by the exporter.
|
||||
cmds.displaySmoothness(du=0, dv=0, pw=4, ps=0, po=1)
|
||||
|
||||
if self.cbx_custom_path.isChecked() and not self.field_custom_path.text() == self.conf.custompath:
|
||||
self.set_config()
|
||||
|
||||
if self.link and self.cbx_use_link.isChecked():
|
||||
print("Rizom Link version", self.link.Version())
|
||||
|
||||
# FBX Export
|
||||
self.fbx_export()
|
||||
if not self.port:
|
||||
self.riz_run()
|
||||
|
||||
params = {
|
||||
"File.Path": self.conf.exportFile,
|
||||
"File.XYZUVW": True, # 3D + UV data loaded (use File.XYZ instead to load 3D data only)
|
||||
"File.UVWProps": True, # UVs properties such as pinning, texel density settings etc... will be loaded
|
||||
"File.ImportGroups": True, # Island group hierarchy will be loaded
|
||||
"__Focus": True, # Focus viewports on the loaded mesh
|
||||
# "Data.UseImportedUVWPolygons": False,
|
||||
}
|
||||
|
||||
self.link.Load(params)
|
||||
|
||||
if DEBUG: print("{} EXPORTED TO:".format(PRNT_STRING), self.conf.exportFile)
|
||||
cmds.select(self.exported_objects)
|
||||
|
||||
# Enable import button if it was disabled.
|
||||
self.btn_import.setEnabled(True)
|
||||
else:
|
||||
# Classic Method #
|
||||
print("{} Exporting model".format(PRNT_STRING))
|
||||
self.cleanse_namespaces() # Agressive cleanse
|
||||
if not self.exported_objects:
|
||||
return False
|
||||
|
||||
# Delete history in case their are empty groups that would be removed by this action.
|
||||
# Otherwise they would be cleared during import and we have an object count missmatch.
|
||||
cmds.bakePartialHistory()
|
||||
cmd = ''
|
||||
|
||||
# Export Options
|
||||
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'
|
||||
|
||||
# Add scripts
|
||||
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("{} {}".format(PRNT_STRING, cmd))
|
||||
|
||||
# FBX Export
|
||||
self.fbx_export()
|
||||
|
||||
if DEBUG: print("{} EXPORTED TO: {}".format(PRNT_STRING, self.conf.exportFile))
|
||||
cmds.select(self.exported_objects)
|
||||
|
||||
self.write_to_lua_file(cmd)
|
||||
self.btn_import.setEnabled(True)
|
||||
return True
|
||||
|
||||
def riz_import(self):
|
||||
if not os.path.exists(self.conf.exportFile):
|
||||
om.MGlobal.displayError("Could not locate exported file: %s" % 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('Could not send Save command to Rizom over Python Link. Was it enabled when you exported?')
|
||||
|
||||
# FBX Import
|
||||
namespace = ':RIZOMUV'
|
||||
if not cmds.namespace(ex=namespace):
|
||||
cmds.namespace(add=namespace)
|
||||
cmds.namespace(set=namespace)
|
||||
|
||||
mel.eval('FBXImportMode -v add;')
|
||||
mel.eval('FBXImport -f "{}";'.format(self.conf.exportFile))
|
||||
# END FBX Import
|
||||
|
||||
imported_objects = cmds.ls('RIZOMUV:*', long=True, type="transform")
|
||||
original_matches = []
|
||||
for riz_obj in imported_objects:
|
||||
print ("Imported object name", riz_obj)
|
||||
original = riz_obj.replace('RIZOMUV:', '')
|
||||
original_matches.append(original)
|
||||
|
||||
# List UV sets of each item, skip if it has none. This is simply to exclude objects like group nodes.
|
||||
original_uvsets = cmds.polyUVSet(original, allUVSets=True, q=True)
|
||||
if not original_uvsets:
|
||||
print("Object {} has no UVSets. Will not attempt UV Transfer".format(riz_obj))
|
||||
continue
|
||||
|
||||
imported_uvsets = cmds.polyUVSet(riz_obj, allUVSets=True, q=True)
|
||||
|
||||
if original_uvsets:
|
||||
# Check names #
|
||||
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:
|
||||
# This can happen due to a weird Maya bug that I don't understand where the object has more
|
||||
# uvSets listed with the polyUVSet comman, than it has in the UV Set Editor
|
||||
print ("UV Set Index Error. Objects don't seem to have the same amount of UV sets")
|
||||
print("Original UV Sets:", original_uvsets, original)
|
||||
print("Imported UV Sets:", imported_uvsets, riz_obj)
|
||||
pass
|
||||
try:
|
||||
#cmds.polyTransfer(original, ao=riz_obj, ch=False, uv=True)
|
||||
cmds.polyTransfer(original, ao=riz_obj, ch=False, vc=False, v=False, uv=True)
|
||||
except RuntimeError as rt:
|
||||
print ("<RizomUV Bridge> Could not transfer UVs from", riz_obj, "to", original)
|
||||
if DEBUG: print ("<RizomUV Bridge> Transfering UVs from", riz_obj, "to", original)
|
||||
|
||||
cmds.select(original_matches)
|
||||
cmds.bakePartialHistory()
|
||||
cmds.delete(':RIZOMUV:*')
|
||||
cmds.namespace(rm=':RIZOMUV')
|
||||
|
||||
return
|
||||
|
||||
def riz_link(self):
|
||||
"""
|
||||
Establishes link with RizomUV. Even though this is just a single line, I want it in a function to refresh UI.
|
||||
"""
|
||||
pass
|
||||
|
||||
def browse_settings_location(self):
|
||||
os.startfile(os.path.dirname(self.conf.config_file_path))
|
||||
|
||||
def ui_pack_update_labels(self):
|
||||
print("Value changed")
|
||||
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 validate_export_path(self):
|
||||
if self.cbx_custom_path.isChecked():
|
||||
if not os.path.exists(self.field_custom_path.text()):
|
||||
self.btn_import.setEnabled(False)
|
||||
self.btn_export.setEnabled(False)
|
||||
self.field_custom_path.setStyleSheet("color: red")
|
||||
return
|
||||
else:
|
||||
self.conf.exportFile = os.path.join(self.conf.custompath, self.conf.objname)
|
||||
self.btn_import.setEnabled(True)
|
||||
self.btn_export.setEnabled(True)
|
||||
self.field_custom_path.setStyleSheet("color: white")
|
||||
else:
|
||||
self.conf.reset_export_path()
|
||||
self.field_custom_path.setStyleSheet("color: grey")
|
||||
self.btn_import.setEnabled(True)
|
||||
self.btn_export.setEnabled(True)
|
||||
|
||||
print(self.conf.exportFile)
|
||||
|
||||
def cleanse_namespaces(self):
|
||||
""" Step One --
|
||||
Try to delete all existing RIZOMUV namespaces until it fails, this should get rid of stacked instances like
|
||||
RIZOMUV:RIZOMUV:RIZOMUV
|
||||
"""
|
||||
fail = False
|
||||
while(not fail):
|
||||
try:
|
||||
cmds.namespace(rm=':RIZOMUV', mnr=True)
|
||||
except:
|
||||
fail = True
|
||||
|
||||
""" Step Two --
|
||||
In one scene I got the namespaces had gotten renamed to RIZOMUV1, RIZOMUV2, RIZOMUV3 etc
|
||||
so this finds any leftoverrs and deletes them.
|
||||
"""
|
||||
# existing_ns = cmds.namespaceInfo(listNamespace=True, listOnlyNamespaces=True)
|
||||
# for entry in existing_ns:
|
||||
# if 'RIZOMUV' in entry:
|
||||
# cmds.namespace(rm=entry, mnr=True)
|
||||
#
|
||||
# return
|
||||
|
||||
""" Step Two, updated --
|
||||
Let's try to remove other namespaces than simply RIZOM as having others can prevent the import from working
|
||||
"""
|
||||
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="Selected objects have namespaces assigned. \n"
|
||||
"These must go before you can use the tool correctly\n"
|
||||
"Should I delete them for you? (Including on unselected objects)\n\n"
|
||||
"Namespace on object: \n%s" % ns_split[0], button=['Remove', 'Cancel'])
|
||||
if result == 'Remove':
|
||||
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> Wrote command to lua file:", 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.conf.usecustompath = self.cbx_custom_path.isChecked()
|
||||
self.conf.custompath = self.field_custom_path.text()
|
||||
|
||||
self.conf.save_xml()
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.conf.save_xml()
|
||||
|
||||
|
||||
def GetMayaWidget():
|
||||
"""
|
||||
Return the Maya main window widget as a Python object
|
||||
"""
|
||||
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("Soften all")
|
||||
|
||||
# Select object UVs
|
||||
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 = []
|
||||
|
||||
# Magical filter
|
||||
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, "Killing preexisting scriptJob:", sj)
|
||||
cmds.scriptJob(kill=int(sj.split(':')[0]))
|
||||
|
||||
d = RizomUVBridgeWindow()
|
||||
d.show(dockable=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
4
Scripts/Modeling/UV/RizomBridge/__init__.py
Normal file
4
Scripts/Modeling/UV/RizomBridge/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import *
|
4
Scripts/Modeling/UV/RizomBridge/lua_scripts/__init__.py
Normal file
4
Scripts/Modeling/UV/RizomBridge/lua_scripts/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import *
|
1
Scripts/Modeling/UV/RizomBridge/lua_scripts/blank.lua
Normal file
1
Scripts/Modeling/UV/RizomBridge/lua_scripts/blank.lua
Normal file
@ -0,0 +1 @@
|
||||
ZomSet({Path="Prefs.FileSuffix", Value=""})
|
@ -0,0 +1,6 @@
|
||||
ZomSelect({PrimType="Edge", WorkingSet="Visible", Select=true, ResetBefore=true, ProtectMapName="Protect", FilterIslandVisible=true, Auto={Box={ActiveEdges={ "XPYP", "XPZP", "XPYM", "XPZM", "YPZP", "YMZP", "YMZM", "YPZM", "XMYP", "XMZP", "XMYM", "XMZM" }}, HandleCutter=true, StoreCoordsUVW=true, FlatteningMode=0, FlatteningUnfoldParams={Iterations=1, BorderIntersections=true, TriangleFlips=true}}})
|
||||
ZomCut({PrimType="Edge", WorkingSet="Visible"})
|
||||
ZomLoad({Data={CoordsUVWInternalPath="Mesh.Tmp.AutoSelect.UVW"}})
|
||||
ZomIslandGroups({Mode="DistributeInTilesByBBox", WorkingSet="Visible", MergingPolicy=8322})
|
||||
ZomIslandGroups({Mode="DistributeInTilesEvenly", WorkingSet="Visible", MergingPolicy=8322, UseTileLocks=true, UseIslandLocks=true})
|
||||
ZomPack({ProcessTileSelection=false, RecursionDepth=1, RootGroup="RootGroup", WorkingSet="Visible", Scaling={Mode=0}, Rotate={}, Translate=true, LayoutScalingMode=2})
|
@ -0,0 +1,10 @@
|
||||
var = ZomGet("Vars.AutoSelect.Mosaic.Developability")
|
||||
|
||||
ZomSet({Path="Prefs.FileSuffix", Value=""})
|
||||
ZomSelect({PrimType="Edge", WorkingSet="Visible", Select=true, ResetBefore=true, ProtectMapName="Protect", FilterIslandVisible=true, Auto={QuasiDevelopable={Developability=var, IslandPolyNBMin=1, FitCones=false, Straighten=true}, HandleCutter=true, StoreCoordsUVW=true, FlatteningMode=0, FlatteningUnfoldParams={Iterations=1, BorderIntersections=true, TriangleFlips=true}}})
|
||||
ZomCut({PrimType="Edge", WorkingSet="Visible"})
|
||||
ZomLoad({Data={CoordsUVWInternalPath="Mesh.Tmp.AutoSelect.UVW"}})
|
||||
ZomIslandGroups({Mode="DistributeInTilesByBBox", WorkingSet="Visible", MergingPolicy=8322})
|
||||
ZomIslandGroups({Mode="DistributeInTilesEvenly", WorkingSet="Visible", MergingPolicy=8322, UseTileLocks=true, UseIslandLocks=true})
|
||||
ZomPack({ProcessTileSelection=false, RecursionDepth=1, RootGroup="RootGroup", WorkingSet="Visible", Scaling={Mode=2}, Rotate={}, Translate=true, LayoutScalingMode=2})
|
||||
|
@ -0,0 +1,11 @@
|
||||
var = ZomGet("Vars.AutoSelect.SharpEdges.Angle")
|
||||
|
||||
ZomSet({Path="Prefs.FileSuffix", Value=""})
|
||||
ZomSet({Path="Vars.EditMode.ElementMode", Value=1})
|
||||
ZomSelect({PrimType="Edge", WorkingSet="Visible", Select=true, All=true})
|
||||
ZomSelect({PrimType="Edge", WorkingSet="Visible", Select=true, ResetBefore=true, ProtectMapName="Protect", FilterIslandVisible=true, Auto={SharpEdges={AngleMin=var}, PipesCutter=false, HandleCutter=true, StoreCoordsUVW=true, FlatteningMode=0, FlatteningUnfoldParams={Iterations=1, BorderIntersections=true, TriangleFlips=true}}})
|
||||
ZomCut({PrimType="Edge", WorkingSet="Visible"})
|
||||
ZomLoad({Data={CoordsUVWInternalPath="Mesh.Tmp.AutoSelect.UVW"}})
|
||||
ZomIslandGroups({Mode="DistributeInTilesByBBox", WorkingSet="Visible", MergingPolicy=8322})
|
||||
ZomIslandGroups({Mode="DistributeInTilesEvenly", WorkingSet="Visible", MergingPolicy=8322, UseTileLocks=true, UseIslandLocks=true})
|
||||
ZomPack({ProcessTileSelection=false, RecursionDepth=1, RootGroup="RootGroup", WorkingSet="Visible", Scaling={Mode=2}, Rotate={}, Translate=true, LayoutScalingMode=2})
|
BIN
Scripts/Modeling/UV/RizomBridge/rizom_icon.png
Normal file
BIN
Scripts/Modeling/UV/RizomBridge/rizom_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 851 B |
2
Scripts/Modeling/UV/RizomBridge/scriptlist.py
Normal file
2
Scripts/Modeling/UV/RizomBridge/scriptlist.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Edit this list when adding new scripts to the lua_script folder
|
||||
scripts = [("box_algorithm.lua", "Box"), ("mosaic_algorithm.lua", "Mosaic"), ("pelt_algorithm.lua", "Pelt"), ("sharp_edge_algorithm.lua", "Sharp Edge")]
|
4
Scripts/Modeling/UV/UVDeluxe/__init__.py
Normal file
4
Scripts/Modeling/UV/UVDeluxe/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import *
|
53
Scripts/Modeling/UV/UVDeluxe/config.uvd
Normal file
53
Scripts/Modeling/UV/UVDeluxe/config.uvd
Normal file
@ -0,0 +1,53 @@
|
||||
(iModeling.UV.UVDeluxe.uistates
|
||||
UiStates
|
||||
p0
|
||||
(dp1
|
||||
S'collapseFrame2'
|
||||
p2
|
||||
I01
|
||||
sS'collapseFrame3'
|
||||
p3
|
||||
I01
|
||||
sS'collapseFrame0'
|
||||
p4
|
||||
I00
|
||||
sS'collapseFrame1'
|
||||
p5
|
||||
I01
|
||||
sS'collapseFrame6'
|
||||
p6
|
||||
I00
|
||||
sS'collapseFrame7'
|
||||
p7
|
||||
I01
|
||||
sS'collapseFrame4'
|
||||
p8
|
||||
I00
|
||||
sS'collapseFrame5'
|
||||
p9
|
||||
I00
|
||||
sS'collapseFrame8'
|
||||
p10
|
||||
I00
|
||||
sS'matchDist'
|
||||
p11
|
||||
F0.05
|
||||
sS'version'
|
||||
p12
|
||||
I120
|
||||
sS'retainCS'
|
||||
p13
|
||||
I01
|
||||
sS'snapPath'
|
||||
p14
|
||||
VC:/Users/zhoushuhua/Documents/maya/projects/default/
|
||||
p15
|
||||
sS'detectTextureSize'
|
||||
p16
|
||||
I01
|
||||
sS'widthHeight'
|
||||
p17
|
||||
(lp18
|
||||
L1150L
|
||||
aL700L
|
||||
asb.
|
89
Scripts/Modeling/UV/UVDeluxe/uistates.py
Normal file
89
Scripts/Modeling/UV/UVDeluxe/uistates.py
Normal file
@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pickle
|
||||
import os
|
||||
import maya.cmds as mc
|
||||
|
||||
version = 120
|
||||
|
||||
class UiStates():
|
||||
file = 'config.uvd'
|
||||
filepath = os.path.join(os.path.dirname(__file__), file)
|
||||
|
||||
def __init__(self):
|
||||
self.version = version
|
||||
#Window
|
||||
self.widthHeight = (1150,700)
|
||||
self.collapseFrame0 = False
|
||||
self.collapseFrame1 = True
|
||||
self.collapseFrame2 = True
|
||||
self.collapseFrame3 = True
|
||||
self.collapseFrame4 = True
|
||||
self.collapseFrame5 = True
|
||||
self.collapseFrame6 = True
|
||||
self.collapseFrame7 = True
|
||||
self.collapseFrame8 = True
|
||||
|
||||
#Settings
|
||||
#self.textureSize = (5,5)
|
||||
#self.forgetTextureSize = False
|
||||
self.detectTextureSize = True
|
||||
self.retainCS = mc.texMoveContext('texMoveContext',q=True,scr=True)
|
||||
self.matchDist = 0.05
|
||||
|
||||
#Quicksnap
|
||||
self.snapPath = mc.workspace(q=True,rd=True)
|
||||
|
||||
@staticmethod
|
||||
def pickleDump(uis):
|
||||
with open(UiStates.filepath, 'wb') as datafile:
|
||||
pickle.dump(uis, datafile)
|
||||
|
||||
@staticmethod
|
||||
def pickleLoad():
|
||||
if os.path.exists(UiStates.filepath):
|
||||
print("%s found, loading settings." % UiStates.file)
|
||||
try:
|
||||
with open(UiStates.filepath, 'rb') as datafile:
|
||||
uis = pickle.load(datafile)
|
||||
except EOFError:
|
||||
print("Warning: The file is empty or corrupted.")
|
||||
os.remove(UiStates.filepath)
|
||||
return UiStates()
|
||||
except Exception as e:
|
||||
print("Error loading settings: {}".format(e))
|
||||
os.remove(UiStates.filepath)
|
||||
return UiStates()
|
||||
try:
|
||||
pickledVer = uis.version
|
||||
if pickledVer < version:
|
||||
os.remove(UiStates.filepath)
|
||||
return UiStates()
|
||||
except:
|
||||
os.remove(UiStates.filepath)
|
||||
return UiStates()
|
||||
return uis
|
||||
else:
|
||||
return UiStates()
|
||||
|
||||
def setUiState(self):
|
||||
#Window
|
||||
self.widthHeight = mc.window('UVDeluxe',query=True,wh=True)
|
||||
self.collapseFrame0 = mc.frameLayout('layout_Settings', query=True, cl=True)
|
||||
self.collapseFrame1 = mc.frameLayout('layout_Mover', query=True, cl=True)
|
||||
self.collapseFrame2 = mc.frameLayout('layout_Scaler', query=True, cl=True)
|
||||
self.collapseFrame3 = mc.frameLayout('layout_Ratio', query=True, cl=True)
|
||||
self.collapseFrame4 = mc.frameLayout('layout_Straighten', query=True, cl=True)
|
||||
self.collapseFrame5 = mc.frameLayout('layout_Align', query=True, cl=True)
|
||||
self.collapseFrame6 = mc.frameLayout('layout_QuickSnap', query=True, cl=True)
|
||||
self.collapseFrame7 = mc.frameLayout('layout_MatchUV', query=True, cl=True)
|
||||
self.collapseFrame8 = mc.frameLayout('layout_SelectionSets',query=True, cl=True)
|
||||
|
||||
self.detectTextureSize = mc.checkBox ('DTR', query=True, v=True)
|
||||
|
||||
self.retainCS = mc.texMoveContext('texMoveContext',q=True,scr=True)
|
||||
#Qucksnap
|
||||
self.snapPath = mc.textField("pathField",query=True,text=True)
|
||||
''' Dump '''
|
||||
UiStates.pickleDump(self)
|
1400
Scripts/Modeling/UV/UVDeluxe/uvdeluxe.py
Normal file
1400
Scripts/Modeling/UV/UVDeluxe/uvdeluxe.py
Normal file
File diff suppressed because it is too large
Load Diff
215
Scripts/Modeling/UV/UVSetEditor.py
Normal file
215
Scripts/Modeling/UV/UVSetEditor.py
Normal file
@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import maya.cmds as cmds
|
||||
|
||||
# Define minimum window width and height
|
||||
MIN_WINDOW_WIDTH = 300 # Increased from 200 to 300
|
||||
MIN_WINDOW_HEIGHT = 300
|
||||
|
||||
# Function: Get and display UV sets
|
||||
def refresh_uv_sets():
|
||||
selection = cmds.ls(selection=True)
|
||||
if not selection:
|
||||
cmds.warning("Please select an object first.")
|
||||
return
|
||||
|
||||
selected_object = selection[0]
|
||||
uv_sets = cmds.polyUVSet(selected_object, query=True, allUVSets=True)
|
||||
cmds.textScrollList('uvList', edit=True, removeAll=True)
|
||||
for uv_set in uv_sets:
|
||||
cmds.textScrollList('uvList', edit=True, append=uv_set)
|
||||
|
||||
# Function: Switch UV set
|
||||
def switch_uv_set(*args):
|
||||
selected_uv_set = cmds.textScrollList('uvList', query=True, selectItem=True)
|
||||
if selected_uv_set:
|
||||
selected_object = cmds.ls(selection=True)
|
||||
if selected_object:
|
||||
selected_object = selected_object[0]
|
||||
# Switch current UV set
|
||||
cmds.polyUVSet(selected_object, currentUVSet=True, uvSet=selected_uv_set[0])
|
||||
print("Switched to UV set: {}".format(selected_uv_set[0]))
|
||||
else:
|
||||
cmds.warning("Please select an object.")
|
||||
else:
|
||||
cmds.warning("Please select a UV set.")
|
||||
|
||||
# Function: Delete selected UV set
|
||||
def delete_selected_uv_set():
|
||||
selected_uv_set = cmds.textScrollList('uvList', query=True, selectItem=True)
|
||||
if selected_uv_set:
|
||||
cmds.polyUVSet(delete=True, uvSet=selected_uv_set[0])
|
||||
refresh_uv_sets()
|
||||
|
||||
# Function: Rename selected UV set
|
||||
def rename_selected_uv_set(new_name):
|
||||
selected_uv_set = cmds.textScrollList('uvList', query=True, selectItem=True)
|
||||
if selected_uv_set:
|
||||
if new_name:
|
||||
cmds.polyUVSet(rename=True, newUVSet=new_name, uvSet=selected_uv_set[0])
|
||||
refresh_uv_sets()
|
||||
cmds.textFieldGrp('newNameField', edit=True, text='') # Clear input field content
|
||||
else:
|
||||
cmds.warning("Please enter a new name.")
|
||||
else:
|
||||
cmds.warning("Please select a UV set first.")
|
||||
|
||||
# Function: Create new UV set
|
||||
def create_new_uv_set(new_name):
|
||||
if new_name:
|
||||
cmds.polyUVSet(create=True, uvSet=new_name)
|
||||
refresh_uv_sets()
|
||||
cmds.textFieldGrp('newNameField', edit=True, text='') # Clear input field content
|
||||
else:
|
||||
cmds.warning("Please enter a name for the new UV set.")
|
||||
|
||||
# Function: Set UV set 1 name
|
||||
def set_uv_set1_name(*args):
|
||||
selected_uv_set = cmds.textScrollList('uvList', query=True, selectItem=True)
|
||||
if selected_uv_set:
|
||||
cmds.textFieldGrp("uvSet1", edit=True, text=selected_uv_set[0])
|
||||
else:
|
||||
cmds.warning("Please select a UV set first.")
|
||||
|
||||
# Function: Set UV set 2 name
|
||||
def set_uv_set2_name(*args):
|
||||
selected_uv_set = cmds.textScrollList('uvList', query=True, selectItem=True)
|
||||
if selected_uv_set:
|
||||
cmds.textFieldGrp("uvSet2", edit=True, text=selected_uv_set[0])
|
||||
else:
|
||||
cmds.warning("Please select a UV set first.")
|
||||
|
||||
# Function: UV set swap
|
||||
def UVsetSwap(*args):
|
||||
UVname1 = cmds.textFieldGrp("uvSet1", query=True, text=True)
|
||||
UVname2 = cmds.textFieldGrp("uvSet2", query=True, text=True)
|
||||
cmds.polyUVSet(query=True, allUVSets=True)
|
||||
cmds.polyUVSet(create=True, uvSet='TempUV')
|
||||
cmds.polyUVSet(copy=True, nuv='TempUV', uvSet=UVname1)
|
||||
cmds.polyUVSet(copy=True, nuv=UVname1, uvSet=UVname2)
|
||||
cmds.polyUVSet(copy=True, nuv=UVname2, uvSet='TempUV')
|
||||
cmds.polyUVSet(delete=True, uvSet='TempUV')
|
||||
refresh_uv_sets() # Refresh list after execution
|
||||
|
||||
def UVsetReorder(*args):
|
||||
UVname1 = cmds.textFieldGrp("uvSet1", query=True, text=True)
|
||||
UVname2 = cmds.textFieldGrp("uvSet2", query=True, text=True)
|
||||
print("Reorder object is " + UVname1 + " + " + UVname2)
|
||||
cmds.polyUVSet(reorder=True, uvSet=UVname1, newUVSet=UVname2)
|
||||
UVobj = cmds.ls(sl=True)
|
||||
cmds.select(UVobj)
|
||||
refresh_uv_sets() # Refresh list after execution
|
||||
|
||||
# Function: UV set transfer
|
||||
def get_object_name(*args):
|
||||
# Get currently selected object and fill its name in the text field
|
||||
selected = cmds.ls(sl=True)
|
||||
if selected:
|
||||
cmds.textField('objectNameField', edit=True, text=selected[0])
|
||||
else:
|
||||
cmds.warning("No object selected.")
|
||||
|
||||
def set_uv(*args):
|
||||
# Get source and target objects, perform UV transfer, and clean up history
|
||||
source_object = cmds.textField('objectNameField', query=True, text=True)
|
||||
target_object = cmds.ls(sl=True)
|
||||
if not source_object or not target_object:
|
||||
cmds.warning("Please ensure both source and target objects are selected.")
|
||||
return
|
||||
target_object = target_object[0]
|
||||
sample_space_dict = {'World': 0, 'Local': 1, 'UV': 5, 'Component': 4}
|
||||
sample_space = cmds.radioCollection('sampleSpaceRadio', query=True, select=True)
|
||||
sample_space = cmds.radioButton(sample_space, query=True, label=True)
|
||||
sample_space = sample_space_dict.get(sample_space, 0)
|
||||
cmds.transferAttributes(source_object, target_object, transferPositions=0, transferNormals=0, transferUVs=2, transferColors=0, sampleSpace=sample_space, searchMethod=3)
|
||||
cmds.delete(target_object, constructionHistory=True) # Clean up history
|
||||
|
||||
def on_window_resize(*args):
|
||||
window_name = "UVSetEditor"
|
||||
# Get current window size
|
||||
current_width = cmds.window(window_name, query=True, width=True)
|
||||
current_height = cmds.window(window_name, query=True, height=True)
|
||||
|
||||
# Check and limit window size
|
||||
if current_width < MIN_WINDOW_WIDTH:
|
||||
cmds.window(window_name, edit=True, width=MIN_WINDOW_WIDTH)
|
||||
if current_height < MIN_WINDOW_HEIGHT:
|
||||
cmds.window(window_name, edit=True, height=MIN_WINDOW_HEIGHT)
|
||||
|
||||
def show(*args):
|
||||
window_name = "UVSetEditor"
|
||||
# Check if window exists, if so, delete it
|
||||
if cmds.window(window_name, exists=True):
|
||||
cmds.deleteUI('UV Set Editor', window=True)
|
||||
# Window
|
||||
# Create a new window and set its title and initial size
|
||||
window = cmds.window(window_name, title=" UV Set Editor", widthHeight=(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT), sizeable=True, tlb=True) # tlb=True
|
||||
|
||||
cmds.frameLayout(label='UV-Set')
|
||||
cmds.columnLayout(adjustableColumn=True)
|
||||
# Create a textScrollList control and set the selection change command
|
||||
cmds.textScrollList('uvList', numberOfRows=8, allowMultiSelection=False, width=280, selectCommand=switch_uv_set)
|
||||
cmds.textFieldGrp('newNameField', placeholderText=' Enter new name, then click Re to rename', width=280, columnAlign=[1, 'center'] ,columnWidth=[1,280])
|
||||
cmds.rowLayout(numberOfColumns=4,
|
||||
columnWidth4=(65, 65, 65, 65),
|
||||
columnAttach4=('both', 'both', 'both', 'both'))
|
||||
cmds.button( label='Get', height=32, command=lambda x: refresh_uv_sets(),backgroundColor=(0.53, 0.81, 0.98))
|
||||
cmds.button( label='Del', height=32, command=lambda x: delete_selected_uv_set())
|
||||
cmds.button( label='New', height=32, command=lambda x: create_new_uv_set(cmds.textFieldGrp('newNameField', query=True, text=True)))
|
||||
cmds.button( label='Re', height=32, command=lambda x: rename_selected_uv_set(cmds.textFieldGrp('newNameField', query=True, text=True)))
|
||||
cmds.setParent( '..' )
|
||||
|
||||
cmds.setParent('..') # End current form layout
|
||||
|
||||
cmds.frameLayout(label='UV-Swap')
|
||||
cmds.columnLayout(adjustableColumn=True, width=280)
|
||||
cmds.text(l='Enter UV set names in "uv1" and "uv2"', h=15)
|
||||
cmds.text(l=' UV swap or reorder swap. ', h=15)
|
||||
cmds.text(l='', h=5)
|
||||
cmds.rowLayout(numberOfColumns=3, columnWidth3=(65, 65, 130), columnAttach3=('both', 'both', 'both'))
|
||||
cmds.button(label='Get', height=25, command=set_uv_set1_name, backgroundColor=(0.53, 0.81, 0.98))
|
||||
cmds.textFieldGrp("uvSet1", placeholderText='uv1', editable=True, width=200)
|
||||
cmds.setParent('..')
|
||||
cmds.rowLayout(numberOfColumns=3, columnWidth3=(65, 65, 130), columnAttach3=('both', 'both', 'both'))
|
||||
cmds.button(label='Get', height=25, command=set_uv_set2_name, backgroundColor=(0.53, 0.81, 0.98))
|
||||
cmds.textFieldGrp("uvSet2", placeholderText='uv2', editable=True, width=200)
|
||||
cmds.setParent('..')
|
||||
cmds.rowColumnLayout(numberOfColumns=2, columnWidth=[(1, 135), (2, 135)])
|
||||
cmds.button(label='UV Swap', command=UVsetSwap, backgroundColor=(0.53, 0.81, 0.98))
|
||||
cmds.button(label='Reorder Swap', command=UVsetReorder, backgroundColor=(0.53, 0.81, 0.98))
|
||||
|
||||
UVname1 = cmds.textFieldGrp("uvSet1", query=True, text=True)
|
||||
UVname2 = cmds.textFieldGrp("uvSet2", query=True, text=True)
|
||||
print("Now we have UVset = {}, {}".format(UVname1, UVname2))
|
||||
|
||||
cmds.setParent('..') # End current form layout
|
||||
|
||||
# Separator
|
||||
cmds.separator(height=20, style='in')
|
||||
|
||||
# Create a column layout, all child elements will be vertically arranged
|
||||
cmds.frameLayout(label='UV-Transfer')
|
||||
cmds.columnLayout(adjustableColumn=True, width=230, height=130)
|
||||
cmds.rowLayout(numberOfColumns=3, columnWidth3=(50, 100, 50))
|
||||
cmds.button(label='Get', command=get_object_name, backgroundColor=(0.53, 0.81, 0.98), width=45) # Create a button that calls get_object_name function when clicked
|
||||
cmds.textField('objectNameField', enable=False, width=120) # Create a text field to display the name of the selected object
|
||||
cmds.button(label='Set', command=set_uv, backgroundColor=(0.53, 0.81, 0.98), width=45) # Create a button that calls set_uv function when clicked
|
||||
cmds.setParent('..') # End current form layout
|
||||
|
||||
# cmds.frameLayout(label='Sample Space')
|
||||
cmds.text(l='Sample Space:', h=20, align='left')
|
||||
form = cmds.formLayout()
|
||||
|
||||
cmds.radioCollection('sampleSpaceRadio') # Create a radio button group
|
||||
rb1 = cmds.radioButton(label='World', select=True) # Create a radio button
|
||||
rb2 = cmds.radioButton(label='Local')
|
||||
rb3 = cmds.radioButton(label='UV')
|
||||
rb4 = cmds.radioButton(label='Component')
|
||||
# Set form layout parameters to keep radio buttons horizontally aligned and centered when window size changes
|
||||
cmds.formLayout(form, edit=True, attachForm=[(rb1, 'left', 10), (rb4, 'right', 10)], attachControl=[(rb2, 'left', 5, rb1), (rb3, 'left', 5, rb2), (rb4, 'left', 5, rb3)])
|
||||
cmds.setParent('..') # End current form layout
|
||||
|
||||
cmds.scriptJob(event=["idle", on_window_resize], parent=window) # Listen for window resize events
|
||||
|
||||
cmds.showWindow(window) # Show window
|
0
Scripts/Modeling/UV/__init__.py
Normal file
0
Scripts/Modeling/UV/__init__.py
Normal file
Reference in New Issue
Block a user