You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
8.2 KiB
Python
204 lines
8.2 KiB
Python
#
|
|
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
# its licensors.
|
|
#
|
|
# For complete copyright and license terms please see the LICENSE at the root of this
|
|
# distribution (the "License"). All use of this software is governed by the License,
|
|
# or, if provided, by the license below or the license accompanying this file. Do not
|
|
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
#
|
|
from maya import cmds
|
|
from time import time
|
|
import cryDecorators
|
|
import sys
|
|
|
|
import logging
|
|
logging.basicConfig()
|
|
log = logging.getLogger(__name__)
|
|
#log.setLevel(logging.DEBUG)
|
|
|
|
standardSGs = set(['initialParticleSE', 'initialShadingGroup'])
|
|
|
|
|
|
def renameShadingEngines(lstShadingEngines=None):
|
|
'''
|
|
Renames ShadingEngines, to match their material's name.
|
|
This is required, because maya exports FaceSets from ShadingEngines
|
|
and GeomCaches derives material IDs from FaceSets.
|
|
'''
|
|
|
|
if not lstShadingEngines:
|
|
lstShadingEngines = cmds.ls(type="shadingEngine")
|
|
elif isinstance(lstShadingEngines, basestring):
|
|
lstShadingEngines = [lstShadingEngines]
|
|
|
|
# can't rename standard shading engines
|
|
lstShadingEngines = list(set(lstShadingEngines) - standardSGs)
|
|
|
|
if not lstShadingEngines:
|
|
return []
|
|
|
|
log.info("Renaming ShadingEngines...")
|
|
log.debug(" %s" % ', '.join(lstShadingEngines))
|
|
|
|
for i in range(len(lstShadingEngines)):
|
|
lstMaterials = cmds.listConnections("%s.surfaceShader" % lstShadingEngines[i], d=False, s=True, p=False)
|
|
if lstMaterials:
|
|
log.debug(" Materials: %s" % ', '.join(lstMaterials))
|
|
dgMaterial = lstMaterials[0]
|
|
destName = "%sSG" % dgMaterial
|
|
if lstShadingEngines[i] == destName:
|
|
continue
|
|
|
|
log.debug("Renaming " + lstShadingEngines[i] + " to " + destName + " ...")
|
|
try:
|
|
lstShadingEngines[i] = cmds.rename(lstShadingEngines[i], destName)
|
|
print('lstShadingEngines[i]: %s' % lstShadingEngines[i])
|
|
except:
|
|
log.debug(" !!! FAILED TO RENAME !!!")
|
|
else:
|
|
log.debug("No material found for shadingEngine: " + lstShadingEngines[i])
|
|
return lstShadingEngines
|
|
|
|
|
|
def enforcePerFaceAssignment(lstShadingEngines=None):
|
|
'''
|
|
Workaround for Maya 2014/2015 bug.
|
|
Ensures that ShadingEngines contain only faces, rather than full shapes.
|
|
Alembic 1.1.5 contains a bug, so that it doesn't export FaceSets
|
|
for objects where the entire shape is in the ShadingEngine.
|
|
'''
|
|
if lstShadingEngines is None:
|
|
lstShadingEngines = cmds.ls(type="shadingEngine")
|
|
else:
|
|
if isinstance(lstShadingEngines, basestring):
|
|
lstShadingEngines = [lstShadingEngines]
|
|
|
|
# cant apply per face assignment on standardSGs
|
|
lstShadingEngines = set(lstShadingEngines) - standardSGs
|
|
|
|
if not lstShadingEngines:
|
|
return False
|
|
|
|
log.info("Enforcing per-face material assignment ...")
|
|
|
|
for dgShadingEngine in lstShadingEngines:
|
|
log.debug(" shadingEngine: " + dgShadingEngine)
|
|
|
|
lstSetMembers = cmds.sets(dgShadingEngine, q=True)
|
|
if not lstSetMembers:
|
|
continue
|
|
|
|
for sSetMember in lstSetMembers:
|
|
if ".f[" not in sSetMember:
|
|
log.debug(" Converting shape-assignment to face-assignment: " + sSetMember)
|
|
# make sure sSetMember is a mesh!
|
|
meshMember = cmds.ls(cmds.listHistory(sSetMember), type='mesh')
|
|
if not meshMember:
|
|
log.debug('SetMember "%s" (from %s) did not yield a mesh!' % sSetMember, dgShadingEngine)
|
|
continue
|
|
meshMember = meshMember[0]
|
|
try:
|
|
#iFaceCount = cmds.polyEvaluate(meshMember, f=True)
|
|
#sFaces = meshMember + ".f[0:" + str(iFaceCount - 1) + "]"
|
|
sFaces = "%s.f[*]" % meshMember
|
|
# nasty hack: temporarily apply default material
|
|
# to generate objectGroup datastructures
|
|
if dgShadingEngine not in standardSGs:
|
|
cmds.sets("%s.f[0]" % meshMember, e=True, fe="initialShadingGroup")
|
|
cmds.sets(sFaces, e=1, fe=dgShadingEngine)
|
|
# remove original member from that we added all faces if still in
|
|
if cmds.sets(sSetMember, isMember=dgShadingEngine):
|
|
cmds.sets(sSetMember, rm=dgShadingEngine)
|
|
except StandardError, error:
|
|
log.error(error)
|
|
log.debug("Failed to all faces to material for object: " + sSetMember + "!")
|
|
log.debug(sys.exc_info())
|
|
|
|
# fix for abc import which throws per face warnings if you have
|
|
# get connected visible shapes
|
|
connMesh = set(cmds.ls(cmds.listConnections('%s.dagSetMembers' % dgShadingEngine, shapes=True), type='mesh'))
|
|
connMesh = set([m for m in connMesh if not cmds.getAttr('%s.intermediateObject' % m)])
|
|
shapeMembers = set(cmds.ls(lstSetMembers, s=True, dag=True, o=True))
|
|
# find nodes connected to dagSetMembers but are not members
|
|
nonMemberShapes = list(connMesh - shapeMembers)
|
|
if nonMemberShapes:
|
|
log.debug('ShadingEngine "%s" has false member connection to mesh shape: %s'
|
|
% (dgShadingEngine, ', '.join(nonMemberShapes)))
|
|
# find out exact connection and disconnect
|
|
for shape in nonMemberShapes:
|
|
conns = cmds.listConnections(shape, type='shadingEngine', c=True, p=True)
|
|
for i in range(1, len(conns), 2):
|
|
if conns[i].startswith('%s.dagSetMembers' % dgShadingEngine):
|
|
log.debug('unplugging "%s" from "%s"' % (conns[i - 1], conns[i]))
|
|
cmds.disconnectAttr(conns[i - 1], conns[i])
|
|
|
|
return True
|
|
|
|
@cryDecorators.pluginDependency("AbcExport.mll")
|
|
def exportAlembicForGeomCache(nodes=[]):
|
|
'''
|
|
Exports objects to Alembic with settings required for GeomCache pipeline.
|
|
'''
|
|
if not nodes:
|
|
nodes = cmds.ls(sl=True, tr=True)
|
|
if not nodes:
|
|
log.error('Select some transforms to export!')
|
|
return
|
|
|
|
selMesh, selSGs = getMeshAndSGs(nodes)
|
|
if not selMesh:
|
|
log.error('No meshes found to export!')
|
|
|
|
if selSGs.intersection(standardSGs):
|
|
log.warning('Selected objects have default materials applied! Please replace!')
|
|
|
|
#lstFilePath = cmds.fileDialog2(cap="Export Alembic for GeomCache", ff="*.abc", ds=1, fm=0, dir=(cmds.workspace(q=1, rd=1)))
|
|
lstFilePath = cmds.fileDialog2(cap="Export Alembic for GeomCache", fileFilter="Alembic .abc (*.abc)", fileMode=0)
|
|
if not lstFilePath:
|
|
return
|
|
|
|
t0 = time()
|
|
selSGs = renameShadingEngines(selSGs)
|
|
enforcePerFaceAssignment(selSGs)
|
|
|
|
sOptions = "-frameRange %s %s" % (cmds.playbackOptions(q=1, min=1), cmds.playbackOptions(q=1, max=1))
|
|
sOptions += " -uvWrite -writeColorSets -writeFaceSets -writeVisibility "
|
|
sOptions += ' '.join(['-root %s' % n for n in nodes])
|
|
sOptions += ' -file "%s"' % lstFilePath[0]
|
|
log.debug("AbcExport params:\n\t%s" % sOptions)
|
|
|
|
log.info('Exporting file: "%s" ...' % lstFilePath[0])
|
|
try:
|
|
cmds.AbcExport(jobArg=sOptions)
|
|
except StandardError, error:
|
|
log.error(error)
|
|
log.debug(sys.exc_info())
|
|
|
|
log.info('Export finished! %ssec' % round(time() - t0, 1))
|
|
|
|
|
|
def getMeshAndSGs(nodes=[]):
|
|
"""
|
|
filters visible mesh shapes from given or selected nodes and returns
|
|
them with thier shading groups
|
|
"""
|
|
if not nodes:
|
|
nodes = cmds.ls(sl=True)
|
|
if not nodes:
|
|
return None, None
|
|
selMesh = set(cmds.ls(cmds.listRelatives(nodes, allDescendents=True), type='mesh'))
|
|
selMesh = [m for m in selMesh if not cmds.getAttr('%s.intermediateObject' % m)]
|
|
if not selMesh:
|
|
return None, None
|
|
selSGs = set(cmds.ls(cmds.listConnections(selMesh), type='shadingEngine'))
|
|
return selMesh, selSGs
|
|
|
|
|
|
def prepareMaterials(*args):
|
|
"""inline call for Ui"""
|
|
mesh, sgs = getMeshAndSGs()
|
|
sgs= renameShadingEngines(sgs)
|
|
enforcePerFaceAssignment(sgs)
|