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)