363 import maya.OpenMaya 
as OpenMaya
 
  364 import maya.OpenMayaMPx 
as OpenMayaMPx
 
  367 def statusError(message):
 
  368     fullMsg = 
"Status failed: %s\n" % message
 
  369     sys.stderr.write(fullMsg)
 
  373 def statusAssert(condition, message):
 
  375         fullMsg = 
"Assertion failed: %s\n" % message
 
  376         sys.stderr.write(fullMsg)
 
  381 class polyModifierCmd(OpenMayaMPx.MPxCommand):
 
  383         OpenMayaMPx.MPxCommand.__init__(self)
 
  390         self.__fDagPathInitialized = 
False 
  395         self.__fModifierNodeTypeInitialized = 
False 
  396         self.__fModifierNodeNameInitialized = 
False 
  398         self.__fModifierNodeName = 
"" 
  401         self.__fHasHistory = 
False 
  402         self.__fHasTweaks = 
False 
  403         self.__fHasRecordHistory = 
False 
  448     def _setMeshNode(self, mesh):
 
  450         Target polyMesh to modify 
  452         self.__fDagPath = mesh
 
  453         self.__fDagPathInitialized = 
True 
  456     def _getMeshNode(self):
 
  457         return self.__fDagPath
 
  460     def _setModifierNodeType(self, nodeType):
 
  464         self.__fModifierNodeType = nodeType
 
  465         self.__fModifierNodeTypeInitialized = 
True 
  468     def _setModifierNodeName(self, nodeName):
 
  469         self.__fModifierNodeName = nodeName
 
  470         self.__fModifierNodeNameInitialized = 
True 
  473     def _getModifierNodeType(self):
 
  474         return self.__fModifierNodeType
 
  477     def _getModifierNodeName(self):
 
  478         return self.__fModifierNodeName
 
  484     def _initModifierNode(self, modifierNode):
 
  486         Derived classes should override this method if they wish to initialize 
  487         input attributes on the modifierNode 
  492     def _directModifier(self, mesh):
 
  494         Derived classes should override this method to provide direct 
  495         modifications on the meshNode in the case where no history exists and 
  496         construction history is turned off. (ie. no DG operations desired) 
  498         This method is called only if history does not exist and history is turned 
  499         off. At this point, a handle to the meshNode is passed in so a derived 
  500         class may directly modify the mesh. 
  505     def _doModifyPoly(self):
 
  506         if self.__isCommandDataValid():
 
  509             self.__collectNodeState()
 
  511             if (
not self.__fHasHistory) 
and (
not self.__fHasRecordHistory):
 
  512                 meshNode = self.__fDagPath.node()
 
  516                 self.__cacheMeshData()
 
  517                 self.__cacheMeshTweaks()
 
  521                 self._directModifier(meshNode)
 
  523                 modifierNode = self.__createModifierNode()
 
  524                 self._initModifierNode(modifierNode)
 
  525                 self.__connectNodes(modifierNode)
 
  528     def _redoModifyPoly(self):
 
  529         if (
not self.__fHasHistory) 
and (
not self.__fHasRecordHistory):
 
  530             meshNode = self.__fDagPath.node()
 
  534             self._directModifier(meshNode)
 
  538             if self.__fHasHistory:
 
  539                 self.__fDagModifier.doIt()
 
  540             self.__fDGModifier.doIt()
 
  543     def _undoModifyPoly(self):
 
  544         if (
not self.__fHasHistory) 
and (
not self.__fHasRecordHistory):
 
  545             self.__undoDirectModifier()
 
  547             self.__fDGModifier.undoIt()
 
  553             if not self.__fHasHistory:
 
  555                     self.__undoCachedMesh()
 
  557                     statusError(
"undoCachedMesh")
 
  558                 self.__fDagModifier.undoIt()
 
  561                 self.__undoTweakProcessing()
 
  563                 statusError(
"undoTweakProcessing")
 
  588     class __modifyPolyData:
 
  612     def __isCommandDataValid(self):
 
  617         if self.__fDagPathInitialized:
 
  618             self.__fDagPath.extendToShape()
 
  619             if (
not self.__fDagPath.isValid()) 
or (self.__fDagPath.apiType() != OpenMaya.MFn.kMesh):
 
  626         if (
not self.__fModifierNodeTypeInitialized) 
and (
not self.__fModifierNodeNameInitialized):
 
  632     def __collectNodeState(self):
 
  634         Collect node state information on the given polyMeshShape 
  635             - HasHistory (Construction History exists) 
  637             - HasRecordHistory (Construction History is turned on) 
  639         self.__fDagPath.extendToShape()
 
  640         meshNodeShape = self.__fDagPath.node()
 
  643         depNodeFn.setObject(meshNodeShape)
 
  645         inMeshPlug = depNodeFn.findPlug(
"inMesh")
 
  646         self.__fHasHistory = inMeshPlug.isConnected()
 
  652         self.__fHasTweaks = 
