363 from builtins
import object
364 from builtins
import range
365 import maya.OpenMaya
as OpenMaya
366 import maya.OpenMayaMPx
as OpenMayaMPx
369 def statusError(message):
370 fullMsg =
"Status failed: %s\n" % message
371 sys.stderr.write(fullMsg)
375 def statusAssert(condition, message):
377 fullMsg =
"Assertion failed: %s\n" % message
378 sys.stderr.write(fullMsg)
383 class polyModifierCmd(OpenMayaMPx.MPxCommand):
385 OpenMayaMPx.MPxCommand.__init__(self)
392 self.__fDagPathInitialized =
False
397 self.__fModifierNodeTypeInitialized =
False
398 self.__fModifierNodeNameInitialized =
False
400 self.__fModifierNodeName =
""
403 self.__fHasHistory =
False
404 self.__fHasTweaks =
False
405 self.__fHasRecordHistory =
False
450 def _setMeshNode(self, mesh):
452 Target polyMesh to modify
454 self.__fDagPath = mesh
455 self.__fDagPathInitialized =
True
458 def _getMeshNode(self):
459 return self.__fDagPath
462 def _setModifierNodeType(self, nodeType):
466 self.__fModifierNodeType = nodeType
467 self.__fModifierNodeTypeInitialized =
True
470 def _setModifierNodeName(self, nodeName):
471 self.__fModifierNodeName = nodeName
472 self.__fModifierNodeNameInitialized =
True
475 def _getModifierNodeType(self):
476 return self.__fModifierNodeType
479 def _getModifierNodeName(self):
480 return self.__fModifierNodeName
486 def _initModifierNode(self, modifierNode):
488 Derived classes should override this method if they wish to initialize
489 input attributes on the modifierNode
494 def _directModifier(self, mesh):
496 Derived classes should override this method to provide direct
497 modifications on the meshNode in the case where no history exists and
498 construction history is turned off. (ie. no DG operations desired)
500 This method is called only if history does not exist and history is turned
501 off. At this point, a handle to the meshNode is passed in so a derived
502 class may directly modify the mesh.
507 def _doModifyPoly(self):
508 if self.__isCommandDataValid():
511 self.__collectNodeState()
513 if (
not self.__fHasHistory)
and (
not self.__fHasRecordHistory):
514 meshNode = self.__fDagPath.node()
518 self.__cacheMeshData()
519 self.__cacheMeshTweaks()
523 self._directModifier(meshNode)
525 modifierNode = self.__createModifierNode()
526 self._initModifierNode(modifierNode)
527 self.__connectNodes(modifierNode)
530 def _redoModifyPoly(self):
531 if (
not self.__fHasHistory)
and (
not self.__fHasRecordHistory):
532 meshNode = self.__fDagPath.node()
536 self._directModifier(meshNode)
540 if self.__fHasHistory:
541 self.__fDagModifier.doIt()
542 self.__fDGModifier.doIt()
545 def _undoModifyPoly(self):
546 if (
not self.__fHasHistory)
and (
not self.__fHasRecordHistory):
547 self.__undoDirectModifier()
549 self.__fDGModifier.undoIt()
555 if not self.__fHasHistory:
557 self.__undoCachedMesh()
559 statusError(
"undoCachedMesh")
560 self.__fDagModifier.undoIt()
563 self.__undoTweakProcessing()
565 statusError(
"undoTweakProcessing")
590 class __modifyPolyData(object):
614 def __isCommandDataValid(self):
619 if self.__fDagPathInitialized:
620 self.__fDagPath.extendToShape()
621 if (
not self.__fDagPath.isValid())
or (self.__fDagPath.apiType() != OpenMaya.MFn.kMesh):
628 if (
not self.__fModifierNodeTypeInitialized)
and (
not self.__fModifierNodeNameInitialized):
634 def __collectNodeState(self):
636 Collect node state information on the given polyMeshShape
637 - HasHistory (Construction History exists)
639 - HasRecordHistory (Construction History is turned on)
641 self.__fDagPath.extendToShape()
642 meshNodeShape = self.__fDagPath.node()
645 depNodeFn.setObject(meshNodeShape)
647 inMeshPlug = depNodeFn.findPlug(
"inMesh")
648 self.__fHasHistory = inMeshPlug.isConnected()
654 self.__fHasTweaks =
False
655 tweakPlug = depNodeFn.findPlug(
"pnts")
656 if not tweakPlug.isNull():
659 statusAssert(tweakPlug.isArray(),
660 "tweakPlug.isArray() -- tweakPlug is not an array plug")
662 numElements = tweakPlug.numElements()
663 for i
in range(numElements):
664 tweak = tweakPlug.elementByPhysicalIndex(i)
665 if not tweak.isNull():
666 tweakData = self.__getFloat3PlugValue(tweak)
667 if 0 != tweakData.x
or 0 != tweakData.y
or 0 != tweakData.z:
668 self.__fHasTweaks =
True
672 OpenMaya.MGlobal.executeCommand(
"constructionHistory -q -tgl", result)
673 self.__fHasRecordHistory = (0 != result)
678 def __createModifierNode(self):
680 if self.__fModifierNodeTypeInitialized
or self.__fModifierNodeNameInitialized:
681 if self.__fModifierNodeTypeInitialized:
682 modifierNode = self.__fDGModifier.createNode(self.__fModifierNodeType)
683 elif self.__fModifierNodeNameInitialized:
684 modifierNode = self.__fDGModifier.createNode(self.__fModifierNodeName)
690 inMeshAttr = depNodeFn.attribute(
"inMesh")
691 outMeshAttr = depNodeFn.attribute(
"outMesh")
693 statusAssert(
not (inMeshAttr.isNull()
or outMeshAttr.isNull()),
694 "Invalid Modifier Node: inMesh and outMesh attributes are required.")
700 def __processMeshNode(self, data):
708 data.meshNodeShape = self.__fDagPath.node()
709 dagNodeFn.setObject(data.meshNodeShape)
713 statusAssert(0 < dagNodeFn.parentCount(),
714 "0 < dagNodeFn.parentCount() -- meshNodeshape has no parent transform")
715 data.meshNodeTransform = dagNodeFn.parent(0)
717 data.meshNodeDestPlug = dagNodeFn.findPlug(
"inMesh")
718 data.meshNodeDestAttr = data.meshNodeDestPlug.attribute()
721 def __processUpstreamNode(self, data):
741 if self.__fHasHistory:
746 data.meshNodeDestPlug.connectedTo(tempPlugArray,
True,
False)
750 statusAssert(tempPlugArray.length() == 1,
751 "tempPlugArray.length() == 1 -- 0 or >1 connections on meshNodeShape.inMesh")
752 data.upstreamNodeSrcPlug = tempPlugArray[0]
757 data.upstreamNodeShape = data.upstreamNodeSrcPlug.node()
758 depNodeFn.setObject(data.upstreamNodeShape)
759 data.upstreamNodeSrcAttr = data.upstreamNodeSrcPlug.attribute()
764 self.__fDGModifier.disconnect(data.upstreamNodeSrcPlug, data.meshNodeDestPlug)
773 dagNodeFn.setObject(data.meshNodeShape)
774 data.upstreamNodeTransform = dagNodeFn.duplicate(
False,
False)
775 dagNodeFn.setObject(data.upstreamNodeTransform)
779 statusAssert(0 < dagNodeFn.childCount(),
780 "0 < dagNodeFn.childCount() -- Duplicate meshNode transform has no shape.")
781 data.upstreamNodeShape = dagNodeFn.child(0)
786 self.__fDagModifier.reparentNode(data.upstreamNodeShape, data.meshNodeTransform)
788 statusError(
"reparentNode")
796 self.__fDagModifier.doIt()
798 statusError(
"fDagModifier.doIt()")
803 dagNodeFn.setObject(data.upstreamNodeShape)
804 dagNodeFn.setIntermediateObject(
True)
808 data.upstreamNodeSrcAttr = dagNodeFn.attribute(
"outMesh")
809 data.upstreamNodeSrcPlug = dagNodeFn.findPlug(
"outMesh")
814 self.__fDagModifier.deleteNode(data.upstreamNodeTransform)
816 statusError(
"deleteNode")
825 self.__fDagModifier.doIt()
827 statusError(
"fDagModifier.doIt()")
831 dagNodeFn.getPath(self.__fDuplicateDagPath)
834 def __processModifierNode(self, modifierNode, data):
836 data.modifierNodeSrcAttr = depNodeFn.attribute(
"outMesh")
837 data.modifierNodeDestAttr = depNodeFn.attribute(
"inMesh")
840 def __processTweaks(self, data):
843 self.__fTweakIndexArray.clear()
844 self.__fTweakVectorArray.clear()
854 if self.__fHasTweaks:
870 data.tweakNode = self.__fDGModifier.createNode(
"polyTweak")
871 depNodeFn.setObject(data.tweakNode)
872 data.tweakNodeSrcAttr = depNodeFn.attribute(
"output")
873 data.tweakNodeDestAttr = depNodeFn.attribute(
"inputPolymesh")
874 tweakNodeTweakAttr = depNodeFn.attribute(
"tweak")
876 depNodeFn.setObject(data.meshNodeShape)
877 meshTweakPlug = depNodeFn.findPlug(
"pnts")
881 statusAssert(meshTweakPlug.isArray(),
882 "meshTweakPlug.isArray() -- meshTweakPlug is not an array plug")
886 numElements = meshTweakPlug.numElements()
887 for i
in range(numElements):
892 tweak = meshTweakPlug.elementByPhysicalIndex(i)
897 if not tweak.isNull():
900 logicalIndex = tweak.logicalIndex()
904 tweakData = tweak.asMObject()
905 tweakDataArray.append(tweakData)
906 tweakVector = self.__getFloat3PlugValue(tweak)
907 self.__fTweakIndexArray.append(logicalIndex)
908 self.__fTweakVectorArray.append(tweakVector)
920 statusAssert(tweak.isCompound(),
921 "tweak.isCompound() -- Element tweak plug is not compound")
923 numChildren = tweak.numChildren()
924 for j
in range(numChildren):
925 tweakChild = tweak.child(j)
926 if tweakChild.isConnected():
929 tempPlugArray.clear()
930 if tweakChild.connectedTo(tempPlugArray,
False,
True):
931 numSrcConnections = tempPlugArray.length()
932 tweakSrcConnectionCountArray.append(numSrcConnections)
934 for k
in range(numSrcConnections):
935 tweakSrcConnectionPlugArray.append(tempPlugArray[k])
936 self.__fDGModifier.disconnect(tweakChild, tempPlugArray[k])
938 tweakSrcConnectionCountArray.append(0)
942 tempPlugArray.clear()
943 if tweakChild.connectedTo(tempPlugArray,
True,
False):
946 statusAssert(tempPlugArray.length() == 1,
947 "tempPlugArray.length() == 1 -- 0 or >1 connections on tweakChild")
949 tweakDstConnectionCountArray.append(1)
950 tweakDstConnectionPlugArray.append(tempPlugArray[0])
951 self.__fDGModifier.disconnect(tempPlugArray[0], tweakChild)
953 tweakDstConnectionCountArray.append(0)
955 tweakSrcConnectionCountArray.append(0)
956 tweakDstConnectionCountArray.append(0)
960 polyTweakPlug =
OpenMaya.MPlug(data.tweakNode, tweakNodeTweakAttr)
961 numTweaks = self.__fTweakIndexArray.length()
964 for i
in range(numTweaks):
967 tweak = polyTweakPlug.elementByLogicalIndex(self.__fTweakIndexArray[i])
968 tweak.setMObject(tweakDataArray[i])
972 statusAssert(tweak.isCompound(),
973 "tweak.isCompound() -- Element plug, 'tweak', is not compound")
975 numChildren = tweak.numChildren()
976 for j
in range(numChildren):
977 tweakChild = tweak.child(j)
981 if 0 < tweakSrcConnectionCountArray[i*numChildren + j]:
983 while (k < tweakSrcConnectionCountArray[i*numChildren + j]):
984 self.__fDGModifier.connect(tweakChild, tweakSrcConnectionPlugArray[srcOffset])
990 if 0 < tweakDstConnectionCountArray[i*numChildren + j]:
991 self.__fDGModifier.connect(tweakDstConnectionPlugArray[dstOffset], tweakChild)
997 def __connectNodes(self, modifierNode):
999 This method connects up the modifier nodes, while accounting for DG factors
1000 such as construction history and tweaks. The method has a series of steps which
1001 it runs through to process nodes under varying circumstances:
1003 1) Gather meshNode connection data (ie. attributes and plugs)
1005 2) Gather upstreamNode data - This is history-dependent. If the node has history,
1006 an actual upstreamNode exists and that is used to
1007 drive the input of our modifierNode.
1009 Otherwise, if the node does not have history, the
1010 meshNode is duplicated, set as an intermediate object
1011 and regarded as our new upstreamNode which will drive
1012 the input of our modifierNode. The case with history
1013 already has this duplicate meshNode at the top, driving
1014 all other history nodes and serving as a reference
1015 to the "original state" of the node before any
1018 3) Gather modifierNode connection data
1020 4) Process tweak data (if it exists) - This is history-dependent. If there is
1021 history, the tweak data is extracted and deleted
1022 from the meshNode and encapsulated inside a
1023 polyTweak node. The polyTweak node is then
1024 inserted ahead of the modifier node.
1026 If there is no history, the same is done as
1027 in the history case, except the tweaks are
1028 deleted from the duplicate meshNode in addition
1029 to the actual meshNode.
1031 5) Connect the nodes
1033 6) Collapse/Bake nodes into the actual meshNode if the meshNode had no previous
1034 construction history and construction history recording is turned off.
1035 (ie. (!fHasHistory && !fHasRecordHistory) == true )
1040 data = polyModifierCmd.__modifyPolyData()
1045 self.__processMeshNode(data)
1047 statusError(
"processMeshNode")
1052 self.__processUpstreamNode(data)
1054 statusError(
"processUpstreamNode")
1059 self.__processModifierNode(modifierNode, data)
1061 statusError(
"processModifierNode")
1066 self.__processTweaks(data)
1068 statusError(
"processTweaks")
1072 if self.__fHasTweaks:
1073 tweakDestPlug =
OpenMaya.MPlug(data.tweakNode, data.tweakNodeDestAttr)
1074 self.__fDGModifier.connect(data.upstreamNodeSrcPlug, tweakDestPlug)
1076 tweakSrcPlug =
OpenMaya.MPlug(data.tweakNode, data.tweakNodeSrcAttr)
1077 modifierDestPlug =
OpenMaya.MPlug(modifierNode, data.modifierNodeDestAttr)
1078 self.__fDGModifier.connect(tweakSrcPlug, modifierDestPlug)
1080 modifierDestPlug =
OpenMaya.MPlug(modifierNode, data.modifierNodeDestAttr)
1081 self.__fDGModifier.connect(data.upstreamNodeSrcPlug, modifierDestPlug)
1083 modifierSrcPlug =
OpenMaya.MPlug(modifierNode, data.modifierNodeSrcAttr)
1084 meshDestAttr =
OpenMaya.MPlug(data.meshNodeShape, data.meshNodeDestAttr)
1085 self.__fDGModifier.connect(modifierSrcPlug, meshDestAttr)
1087 self.__fDGModifier.doIt()
1092 def __cacheMeshData(self):
1096 meshNode = self.__fDagPath.node()
1100 dagNodeFn.setObject(meshNode)
1101 dupMeshNode = dagNodeFn.duplicate()
1105 dupMeshDagPath.extendToShape()
1107 depNodeFn.setObject(dupMeshDagPath.node())
1109 dupMeshNodeOutMeshPlug = depNodeFn.findPlug(
"outMesh")
1111 statusError(
"Could not retrieve outMesh")
1116 self.__fMeshData = dupMeshNodeOutMeshPlug.asMObject()
1118 statusError(
"Could not retrieve meshData")
1125 def __cacheMeshTweaks(self):
1128 self.__fTweakIndexArray.clear()
1129 self.__fTweakVectorArray.clear()
1133 if self.__fHasTweaks:
1138 meshNode = self.__fDagPath.node()
1140 depNodeFn.setObject(meshNode)
1141 meshTweakPlug = depNodeFn.findPlug(
"pnts")
1145 statusAssert(meshTweakPlug.isArray(),
1146 "meshTweakPlug.isArray() -- meshTweakPlug is not an array plug" )
1150 numElements = meshTweakPlug.numElements()
1151 for i
in range(numElements):
1156 tweak = meshTweakPlug.elementByPhysicalIndex(i)
1161 if not tweak.isNull():
1164 logicalIndex = tweak.logicalIndex()
1168 tweakVector = self.__getFloat3PlugValue(tweak)
1169 self.__fTweakIndexArray.append(logicalIndex)
1170 self.__fTweakVectorArray.append(tweakVector)
1175 def __undoCachedMesh(self):
1179 statusAssert(self.__fHasRecordHistory,
"fHasRecordHistory == true")
1181 if not self.__fHasHistory:
1184 meshNodeShape = self.__fDagPath.node()
1185 dupMeshNodeShape = self.__fDuplicateDagPath.node()
1187 depNodeFn.setObject(meshNodeShape)
1188 meshNodeName = depNodeFn.name()
1190 meshNodeDestPlug = depNodeFn.findPlug(
"inMesh")
1192 statusError(
"Could not retrieve inMesh")
1194 meshNodeOutMeshPlug = depNodeFn.findPlug(
"outMesh")
1196 statusError(
"Could not retrieve outMesh")
1198 depNodeFn.setObject(dupMeshNodeShape)
1200 dupMeshNodeSrcPlug = depNodeFn.findPlug(
"outMesh")
1202 statusError(
"Could not retrieve outMesh")
1212 if self.__fHasTweaks:
1214 dgModifier.connect(dupMeshNodeSrcPlug, meshNodeDestPlug)
1218 statusError(
"Could not connect dupMeshNode -> meshNode")
1222 cmd =
"dgeval -src %s.inMesh" % meshNodeName
1224 OpenMaya.MGlobal.executeCommand(cmd,
False,
False)
1226 statusError(
"Could not force DG eval")
1233 meshData = dupMeshNodeSrcPlug.asMObject()
1235 meshNodeOutMeshPlug.setMObject(meshData)
1237 statusError(
"Could not set outMesh")
1239 statusError(
"Could not retrieve meshData")
1242 def __undoTweakProcessing(self):
1243 if self.__fHasTweaks:
1244 meshNodeShape = self.__fDagPath.node()
1246 depNodeFn.setObject(meshNodeShape)
1247 meshTweakPlug = depNodeFn.findPlug(
"pnts")
1249 statusAssert(meshTweakPlug.isArray(),
1250 "meshTweakPlug.isArray() -- meshTweakPlug is not an array plug")
1252 numElements = self.__fTweakIndexArray.length()
1254 for i
in range(numElements):
1255 tweak = meshTweakPlug.elementByLogicalIndex(self.__fTweakIndexArray[i])
1256 tweakData = self.__getFloat3asMObject(self.__fTweakVectorArray[i])
1257 tweak.setMObject(tweakData)
1264 def __undoDirectModifier(self):
1268 meshNode = self.__fDagPath.node()
1269 depNodeFn.setObject( meshNode )
1280 if self.__fHasTweaks:
1283 depNodeFn.setObject(meshNode)
1285 meshNodeInMeshPlug = depNodeFn.findPlug(
"inMesh")
1287 statusError(
"Could not retrieve inMesh")
1289 meshNodeName = depNodeFn.name()
1293 dagNodeFn.setObject(meshNode)
1294 dupMeshNode = dagNodeFn.duplicate()
1301 dupMeshDagPath.extendToShape()
1306 depNodeFn.setObject(dupMeshDagPath.node())
1308 dupMeshNodeOutMeshPlug = depNodeFn.findPlug(
"outMesh")
1310 statusError(
"Could not retrieve outMesh")
1311 dupMeshNodeOutMeshPlug.setMObject(self.__fMeshData)
1316 dgModifier.connect(dupMeshNodeOutMeshPlug, meshNodeInMeshPlug)
1320 statusError(
"Could not connect dupMeshNode -> meshNode")
1324 cmd =
"dgeval -src %s.inMesh" % meshNodeName
1326 OpenMaya.MGlobal.executeCommand(cmd,
False,
False)
1328 statusError(
"Could not force DG eval")
1337 self.__undoTweakProcessing()
1342 depNodeFn.setObject(meshNode)
1344 meshNodeOutMeshPlug = depNodeFn.findPlug(
"outMesh")
1346 statusError(
"Could not retrieve outMesh")
1348 meshNodeOutMeshPlug.setMObject(self.__fMeshData)
1350 statusError(
"Could not set meshData")
1357 def __getFloat3PlugValue(self, plug):
1359 object = plug.asMObject()
1363 xParam = OpenMaya.MScriptUtil(0.0)
1364 xPtr = xParam.asFloatPtr()
1365 yParam = OpenMaya.MScriptUtil(0.0)
1366 yPtr = yParam.asFloatPtr()
1367 zParam = OpenMaya.MScriptUtil(0.0)
1368 zPtr = zParam.asFloatPtr()
1369 numDataFn.getData3Float(xPtr, yPtr, zPtr)
1371 xParam.getFloat(xPtr),
1372 yParam.getFloat(yPtr),
1373 zParam.getFloat(zPtr))
1376 def __getFloat3asMObject(self, value):
1379 numDataFn.create(OpenMaya.MFnNumericData.k3Float)
1380 numDataFn.setData3Float(value[0], value[1], value[2])
1381 return numDataFn.object()
1411 class polyModifierFty(object):
1470 class polyModifierNode(OpenMayaMPx.MPxNode):
1479 OpenMayaMPx.MPxNode.__init__(self)