"""
Author: Jeremy Ernst
"""
import json
import os
from functools import partial
import System.utils as utils
import maya.cmds as cmds
from ThirdParty.Qt import QtGui, QtCore, QtWidgets
[docs]class ART_ExportMeshes(QtWidgets.QMainWindow):
"""
This class is used to export skeletal meshes and skeletal mesh LODs.
The UI has a robust suite of tools for managing LODs, bone removal for LODs, choosing
which meshes are associated with a LOD, where weighting from removed bones will get transferred,
and setting/viewing LOD poses (useful if you wanted to remove finger bones for a LOD, but not have
"paddle hands"
.. image:: /images/exportMeshes.png
A look at the LOD tools for transferring weighting and managing LOD poses:
.. image:: /images/lodTool.png
"""
[docs] def __init__(self, mainUI, parent=None):
"""
Instantiates the class, getting the QSettings, presenting a QMessageBox about saving the current file,
creates a temporary file to do the export work out of (stripping out the rig and removing all connections
from joints), set the model pose, then calls on the UI build.
:param mainUI: Instance of the Rig Creator interface, from which this class was called.
.. seealso:: ART_ExportMeshes.buildUI(), ART_ExportMeshes.populateUI()
"""
super(ART_ExportMeshes, self).__init__(parent)
if cmds.window("pyART_ExportMeshesWin", exists=True):
cmds.deleteUI("pyART_ExportMeshesWin", wnd=True)
# get settings
settings = QtCore.QSettings("Epic Games", "ARTv2")
self.toolsPath = settings.value("toolsPath")
self.scriptPath = settings.value("scriptPath")
self.iconsPath = settings.value("iconPath")
self.projectPath = settings.value("projectPath")
# class vars
self.mainUI = mainUI
self.lodPoseDict = {}
# get current file
self.saveFile = cmds.file(q=True, sceneName=True)
# message box for confirming save action
msgBax = QtWidgets.QMessageBox()
msgBax.setText(
"Please make sure any changes to the current file are saved before continuing. This process will be "
"creating a temporary file to do all of the exporting from.")
msgBax.setIcon(QtWidgets.QMessageBox.Warning)
msgBax.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
msgBax.setDefaultButton(QtWidgets.QMessageBox.Ok)
ret = msgBax.exec_()
if ret == QtWidgets.QMessageBox.Ok:
# save file as a new temporary file
self.tempFile = os.path.join(os.path.dirname(self.saveFile), "exportFile.ma")
cmds.file(rename=self.tempFile)
cmds.file(save=True)
# remove all skeleton connections
cmds.select("root", hi=True)
selection = cmds.ls(sl=True)
for each in selection:
connT = cmds.connectionInfo(each + ".translate", sourceFromDestination=True)
if connT != '':
cmds.disconnectAttr(connT, each + ".translate")
connR = cmds.connectionInfo(each + ".rotate", sourceFromDestination=True)
if connR != '':
cmds.disconnectAttr(connR, each + ".rotate")
connS = cmds.connectionInfo(each + ".scale", sourceFromDestination=True)
if connS != '':
cmds.disconnectAttr(connS, each + ".scale")
# remove rig and driver skeleton
cmds.select("rig_grp", hi=True)
selection = cmds.ls(sl=True)
for each in selection:
cmds.lockNode(each, lock=False)
cmds.delete("rig_grp")
cmds.delete("driver_root")
# show all joints
cmds.select("root", hi=True)
joints = cmds.ls(sl=True)
for joint in joints:
cmds.setAttr(joint + ".v", lock=False)
cmds.setAttr(joint + ".v", 1)
# go to model pose
for inst in self.mainUI.moduleInstances:
try:
# call on the module's bakeOffsets method
inst.setupForRigPose()
inst.setReferencePose("modelPose")
inst.cleanUpRigPose()
except Exception, e:
print e
# build UI
self.buildUI()
# populate UI
self.populateUI()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def closeWin(self, event):
"""
Gathers all information from the temp file (LOD meshes, bones to remove, etc), opens the export file,
and applies that information to the network node in the export file. Lastly removes the temp file.
"""
# save out data
exportData = utils.findExportMeshData()
# open original file
cmds.file(self.saveFile, open=True, force=True)
# apply data
characterNode = utils.returnCharacterModule()
lodData = ["_Pose", "_Bones", "_Meshes", "_FilePath"]
for each in exportData:
# path, meshes, removeBones, poseData, lodAttr
for d in lodData:
# if the attr does not exist, create it
if not cmds.objExists(characterNode + "." + each[4] + d):
if d != "_Meshes":
cmds.addAttr(characterNode, ln=each[4] + d, dt="string")
else:
cmds.addAttr(characterNode, ln=each[4] + d, at="message")
# set the attr data
dataString = json.dumps(each[0])
cmds.setAttr(characterNode + "." + each[4] + "_FilePath", dataString, type="string")
dataString = json.dumps(each[2])
cmds.setAttr(characterNode + "." + each[4] + "_Bones", dataString, type="string")
dataString = json.dumps(each[3])
cmds.setAttr(characterNode + "." + each[4] + "_Pose", dataString, type="string")
# first remove all connections
i = each[4].partition("_")[2]
connections = cmds.listConnections(characterNode + "." + each[4] + "_Meshes")
if connections is not None:
for conn in connections:
cmds.disconnectAttr(characterNode + "." + each[4] + "_Meshes", conn + ".lodGroup" + str(i))
# Add attrs to meshes to connect up to the characterNode LOD meshes attr
for mesh in each[1]:
if not cmds.objExists(mesh + ".lodGroup" + str(i)):
cmds.addAttr(mesh, sn="lodGroup" + str(i), at="message")
cmds.connectAttr(characterNode + "." + each[4] + "_Meshes", mesh + ".lodGroup" + str(i))
# remove temp file
os.remove(self.tempFile)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def buildUI(self):
"""
Builds the main interface for the tool, which allows additions of LODs, assignment of meshes to a given LOD,
and assignment of bones to remove per LOD (which then opens another interface/tool).
.. seealso:: ART_ExportMeshes.addBoneToList_UI(), ART_ExportMeshes.addMeshToList_UI()
.. seealso:: ART_ExportMeshes.addMeshLOD(), ART_ExportMeshes.createLODpage()
.. seealso:: ART_ExportMeshes.createExportMeshesPage(), ART_ExportMeshes.removeLodTab()
.. seealso:: ART_ExportMeshes.export()
Here is a breakdown image showing which UI elements call on which functions:
.. image:: /images/exportMeshesBreakout.png
"""
self.closeEvent = self.closeWin
# load stylesheet
styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss")
f = open(styleSheetFile, "r")
self.style = f.read()
f.close()
self.setStyleSheet(self.style)
# create the main widget
self.mainWidget = QtWidgets.QWidget()
self.mainWidget.setStyleSheet(self.style)
self.setCentralWidget(self.mainWidget)
# set qt object name
self.setObjectName("pyART_ExportMeshesWin")
self.setWindowTitle("Export Skeletal Meshes")
# font
headerFont = QtGui.QFont()
headerFont.setPointSize(8)
headerFont.setBold(True)
# set size policy
mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
# create the mainLayout for the rig creator UI
self.layout = QtWidgets.QVBoxLayout(self.mainWidget)
self.resize(600, 400)
self.setSizePolicy(mainSizePolicy)
self.setMinimumSize(QtCore.QSize(600, 400))
self.setMaximumSize(QtCore.QSize(600, 400))
# build pages
self.createExportMeshesPage()
# show window
self.show()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def createExportMeshesPage(self):
"""
Creates the LOD0 Tab, which is a little bit unique from subsequent LOD tabs, as the Add/Remove LOD buttons
are added, and the LOD0 tab can not be removed. It still calls on createLODpage to create the common
elements, but this creates the framework for all of the LOD tabs.
.. seealso:: ART_ExportMeshes.createLODpage()
"""
# create the QFrame for this page
self.exportMeshPage = QtWidgets.QFrame()
self.exportMeshPage.setStyleSheet(self.style)
self.exportMeshPage.setMinimumSize(560, 250)
self.layout.addWidget(self.exportMeshPage)
self.emPageMainLayout = QtWidgets.QVBoxLayout(self.exportMeshPage)
self.exportMeshPage.setStyleSheet(self.style)
self.exportMeshPage.setObjectName("dark")
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(True)
# add Tab Widget
# tab stylesheet (tab stylesheet via QSS doesn't seem to work for some reason
stylesheet = """
QTabBar::tab
{
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(90,90,90), stop:1 rgb(30,30,30));
border: 2px solid black;
width: 180px;
padding-left: -10px;
font: 8pt;
}
QTabBar::tab:selected
{
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(255,174,0), stop:1 rgb(30,30,30));
border: 2px solid black;
font: bold 10pt;
}
QTabBar::tab:hover
{
background: rgb(132,95,16);
font: bold 10pt;
}
QTabBar::tab:!selected
{
margin-top: 5px;
}
QTabWidget::pane
{
border: 2px solid rgb(0,0,0);
}
"""
self.emPageTabs = QtWidgets.QTabWidget()
self.emPageTabs.setStyleSheet(stylesheet)
self.emPageMainLayout.addWidget(self.emPageTabs)
self.emPageTabBar = QtWidgets.QTabBar()
self.emPageTabs.setTabBar(self.emPageTabBar)
self.emPageTabBar.setContentsMargins(0, 0, 0, 0)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# LOD0 tab
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
self.createLODpage("LOD_0", False)
cornerWidget = QtWidgets.QWidget()
cornerLayout = QtWidgets.QHBoxLayout(cornerWidget)
cornerLayout.setContentsMargins(0, 0, 0, 10)
# remove lod button
self.removeMeshLodBtn = QtWidgets.QPushButton(" - ")
self.removeMeshLodBtn.setObjectName("blueButton")
self.removeMeshLodBtn.setMinimumHeight(20)
self.removeMeshLodBtn.setToolTip("Remove Mesh LOD (current tab)")
cornerLayout.addWidget(self.removeMeshLodBtn)
self.removeMeshLodBtn.clicked.connect(self.removeLodTab)
# add lod button
self.addMeshLodBtn = QtWidgets.QPushButton(" + ")
self.addMeshLodBtn.setObjectName("blueButton")
self.addMeshLodBtn.setMinimumHeight(20)
self.addMeshLodBtn.setToolTip("Add Mesh LOD")
cornerLayout.addWidget(self.addMeshLodBtn)
self.addMeshLodBtn.clicked.connect(partial(self.addMeshLOD))
self.emPageTabs.setCornerWidget(cornerWidget)
# add continue/back buttons
buttonlayout = QtWidgets.QHBoxLayout()
self.emPageMainLayout.addLayout(buttonlayout)
spacerItem = QtWidgets.QSpacerItem(300, 0)
buttonlayout.addSpacerItem(spacerItem)
self.emPage_continueButton = QtWidgets.QPushButton("Export")
buttonlayout.addWidget(self.emPage_continueButton)
self.emPage_continueButton.setFont(font)
self.emPage_continueButton.setMinimumSize(250, 50)
self.emPage_continueButton.setMaximumSize(250, 50)
self.emPage_continueButton.clicked.connect(partial(self.export))
self.emPage_continueButton.setObjectName("blueButton")
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def createLODpage(self, label, closeable):
"""
Creates a tab (QFrame) for a new LOD, with the UI elements to set the output path for the FBX, to set the
meshes assigned to the LOD, and to launch the bone removal tool for the LOD.
:param label: The text label for the tab (LOD_#)
:param closeable: Whether this LOD can be removed, thus removing the tab.
"""
lodTab = QtWidgets.QFrame()
lodTab.setStyleSheet(self.style)
lodTab.setObjectName("mid")
self.emPageTabs.addTab(lodTab, label)
# horizontal layout
mainLayout = QtWidgets.QHBoxLayout(lodTab)
# left side column
leftSide = QtWidgets.QVBoxLayout()
mainLayout.addLayout(leftSide)
# label
meshLayout = QtWidgets.QHBoxLayout()
leftSide.addLayout(meshLayout)
layoutLabel = QtWidgets.QLabel("Meshes For " + label + ":")
meshLayout.addWidget(layoutLabel)
layoutLabel.setStyleSheet('background: transparent')
chooseBtn = QtWidgets.QPushButton("Choose Meshes")
chooseBtn.setObjectName("blueButton")
meshLayout.addWidget(chooseBtn)
# render mesh list
meshList = QtWidgets.QListWidget()
meshList.setToolTip("Select the meshes you want to include in this mesh LOD.")
meshList.setProperty("listType", "mesh")
leftSide.addWidget(meshList)
meshList.setToolTip("List of meshes to include for this LOD")
meshList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
chooseBtn.clicked.connect(partial(self.addMeshToList_UI, self, label, meshList))
# right side column
rightLayout = QtWidgets.QVBoxLayout()
mainLayout.addLayout(rightLayout)
# horizontal layout for line edit and button
pathLayout = QtWidgets.QHBoxLayout()
rightLayout.addLayout(pathLayout)
lineEdit = QtWidgets.QLineEdit()
pathLayout.addWidget(lineEdit)
lineEdit.setMinimumHeight(35)
lineEdit.setMaximumHeight(35)
lineEdit.setPlaceholderText("FBX Export Path...")
lineEdit.setToolTip("Define the file name and file location for this LOD FBX file.")
lineEdit.textChanged.connect(partial(self.saveFilePath, lineEdit, label))
browseBtn = QtWidgets.QPushButton()
browseBtn.setMinimumSize(35, 35)
browseBtn.setMaximumSize(35, 35)
pathLayout.addWidget(browseBtn)
icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/fileBrowse.png"))
browseBtn.setIconSize(QtCore.QSize(30, 30))
browseBtn.setIcon(icon)
browseBtn.clicked.connect(partial(self.browseToFBX, lineEdit))
# label + add button
removeBonesLayout = QtWidgets.QHBoxLayout()
rightLayout.addLayout(removeBonesLayout)
layoutLabel = QtWidgets.QLabel("Bones to remove for " + label + ":")
removeBonesLayout.addWidget(layoutLabel)
layoutLabel.setStyleSheet('background: transparent;')
removeBtn = QtWidgets.QPushButton("+")
removeBtn.setMinimumSize(35, 35)
removeBtn.setMaximumSize(35, 35)
removeBonesLayout.addWidget(removeBtn)
removeBtn.setObjectName("blueButton")
# bone list
boneList = QtWidgets.QListWidget()
boneList.setProperty("listType", "bone")
rightLayout.addWidget(boneList)
boneList.setToolTip("List of joints to remove from this LOD")
boneList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
boneList.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
# signals/slots
removeBtn.clicked.connect(partial(self.addBoneToList_UI, self, label, boneList))
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def addBoneToList_UI(self, parent, label, listWidget):
"""
Creates an interface to remove bones from a LOD, transfer weighting of those removed bones to the next viable
parent, and handle LOD posing.
:param parent: The instance of the main UI created by ART_ExportMeshes.buildUI()
:param label: The label for the window title to show what LOD this interface represents.
:param listWidget: The list widget on the main LOD page that lists all bones being removed.
.. seealso:: ART_ExportMeshes.addBoneToList_Accept(), ART_ExportMeshes.addWeightingTransferEntry()
.. seealso:: ART_ExportMeshes.viewLodPose(), ART_ExportMeshes.resetLodPose(), ART_ExportMeshes.resetLodPose()
"""
# create the main window
mainWin = QtWidgets.QMainWindow(parent)
mainWin.setStyleSheet(self.style)
# create the main widget
mainWidget = QtWidgets.QFrame()
mainWidget.setObjectName("mid")
mainWin.setCentralWidget(mainWidget)
mainWin.setMinimumSize(725, 525)
mainWin.setMaximumSize(725, 525)
# set qt object name
mainWin.setObjectName("ART_AddBoneToLODlistWin")
mainWin.setWindowTitle(label)
# font
headerFont = QtGui.QFont()
headerFont.setPointSize(8)
headerFont.setBold(True)
# create the mainLayout for the rig creator UI
layout = QtWidgets.QVBoxLayout(mainWidget)
mainLayout = QtWidgets.QHBoxLayout()
layout.addLayout(mainLayout)
# treeWidget
self.tree = QtWidgets.QTreeWidget()
self.tree.setObjectName("light")
mainLayout.addWidget(self.tree)
self.tree.headerItem().setText(0, "")
self.tree.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.tree.setMinimumSize(QtCore.QSize(260, 470))
self.tree.setMaximumSize(QtCore.QSize(260, 470))
self.tree.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.tree.header().setResizeMode(QtWidgets.QHeaderView.ResizeToContents)
self.tree.headerItem().setText(0, "Skeleton Tree")
self.tree.setColumnWidth(0, 260)
self.tree.setIndentation(10)
# right side
rightLayout = QtWidgets.QVBoxLayout()
mainLayout.addLayout(rightLayout)
# Weighting Transfer
xferGroupBox = QtWidgets.QGroupBox("Weighting Transfer")
rightLayout.addWidget(xferGroupBox)
xferGroupBox.setMinimumSize(QtCore.QSize(425, 330))
xferGroupBox.setMaximumSize(QtCore.QSize(425, 330))
xferGroupBox.setObjectName("light")
# vboxLayout--hbox button layout for spacer/button, vboxlayout/scroll area for entries
weightingXferLayout = QtWidgets.QVBoxLayout(xferGroupBox)
rightLayout.addLayout(weightingXferLayout)
buttonLayout = QtWidgets.QHBoxLayout()
weightingXferLayout.addLayout(buttonLayout)
buttonLayout.addSpacerItem(
QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed))
addEntryButton = QtWidgets.QPushButton(" Add Entry ")
addEntryButton.setObjectName("blueButton")
buttonLayout.addWidget(addEntryButton)
# ScrollArea
scrollLayout = QtWidgets.QVBoxLayout()
weightingXferLayout.addLayout(scrollLayout)
scrollArea = QtWidgets.QScrollArea()
scrollLayout.addWidget(scrollArea)
scrollContents = QtWidgets.QFrame()
scrollContents.setObjectName("dark")
scrollArea.setWidget(scrollContents)
entriesLayout = QtWidgets.QVBoxLayout(scrollContents)
scrollArea.setWidgetResizable(True)
entriesLayout.addSpacerItem(
QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding))
# LOD Pose
lodPoseGroupBox = QtWidgets.QGroupBox("LOD Posing")
rightLayout.addWidget(lodPoseGroupBox)
lodPoseGroupBox.setMinimumSize(QtCore.QSize(425, 75))
lodPoseGroupBox.setMaximumSize(QtCore.QSize(425, 75))
lodPoseGroupBox.setObjectName("light")
lodPoseLayout = QtWidgets.QHBoxLayout(lodPoseGroupBox)
viewPoseBtn = QtWidgets.QPushButton("View LOD Pose")
viewPoseBtn.setCheckable(True)
lodPoseLayout.addWidget(viewPoseBtn)
viewPoseBtn.clicked.connect(partial(self.viewLodPose, label, viewPoseBtn))
resetPoseBtn = QtWidgets.QPushButton("Reset LOD Pose")
lodPoseLayout.addWidget(resetPoseBtn)
resetPoseBtn.setObjectName("blueButton")
resetPoseBtn.clicked.connect(partial(self.resetLodPose, label))
savePoseBtn = QtWidgets.QPushButton("Save LOD Pose")
lodPoseLayout.addWidget(savePoseBtn)
savePoseBtn.setObjectName("blueButton")
savePoseBtn.clicked.connect(partial(self.saveLodPose, label))
# button
addButton = QtWidgets.QPushButton("Save and Close")
addButton.setMinimumHeight(40)
addButton.setMaximumHeight(40)
rightLayout.addWidget(addButton)
addButton.clicked.connect(
partial(self.addBoneToList_Accept, self.tree, listWidget, label, entriesLayout, viewPoseBtn))
addButton.setObjectName("blueButton")
addEntryButton.clicked.connect(partial(self.addWeightingTransferEntry, entriesLayout))
# joints
cmds.select("root", hi=True)
joints = cmds.ls(sl=True, type="joint")
for joint in joints:
parent = cmds.listRelatives(joint, parent=True)
if parent is None:
item = QtWidgets.QTreeWidgetItem(self.tree)
item.setText(0, joint)
item.setBackground(0, QtCore.Qt.NoBrush)
children = cmds.listRelatives(joint, children=True, type="joint")
for child in children:
self.addBoneToListUI_addChildren(child, item)
# show
self.tree.expandAll()
mainWin.show()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# populate ui automatically if the settings exist
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
characterNode = utils.returnCharacterModule()
attrs = cmds.listAttr(characterNode, ud=True, string="LOD_*_Bones")
lodAttrs = []
if attrs is not None:
for attr in attrs:
lodAttrs.append(attr.partition("_Bones")[0])
if lodAttrs is not None:
# get json data from attribute
lodData = ["_Bones"]
boneValues = []
for attr in lodAttrs:
if attr == label:
for entry in lodData:
if cmds.objExists(characterNode + "." + attr + entry):
if entry == "_Bones":
boneValues = json.loads(cmds.getAttr(characterNode + "." + attr + entry))
for item in boneValues:
bones = item[1]
xferBone = item[0]
# create an entry (need to return back the layouts needed for the next part
entryData = self.addWeightingTransferEntry(entriesLayout)
self.tree.clearSelection()
for each in bones:
item = self.tree.findItems(each, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive, 0)
if item is not None:
item = item[0]
item.setSelected(True)
self.addItemsToWeightXferList(entryData[0], entryData[1])
self.tree.clearSelection()
item = self.tree.findItems(xferBone, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive, 0)
if item is not None:
item = item[0]
item.setSelected(True)
self.addXferBoneToList(entryData[1], False)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def addWeightingTransferEntry(self, layout):
"""
Adds a Groupbox with two QListWidgets, where the left shows bones to remove, and the right shows the next
viable parent bone to transfer the weighting to. Both lists can then be edited as well.
This is what that would like like:
.. image:: /images/xferEntry.png
:param layout: The QVboxLayout to add the QGroupbox to.
:return: returns both QListWidgets (in memory)
.. seealso:: ART_ExportMeshes.addItemsToWeightXferList(), ART_ExportMeshes.removeBonesFromList()
"""
groupbox = QtWidgets.QGroupBox()
layout.insertWidget(0, groupbox)
groupbox.setMaximumSize(QtCore.QSize(370, 140))
groupbox.setMaximumSize(QtCore.QSize(370, 140))
groupbox.setCheckable(True)
groupbox.setFlat(True)
# load style sheet file
styleSheetFile = utils.returnNicePath(self.toolsPath,
"Core/Scripts/Interfaces/StyleSheets/skeletonSettings.qss")
f = open(styleSheetFile, "r")
style = f.read()
f.close()
groupbox.setStyleSheet(style)
mainLayout = QtWidgets.QHBoxLayout(groupbox)
groupbox.toggled.connect(partial(self.collapseBox, groupbox))
removeBonesList = QtWidgets.QListWidget()
mainLayout.addWidget(removeBonesList)
removeBonesList.setMinimumSize(QtCore.QSize(150, 122))
removeBonesList.setMaximumSize(QtCore.QSize(150, 122))
removeBonesList.setProperty("list", "remove")
removeBonesList.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
removeBonesList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
removeBonesList.customContextMenuRequested.connect(partial(self.lodContextMenu, removeBonesList, groupbox))
buttonLayout = QtWidgets.QVBoxLayout()
mainLayout.addLayout(buttonLayout)
addButton = QtWidgets.QPushButton(" + ")
buttonLayout.addWidget(addButton)
addButton.setObjectName("blueButton")
buttonLayout.addSpacerItem(
QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding))
remButton = QtWidgets.QPushButton(" - ")
buttonLayout.addWidget(remButton)
remButton.setObjectName("blueButton")
xferBoneList = QtWidgets.QListWidget()
mainLayout.addWidget(xferBoneList)
xferBoneList.setMinimumSize(QtCore.QSize(120, 122))
xferBoneList.setMaximumSize(QtCore.QSize(120, 122))
xferBoneList.setProperty("list", "xfer")
setButton = QtWidgets.QPushButton("<-")
mainLayout.addWidget(setButton)
setButton.setObjectName("blueButton")
setButton.clicked.connect(partial(self.addXferBoneToList, xferBoneList, False))
addButton.clicked.connect(partial(self.addItemsToWeightXferList, removeBonesList, xferBoneList))
remButton.clicked.connect(partial(self.removeBonesFromList, removeBonesList))
return [removeBonesList, xferBoneList]
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def collapseBox(self, groupBox, *args):
"""
Collapses the given groupBox down to 16 pixel high, or restores its original height, given the state.
:param groupBox: Which groupBox to operate on and manipulate the height.
:param args: What the state is of the groupBox checkBox.
"""
if args[0]:
groupBox.setMaximumSize(QtCore.QSize(370, 140))
groupBox.setMinimumSize(QtCore.QSize(370, 140))
else:
groupBox.setMaximumSize(QtCore.QSize(370, 16))
groupBox.setMinimumSize(QtCore.QSize(370, 16))
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def addMeshLOD(self):
"""
Finds the current number of LOD tabs, constructs a label for the new tab, iterating the count by 1,
and calls on createLODpage, passing in that label.
.. seealso:: ART_ExportMeshes.createLODpage()
"""
# get current count of tabs
numTabs = self.emPageTabs.count()
label = "LOD_" + str(numTabs)
self.createLODpage(label, True)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def addMeshToList_UI(self, parent, label, listWidget):
"""
Creates a UI that lists all meshes for selection to assign meshes to a given LOD.
.. image:: /images/addMeshesUI.png
:param parent: The UI instance to parent this interface to
:param label: The LOD text label for this interface's window title.
:param listWidget: The listWidget on the main LOD page that will list the selected meshes
.. image:: /images/meshList.png
.. seealso:: ART_ExportMeshes.populateRenderMeshes(), ART_ExportMeshes.addMeshesToLodList()
"""
# create the main window
mainWin = QtWidgets.QMainWindow(parent)
mainWin.setStyleSheet(self.style)
# create the main widget
mainWidget = QtWidgets.QFrame()
mainWidget.setObjectName("mid")
mainWin.setCentralWidget(mainWidget)
mainWin.setMinimumSize(300, 525)
mainWin.setMaximumSize(300, 525)
# set qt object name
mainWin.setObjectName("ART_AddMeshesToLODlistWin")
mainWin.setWindowTitle(label)
# font
headerFont = QtGui.QFont()
headerFont.setPointSize(8)
headerFont.setBold(True)
# create the mainLayout for the rig creator UI
layout = QtWidgets.QVBoxLayout(mainWidget)
mainLayout = QtWidgets.QHBoxLayout()
layout.addLayout(mainLayout)
# treeWidget
meshTree = QtWidgets.QTreeWidget()
meshTree.setObjectName("light")
mainLayout.addWidget(meshTree)
meshTree.headerItem().setText(0, "")
meshTree.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
meshTree.setMinimumSize(QtCore.QSize(260, 470))
meshTree.setMaximumSize(QtCore.QSize(260, 470))
meshTree.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
meshTree.header().setResizeMode(QtWidgets.QHeaderView.ResizeToContents)
meshTree.headerItem().setText(0, "Geometry")
meshTree.setColumnWidth(0, 260)
meshTree.setIndentation(10)
chooseBtn = QtWidgets.QPushButton("Save and Close")
chooseBtn.setObjectName("blueButton")
layout.addWidget(chooseBtn)
chooseBtn.clicked.connect(partial(self.addMeshesToLodList, listWidget, meshTree, mainWin, label))
# populate tree
self.populateRenderMeshes(meshTree)
# if items in listWidget, select those items in the tree
for i in range(listWidget.count()):
item = listWidget.item(i).text()
matches = meshTree.findItems(item, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive, 0)
for match in matches:
match.setSelected(True)
# show the window
mainWin.show()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def addMeshesToLodList(self, listWidget, meshTree, mainWin, label):
"""
Finds the selected items in addMeshToList_UI's treeWidget, and adds them back to the main LOD page's
listWidget for meshes associated with that LOD.
:param listWidget: The list widget to add selected items in the treeWidget to.
:param meshTree: The treeWidget from addMeshToList_UI(), whose selection will be queried.
:param mainWin: The window instance from addMeshToList_UI()
:param label: The name of the LOD tab
.. seealso:: ART_ExportMeshes.saveMeshList()
"""
# add to list widget
selected = meshTree.selectedItems()
listWidget.clear()
for each in selected:
icon = QtGui.QIcon(utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/mesh.png")))
listWidgetItem = QtWidgets.QListWidgetItem(icon, each.text(0))
listWidget.addItem(listWidgetItem)
# hook up connections
self.saveMeshList(listWidget, label)
# close window
mainWin.close()
mainWin.deleteLater()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def saveLodPose(self, lod):
"""
Queries the joints in the skeleton and get the attribute values to store for the given lod.
:param lod: the text label of the LOD to operate on.
.. todo:: Suggested feature request was to have this operate on the rig controls and not just joints. That
would mean storing both controls and joints. The desire behind this was to do all LOD posing in the
rig file, rather than the temp file that gets created for exporting.
"""
# get all joints
modules = utils.returnRigModules()
# go through each one, and find the created bones for that modules
createdJoints = []
for module in modules:
if module != "ART_Root_Module":
joints = cmds.getAttr(module + ".Created_Bones")
splitJoints = joints.split("::")
for bone in splitJoints:
if bone != "":
createdJoints.append(bone)
dataDict = {}
for each in createdJoints:
translateData = cmds.getAttr(each + ".translate")[0]
translate = []
for d in translateData:
translate.append(float('{:.3f}'.format(d)))
rotateData = cmds.getAttr(each + ".rotate")[0]
rotate = []
for r in rotateData:
rotate.append(float('{:.3f}'.format(r)))
scaleData = cmds.getAttr(each + ".scale")[0]
scale = []
for s in scaleData:
scale.append(float('{:.3f}'.format(s)))
dataDict[str(each)] = [translate, rotate, scale]
self.lodPoseDict[lod] = dataDict
for inst in self.mainUI.moduleInstances:
try:
# set the model pose again (un-altered)
inst.setupForRigPose()
inst.setReferencePose("modelPose")
inst.cleanUpRigPose()
except Exception, e:
print e
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def viewLodPose(self, lod, button):
"""
Gather LOD pose attribute data for the given LOD and set those values on the joints.
:param lod: The text label for the LOD to operate on.
:param button: the "View LOD Pose" button instance
.. todo:: Suggested feature request was to have this operate on the rig controls and not just joints. That
would mean getting data for both controls and joints. The desire behind this was to do all LOD posing
in the rig file, rather than the temp file that gets created for exporting.
"""
if button.isChecked():
try:
for key in self.lodPoseDict[lod]:
data = self.lodPoseDict[lod].get(key)
if cmds.objExists(key):
for each in data:
# translate, rotate, scale
if each == data[0]:
cmds.setAttr(key + ".translateX", each[0])
cmds.setAttr(key + ".translateY", each[1])
cmds.setAttr(key + ".translateZ", each[2])
if each == data[1]:
cmds.setAttr(key + ".rotateX", each[0])
cmds.setAttr(key + ".rotateY", each[1])
cmds.setAttr(key + ".rotateZ", each[2])
if each == data[2]:
cmds.setAttr(key + ".scaleX", each[0])
cmds.setAttr(key + ".scaleY", each[1])
cmds.setAttr(key + ".scaleZ", each[2])
except:
try:
characterNode = utils.returnCharacterModule()
if cmds.objExists(characterNode + "." + lod + "_Pose"):
dict = json.loads(cmds.getAttr(characterNode + "." + lod + "_Pose"))
for key in dict:
data = dict.get(key)
for each in data:
# translate, rotate, scale
if each == data[0]:
cmds.setAttr(key + ".translateX", each[0])
cmds.setAttr(key + ".translateY", each[1])
cmds.setAttr(key + ".translateZ", each[2])
if each == data[1]:
cmds.setAttr(key + ".rotateX", each[0])
cmds.setAttr(key + ".rotateY", each[1])
cmds.setAttr(key + ".rotateZ", each[2])
if each == data[2]:
cmds.setAttr(key + ".scaleX", each[0])
cmds.setAttr(key + ".scaleY", each[1])
cmds.setAttr(key + ".scaleZ", each[2])
except Exception, e:
print e
else:
for inst in self.mainUI.moduleInstances:
try:
# call on the module's bakeOffsets method
inst.setupForRigPose()
inst.setReferencePose("modelPose")
inst.cleanUpRigPose()
except Exception, e:
print e
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def resetLodPose(self, lod):
"""
Resets the LOD pose for the given LOD to the model pose.
:param lod: the LOD text label to operate on.
"""
for inst in self.mainUI.moduleInstances:
try:
# call on the module's bakeOffsets method
inst.setupForRigPose()
inst.setReferencePose("modelPose")
inst.cleanUpRigPose()
except Exception, e:
print e
self.saveLodPose(lod)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def removeTransferEntry(self, groupBox):
"""
Removes the given groupBox, deleting a weighting transfer entry.
:param groupBox: Which groupBox to remove.
"""
groupBox.close()
groupBox.deleteLater()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def removeLodTab(self):
"""
Removes the current tab index from the tabWidget. Also removes and LOD attributes associated with this LOD
from the character node.
"""
currentTab = self.emPageTabs.currentIndex()
tabText = self.emPageTabs.tabText(currentTab)
if currentTab != 0:
self.emPageTabs.removeTab(currentTab)
# remove LOD attrs
characterNode = utils.returnCharacterModule()
attrs = cmds.listAttr(characterNode, ud=True, string=tabText + "*")
if attrs is not None:
for attr in attrs:
cmds.deleteAttr(characterNode, at=attr)
else:
msgBox = QtWidgets.QMessageBox()
msgBox.setText("Cannot remove LOD 0")
msgBox.setIcon(QtWidgets.QMessageBox.Critical)
msgBox.exec_()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def browseToFBX(self, lineEdit):
"""
Calls on a fileDialog for the user to browse to an FBX file for saving. Either one that exists, or creating
a new one.
:param lineEdit: The QLineEdit whose text to set with the path to the FBX file.
"""
fileName = cmds.fileDialog2(fm=0, dir=self.projectPath, ff="*.fbx")
fileName = utils.returnFriendlyPath(fileName[0])
lineEdit.setText(fileName)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def addItemsToWeightXferList(self, widget, boneWidget):
"""
Takes the items selected in the skeleton tree (pictured below) and auto-selects and children under the
selected items, as removing the parent will also remove the children of those parents, and then adds all of
those items to the given widget.
.. image:: /images/boneTree.png
:param widget: The QListWidget in the weighting transfer entry widget showing bones to remove.
:param boneWidget: The QListWidget in the weighting transfer entry widget showing bone who will receive
weights from removed bones.
.. image:: /images/xferEntry.png
.. seealso:: ART_ExportMeshes.addXferBoneToList()
"""
# get selected items in self.tree
selected = self.tree.selectedItems()
fullList = []
itemList = []
for each in selected:
self.findTreeChildren(fullList, itemList, each)
for item in itemList:
item.setSelected(True)
for each in fullList:
widget.addItem(each)
self.addXferBoneToList(boneWidget)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def findTreeChildren(self, fullList, itemList, item):
"""
Finds any child items from selected items in the Skeleton Tree and appends them to the input lists.
:param fullList: The full list of all items, including the original selected parent items and any child items
:param itemList: The instances in memory of the selected items in the Skeleton Tree.
:param item: The parent item in the Skeleton Tree to check for children
.. seealso:: ART_ExportMeshes.addItemsToWeightXferList()
"""
fullList.append(item.text(0))
itemList.append(item)
for i in range(item.childCount()):
self.findTreeChildren(fullList, itemList, item.child(i))
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def addXferBoneToList(self, widget, autoFind=True):
"""
Finds the selected items in the Skeleton Tree (for removal) and locates the next viable parent to transfer
weighting to, then adds that bone to the passed in QListWidget.
:param widget: The QListWidget to add the bone that will receive weights from removed bones.
:param autoFind: Whether or not to auto-locate the next viable parent or use the currently selected item.
"""
if autoFind:
# find selected items, find the first item parent that is not selected
selected = self.tree.selectedItems()
viableParent = None
for each in selected:
if each.parent().isSelected() is False:
viableParent = each.parent().text(0)
widget.clear()
widget.addItem(viableParent)
else:
selected = self.tree.selectedItems()
if len(selected) > 1:
msgBox = QtWidgets.QMessageBox()
msgBox.setText("Only 1 bone can be selected to transfer weighting to.")
msgBox.setIcon(QtWidgets.QMessageBox.Critical)
msgBox.exec_()
else:
widget.clear()
widget.addItem(selected[0].text(0))
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def removeBonesFromList(self, widget):
"""
Takes the selected items from the left QListWidget in a weighting transfer widget and attempts to remove
them from the list.
:param widget: The QListWidget to check for selected items in.
"""
selected = widget.selectedItems()
# find the text of each selected item and list items in the main skeleton tree that match that text
for each in selected:
text = each.text()
match = self.tree.findItems(text, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive, 0)
# check to make sure (if there was a match) that the parent of that item is not also being removed.
if len(match) > 0:
parent = match[0].parent()
parentMatch = widget.findItems(parent.text(0), QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive)
if len(parentMatch) == 0:
row = widget.row(each)
widget.takeItem(row)
else:
msgBox = QtWidgets.QMessageBox()
msgBox.setText(
"Parent of the joint is also being removed. Cannot complete request unless parent joint "
"is not removed.")
msgBox.setIcon(QtWidgets.QMessageBox.Critical)
msgBox.exec_()
else:
msgBox = QtWidgets.QMessageBox()
msgBox.setText("Was not able to find a matching entry in the skeleton tree.")
msgBox.setIcon(QtWidgets.QMessageBox.Critical)
msgBox.exec_()
return
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def addBoneToListUI_addChildren(self, name, parent):
"""
Populates the Skeleton Tree (self.tree) by recursively looking for relatives of the given name and making more
QTreeWidgetItems using the passed in name, and parenting under the passed in parent.
:param name: The bone name, which will be used to look for children, and also as the text for the treeWidgetItem
:param parent: The parent treeWidgetItem that the created item will be a child of.
"""
item = QtWidgets.QTreeWidgetItem(parent)
item.setText(0, name)
item.setBackground(0, QtCore.Qt.NoBrush)
children = cmds.listRelatives(name, children=True, type="joint")
if children:
for child in children:
self.addBoneToListUI_addChildren(child, item)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def addBoneToList_Accept(self, tree, listWidget, lod, layout, viewBtn):
"""
Called from the "Save and Close" button of the addBoneToList_UI, clears all items in the main listWidget,
then populates that listWidget with the new bones to remove. Adds all LOD pose info to the character node as
well as weighting transfer info for that lod.
:param listWidget: The listWidget on the main LOD page on the bottom right that lists bones to remove.
:param lod: The LOD text (LOD_#) to operate on.
:param layout: The QVboxLayout to query for weighting transfer entries.
:param viewBtn: The "View LOD Pose" button instance
"""
# find items already in listWidget
listWidget.clear()
# get the character node
characterNode = utils.returnCharacterModule()
# add info to characterNode
if not cmds.objExists(characterNode + "." + lod + "_Pose"):
cmds.addAttr(characterNode, ln=lod + "_Pose", dt="string")
if not cmds.objExists(characterNode + "." + lod + "_Bones"):
cmds.addAttr(characterNode, ln=lod + "_Bones", dt="string")
# gather weight transfer info and pose info
lodXferList = []
for i in range(layout.count()):
if type(layout.itemAt(i).widget()) == QtWidgets.QGroupBox:
children = layout.itemAt(i).widget().children()
data = []
removeBones = []
for each in children:
if type(each) == QtWidgets.QListWidget:
# [[list of bones], xferbone]
if each.property("list") == "remove":
for x in range(each.count()):
item = each.item(x)
if item.text() not in removeBones:
removeBones.append(item.text())
elif each.property("list") == "xfer":
for y in range(each.count()):
item = each.item(y)
data.append(item.text())
data.append(removeBones)
lodXferList.append(data)
for item in lodXferList:
bones = item[1]
for bone in bones:
icon = QtGui.QIcon(utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/boneDisplay.png")))
listWidgetItem = QtWidgets.QListWidgetItem(icon, str(bone))
listWidget.addItem(listWidgetItem)
string = json.dumps(lodXferList)
cmds.setAttr(characterNode + "." + lod + "_Bones", string, type="string")
# Pose Info
try:
poseString = json.dumps(self.lodPoseDict[lod])
cmds.setAttr(characterNode + "." + lod + "_Pose", poseString, type="string")
except:
print "no LOD pose given. Using model pose."
# set viewPoseBtn to unchecked
viewBtn.setChecked(False)
self.viewLodPose(lod, viewBtn)
# remove UI
if cmds.window("ART_AddBoneToLODlistWin", exists=True):
cmds.deleteUI("ART_AddBoneToLODlistWin", wnd=True)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def populateRenderMeshes(self, tree):
"""
Finds all skinned meshes in the scene and adds them as items to the given TreeWidget.
.. image:: /images/addMeshesUI.png
:param tree: The QTreeWidget to add found meshes to
"""
# find all skinned meshes in scene
skinClusters = cmds.ls(type='skinCluster')
renderMeshes = []
for cluster in skinClusters:
geometry = cmds.skinCluster(cluster, q=True, g=True)[0]
geoTransform = cmds.listRelatives(geometry, parent=True)[0]
renderMeshes.append(geoTransform)
geoParents = []
worldGeo = []
meshDict = {}
for geo in renderMeshes:
parent = cmds.listRelatives(geo, parent=True)
if parent is not None:
parent = parent[0]
if parent not in geoParents:
geoParents.append(parent)
else:
worldGeo.append(geo)
for each in geoParents:
children = cmds.listRelatives(each)
meshDict[each] = children
for key in meshDict.keys():
item = QtWidgets.QTreeWidgetItem(tree)
item.setText(0, key)
item.setBackground(0, QtCore.Qt.NoBrush)
children = meshDict.get(key)
for child in children:
icon = QtGui.QIcon(utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/mesh.png")))
childItem = QtWidgets.QTreeWidgetItem(item)
childItem.setText(0, child)
childItem.setBackground(0, QtCore.Qt.NoBrush)
childItem.setIcon(0, icon)
for geo in worldGeo:
icon = QtGui.QIcon(utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/mesh.png")))
geoItem = QtWidgets.QTreeWidgetItem(tree)
geoItem.setText(0, geo)
geoItem.setBackground(0, QtCore.Qt.NoBrush)
geoItem.setIcon(0, icon)
tree.expandAll()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def populateUI(self):
"""
Checks to see if LOD attributes exist on the character node, and if so, builds and populates the UI based on
those settings.
"""
# get the character node
characterNode = utils.returnCharacterModule()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# populate ui automatically if the settings exist
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
attrs = cmds.listAttr(characterNode, ud=True, string="LOD_*_FilePath")
lodAttrs = []
createdTabs = []
if attrs is not None:
for attr in attrs:
lodAttrs.append(attr.partition("_FilePath")[0])
if lodAttrs is not None:
# get json data from attribute
lodData = ["_Pose", "_Bones", "_Meshes", "_FilePath"]
meshValue = None
boneValue = None
pathValue = None
for attr in lodAttrs:
for entry in lodData:
if cmds.objExists(characterNode + "." + attr + entry):
if entry == "_Bones":
boneValue = json.loads(cmds.getAttr(characterNode + "." + attr + entry))
if entry == "_Meshes":
meshValue = cmds.listConnections(characterNode + "." + attr + entry)
if entry == "_FilePath":
pathValue = json.loads(cmds.getAttr(characterNode + "." + attr + entry))
# compare number of LOD attrs to number of current tabs. If needed, add more tabs for the lods
numTabs = self.emPageTabs.count()
if numTabs < len(lodAttrs):
for i in range(numTabs):
tab = self.emPageTabs.widget(i)
tabText = self.emPageTabs.tabText(i)
if attr != tabText:
if attr not in createdTabs:
self.createLODpage(attr, True)
createdTabs.append(attr)
# find the associated tab and set the data
for i in range(self.emPageTabs.count()):
tab = self.emPageTabs.widget(i)
tabText = self.emPageTabs.tabText(i)
if tabText == attr:
# tab children
lists = lineEdits = tab.findChildren(QtWidgets.QListWidget)
lineEdits = tab.findChildren(QtWidgets.QLineEdit)
# set meshes and bones for this tab
for list in lists:
property = list.property("listType")
if property == "mesh":
if meshValue is not None:
for mesh in meshValue:
icon = QtGui.QIcon(
utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/mesh.png")))
listWidgetItem = QtWidgets.QListWidgetItem(icon, mesh)
list.addItem(listWidgetItem)
if property == "bone":
if boneValue is not None:
for item in boneValue:
bones = item[1]
for bone in bones:
icon = QtGui.QIcon(utils.returnFriendlyPath(
os.path.join(self.iconsPath, "System/boneDisplay.png")))
listWidgetItem = QtWidgets.QListWidgetItem(icon, bone)
list.addItem(listWidgetItem)
# set file path
if os.path.exists(os.path.dirname(pathValue)):
lineEdits[0].setText(pathValue)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def saveFilePath(self, lineEdit, tabText, *args):
"""
Gathers the FBX output path for the LOD and stores that information to the character node.
:param lineEdit: The QLineEdit which stores the output path text.
:param tabText: The LOD text to operate on (LOD_#)
"""
characterNode = utils.returnCharacterModule()
# get file path
path = lineEdit.text()
dataString = json.dumps(path)
if not cmds.objExists(characterNode + "." + tabText + "_FilePath"):
cmds.addAttr(characterNode, ln=tabText + "_FilePath", dt="string")
cmds.setAttr(characterNode + "." + tabText + "_FilePath", dataString, type="string")
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def saveMeshList(self, listWidget, tabText):
"""
Gathers the associated meshes for an LOD and stores that information to the character node.
:param listWidget: The QListWidget of the LOD tab to search for associated meshes.
:param tabText: The LOD text to operate on (LOD_#)
"""
characterNode = utils.returnCharacterModule()
if not cmds.objExists(characterNode + "." + tabText + "_Meshes"):
cmds.addAttr(characterNode, ln=tabText + "_Meshes", at="message")
if not cmds.objExists(characterNode + "." + tabText + "_FilePath"):
cmds.addAttr(characterNode, ln=tabText + "_FilePath", dt="string")
i = tabText.partition("_")[2]
# first remove all connections
connections = cmds.listConnections(characterNode + "." + tabText + "_Meshes")
if connections is not None:
for conn in connections:
cmds.disconnectAttr(characterNode + "." + tabText + "_Meshes", conn + ".lodGroup" + str(i))
# Add attrs to meshes to connect up to the characterNode LOD meshes attr
meshList = []
for x in range(listWidget.count()):
meshList.append(listWidget.item(x).text())
for mesh in meshList:
if not cmds.objExists(mesh + ".lodGroup" + str(i)):
cmds.addAttr(mesh, sn="lodGroup" + str(i), at="message")
cmds.connectAttr(characterNode + "." + tabText + "_Meshes", mesh + ".lodGroup" + str(i))
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def export(self):
"""
Gathers all export data by calling on utils.findExportMeshData, parses the information, and for each LOD in
the list, calls on utils.ExportMesh(), passing in the appropriate data.
.. seealso:: utils.ExportMesh(), utils.findExportMeshData()
"""
exportData = utils.findExportMeshData()
for each in exportData:
if not os.path.exists(os.path.dirname(each[0])):
msgBox = QtWidgets.QMessageBox()
msgBox.setText("There are LODs with no valid file path to export to. Aborting.")
msgBox.setIcon(QtWidgets.QMessageBox.Critical)
msgBox.exec_()
return
if each[1] is None:
msgBox = QtWidgets.QMessageBox()
msgBox.setText("There are LODs that have no meshes associated with them. Aborting.")
msgBox.setIcon(QtWidgets.QMessageBox.Critical)
msgBox.exec_()
return
# save the file
saveFile = cmds.file(q=True, sceneName=True)
try:
cmds.file(save=True)
except Exception, e:
cmds.error("Could not save file. Error: " + str(e))
return
# export
fileData = []
for each in exportData:
meshValue = each[1]
pathValue = each[0]
boneValue = each[2]
poseData = each[3]
utils.exportMesh(self.mainUI, meshValue, pathValue, boneValue, poseData)
fileData.append(pathValue)
# open the file
cmds.file(saveFile, open=True, force=True)
# report and close
self.close()
msgBox = QtWidgets.QMessageBox()
msgBox.setText("Export Complete!")
string = ""
for each in fileData:
string += each + "\n"
msgBox.setDetailedText(string)
msgBox.setIcon(QtWidgets.QMessageBox.Information)
msgBox.exec_()