False 
  653         tweakPlug = depNodeFn.findPlug(
"pnts")
 
  654         if not tweakPlug.isNull():
 
  657             statusAssert(tweakPlug.isArray(),
 
  658                         "tweakPlug.isArray() -- tweakPlug is not an array plug")
 
  660             numElements = tweakPlug.numElements()
 
  661             for i 
in range(numElements):
 
  662                 tweak = tweakPlug.elementByPhysicalIndex(i)
 
  663                 if not tweak.isNull():
 
  664                     tweakData = self.__getFloat3PlugValue(tweak)
 
  665                     if 0 != tweakData.x 
or 0 != tweakData.y 
or 0 != tweakData.z:
 
  666                         self.__fHasTweaks = 
True 
  670         OpenMaya.MGlobal.executeCommand(
"constructionHistory -q -tgl", result)
 
  671         self.__fHasRecordHistory = (0 != result)
 
  676     def __createModifierNode(self):
 
  678         if self.__fModifierNodeTypeInitialized 
or self.__fModifierNodeNameInitialized:
 
  679             if self.__fModifierNodeTypeInitialized:
 
  680                 modifierNode = self.__fDGModifier.createNode(self.__fModifierNodeType)
 
  681             elif self.__fModifierNodeNameInitialized:
 
  682                 modifierNode = self.__fDGModifier.createNode(self.__fModifierNodeName)
 
  688             inMeshAttr = depNodeFn.attribute(
"inMesh")
 
  689             outMeshAttr = depNodeFn.attribute(
"outMesh")
 
  691             statusAssert(
not (inMeshAttr.isNull() 
or outMeshAttr.isNull()),
 
  692                         "Invalid Modifier Node: inMesh and outMesh attributes are required.")
 
  698     def __processMeshNode(self, data):
 
  706         data.meshNodeShape = self.__fDagPath.node()
 
  707         dagNodeFn.setObject(data.meshNodeShape)
 
  711         statusAssert(0 < dagNodeFn.parentCount(),
 
  712                         "0 < dagNodeFn.parentCount() -- meshNodeshape has no parent transform")
 
  713         data.meshNodeTransform = dagNodeFn.parent(0)
 
  715         data.meshNodeDestPlug = dagNodeFn.findPlug(
"inMesh")
 
  716         data.meshNodeDestAttr = data.meshNodeDestPlug.attribute()
 
  719     def __processUpstreamNode(self, data):
 
  739         if self.__fHasHistory:
 
  744             data.meshNodeDestPlug.connectedTo(tempPlugArray, 
True, 
False)
 
  748             statusAssert(tempPlugArray.length() == 1,
 
  749                             "tempPlugArray.length() == 1 -- 0 or >1 connections on meshNodeShape.inMesh")
 
  750             data.upstreamNodeSrcPlug = tempPlugArray[0]
 
  755             data.upstreamNodeShape = data.upstreamNodeSrcPlug.node()
 
  756             depNodeFn.setObject(data.upstreamNodeShape)
 
  757             data.upstreamNodeSrcAttr = data.upstreamNodeSrcPlug.attribute()
 
  762             self.__fDGModifier.disconnect(data.upstreamNodeSrcPlug, data.meshNodeDestPlug)
 
  771             dagNodeFn.setObject(data.meshNodeShape)
 
  772             data.upstreamNodeTransform = dagNodeFn.duplicate(
False, 
False)
 
  773             dagNodeFn.setObject(data.upstreamNodeTransform)
 
  777             statusAssert(0 < dagNodeFn.childCount(),
 
  778                         "0 < dagNodeFn.childCount() -- Duplicate meshNode transform has no shape.")
 
  779             data.upstreamNodeShape = dagNodeFn.child(0)
 
  784                 self.__fDagModifier.reparentNode(data.upstreamNodeShape, data.meshNodeTransform)
 
  786                 statusError(
"reparentNode")
 
  794                 self.__fDagModifier.doIt()
 
  796                 statusError(
"fDagModifier.doIt()")
 
  801             dagNodeFn.setObject(data.upstreamNodeShape)
 
  802             dagNodeFn.setIntermediateObject(
True)
 
  806             data.upstreamNodeSrcAttr = dagNodeFn.attribute(
"outMesh")
 
  807             data.upstreamNodeSrcPlug = dagNodeFn.findPlug(
"outMesh")
 
  812                 self.__fDagModifier.deleteNode(data.upstreamNodeTransform)
 
  814                 statusError(
"deleteNode")
 
  823                 self.__fDagModifier.doIt()
 
  825                 statusError(
"fDagModifier.doIt()")
 
  829             dagNodeFn.getPath(self.__fDuplicateDagPath)
 
  832     def __processModifierNode(self, modifierNode, data):
 
  834         data.modifierNodeSrcAttr = depNodeFn.attribute(
"outMesh")
 
  835         data.modifierNodeDestAttr = depNodeFn.attribute(
"inMesh")
 
  838     def __processTweaks(self, data):
 
  841         self.__fTweakIndexArray.clear()
 
  842         self.__fTweakVectorArray.clear()
 
  852         if self.__fHasTweaks:
 
  868             data.tweakNode = self.__fDGModifier.createNode(
"polyTweak")
 
  869             depNodeFn.setObject(data.tweakNode)
 
  870             data.tweakNodeSrcAttr = depNodeFn.attribute(
"output")
 
  871             data.tweakNodeDestAttr = depNodeFn.attribute(
"inputPolymesh")
 
  872             tweakNodeTweakAttr = depNodeFn.attribute(
"tweak")
 
  874             depNodeFn.setObject(data.meshNodeShape)
 
  875             meshTweakPlug = depNodeFn.findPlug(
"pnts")
 
  879             statusAssert(meshTweakPlug.isArray(),
 
  880                             "meshTweakPlug.isArray() -- meshTweakPlug is not an array plug")
 
  884             numElements = meshTweakPlug.numElements()
 
  885             for i 
in range(numElements):
 
  890                 tweak = meshTweakPlug.elementByPhysicalIndex(i)
 
  895                 if not tweak.isNull():
 
  898                     logicalIndex = tweak.logicalIndex()
 
  902                     tweakData = tweak.asMObject()
 
  903                     tweakDataArray.append(tweakData)
 
  904                     tweakVector = self.__getFloat3PlugValue(tweak)
 
  905                     self.__fTweakIndexArray.append(logicalIndex)
 
  906                     self.__fTweakVectorArray.append(tweakVector)
 
  918                     statusAssert(tweak.isCompound(),
 
  919                                 "tweak.isCompound() -- Element tweak plug is not compound")
 
  921                     numChildren = tweak.numChildren()
 
  922                     for j 
in range(numChildren):
 
  923                         tweakChild = tweak.child(j)
 
  924                         if tweakChild.isConnected():
 
  927                             tempPlugArray.clear()
 
  928                             if tweakChild.connectedTo(tempPlugArray, 
False, 
True):
 
  929                                 numSrcConnections = tempPlugArray.length()
 
  930                                 tweakSrcConnectionCountArray.append(numSrcConnections)
 
  932                                 for k 
in range(numSrcConnections):
 
  933                                     tweakSrcConnectionPlugArray.append(tempPlugArray[k])
 
  934                                     self.__fDGModifier.disconnect(tweakChild, tempPlugArray[k])
 
  936                                 tweakSrcConnectionCountArray.append(0)
 
  940                             tempPlugArray.clear()
 
  941                             if tweakChild.connectedTo(tempPlugArray, 
True, 
False):
 
  944                                 statusAssert(tempPlugArray.length() == 1,
 
  945                                             "tempPlugArray.length() == 1 -- 0 or >1 connections on tweakChild")
 
  947                                 tweakDstConnectionCountArray.append(1)
 
  948                                 tweakDstConnectionPlugArray.append(tempPlugArray[0])
 
  949                                 self.__fDGModifier.disconnect(tempPlugArray[0], tweakChild)
 
  951                                 tweakDstConnectionCountArray.append(0)
 
  953                             tweakSrcConnectionCountArray.append(0)
 
  954                             tweakDstConnectionCountArray.append(0)
 
  958             polyTweakPlug = 
OpenMaya.MPlug(data.tweakNode, tweakNodeTweakAttr)
 
  959             numTweaks = self.__fTweakIndexArray.length()
 
  962             for i 
in range(numTweaks):
 
  965                 tweak = polyTweakPlug.elementByLogicalIndex(self.__fTweakIndexArray[i])
 
  966                 tweak.setMObject(tweakDataArray[i])
 
  970                 statusAssert(tweak.isCompound(),
 
  971                             "tweak.isCompound() -- Element plug, 'tweak', is not compound")
 
  973                 numChildren = tweak.numChildren()
 
  974                 for j 
in range(numChildren):
 
  975                     tweakChild = tweak.child(j)
 
  979                     if 0 < tweakSrcConnectionCountArray[i*numChildren + j]:
 
  981                         while (k < tweakSrcConnectionCountArray[i*numChildren + j]):
 
  982                             self.__fDGModifier.connect(tweakChild, tweakSrcConnectionPlugArray[srcOffset])
 
  988                     if 0 < tweakDstConnectionCountArray[i*numChildren + j]:
 
  989                         self.__fDGModifier.connect(tweakDstConnectionPlugArray[dstOffset], tweakChild)
 
  995     def __connectNodes(self, modifierNode):
 
  997         This method connects up the modifier nodes, while accounting for DG factors 
  998         such as construction history and tweaks. The method has a series of steps which 
  999         it runs through to process nodes under varying circumstances: 
 1001         1) Gather meshNode connection data (ie. attributes and plugs) 
 1003         2) Gather upstreamNode data - This is history-dependent. If the node has history, 
 1004                                     an actual upstreamNode exists and that is used to 
 1005                                     drive the input of our modifierNode. 
 1007                                     Otherwise, if the node does not have history, the 
 1008                                     meshNode is duplicated, set as an intermediate object 
 1009                                     and regarded as our new upstreamNode which will drive 
 1010                                     the input of our modifierNode. The case with history 
 1011                                     already has this duplicate meshNode at the top, driving 
 1012                                     all other history nodes and serving as a reference 
 1013                                     to the "original state" of the node before any 
 1016         3) Gather modifierNode connection data 
 1018         4) Process tweak data (if it exists) - This is history-dependent. If there is 
 1019                                             history, the tweak data is extracted and deleted 
 1020                                             from the meshNode and encapsulated inside a 
 1021                                             polyTweak node. The polyTweak node is then 
 1022                                             inserted ahead of the modifier node. 
 1024                                             If there is no history, the same is done as 
 1025                                             in the history case, except the tweaks are 
 1026                                             deleted from the duplicate meshNode in addition 
 1027                                             to the actual meshNode. 
 1029         5) Connect the nodes 
 1031         6) Collapse/Bake nodes into the actual meshNode if the meshNode had no previous 
 1032             construction history and construction history recording is turned off. 
 1033             (ie. (!fHasHistory && !fHasRecordHistory) == true ) 
 1038         data = polyModifierCmd.__modifyPolyData()
 
 1043             self.__processMeshNode(data)
 
 1045             statusError(
"processMeshNode")
 
 1050             self.__processUpstreamNode(data)
 
 1052             statusError(
"processUpstreamNode")
 
 1057             self.__processModifierNode(modifierNode, data)
 
 1059             statusError(
"processModifierNode")
 
 1064             self.__processTweaks(data)
 
 1066             statusError(
"processTweaks")
 
 1070         if self.__fHasTweaks:
 
 1071             tweakDestPlug = 
OpenMaya.MPlug(data.tweakNode, data.tweakNodeDestAttr)
 
 1072             self.__fDGModifier.connect(data.upstreamNodeSrcPlug, tweakDestPlug)
 
 1074             tweakSrcPlug = 
OpenMaya.MPlug(data.tweakNode, data.tweakNodeSrcAttr)
 
 1075             modifierDestPlug = 
OpenMaya.MPlug(modifierNode, data.modifierNodeDestAttr)
 
 1076             self.__fDGModifier.connect(tweakSrcPlug, modifierDestPlug)
 
 1078             modifierDestPlug = 
OpenMaya.MPlug(modifierNode, data.modifierNodeDestAttr)
 
 1079             self.__fDGModifier.connect(data.upstreamNodeSrcPlug, modifierDestPlug)
 
 1081         modifierSrcPlug = 
OpenMaya.MPlug(modifierNode, data.modifierNodeSrcAttr)
 
 1082         meshDestAttr = 
OpenMaya.MPlug(data.meshNodeShape, data.meshNodeDestAttr)
 
 1083         self.__fDGModifier.connect(modifierSrcPlug, meshDestAttr)
 
 1085         self.__fDGModifier.doIt()
 
 1090     def __cacheMeshData(self):
 
 1094         meshNode = self.__fDagPath.node()
 
 1098         dagNodeFn.setObject(meshNode)
 
 1099         dupMeshNode = dagNodeFn.duplicate()
 
 1103         dupMeshDagPath.extendToShape()
 
 1105         depNodeFn.setObject(dupMeshDagPath.node())
 
 1107             dupMeshNodeOutMeshPlug = depNodeFn.findPlug(
"outMesh")
 
 1109             statusError(
"Could not retrieve outMesh")
 
 1114             self.__fMeshData = dupMeshNodeOutMeshPlug.asMObject()
 
 1116             statusError(
"Could not retrieve meshData")
 
 1123     def __cacheMeshTweaks(self):
 
 1126         self.__fTweakIndexArray.clear()
 
 1127         self.__fTweakVectorArray.clear()
 
 1131         if self.__fHasTweaks:
 
 1136             meshNode = self.__fDagPath.node()
 
 1138             depNodeFn.setObject(meshNode)
 
 1139             meshTweakPlug = depNodeFn.findPlug(
"pnts")
 
 1143             statusAssert(meshTweakPlug.isArray(),
 
 1144                         "meshTweakPlug.isArray() -- meshTweakPlug is not an array plug" )
 
 1148             numElements = meshTweakPlug.numElements()
 
 1149             for i 
in range(numElements):
 
 1154                 tweak = meshTweakPlug.elementByPhysicalIndex(i)
 
 1159                 if not tweak.isNull():
 
 1162                     logicalIndex = tweak.logicalIndex()
 
 1166                     tweakVector = self.__getFloat3PlugValue(tweak)
 
 1167                     self.__fTweakIndexArray.append(logicalIndex)
 
 1168                     self.__fTweakVectorArray.append(tweakVector)
 
 1173     def __undoCachedMesh(self):
 
 1177         statusAssert(self.__fHasRecordHistory, 
"fHasRecordHistory == true")
 
 1179         if not self.__fHasHistory:
 
 1182             meshNodeShape = self.__fDagPath.node()
 
 1183             dupMeshNodeShape = self.__fDuplicateDagPath.node()
 
 1185             depNodeFn.setObject(meshNodeShape)
 
 1186             meshNodeName = depNodeFn.name()
 
 1188                 meshNodeDestPlug = depNodeFn.findPlug(
"inMesh")
 
 1190                 statusError(
"Could not retrieve inMesh")
 
 1192                 meshNodeOutMeshPlug = depNodeFn.findPlug(
"outMesh")
 
 1194                 statusError(
"Could not retrieve outMesh")
 
 1196             depNodeFn.setObject(dupMeshNodeShape)
 
 1198                 dupMeshNodeSrcPlug = depNodeFn.findPlug(
"outMesh")
 
 1200                 statusError(
"Could not retrieve outMesh")
 
 1210             if self.__fHasTweaks:
 
 1212                 dgModifier.connect(dupMeshNodeSrcPlug, meshNodeDestPlug)
 
 1216                     statusError(
"Could not connect dupMeshNode -> meshNode")
 
 1220                 cmd = 
"dgeval -src %s.inMesh" % meshNodeName
 
 1222                     OpenMaya.MGlobal.executeCommand(cmd, 
False, 
False)
 
 1224                     statusError(
"Could not force DG eval")
 
 1231                     meshData = dupMeshNodeSrcPlug.asMObject()
 
 1233                         meshNodeOutMeshPlug.setMObject(meshData)
 
 1235                         statusError(
"Could not set outMesh")
 
 1237                     statusError(
"Could not retrieve meshData")
 
 1240     def __undoTweakProcessing(self):
 
 1241         if self.__fHasTweaks:
 
 1242             meshNodeShape = self.__fDagPath.node()
 
 1244             depNodeFn.setObject(meshNodeShape)
 
 1245             meshTweakPlug = depNodeFn.findPlug(
"pnts")
 
 1247             statusAssert(meshTweakPlug.isArray(),
 
 1248                     "meshTweakPlug.isArray() -- meshTweakPlug is not an array plug")
 
 1250             numElements = self.__fTweakIndexArray.length()
 
 1252             for i 
in range(numElements):
 
 1253                 tweak = meshTweakPlug.elementByLogicalIndex(self.__fTweakIndexArray[i])
 
 1254                 tweakData = self.__getFloat3asMObject(self.__fTweakVectorArray[i])
 
 1255                 tweak.setMObject(tweakData)
 
 1262     def __undoDirectModifier(self):
 
 1266         meshNode = self.__fDagPath.node()
 
 1267         depNodeFn.setObject( meshNode )
 
 1278         if self.__fHasTweaks:
 
 1281             depNodeFn.setObject(meshNode)
 
 1283                 meshNodeInMeshPlug = depNodeFn.findPlug(
"inMesh")
 
 1285                 statusError(
"Could not retrieve inMesh")
 
 1287             meshNodeName = depNodeFn.name()
 
 1291             dagNodeFn.setObject(meshNode)
 
 1292             dupMeshNode = dagNodeFn.duplicate()
 
 1299             dupMeshDagPath.extendToShape()
 
 1304             depNodeFn.setObject(dupMeshDagPath.node())
 
 1306                 dupMeshNodeOutMeshPlug = depNodeFn.findPlug(
"outMesh")
 
 1308                 statusError(
"Could not retrieve outMesh")
 
 1309             dupMeshNodeOutMeshPlug.setMObject(self.__fMeshData)
 
 1314             dgModifier.connect(dupMeshNodeOutMeshPlug, meshNodeInMeshPlug)
 
 1318                 statusError(
"Could not connect dupMeshNode -> meshNode")
 
 1322             cmd = 
"dgeval -src %s.inMesh" % meshNodeName
 
 1324                 OpenMaya.MGlobal.executeCommand(cmd, 
False, 
False)
 
 1326                 statusError(
"Could not force DG eval")
 
 1335             self.__undoTweakProcessing()
 
 1340             depNodeFn.setObject(meshNode)
 
 1342                 meshNodeOutMeshPlug = depNodeFn.findPlug(
"outMesh")
 
 1344                 statusError(
"Could not retrieve outMesh")
 
 1346                 meshNodeOutMeshPlug.setMObject(self.__fMeshData)
 
 1348                 statusError(
"Could not set meshData")
 
 1355     def __getFloat3PlugValue(self, plug):
 
 1357         object = plug.asMObject()
 
 1361         xParam = OpenMaya.MScriptUtil(0.0)
 
 1362         xPtr = xParam.asFloatPtr()
 
 1363         yParam = OpenMaya.MScriptUtil(0.0)
 
 1364         yPtr = yParam.asFloatPtr()
 
 1365         zParam = OpenMaya.MScriptUtil(0.0)
 
 1366         zPtr = zParam.asFloatPtr()
 
 1367         numDataFn.getData3Float(xPtr, yPtr, zPtr)
 
 1369                         xParam.getFloat(xPtr),
 
 1370                         yParam.getFloat(yPtr),
 
 1371                         zParam.getFloat(zPtr))
 
 1374     def __getFloat3asMObject(self, value):
 
 1377         numDataFn.create(OpenMaya.MFnNumericData.k3Float)
 
 1378         numDataFn.setData3Float(value[0], value[1], value[2])
 
 1379         return numDataFn.object()
 
 1409 class polyModifierFty:
 
 1468 class polyModifierNode(OpenMayaMPx.MPxNode):
 
 1477         OpenMayaMPx.MPxNode.__init__(self)