class AngleToDouble(om2.MPxNode): """ Main class of gfUtilAngleToDouble node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inAngle = om2.MObject() outDouble = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return AngleToDouble() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to AngleToDouble class. Instances of AngleToDouble will use these attributes to create plugs for use in the compute() method. """ uAttr = om2.MFnUnitAttribute() nAttr = om2.MFnNumericAttribute() AngleToDouble.inAngle = uAttr.create("angle", "angle", om2.MFnUnitAttribute.kAngle, 0.0) INPUT_ATTR(uAttr) AngleToDouble.outDouble = nAttr.create("outDouble", "od", om2.MFnNumericData.kDouble, 0.0) OUTPUT_ATTR(nAttr) AngleToDouble.addAttribute(AngleToDouble.inAngle) AngleToDouble.addAttribute(AngleToDouble.outDouble) AngleToDouble.attributeAffects(AngleToDouble.inAngle, AngleToDouble.outDouble) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use if plug != AngleToDouble.outDouble: return om2.kUnknownParameter angle = dataBlock.inputValue( AngleToDouble.inAngle).asAngle().asDegrees() outDoubleHandle = dataBlock.outputValue(AngleToDouble.outDouble) outDoubleHandle.setDouble(angle) outDoubleHandle.setClean()
class TestNode(om2.MPxNode): """ Main class of gfTestNode node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inAttr = om2.MObject() outAttr = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return TestNode() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to TestNode class. Instances of TestNode will use these attributes to create plugs for use in the compute() method. """ nAttr = om2.MFnNumericAttribute() TestNode.inAttr = nAttr.createPoint("inAttr", "ina") INPUT_ATTR(nAttr) TestNode.outAttr = nAttr.createPoint("outAttr", "outa") OUTPUT_ATTR(nAttr) TestNode.addAttribute(TestNode.inAttr) TestNode.addAttribute(TestNode.outAttr) TestNode.attributeAffects(TestNode.inAttr, TestNode.outAttr) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use if plug != TestNode.outAttr: return om2.kUnknownParameter inAttrValue = dataBlock.inputValue(TestNode.inAttr).asVector() outAttrHandle = dataBlock.outputValue(TestNode.outAttr) outAttrHandle.set3Float(inAttrValue.x, inAttrValue.y, inAttrValue.z) outAttrHandle.setClean()
class MeshController(omui2.MPxLocatorNode): """ Main class of gfMeshController node. """ kNodeName = "" kNodeClassify = "" kNodeRegistrantID = "" kNodeID = "" inIndexList = om2.MObject() inOffset = om2.MObject() inMesh = om2.MObject() inColor = om2.MObject() ctrlVertices = [] bBox = om2.MBoundingBox() def __init__(self): """ Constructor. """ omui2.MPxLocatorNode.__init__(self) def postConstructor(self): """ Post Constructor. """ thisMob = self.thisMObject() om2.MFnDependencyNode(thisMob).setName("%sShape#" % MeshController.kNodeName) @staticmethod def creator(): """ Maya creator function. """ return MeshController() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to MeshController class. Instances of MeshController will use these attributes to create plugs for use in the compute() method. """ tAttr = om2.MFnTypedAttribute() nAttr = om2.MFnNumericAttribute() MeshController.inIndexList = tAttr.create("indexList", "index", om2.MFnData.kString) INPUT_ATTR(tAttr) MeshController.inOffset = nAttr.create("offset", "offset", om2.MFnNumericData.kFloat, 0.0) INPUT_ATTR(nAttr) MeshController.inMesh = tAttr.create("controlMesh", "controlMesh", om2.MFnData.kMesh) INPUT_ATTR(tAttr) MeshController.inColor = nAttr.createColor("color", "color") nAttr.default = (1.0, 0.455, 0.086) INPUT_ATTR(nAttr) MeshController.addAttribute(MeshController.inIndexList) MeshController.addAttribute(MeshController.inOffset) MeshController.addAttribute(MeshController.inMesh) MeshController.addAttribute(MeshController.inColor) @staticmethod def listToMIntArray(strList): """ Convert a list of int to a MIntArray instance. """ # pylint: disable=undefined-variable instance = om2.MIntArray() for i in strList: try: instance.append(int(i)) except ValueError: pass return instance @staticmethod def getGeometryPoints(meshMob, indexStr, offset, transform): """ Find the info of the geometry who will be drawed. """ # vtxNormals = [] # vtxPositions = [] # if not meshMob.isNull(): # polyIndexList = MeshController.listToMIntArray(indexStr.split(",")) # itPoly = om2.MItMeshPolygon(meshMob) # while not itPoly.isDone(): # if itPoly.index() in polyIndexList: # vtxNormals.append(itPoly.getNormals()) # vtxPositions.append(itPoly.getPoints(om2.MSpace.kWorld)) # itPoly.next(None) # return [vtxNormals, vtxPositions] #============================================================================== # # 47 FPS AVERAGE WITH NO EDGES | 27 FPS AVERAGE WITH EDGES # vtxNormals = [] # vtxPositions = [] # if not meshMob.isNull(): # polyIndexList = MeshController.listToMIntArray(indexStr.split(",")) # itPoly = om2.MItMeshPolygon(meshMob) # for index in polyIndexList: # itPoly.setIndex(index) # vtxNormals.append(itPoly.getNormals()) # vtxPositions.append(itPoly.getPoints(om2.MSpace.kWorld)) # return [vtxNormals, vtxPositions] #============================================================================== # # 45 FPS AVERAGE WITH NO EDGES | 25 FPS AVERAGE WITH EDGES # meshVtxPos = om2.MPointArray() # vtxIndexList = [] # vtxPositions = [] # if not meshMob.isNull(): # polyIndexList = MeshController.listToMIntArray(indexStr.split(",")) # meshFn = om2.MFnMesh(meshMob) # meshVtxPos = meshFn.getPoints(om2.MSpace.kWorld) # for index in polyIndexList: # vtxIndexList.append(meshFn.getPolygonVertices(index)) # for poly in vtxIndexList: # pntArray = om2.MPointArray() # for pnt in poly: # pntArray.append(meshVtxPos[pnt]) # vtxPositions.append(pntArray) # return [[], vtxPositions] #============================================================================== # 47 FPS AVERAGE WITH NO EDGES | 27 FPS AVERAGE WITH EDGES outPnts = [] pntOffTolerance = 0.01 tolerance = offset + pntOffTolerance bBox = om2.MBoundingBox() if not meshMob.isNull(): polyIndexList = MeshController.listToMIntArray(indexStr.split(",")) itPoly = om2.MItMeshPolygon(meshMob) for index in polyIndexList: itPoly.setIndex(index) polyVtxNormals = itPoly.getNormals() polyVtxPos = itPoly.getPoints(om2.MSpace.kWorld) outPolyVtxPos = om2.MPointArray() for i, pnt in enumerate(polyVtxPos): curPnt = pnt curNormal = polyVtxNormals[i] outPnt = (curPnt + (curNormal * tolerance)) * transform bBox.expand(outPnt) outPolyVtxPos.append(outPnt) outPnts.append(outPolyVtxPos) MeshController.ctrlVertices = outPnts MeshController.bBox = bBox def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=unused-argument return def draw(self, view, path, style, status): """ Draw custom geometry in the viewport using OpenGL calls. * view [M3dView] is a 3D view that is being drawn into. * path [MDagPath] to the parent (transform node) of this locator in the DAG. To obtain the locator shape node, use MDagPath::extendToShape() if there is only one shape node under the transform or MDagPath::extendToShapeDirectlyBelow(unsigned int index) with the shape index if there are multiple shapes under the transform. * style [M3dView.DisplayStyle] is the style to draw object in. * status [M3dView.DisplayStatus] is the selection status of the object. """ # pylint: disable=unused-argument # thisMob = self.thisMObject() # mWorldInv = path.inclusiveMatrixInverse() # indexStr = om2.MPlug(thisMob, MeshController.inIndexList).asString() # offset = om2.MPlug(thisMob, MeshController.inOffset).asFloat() # mesh = om2.MPlug(thisMob, MeshController.inMesh).asMDataHandle().asMesh() # color = om2.MPlug(thisMob, MeshController.inColor).asMDataHandle().asFloatVector() # # If plugs are dirty calculate the geometry points # MeshController.getGeometryPoints(mesh, indexStr, offset, mWorldInv) # view.beginGL() # glRenderer = omr1.MHardwareRenderer.theRenderer() # glFT = glRenderer.glFunctionTable() # glFT.glPushAttrib(omr1.MGL_CURRENT_BIT) # glFT.glDisable(omr1.MGL_CULL_FACE) # glFT.glEnable(omr1.MGL_BLEND) # glFT.glBlendFunc(omr1.MGL_SRC_ALPHA, omr1.MGL_ONE_MINUS_SRC_ALPHA) # color = om2.MFloatVector(color.x, color.y, color.z) # if status == view.kDormant: # # Not selected # alpha = 0.25 # elif status == view.kActive: # # Multiselection # alpha = 0.65 # elif status == view.kLead: # # Selected # alpha = 0.5 # if style == view.kFlatShaded or style == view.kGouraudShaded: # glFT.glColor4f(color.x, color.y, color.z, alpha) # for poly in MeshController.ctrlVertices: # glFT.glBegin(omr1.MGL_POLYGON) # for pnt in poly: # glFT.glVertex3f(pnt.x, pnt.y, pnt.z) # glFT.glEnd() # if style == view.kWireFrame: # glFT.glColor4f(color.x, color.y, color.z, 1.0) # glFT.glBegin(omr1.MGL_LINES) # for poly in MeshController.ctrlVertices: # for i, pnt in enumerate(poly): # glFT.glVertex3f(pnt.x, pnt.y, pnt.z) # if i == len(poly) - 1: # glFT.glVertex3f(poly[0].x, poly[0].y, poly[0].z) # else: # glFT.glVertex3f(poly[i+1].x, poly[i+1].y, poly[i+1].z) # glFT.glEnd() # glFT.glPopAttrib() # view.endGL() return def isBounded(self): """isBounded?""" return True def isTransparent(self): """isTransparent?""" return True def boundingBox(self): """Return the boundingBox""" if not MeshController.ctrlVertices: thisMob = self.thisMObject() thisPath = om2.MDagPath.getAPathTo(thisMob) transformPath = om2.MDagPath.getAPathTo( om2.MFnDagNode(thisPath).parent(0)) mWorldInv = transformPath.inclusiveMatrixInverse() indexStr = om2.MPlug(thisMob, MeshController.inIndexList).asString() offset = om2.MPlug(thisMob, MeshController.inOffset).asFloat() mesh = om2.MPlug(thisMob, MeshController.inMesh).asMDataHandle().asMesh() MeshController.getGeometryPoints(mesh, indexStr, offset, mWorldInv) return MeshController.bBox def preEvaluation(self, context, evaluationNode): """ Called before this node is evaluated by Evaluation Manager. * context [MDGContext] is the context which the evaluation is happening. * evaluationNode [MEvaluationNode] the evaluation node which contains information about the dirty plugs that are about to be evaluated for the context. Should be only used to query information. """ if context.isNormal(): if evaluationNode.dirtyPlugExists(MeshController.inOffset): omr2.MRenderer.setGeometryDrawDirty(self.thisMObject()) elif evaluationNode.dirtyPlugExists(MeshController.inIndexList): omr2.MRenderer.setGeometryDrawDirty(self.thisMObject()) elif evaluationNode.dirtyPlugExists(MeshController.inMesh): omr2.MRenderer.setGeometryDrawDirty(self.thisMObject())
class SpaceConstraint(om2.MPxNode): """ Main class of gfUtilSpaceConstraint node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" kCallbackIDs = om2.MCallbackIdArray() kCallbackNodes = om2.MObjectArray() inSpace = om2.MObject() inOffset = om2.MObject() inOffsetMatches = om2.MObject() inTarget = om2.MObject() inAutoFillOff = om2.MObject() inSpaceMatch = om2.MObject() outConstTrans = om2.MObject() outConstRot = om2.MObject() outConstSca = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) self.lastSpace = 0 self.constraintObject = None @staticmethod def creator(): """ Maya creator function. """ return SpaceConstraint() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to SpaceConstraint class. Instances of SpaceConstraint will use these attributes to create plugs for use in the compute() method. """ mAttr = om2.MFnMatrixAttribute() nAttr = om2.MFnNumericAttribute() uAttr = om2.MFnUnitAttribute() SpaceConstraint.inSpace = nAttr.create("space", "space", om2.MFnNumericData.kShort, 0) nAttr.setMin(0) INPUT_ATTR(nAttr) SpaceConstraint.inOffset = mAttr.create("offset", "offset", om2.MFnMatrixAttribute.kDouble) mAttr.array = True INPUT_ATTR(mAttr) SpaceConstraint.inOffsetMatches = mAttr.create( "offsetMatch", "offm", om2.MFnMatrixAttribute.kDouble) mAttr.array = True INPUT_ATTR(mAttr) SpaceConstraint.inTarget = mAttr.create("target", "target", om2.MFnMatrixAttribute.kDouble) mAttr.array = True INPUT_ATTR(mAttr) SpaceConstraint.inAutoFillOff = nAttr.create( "autoFillOffsets", "afoff", om2.MFnNumericData.kBoolean, True) INPUT_ATTR(nAttr) SpaceConstraint.inSpaceMatch = nAttr.create( "spaceMatch", "spaceMatch", om2.MFnNumericData.kBoolean, True) INPUT_ATTR(nAttr) SpaceConstraint.outConstTrans = nAttr.createPoint( "constraintTranslate", "ctrans") OUTPUT_ATTR(nAttr) rotX = uAttr.create("constraintRotateX", "crotx", om2.MFnUnitAttribute.kAngle, 0.0) rotY = uAttr.create("constraintRotateY", "croty", om2.MFnUnitAttribute.kAngle, 0.0) rotZ = uAttr.create("constraintRotateZ", "crotz", om2.MFnUnitAttribute.kAngle, 0.0) SpaceConstraint.outConstRot = nAttr.create("constraintRotate", "crot", rotX, rotY, rotZ) OUTPUT_ATTR(nAttr) SpaceConstraint.outConstSca = nAttr.createPoint( "constraintScale", "csca") nAttr.default = (1.0, 1.0, 1.0) OUTPUT_ATTR(nAttr) SpaceConstraint.addAttribute(SpaceConstraint.inSpace) SpaceConstraint.addAttribute(SpaceConstraint.inAutoFillOff) SpaceConstraint.addAttribute(SpaceConstraint.inSpaceMatch) SpaceConstraint.addAttribute(SpaceConstraint.inOffset) SpaceConstraint.addAttribute(SpaceConstraint.inOffsetMatches) SpaceConstraint.addAttribute(SpaceConstraint.inTarget) SpaceConstraint.addAttribute(SpaceConstraint.outConstTrans) SpaceConstraint.addAttribute(SpaceConstraint.outConstRot) SpaceConstraint.addAttribute(SpaceConstraint.outConstSca) SpaceConstraint.attributeAffects(SpaceConstraint.inSpace, SpaceConstraint.outConstTrans) SpaceConstraint.attributeAffects(SpaceConstraint.inOffset, SpaceConstraint.outConstTrans) SpaceConstraint.attributeAffects(SpaceConstraint.inOffsetMatches, SpaceConstraint.outConstTrans) SpaceConstraint.attributeAffects(SpaceConstraint.inTarget, SpaceConstraint.outConstTrans) SpaceConstraint.attributeAffects(SpaceConstraint.inSpace, SpaceConstraint.outConstRot) SpaceConstraint.attributeAffects(SpaceConstraint.inOffset, SpaceConstraint.outConstRot) SpaceConstraint.attributeAffects(SpaceConstraint.inOffsetMatches, SpaceConstraint.outConstRot) SpaceConstraint.attributeAffects(SpaceConstraint.inTarget, SpaceConstraint.outConstRot) SpaceConstraint.attributeAffects(SpaceConstraint.inSpace, rotX) SpaceConstraint.attributeAffects(SpaceConstraint.inOffset, rotX) SpaceConstraint.attributeAffects(SpaceConstraint.inOffsetMatches, rotX) SpaceConstraint.attributeAffects(SpaceConstraint.inTarget, rotX) SpaceConstraint.attributeAffects(SpaceConstraint.inSpace, rotY) SpaceConstraint.attributeAffects(SpaceConstraint.inOffset, rotY) SpaceConstraint.attributeAffects(SpaceConstraint.inOffsetMatches, rotY) SpaceConstraint.attributeAffects(SpaceConstraint.inTarget, rotY) SpaceConstraint.attributeAffects(SpaceConstraint.inSpace, rotZ) SpaceConstraint.attributeAffects(SpaceConstraint.inOffset, rotZ) SpaceConstraint.attributeAffects(SpaceConstraint.inOffsetMatches, rotZ) SpaceConstraint.attributeAffects(SpaceConstraint.inTarget, rotZ) SpaceConstraint.attributeAffects(SpaceConstraint.inSpace, SpaceConstraint.outConstSca) SpaceConstraint.attributeAffects(SpaceConstraint.inOffset, SpaceConstraint.outConstSca) SpaceConstraint.attributeAffects(SpaceConstraint.inOffsetMatches, SpaceConstraint.outConstSca) SpaceConstraint.attributeAffects(SpaceConstraint.inTarget, SpaceConstraint.outConstSca) def postConstructor(self): """ Post Constructor. """ thisMob = self.thisMObject() # # Add space match callback # callback = om2.MNodeMessage.addAttributeChangedCallback(thisMob, SpaceConstraint.spaceMatchCallback, self) # SpaceConstraint.kCallbackIDs.append(callback) # SpaceConstraint.kCallbackNodes.append(thisMob) # spacePlug = om2.MPlug(thisMob, SpaceConstraint.inSpace) # callback = om2.MNodeMessage.addKeyableChangeOverride(spacePlug, SpaceConstraint.spaceMatchCallback2, self) # SpaceConstraint.kCallbackIDs.append(callback) # SpaceConstraint.kCallbackNodes.append(thisMob) # om2.MGlobal.displayInfo("Node callback: %s" % str(callback)) # om2.MGlobal.displayInfo("All callbacks: %s" % str(SpaceConstraint.kCallbackIDs)) # callback = om2.MNodeMessage.addNodeDirtyPlugCallback(thisMob, SpaceConstraint.spaceMatchCallback2, self) # SpaceConstraint.kCallbackIDs.append(callback) # SpaceConstraint.kCallbackNodes.append(thisMob) # om2.MGlobal.displayInfo("Node callback: %s" % str(callback)) # om2.MGlobal.displayInfo("All callbacks: %s" % str(SpaceConstraint.kCallbackIDs)) # om2.MUserEventMessage.registerUserEvent("preCompute") # callback1 = om2.MUserEventMessage.addUserEventCallback("preCompute", SpaceConstraint.spaceMatchCallback3, self) # callback1 = om2.MDGMessage.addTimeChangeCallback(SpaceConstraint.spaceMatchCallback3, self) # callback2 = om2.MNodeMessage.addAttributeChangedCallback(thisMob, SpaceConstraint.spaceMatchCallback4, self) # SpaceConstraint.kCallbackIDs.append(callback1) # SpaceConstraint.kCallbackIDs.append(callback2) # SpaceConstraint.kCallbackNodes.append(thisMob) # om2.MGlobal.displayInfo("All callbacks: %s" % str(SpaceConstraint.kCallbackIDs)) callback1 = oma2.MAnimMessage.addAnimKeyframeEditedCallback( SpaceConstraint.spaceMatchCallback5, self) callback2 = om2.MDGMessage.addTimeChangeCallback( SpaceConstraint.spaceMatchCallback3, self) SpaceConstraint.kCallbackIDs.append(callback1) SpaceConstraint.kCallbackIDs.append(callback2) SpaceConstraint.kCallbackNodes.append(thisMob) om2.MGlobal.displayInfo("All callbacks: %s" % str(SpaceConstraint.kCallbackIDs)) # TODO: Using MDGMessage.timeChanged won't work because of jump in time. Instead use MAnimMessage to track keyframes. # TODO: If the time in a animation curve change, and the plug is animated, read the anim curve and set the space match. @staticmethod def spaceMatchCallback3(time, clientData): """Test""" om2.MGlobal.displayInfo("Updating animation...") oma2.MAnimMessage.flushAnimKeyframeEditedCallbacks() # thisMob = clientData.thisMObject() # spacePlug = om2.MPlug(thisMob, SpaceConstraint.inSpace) # oldSpaceValue = str(spacePlug.asShort()) # clientData.setDependentsDirty() # spacePlug = om2.MPlug(thisMob, SpaceConstraint.inSpace) # newSpaceValue = str(spacePlug.asShort()) # om2.MGlobal.displayInfo("[timeChanged] Old space value: %s | Current space value: %s" % (oldSpaceValue, newSpaceValue)) @staticmethod def spaceMatchCallback5(editedKeys, clientData): """Test""" om2.MGlobal.displayInfo("Hello ma friend!") return True @staticmethod def spaceMatchCallback4(msg, plug, otherPlug, clientData): """Test""" thisMob = clientData.thisMObject() if msg == 2056: if plug == SpaceConstraint.inSpace: spacePlug = om2.MPlug(thisMob, SpaceConstraint.inSpace) om2.MGlobal.displayInfo( "[attributeChanged] Current space value: %s" % str(spacePlug.asShort())) @staticmethod def spaceMatchCallback2(node, plug, clientData): """Node dirty callback. Works, but not quite. """ # 1- Check if space plug is dirty. if plug == SpaceConstraint.inSpace: # 2- Check if clientData is valid. thisMob = clientData.thisMObject() if not isinstance(clientData, SpaceConstraint): om2.MGlobal.displayError( "[gfTools] gfSpaceConstraint don't recognize the clientData for space matching. Callback functionality skipped." ) return # 3- Check if automatic space matching is enable. match = om2.MPlug(thisMob, SpaceConstraint.inSpaceMatch).asBool() if not match: return # 4- Check if space attribute changed. lastValue = clientData.lastSpace curValue = plug.asShort() spacePlug = om2.MPlug(thisMob, SpaceConstraint.inSpace) om2.MGlobal.displayInfo("Current space value: %s" % str(spacePlug.asShort())) if curValue == lastValue: return # 5- Check if any output is been used and get the output object. allClear = clientData.checkOutputConnections(thisMob) # 6- Check the inputs. targetPlug = om2.MPlug(thisMob, SpaceConstraint.inTarget) offsetPlug = om2.MPlug(thisMob, SpaceConstraint.inOffset) offsetMatchPlug = om2.MPlug(thisMob, SpaceConstraint.inOffsetMatches) numTarget = targetPlug.numElements() - 1 if curValue > numTarget: curValue = numTarget # 7- If the node have necessary connections... if not allClear and (numTarget + 1) > 0: outObj = clientData.constraintObject if outObj.hasFn(om2.MFn.kDagNode): # 8- Get the last world matrix of the target. lastOffsetMatchPlug = offsetMatchPlug.elementByLogicalIndex( lastValue) lastOffsetMatchObj = lastOffsetMatchPlug.asMObject() mtxDataFn = om2.MFnMatrixData(lastOffsetMatchObj) mOffsetMatch = mtxDataFn.matrix() lastOffsetPlug = offsetPlug.elementByLogicalIndex( lastValue) lastOffsetObj = lastOffsetPlug.asMObject() mtxDataFn = om2.MFnMatrixData(lastOffsetObj) mOffset = mtxDataFn.matrix() lastTargetPlug = targetPlug.elementByLogicalIndex( lastValue) lastTargetObj = lastTargetPlug.asMObject() mtxDataFn = om2.MFnMatrixData(lastTargetObj) mTarget = mtxDataFn.matrix() mLastOutputW = mOffsetMatch * mOffset * mTarget # 9- Get the current world matrix of the target. curTargetPlug = targetPlug.elementByLogicalIndex(curValue) curTargetObj = curTargetPlug.asMObject() mtxDataFn = om2.MFnMatrixData(curTargetObj) mTarget = mtxDataFn.matrix() curOffsetPlug = offsetPlug.elementByLogicalIndex(curValue) curOffsetObj = curOffsetPlug.asMObject() mtxDataFn = om2.MFnMatrixData(curOffsetObj) mOffset = mtxDataFn.matrix() mCurOutputW = mOffset * mTarget # 10- Get the result of the match and set it into the plug. mResult = mLastOutputW * mCurOutputW.inverse() curOffsetMatchPlug = offsetMatchPlug.elementByLogicalIndex( curValue) curOffsetMatchHdle = curOffsetMatchPlug.asMDataHandle() curOffsetMatchHdle.setMMatrix(mResult) curOffsetMatchPlug.setMDataHandle(curOffsetMatchHdle) # 11- Clean the offset match plug from last value. lastOffsetMatchHdle = lastOffsetMatchPlug.asMDataHandle() lastOffsetMatchHdle.setMMatrix(om2.MMatrix()) lastOffsetMatchPlug.setMDataHandle(lastOffsetMatchHdle) om2.MGlobal.displayInfo("Performing space matching...") # 12- Store the current space value in class to be accessed later on. clientData.lastSpace = curValue @staticmethod def spaceMatchCallback(msg, plug, otherPlug, clientData): """ The callback function. Note: Attribute Changed messages will not be generated while Maya is either in playback or scrubbing modes. If you need to do something during playback or scrubbing you will have to register a callback for the timeChanged message which is the only message that is sent during those modes. * msg [MNodeMessage::AttributeMessage] is the kind of attribute change triggering the callback. * plug [MPlug] is the node's plug where the connection changed. * otherPlug [MPlug] is the plug opposite the node's plug where the connection changed. * clientData [void*] is the user defined data passed to this callback function. """ # pylint: disable=unused-argument # 6144 = create input array element. # 16385 = connecting output. # 16386 = disconnecting output. # 2052 = change output in compute method. # 2056 = set attribute. # 10240 = delete element plug from array attribute. # 18434 = disconnect input plug. # 18433 = connect input plug. thisMob = clientData.thisMObject() if not isinstance(clientData, SpaceConstraint): # SpaceConstraint *pMesh = static_cast<SpaceConstraint *>(clientData); om2.MGlobal.displayError( "[gfTools] gfSpaceConstraint don't recognize the clientData for space match callback. Callback functionality skiped." ) return match = om2.MPlug(thisMob, SpaceConstraint.inSpaceMatch).asBool() if not match: return if msg == 2056: if plug == SpaceConstraint.inSpace: lastValue = clientData.lastSpace curValue = plug.asShort() # 1- Get the output plug allClear = clientData.checkOutputConnections(thisMob) # 2- Check if the curValue is valid. If it is not, get the last valid. targetPlug = om2.MPlug(thisMob, SpaceConstraint.inTarget) offsetPlug = om2.MPlug(thisMob, SpaceConstraint.inOffset) offsetMatchPlug = om2.MPlug(thisMob, SpaceConstraint.inOffsetMatches) numTarget = targetPlug.numElements() - 1 if curValue > numTarget: curValue = numTarget # 3- If the node have necessary connections... if not allClear and (numTarget + 1) > 0: outObj = clientData.constraintObject if outObj.hasFn(om2.MFn.kDagNode): # 4- Get the last world matrix of the target lastOffsetMatchPlug = offsetMatchPlug.elementByLogicalIndex( lastValue) lastOffsetMatchObj = lastOffsetMatchPlug.asMObject() mtxDataFn = om2.MFnMatrixData(lastOffsetMatchObj) mOffsetMatch = mtxDataFn.matrix() lastOffsetPlug = offsetPlug.elementByLogicalIndex( lastValue) lastOffsetObj = lastOffsetPlug.asMObject() mtxDataFn = om2.MFnMatrixData(lastOffsetObj) mOffset = mtxDataFn.matrix() lastTargetPlug = targetPlug.elementByLogicalIndex( lastValue) lastTargetObj = lastTargetPlug.asMObject() mtxDataFn = om2.MFnMatrixData(lastTargetObj) mTarget = mtxDataFn.matrix() mLastOutputW = mOffsetMatch * mOffset * mTarget # 5- Get the current world matrix of the target curTargetPlug = targetPlug.elementByLogicalIndex( curValue) curTargetObj = curTargetPlug.asMObject() mtxDataFn = om2.MFnMatrixData(curTargetObj) mTarget = mtxDataFn.matrix() curOffsetPlug = offsetPlug.elementByLogicalIndex( curValue) curOffsetObj = curOffsetPlug.asMObject() mtxDataFn = om2.MFnMatrixData(curOffsetObj) mOffset = mtxDataFn.matrix() mCurOutputW = mOffset * mTarget # 6- Get the result of the match and set it into the plug mResult = mLastOutputW * mCurOutputW.inverse() curOffsetMatchPlug = offsetMatchPlug.elementByLogicalIndex( curValue) curOffsetMatchHdle = curOffsetMatchPlug.asMDataHandle() curOffsetMatchHdle.setMMatrix(mResult) curOffsetMatchPlug.setMDataHandle(curOffsetMatchHdle) # 7- Clean the offset match plug from last value lastOffsetMatchHdle = lastOffsetMatchPlug.asMDataHandle( ) lastOffsetMatchHdle.setMMatrix(om2.MMatrix()) lastOffsetMatchPlug.setMDataHandle(lastOffsetMatchHdle) clientData.lastSpace = curValue def checkOutputConnections(self, thisMob): """ Check if any output plug is connected. If it have connections, return False and the object connected. """ outPlugArray = om2.MPlugArray([ om2.MPlug(thisMob, SpaceConstraint.outConstTrans), om2.MPlug(thisMob, SpaceConstraint.outConstRot), om2.MPlug(thisMob, SpaceConstraint.outConstSca) ]) allClear = True for plug in outPlugArray: if plug.isConnected: allClear = False objPlugs = plug.connectedTo(False, True) obj = objPlugs[0].node() for outPlug in objPlugs: outNode = outPlug.node() if self.constraintObject is not None: if outNode == self.constraintObject: obj = self.constraintObject break break self.constraintObject = None if allClear else obj return allClear def autoFillOffset(self, thisMob, index): """Auto fill offsets attributes.""" allClear = self.checkOutputConnections(thisMob) obj = self.constraintObject if not allClear: if obj.hasFn(om2.MFn.kDagNode): objPath = om2.MFnDagNode(obj).getPath() mOut = objPath.inclusiveMatrix() targetPlug = om2.MPlug(thisMob, SpaceConstraint.inTarget) curTargetPlug = targetPlug.elementByLogicalIndex(index) curTargetObj = curTargetPlug.asMObject() mtxDataFn = om2.MFnMatrixData(curTargetObj) mTarget = mtxDataFn.matrix() mOffset = mOut * mTarget.inverse() offsetPlug = om2.MPlug(thisMob, SpaceConstraint.inOffset) curOffsetPlug = offsetPlug.elementByLogicalIndex(index) curOffsetHdle = curOffsetPlug.asMDataHandle() mCurOffset = curOffsetHdle.asMatrix() if mCurOffset == om2.MMatrix(): curOffsetHdle.setMMatrix(mOffset) curOffsetPlug.setMDataHandle(curOffsetHdle) def connectionMade(self, plug, otherPlug, asSrc): """This method gets called when connections are made to attributes of this node. * plug (MPlug) is the attribute on this node. * otherPlug (MPlug) is the attribute on the other node. * asSrc (bool) is this plug a source of the connection. """ thisMob = self.thisMObject() autoFill = om2.MPlug(thisMob, SpaceConstraint.inAutoFillOff).asBool() self.checkOutputConnections(thisMob) if plug == SpaceConstraint.inTarget: # Create empty slots in offset and offset match attributes if they don't exists. targetIndex = plug.logicalIndex() offsetPlug = om2.MPlug(thisMob, SpaceConstraint.inOffset) curOffPlug = offsetPlug.elementByLogicalIndex(targetIndex) curOffPlug.asMDataHandle() offsetMatchPlug = om2.MPlug(thisMob, SpaceConstraint.inOffsetMatches) curOffMatchPlug = offsetMatchPlug.elementByLogicalIndex( targetIndex) curOffMatchPlug.asMDataHandle() # Auto fill offset if it has a connected output if autoFill: self.autoFillOffset(thisMob, targetIndex) elif (plug == SpaceConstraint.outConstTrans or plug == SpaceConstraint.outConstRot or plug == SpaceConstraint.outConstSca): # Check if it has a target input connection. If true autoFill the offsets. targetPlug = om2.MPlug(thisMob, SpaceConstraint.inTarget) numTarget = targetPlug.numElements() if numTarget > 0 and autoFill: for targetIndex in range(numTarget): self.autoFillOffset(thisMob, targetIndex) return om2.MPxNode.connectionMade(self, plug, otherPlug, asSrc) def connectionBroken(self, plug, otherPlug, asSrc): """This method gets called when connections of this node are broken. * plug (MPlug) is the attribute on this node. * otherPlug (MPlug) is the attribute on the other node. * asSrc (bool) is this plug a source of the connection. """ thisMob = self.thisMObject() self.checkOutputConnections(thisMob) if plug == SpaceConstraint.outConstTrans: pass elif plug == SpaceConstraint.outConstRot: pass elif plug == SpaceConstraint.outConstSca: pass return om2.MPxNode.connectionBroken(self, plug, otherPlug, asSrc) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use # if not dataBlock.isClean(SpaceConstraint.inSpace): # om2.MGlobal.displayInfo("\tSpace attribute is dirty.") # om2.MUserEventMessage.postUserEvent("preCompute", self) curSpace = dataBlock.inputValue(SpaceConstraint.inSpace).asShort() offsetMatchHdle = dataBlock.inputArrayValue( SpaceConstraint.inOffsetMatches) offsetHdle = dataBlock.inputArrayValue(SpaceConstraint.inOffset) targetHdle = dataBlock.inputArrayValue(SpaceConstraint.inTarget) offsetMatchList = [] offsetList = [] targetList = [] for i in range(len(offsetMatchHdle)): offsetMatchHdle.jumpToLogicalElement(i) mOffMatch = offsetMatchHdle.inputValue().asMatrix() offsetMatchList.append(mOffMatch) for i in range(len(offsetHdle)): offsetHdle.jumpToLogicalElement(i) mOff = offsetHdle.inputValue().asMatrix() offsetList.append(mOff) for i in range(len(targetHdle)): targetHdle.jumpToLogicalElement(i) mTgt = targetHdle.inputValue().asMatrix() targetList.append(mTgt) if len(offsetList) == 0 or len(targetList) == 0: mResult = om2.MMatrix() else: minRequired = min(len(offsetList), len(targetList)) - 1 if curSpace > minRequired: curSpace = minRequired mResult = offsetMatchList[curSpace] * offsetList[ curSpace] * targetList[curSpace] mtxFn = om2.MTransformationMatrix(mResult) if plug == SpaceConstraint.outConstTrans: vTransD = om2.MVector(mResult[12], mResult[13], mResult[14]) vTrans = om2.MFloatVector(vTransD) outTransHdle = dataBlock.outputValue(SpaceConstraint.outConstTrans) outTransHdle.setMFloatVector(vTrans) outTransHdle.setClean() if plug == SpaceConstraint.outConstRot: eRot = mtxFn.rotation(asQuaternion=False) outRotHdle = dataBlock.outputValue(SpaceConstraint.outConstRot) outRotHdle.setMVector(eRot.asVector()) outRotHdle.setClean() if plug == SpaceConstraint.outConstSca: outSca = mtxFn.scale(om2.MSpace.kWorld) outScaHdle = dataBlock.outputValue(SpaceConstraint.outConstSca) outScaHdle.set3Float(outSca[0], outSca[1], outSca[2]) outScaHdle.setClean()
class DecomposeRowMatrix(om2.MPxNode): """ Main class of gfUtilDecompRowMtx node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inMatrix = om2.MObject() inNormalizeOutput = om2.MObject() outRow1 = om2.MObject() outRow2 = om2.MObject() outRow3 = om2.MObject() outRow4 = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return DecomposeRowMatrix() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to DecomposeRowMatrix class. Instances of DecomposeRowMatrix will use these attributes to create plugs for use in the compute() method. """ nAttr = om2.MFnNumericAttribute() mAttr = om2.MFnMatrixAttribute() DecomposeRowMatrix.inMatrix = mAttr.create( "inputMatrix", "im", om2.MFnMatrixAttribute.kFloat) INPUT_ATTR(mAttr) DecomposeRowMatrix.inNormalizeOutput = nAttr.create( "normalizeOutput", "no", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) DecomposeRowMatrix.outRow1 = nAttr.createPoint("row1", "r1") OUTPUT_ATTR(nAttr) DecomposeRowMatrix.outRow2 = nAttr.createPoint("row2", "r2") OUTPUT_ATTR(nAttr) DecomposeRowMatrix.outRow3 = nAttr.createPoint("row3", "r3") OUTPUT_ATTR(nAttr) DecomposeRowMatrix.outRow4 = nAttr.createPoint("row4", "r4") OUTPUT_ATTR(nAttr) DecomposeRowMatrix.addAttribute(DecomposeRowMatrix.inMatrix) DecomposeRowMatrix.addAttribute(DecomposeRowMatrix.inNormalizeOutput) DecomposeRowMatrix.addAttribute(DecomposeRowMatrix.outRow1) DecomposeRowMatrix.addAttribute(DecomposeRowMatrix.outRow2) DecomposeRowMatrix.addAttribute(DecomposeRowMatrix.outRow3) DecomposeRowMatrix.addAttribute(DecomposeRowMatrix.outRow4) DecomposeRowMatrix.attributeAffects(DecomposeRowMatrix.inMatrix, DecomposeRowMatrix.outRow1) DecomposeRowMatrix.attributeAffects( DecomposeRowMatrix.inNormalizeOutput, DecomposeRowMatrix.outRow1) DecomposeRowMatrix.attributeAffects(DecomposeRowMatrix.inMatrix, DecomposeRowMatrix.outRow2) DecomposeRowMatrix.attributeAffects( DecomposeRowMatrix.inNormalizeOutput, DecomposeRowMatrix.outRow2) DecomposeRowMatrix.attributeAffects(DecomposeRowMatrix.inMatrix, DecomposeRowMatrix.outRow3) DecomposeRowMatrix.attributeAffects( DecomposeRowMatrix.inNormalizeOutput, DecomposeRowMatrix.outRow3) DecomposeRowMatrix.attributeAffects(DecomposeRowMatrix.inMatrix, DecomposeRowMatrix.outRow4) DecomposeRowMatrix.attributeAffects( DecomposeRowMatrix.inNormalizeOutput, DecomposeRowMatrix.outRow4) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use mInput = dataBlock.inputValue( DecomposeRowMatrix.inMatrix).asFloatMatrix() normalize = dataBlock.inputValue( DecomposeRowMatrix.inNormalizeOutput).asBool() if plug == DecomposeRowMatrix.outRow1: vRow1 = om2.MFloatVector(mInput[0], mInput[1], mInput[2]) if normalize: vRow1.normalize() outRow1Handle = dataBlock.outputValue(DecomposeRowMatrix.outRow1) outRow1Handle.setMFloatVector(vRow1) outRow1Handle.setClean() elif plug == DecomposeRowMatrix.outRow2: vRow2 = om2.MFloatVector(mInput[4], mInput[5], mInput[6]) if normalize: vRow2.normalize() outRow2Handle = dataBlock.outputValue(DecomposeRowMatrix.outRow2) outRow2Handle.setMFloatVector(vRow2) outRow2Handle.setClean() elif plug == DecomposeRowMatrix.outRow3: vRow3 = om2.MFloatVector(mInput[8], mInput[9], mInput[10]) if normalize: vRow3.normalize() outRow3Handle = dataBlock.outputValue(DecomposeRowMatrix.outRow3) outRow3Handle.setMFloatVector(vRow3) outRow3Handle.setClean() elif plug == DecomposeRowMatrix.outRow4: vRow4 = om2.MFloatVector(mInput[12], mInput[13], mInput[14]) if normalize: vRow4.normalize() outRow4Handle = dataBlock.outputValue(DecomposeRowMatrix.outRow4) outRow4Handle.setMFloatVector(vRow4) outRow4Handle.setClean() else: return om2.kUnknownParameter
class TwistExtractor(om2.MPxNode): """ Main class of gfTwistExtractor node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inRotation = om2.MObject() inRotationOrder = om2.MObject() inUseUpVec = om2.MObject() inUpVec = om2.MObject() inInvTwist = om2.MObject() inRevDist = om2.MObject() outTwist = om2.MObject() outTwistDist = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return TwistExtractor() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to TwistExtractor class. Instances of TwistExtractor will use these attributes to create plugs for use in the compute() method. """ uAttr = om2.MFnUnitAttribute() nAttr = om2.MFnNumericAttribute() eAttr = om2.MFnEnumAttribute() rotX = uAttr.create("rotationX", "rotx", om2.MFnUnitAttribute.kAngle, 0.0) rotY = uAttr.create("rotationY", "roty", om2.MFnUnitAttribute.kAngle, 0.0) rotZ = uAttr.create("rotationZ", "rotz", om2.MFnUnitAttribute.kAngle, 0.0) TwistExtractor.inRotation = nAttr.create("rotation", "rot", rotX, rotY, rotZ) INPUT_ATTR(nAttr) TwistExtractor.inRotationOrder = eAttr.create("rotationOrder", "roo", 0) # eAttr.addField("Twist First (zyx)", 5) # eAttr.addField("Twist Last (xyz)", 0) # INPUT_ATTR(eAttr) eAttr.addField("xyz", 0) eAttr.addField("yzx", 1) eAttr.addField("zxy", 2) eAttr.addField("xzy", 3) eAttr.addField("yxz", 4) eAttr.addField("zyx", 5) INPUT_ATTR(eAttr) TwistExtractor.inUseUpVec = nAttr.create("useUpVector", "useup", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) TwistExtractor.inUpVec = nAttr.createPoint("upVector", "upVector") nAttr.default = (0.0, 1.0, 0.0) INPUT_ATTR(nAttr) TwistExtractor.inInvTwist = nAttr.create("inverseTwist", "itwist", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) TwistExtractor.inRevDist = nAttr.create("reverseDistribution", "rdist", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) TwistExtractor.outTwist = uAttr.create("twist", "twist", om2.MFnUnitAttribute.kAngle, 0.0) OUTPUT_ATTR(uAttr) TwistExtractor.outTwistDist = uAttr.create("twistDistribution", "twistd", om2.MFnUnitAttribute.kAngle, 0.0) uAttr.array = True OUTPUT_ATTR(uAttr) TwistExtractor.addAttribute(TwistExtractor.inRotation) TwistExtractor.addAttribute(TwistExtractor.inRotationOrder) TwistExtractor.addAttribute(TwistExtractor.inUseUpVec) TwistExtractor.addAttribute(TwistExtractor.inUpVec) TwistExtractor.addAttribute(TwistExtractor.inInvTwist) TwistExtractor.addAttribute(TwistExtractor.inRevDist) TwistExtractor.addAttribute(TwistExtractor.outTwist) TwistExtractor.addAttribute(TwistExtractor.outTwistDist) TwistExtractor.attributeAffects(rotX, TwistExtractor.outTwist) TwistExtractor.attributeAffects(rotY, TwistExtractor.outTwist) TwistExtractor.attributeAffects(rotZ, TwistExtractor.outTwist) TwistExtractor.attributeAffects(TwistExtractor.inRotationOrder, TwistExtractor.outTwist) TwistExtractor.attributeAffects(TwistExtractor.inUseUpVec, TwistExtractor.outTwist) TwistExtractor.attributeAffects(TwistExtractor.inUpVec, TwistExtractor.outTwist) TwistExtractor.attributeAffects(TwistExtractor.inInvTwist, TwistExtractor.outTwist) TwistExtractor.attributeAffects(rotX, TwistExtractor.outTwistDist) TwistExtractor.attributeAffects(rotY, TwistExtractor.outTwistDist) TwistExtractor.attributeAffects(rotZ, TwistExtractor.outTwistDist) TwistExtractor.attributeAffects(TwistExtractor.inRotationOrder, TwistExtractor.outTwistDist) TwistExtractor.attributeAffects(TwistExtractor.inUseUpVec, TwistExtractor.outTwistDist) TwistExtractor.attributeAffects(TwistExtractor.inUpVec, TwistExtractor.outTwistDist) TwistExtractor.attributeAffects(TwistExtractor.inInvTwist, TwistExtractor.outTwistDist) TwistExtractor.attributeAffects(TwistExtractor.inRevDist, TwistExtractor.outTwistDist) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ rotation = dataBlock.inputValue(TwistExtractor.inRotation).asDouble3() rotOrder = dataBlock.inputValue( TwistExtractor.inRotationOrder).asShort() eRoll = om2.MEulerRotation(rotation, rotOrder) useUpObj = dataBlock.inputValue(TwistExtractor.inUseUpVec).asBool() revTwist = dataBlock.inputValue(TwistExtractor.inInvTwist).asBool() # Non flip ROO = XYZ # Not working = XZY twistOrder = om2.MEulerRotation.kXYZ eRoll.reorderIt(twistOrder) # Non-Roll orientation mtxFn = om2.MTransformationMatrix() mtxFn.rotateBy(eRoll, om2.MSpace.kWorld) mRoll = mtxFn.asMatrix() qNonRoll = om2.MQuaternion() nAim = om2.MVector(mRoll[0], mRoll[1], mRoll[2]) nAim.normalize() nAimAxis = om2.MVector.kXaxisVector qAim = om2.MQuaternion(nAimAxis, nAim) qNonRoll *= qAim if useUpObj: vUp = om2.MVector( dataBlock.inputValue(TwistExtractor.inUpVec).asFloat3()) nNormal = vUp - ((vUp * nAim) * nAim) nNormal.normalize() nUp = om2.MVector.kYaxisVector.rotateBy(qAim) angle = nUp.angle(nNormal) qNormal = om2.MQuaternion(angle, nAim) if not nNormal.isEquivalent(nUp.rotateBy(qNormal), 1.0e-5): angle = 2.0 * kPI - angle qNormal = om2.MQuaternion(angle, nAim) qNonRoll *= qNormal eNonRoll = qNonRoll.asEulerRotation() eNonRoll = om2.MEulerRotation(eNonRoll.x, eNonRoll.y, eNonRoll.z, twistOrder) # Extract Twist from orientations qRoll = eRoll.asQuaternion() qExtract180 = qNonRoll * qRoll.inverse() eExtract180 = qExtract180.asEulerRotation() twist = -eExtract180.x if revTwist: twist *= -1.0 # Output Twist if plug == TwistExtractor.outTwist: outTwistHdle = dataBlock.outputValue(TwistExtractor.outTwist) outTwistHdle.setMAngle(om2.MAngle(twist)) outTwistHdle.setClean() # Output Twist Distribution if plug == TwistExtractor.outTwistDist: invDist = dataBlock.inputValue(TwistExtractor.inRevDist).asBool() outTwistDistHdle = dataBlock.outputArrayValue( TwistExtractor.outTwistDist) outputs = len(outTwistDistHdle) step = twist / (outputs - 1) if outputs > 1 else twist outList = [] outList.extend(range(outputs)) if not invDist: outList.reverse() # pylint: disable=consider-using-enumerate for i in range(len(outList)): outTwistDistHdle.jumpToLogicalElement(i) resultHdle = outTwistDistHdle.outputValue() result = step * outList[i] if outputs > 1 else twist resultHdle.setMAngle(om2.MAngle(result)) outTwistDistHdle.setAllClean() return
class QuadraticCurve(om2.MPxNode): """ Main class of gfQuadraticCurve node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inControlPoints = om2.MObject() inEnableTwist = om2.MObject() inStartUpObj = om2.MObject() inEndUpObj = om2.MObject() inLockLength = om2.MObject() inRestLength = om2.MObject() inSlide = om2.MObject() inPreferredAngle = om2.MObject() outTransforms = om2.MObject() outCurve = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return QuadraticCurve() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to QuadraticCurve class. Instances of QuadraticCurve will use these attributes to create plugs for use in the compute() method. """ mAttr = om2.MFnMatrixAttribute() nAttr = om2.MFnNumericAttribute() uAttr = om2.MFnUnitAttribute() tAttr = om2.MFnTypedAttribute() QuadraticCurve.inControlPoints = mAttr.create( "controlPoints", "cp", om2.MFnMatrixAttribute.kDouble) mAttr.array = True INPUT_ATTR(mAttr) QuadraticCurve.inEnableTwist = nAttr.create( "enableTwist", "etw", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) QuadraticCurve.inStartUpObj = mAttr.create( "startUpObjectMatrix", "suom", om2.MFnMatrixAttribute.kFloat) INPUT_ATTR(mAttr) QuadraticCurve.inEndUpObj = mAttr.create("endUpObjectMatrix", "euom", om2.MFnMatrixAttribute.kFloat) INPUT_ATTR(mAttr) QuadraticCurve.inLockLength = nAttr.create("lockLength", "locklen", om2.MFnNumericData.kFloat, 0.0) nAttr.setMin(0.0) nAttr.setMax(1.0) INPUT_ATTR(nAttr) QuadraticCurve.inRestLength = nAttr.create("restLength", "rlength", om2.MFnNumericData.kFloat, 0.0) nAttr.setMin(0.0) INPUT_ATTR(nAttr) QuadraticCurve.inSlide = nAttr.create("slide", "slide", om2.MFnNumericData.kFloat, 0.0) nAttr.setMin(0.0) nAttr.setMax(1.0) INPUT_ATTR(nAttr) QuadraticCurve.inPreferredAngle = uAttr.create( "preferredAngle", "pangle", om2.MFnUnitAttribute.kAngle) uAttr.setMin(0.0) uAttr.setMax(om2.MAngle(360.0, om2.MAngle.kDegrees).asRadians()) INPUT_ATTR(uAttr) QuadraticCurve.outTransforms = mAttr.create( "outTransforms", "otrans", om2.MFnMatrixAttribute.kDouble) mAttr.array = True OUTPUT_ATTR(mAttr) QuadraticCurve.outCurve = tAttr.create("outCurve", "ocrv", om2.MFnData.kNurbsCurve) OUTPUT_ATTR(tAttr) QuadraticCurve.addAttribute(QuadraticCurve.inControlPoints) QuadraticCurve.addAttribute(QuadraticCurve.inEnableTwist) QuadraticCurve.addAttribute(QuadraticCurve.inStartUpObj) QuadraticCurve.addAttribute(QuadraticCurve.inEndUpObj) QuadraticCurve.addAttribute(QuadraticCurve.inLockLength) QuadraticCurve.addAttribute(QuadraticCurve.inRestLength) QuadraticCurve.addAttribute(QuadraticCurve.inSlide) QuadraticCurve.addAttribute(QuadraticCurve.inPreferredAngle) QuadraticCurve.addAttribute(QuadraticCurve.outTransforms) QuadraticCurve.addAttribute(QuadraticCurve.outCurve) QuadraticCurve.attributeAffects(QuadraticCurve.inControlPoints, QuadraticCurve.outCurve) QuadraticCurve.attributeAffects(QuadraticCurve.inControlPoints, QuadraticCurve.outTransforms) QuadraticCurve.attributeAffects(QuadraticCurve.inEnableTwist, QuadraticCurve.outTransforms) QuadraticCurve.attributeAffects(QuadraticCurve.inStartUpObj, QuadraticCurve.outTransforms) QuadraticCurve.attributeAffects(QuadraticCurve.inEndUpObj, QuadraticCurve.outTransforms) QuadraticCurve.attributeAffects(QuadraticCurve.inLockLength, QuadraticCurve.outTransforms) QuadraticCurve.attributeAffects(QuadraticCurve.inRestLength, QuadraticCurve.outTransforms) QuadraticCurve.attributeAffects(QuadraticCurve.inSlide, QuadraticCurve.outTransforms) QuadraticCurve.attributeAffects(QuadraticCurve.inPreferredAngle, QuadraticCurve.outTransforms) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use ctrlPntsHandle = dataBlock.inputArrayValue( QuadraticCurve.inControlPoints) ctrlPnts = om2.MPointArray() if len(ctrlPntsHandle) < 3: return for i in range(3): ctrlPntsHandle.jumpToLogicalElement(i) mCtrlPnt = ctrlPntsHandle.inputValue().asMatrix() ctrlPnt = om2.MPoint(mCtrlPnt[12], mCtrlPnt[13], mCtrlPnt[14]) ctrlPnts.append(ctrlPnt) crvKnots = om2.MDoubleArray([0, 0, 1, 1]) crvDegree = 2 crvForm = om2.MFnNurbsCurve.kOpen crvIs2d = False crvRational = False crvData = om2.MFnNurbsCurveData().create() crvFn = om2.MFnNurbsCurve(crvData) crvFn.create(ctrlPnts, crvKnots, crvDegree, crvForm, crvIs2d, crvRational, crvData) crvFn.updateCurve() if plug == QuadraticCurve.outCurve: outCurveHandle = dataBlock.outputValue(QuadraticCurve.outCurve) outCurveHandle.setMObject(crvData) outCurveHandle.setClean() elif plug == QuadraticCurve.outTransforms: outTransHandle = dataBlock.outputArrayValue( QuadraticCurve.outTransforms) lockLength = dataBlock.inputValue( QuadraticCurve.inLockLength).asFloat() restLength = dataBlock.inputValue( QuadraticCurve.inRestLength).asFloat() slide = dataBlock.inputValue(QuadraticCurve.inSlide).asFloat() numOutputs = len(outTransHandle) crvLength = crvFn.length() parRejected = crvFn.findParamFromLength(crvLength - restLength) stepFull = 1.0 / (numOutputs - 1) if numOutputs > 1 else 0.0 stepLock = crvFn.findParamFromLength(restLength) / ( numOutputs - 1) if numOutputs > 1 else 0.0 step = (1.0 - lockLength) * stepFull + lockLength * stepLock parSlide = parRejected * slide * lockLength for i in range(numOutputs): parameter = (step * i) # + parSlide pos = crvFn.getPointAtParam(parameter, om2.MSpace.kObject) vPos = om2.MVector(pos.x, pos.y, pos.z) mtx = [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, vPos.x, vPos.y, vPos.z, 1.0 ] mOut = om2.MMatrix(mtx) outTransHandle.jumpToLogicalElement(i) resultHandle = outTransHandle.outputValue() resultHandle.setMMatrix(mOut) outTransHandle.setAllClean() else: return om2.kUnknownParameter
class BlendTransform(om2.MPxNode): """ Main class of gfUtilBlendTransform node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inBlender = om2.MObject() inRotInterp = om2.MObject() inTrans1 = om2.MObject() inRot1 = om2.MObject() inSca1 = om2.MObject() inRot1Order = om2.MObject() inTransform1 = om2.MObject() inTrans2 = om2.MObject() inRot2 = om2.MObject() inSca2 = om2.MObject() inRot2Order = om2.MObject() inTransform2 = om2.MObject() inOutRotOrder = om2.MObject() outTrans = om2.MObject() outRot = om2.MObject() outSca = om2.MObject() outVis = om2.MObject() outRevVis = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return BlendTransform() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to BlendTransform class. Instances of BlendTransform will use these attributes to create plugs for use in the compute() method. """ nAttr = om2.MFnNumericAttribute() uAttr = om2.MFnUnitAttribute() cAttr = om2.MFnCompoundAttribute() eAttr = om2.MFnEnumAttribute() BlendTransform.inBlender = nAttr.create("blender", "blender", om2.MFnNumericData.kFloat, 0.5) nAttr.setMin(0.0) nAttr.setMax(1.0) INPUT_ATTR(nAttr) BlendTransform.inRotInterp = eAttr.create("rotationInterpolation", "roti", 0) eAttr.addField("Euler Lerp", 0) eAttr.addField("Quaternion Slerp", 1) INPUT_ATTR(eAttr) BlendTransform.inTrans1 = nAttr.createPoint("translate1", "t1") nAttr.array = True INPUT_ATTR(nAttr) rot1X = uAttr.create("rotate1X", "ro1x", om2.MFnUnitAttribute.kAngle, 0.0) rot1Y = uAttr.create("rotate1Y", "ro1y", om2.MFnUnitAttribute.kAngle, 0.0) rot1Z = uAttr.create("rotate1Z", "ro1z", om2.MFnUnitAttribute.kAngle, 0.0) BlendTransform.inRot1 = nAttr.create("rotate1", "ro1", rot1X, rot1Y, rot1Z) nAttr.array = True INPUT_ATTR(nAttr) BlendTransform.inSca1 = nAttr.createPoint("scale1", "sca1") nAttr.array = True INPUT_ATTR(nAttr) BlendTransform.inRot1Order = eAttr.create("rotateOrder1", "rro1", 0) eAttr.addField("xyz", 0) eAttr.addField("yzx", 1) eAttr.addField("zxy", 2) eAttr.addField("xzy", 3) eAttr.addField("yxz", 4) eAttr.addField("zyx", 5) eAttr.array = True INPUT_ATTR(eAttr) BlendTransform.inTransform1 = cAttr.create("transform1", "tr1") cAttr.addChild(BlendTransform.inTrans1) cAttr.addChild(BlendTransform.inRot1) cAttr.addChild(BlendTransform.inSca1) cAttr.addChild(BlendTransform.inRot1Order) BlendTransform.inTrans2 = nAttr.createPoint("translate2", "t2") nAttr.array = True INPUT_ATTR(nAttr) rot2X = uAttr.create("rotate2X", "ro2x", om2.MFnUnitAttribute.kAngle, 0.0) rot2Y = uAttr.create("rotate2Y", "ro2y", om2.MFnUnitAttribute.kAngle, 0.0) rot2Z = uAttr.create("rotate2Z", "ro2z", om2.MFnUnitAttribute.kAngle, 0.0) BlendTransform.inRot2 = nAttr.create("rotate2", "ro2", rot2X, rot2Y, rot2Z) nAttr.array = True INPUT_ATTR(nAttr) BlendTransform.inSca2 = nAttr.createPoint("scale2", "sca2") nAttr.array = True INPUT_ATTR(nAttr) BlendTransform.inRot2Order = eAttr.create("rotateOrder2", "rro2", 0) eAttr.addField("xyz", 0) eAttr.addField("yzx", 1) eAttr.addField("zxy", 2) eAttr.addField("xzy", 3) eAttr.addField("yxz", 4) eAttr.addField("zyx", 5) eAttr.array = True INPUT_ATTR(eAttr) BlendTransform.inTransform2 = cAttr.create("transform2", "tr2") cAttr.addChild(BlendTransform.inTrans2) cAttr.addChild(BlendTransform.inRot2) cAttr.addChild(BlendTransform.inSca2) cAttr.addChild(BlendTransform.inRot2Order) BlendTransform.inOutRotOrder = eAttr.create("outRotateOrder", "orro", 0) eAttr.addField("xyz", 0) eAttr.addField("yzx", 1) eAttr.addField("zxy", 2) eAttr.addField("xzy", 3) eAttr.addField("yxz", 4) eAttr.addField("zyx", 5) eAttr.array = True INPUT_ATTR(eAttr) BlendTransform.outTrans = nAttr.createPoint("outTranslate", "ot") nAttr.array = True OUTPUT_ATTR(nAttr) oRotX = uAttr.create("outRotateX", "orox", om2.MFnUnitAttribute.kAngle, 0.0) oRotY = uAttr.create("outRotateY", "oroy", om2.MFnUnitAttribute.kAngle, 0.0) oRotZ = uAttr.create("outRotateZ", "oroz", om2.MFnUnitAttribute.kAngle, 0.0) BlendTransform.outRot = nAttr.create("outRotate", "oro", oRotX, oRotY, oRotZ) nAttr.array = True OUTPUT_ATTR(nAttr) BlendTransform.outSca = nAttr.createPoint("outScale", "osca") nAttr.array = True OUTPUT_ATTR(nAttr) BlendTransform.outVis = nAttr.create("visibility", "vis", om2.MFnNumericData.kBoolean, True) OUTPUT_ATTR(nAttr) BlendTransform.outRevVis = nAttr.create("reverseVisibility", "rvis", om2.MFnNumericData.kBoolean, False) OUTPUT_ATTR(nAttr) BlendTransform.addAttribute(BlendTransform.inBlender) BlendTransform.addAttribute(BlendTransform.inRotInterp) BlendTransform.addAttribute(BlendTransform.inTransform1) BlendTransform.addAttribute(BlendTransform.inTransform2) BlendTransform.addAttribute(BlendTransform.inOutRotOrder) BlendTransform.addAttribute(BlendTransform.outTrans) BlendTransform.addAttribute(BlendTransform.outRot) BlendTransform.addAttribute(BlendTransform.outSca) BlendTransform.addAttribute(BlendTransform.outVis) BlendTransform.addAttribute(BlendTransform.outRevVis) BlendTransform.attributeAffects(BlendTransform.inBlender, BlendTransform.outTrans) BlendTransform.attributeAffects(BlendTransform.inTrans1, BlendTransform.outTrans) BlendTransform.attributeAffects(BlendTransform.inTrans2, BlendTransform.outTrans) BlendTransform.attributeAffects(BlendTransform.inBlender, BlendTransform.outRot) BlendTransform.attributeAffects(BlendTransform.inRotInterp, BlendTransform.outRot) BlendTransform.attributeAffects(BlendTransform.inRot1, BlendTransform.outRot) BlendTransform.attributeAffects(BlendTransform.inRot1Order, BlendTransform.outRot) BlendTransform.attributeAffects(BlendTransform.inRot2, BlendTransform.outRot) BlendTransform.attributeAffects(BlendTransform.inRot2Order, BlendTransform.outRot) BlendTransform.attributeAffects(BlendTransform.inOutRotOrder, BlendTransform.outRot) BlendTransform.attributeAffects(BlendTransform.inBlender, BlendTransform.outSca) BlendTransform.attributeAffects(BlendTransform.inSca1, BlendTransform.outSca) BlendTransform.attributeAffects(BlendTransform.inSca2, BlendTransform.outSca) BlendTransform.attributeAffects(BlendTransform.inBlender, BlendTransform.outVis) BlendTransform.attributeAffects(BlendTransform.inBlender, BlendTransform.outRevVis) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=R0201 blender = dataBlock.inputValue(BlendTransform.inBlender).asFloat() if plug == BlendTransform.outTrans: trans1Handle = dataBlock.inputArrayValue(BlendTransform.inTrans1) trans2Handle = dataBlock.inputArrayValue(BlendTransform.inTrans2) outTransHandle = dataBlock.outputArrayValue( BlendTransform.outTrans) outList = [] for i in range(min(len(trans1Handle), len(trans2Handle))): trans1Handle.jumpToLogicalElement(i) trans2Handle.jumpToLogicalElement(i) vTrans1 = trans1Handle.inputValue().asFloatVector() vTrans2 = trans2Handle.inputValue().asFloatVector() vOut = (1.0 - blender) * vTrans1 + blender * vTrans2 outList.append(vOut) for i in range(len(outTransHandle)): outTransHandle.jumpToLogicalElement(i) resultHandle = outTransHandle.outputValue() if i < len(outTransHandle) and i < len(outList): resultHandle.setMFloatVector(outList[i]) else: resultHandle.setMFloatVector(om2.MFloatVector()) outTransHandle.setAllClean() elif plug == BlendTransform.outRot: rotInterp = dataBlock.inputValue( BlendTransform.inRotInterp).asShort() rot1Handle = dataBlock.inputArrayValue(BlendTransform.inRot1) rot2Handle = dataBlock.inputArrayValue(BlendTransform.inRot2) outRotHandle = dataBlock.outputArrayValue(BlendTransform.outRot) rotOrder1Handle = dataBlock.inputArrayValue( BlendTransform.inRot1Order) rotOrder2Handle = dataBlock.inputArrayValue( BlendTransform.inRot2Order) outRotOrderHandle = dataBlock.inputArrayValue( BlendTransform.inOutRotOrder) outList = [] for i in range(min(len(rot1Handle), len(rot2Handle))): rot1Handle.jumpToLogicalElement(i) rot2Handle.jumpToLogicalElement(i) rotOrder1 = BlendTransform.checkRotateOrderArrayHandle( rotOrder1Handle, i) rotOrder2 = BlendTransform.checkRotateOrderArrayHandle( rotOrder2Handle, i) outRotOrder = BlendTransform.checkRotateOrderArrayHandle( outRotOrderHandle, i) rot1 = rot1Handle.inputValue().asVector() rot2 = rot2Handle.inputValue().asVector() eRot1 = om2.MEulerRotation(rot1, rotOrder1) eRot2 = om2.MEulerRotation(rot2, rotOrder2) eRot1.reorderIt(outRotOrder) eRot2.reorderIt(outRotOrder) if rotInterp == 0: vRot1 = eRot1.asVector() vRot2 = eRot2.asVector() vOut = (1.0 - blender) * vRot1 + blender * vRot2 else: qRot1 = eRot1.asQuaternion() qRot2 = eRot2.asQuaternion() eSlerp = om2.MQuaternion.slerp(qRot1, qRot2, blender).asEulerRotation() eSlerp.reorderIt(outRotOrder) vOut = eSlerp.asVector() outList.append(vOut) for i in range(len(outRotHandle)): outRotHandle.jumpToLogicalElement(i) resultHandle = outRotHandle.outputValue() if i < len(outRotHandle) and i < len(outList): resultHandle.setMVector(outList[i]) else: resultHandle.setMVector(om2.MVector()) outRotHandle.setAllClean() elif plug == BlendTransform.outSca: sca1Handle = dataBlock.inputArrayValue(BlendTransform.inSca1) sca2Handle = dataBlock.inputArrayValue(BlendTransform.inSca2) outScaHandle = dataBlock.outputArrayValue(BlendTransform.outSca) outList = [] for i in range(min(len(sca1Handle), len(sca2Handle))): sca1Handle.jumpToLogicalElement(i) sca2Handle.jumpToLogicalElement(i) vSca1 = sca1Handle.inputValue().asFloatVector() vSca2 = sca2Handle.inputValue().asFloatVector() vOut = (1.0 - blender) * vSca1 + blender * vSca2 outList.append(vOut) for i in range(len(outScaHandle)): outScaHandle.jumpToLogicalElement(i) resultHandle = outScaHandle.outputValue() if i < len(outScaHandle) and i < len(outList): resultHandle.setMFloatVector(outList[i]) else: resultHandle.setMFloatVector(om2.MFloatVector()) outScaHandle.setAllClean() elif plug in (BlendTransform.outVis, BlendTransform.outRevVis): outVisHandle = dataBlock.outputValue(BlendTransform.outVis) outRevVisHandle = dataBlock.outputValue(BlendTransform.outRevVis) vis, revVis = BlendTransform.visibilityCalculation(blender) outVisHandle.setBool(vis) outRevVisHandle.setBool(revVis) outVisHandle.setClean() outRevVisHandle.setClean() @staticmethod def visibilityCalculation(blender): """ Calculate the visibility of the objects based on blender value. Threshold can be changed in code to affect the calculation. """ threshold = 0.25 vis = True revVis = False if blender <= 0.0 + threshold: vis = False revVis = True elif blender >= 1.0 - threshold: vis = True revVis = False else: vis = True revVis = True return vis, revVis @staticmethod def checkRotateOrderArrayHandle(arrayHandle, iterValue): """ Check if rotate order MArrayDataHandle is done. If it is return default kXYZ, otherwise return the input value. """ index = len(arrayHandle) if index > 0 and iterValue <= index: arrayHandle.jumpToLogicalElement(iterValue) value = arrayHandle.inputValue().asShort() else: value = 0 return value
class MeshController(omui2.MPxLocatorNode): """ Main class of gfMeshController node. """ kNodeName = "" kNodeClassify = "" kNodeRegistrantID = "" kNodeID = "" inIndexList = om2.MObject() inOffset = om2.MObject() inMesh = om2.MObject() inColor = om2.MObject() inXray = om2.MObject() meshVtxPositions = om2.MPointArray() meshVtxIndices = om2.MUintArray() meshVtxNormals = om2.MVectorArray() bBox = om2.MBoundingBox() def __init__(self): """ Constructor. """ omui2.MPxLocatorNode.__init__(self) def postConstructor(self): """ Post Constructor. """ thisMob = self.thisMObject() om2.MFnDependencyNode(thisMob).setName("%sShape#" % MeshController.kNodeName) @staticmethod def creator(): """ Maya creator function. """ return MeshController() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to MeshController class. Instances of MeshController will use these attributes to create plugs for use in the compute() method. """ tAttr = om2.MFnTypedAttribute() nAttr = om2.MFnNumericAttribute() MeshController.inIndexList = tAttr.create("indexList", "index", om2.MFnData.kString) INPUT_ATTR(tAttr) MeshController.inOffset = nAttr.create("offset", "offset", om2.MFnNumericData.kFloat, 0.0) INPUT_ATTR(nAttr) MeshController.inMesh = tAttr.create("controlMesh", "controlMesh", om2.MFnData.kMesh) INPUT_ATTR(tAttr) MeshController.inColor = nAttr.createColor("color", "color") nAttr.default = (1.0, 0.455, 0.086) INPUT_ATTR(nAttr) MeshController.inXray = nAttr.create("xray", "xray", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) MeshController.addAttribute(MeshController.inIndexList) MeshController.addAttribute(MeshController.inMesh) MeshController.addAttribute(MeshController.inOffset) MeshController.addAttribute(MeshController.inColor) MeshController.addAttribute(MeshController.inXray) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=unused-argument return def connectionMade(self, plug, otherPlug, asSrc): """ This method gets called when connections are made to attributes of this node. * plug (MPlug) is the attribute on this node. * otherPlug (MPlug) is the attribute on the other node. * asSrc (bool) is this plug a source of the connection. """ # generate mesh points here initially return om2.MPxNode.connectionMade(self, plug, otherPlug, asSrc) def connectionBroken(self, plug, otherPlug, asSrc): """This method gets called when connections are made to attributes of this node. * plug (MPlug) is the attribute on this node. * otherPlug (MPlug) is the attribute on the other node. * asSrc (bool) is this plug a source of the connection. """ return om2.MPxNode.connectionBroken(self, plug, otherPlug, asSrc) def setDependentsDirty(self, plugBeingDirtied, affectedPlugs): """ This method can be overridden in user defined nodes to specify which plugs should be set dirty based upon an input plug {plugBeingDirtied} which Maya is marking dirty. The list of plugs for Maya to mark dirty is returned by the plug array {affectedPlugs}. You must not cause any dependency graph computations. * plugBeingDirtied [MPlug] is the plug being dirtied. * affectedPlugs [MPlugArray] is the list of dirty plugs returned by Maya. """ # pylint: disable=unused-argument if (plugBeingDirtied == MeshController.inIndexList or plugBeingDirtied == MeshController.inMesh): self.signalDirtyToViewport() def preEvaluation(self, context, evaluationNode): """ Called before this node is evaluated by Evaluation Manager. * context [MDGContext] is the context which the evaluation is happening. * evaluationNode [MEvaluationNode] the evaluation node which contains information about the dirty plugs that are about to be evaluated for the context. Should be only used to query information. """ if not context.isNormal(): return if (evaluationNode.dirtyPlugExists(MeshController.inMesh) or evaluationNode.dirtyPlugExists(MeshController.inIndexList) or evaluationNode.dirtyPlugExists(MeshController.inOffset)): omr2.MRenderer.setGeometryDrawDirty(self.thisMObject()) @staticmethod def listToMIntArray(strList): """ Convert a list of int to a MIntArray instance. """ # pylint: disable=undefined-variable instance = om2.MIntArray() for i in strList: try: instance.append(int(i)) except ValueError: pass return instance @staticmethod def generateMesh(meshMob, faceIndicesStr): """ Find the info of the geometry who will be drawed. """ meshFn = om2.MFnMesh(meshMob) def draw(self, view, path, style, status): """ Draw custom geometry in the viewport using OpenGL calls. * view [M3dView] is a 3D view that is being drawn into. * path [MDagPath] to the parent (transform node) of this locator in the DAG. To obtain the locator shape node, use MDagPath::extendToShape() if there is only one shape node under the transform or MDagPath::extendToShapeDirectlyBelow(unsigned int index) with the shape index if there are multiple shapes under the transform. * style [M3dView.DisplayStyle] is the style to draw object in. * status [M3dView.DisplayStatus] is the selection status of the object. """ # pylint: disable=unused-argument # NO DRAWING IN VIEWPORT 1.0, JUST RETURN return def isBounded(self): """isBounded?""" return False
class IKVChainSolver(om2.MPxNode): """ Main class of gfRigIKVChainSolver node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inRoot = om2.MObject() inHandle = om2.MObject() inPoleVector = om2.MObject() inOffset = om2.MObject() inJntOri = om2.MObject() inParInvMtx = om2.MObject() inRestLenStart = om2.MObject() inRestLenEnd = om2.MObject() inPreferredAngle = om2.MObject() inTwist = om2.MObject() inPvMode = om2.MObject() inHierarchyMode = om2.MObject() inFlip = om2.MObject() inUseScale = om2.MObject() inCompressionLimit = om2.MObject() inSnapUpVector = om2.MObject() inSnap = om2.MObject() inSoftness = om2.MObject() inStretch = om2.MObject() inClampStretch = om2.MObject() inClampValue = om2.MObject() # inStretchMultStart = om2.MObject() # inStretchMultEnd = om2.MObject() inSquash = om2.MObject() inSquashMultStart = om2.MObject() inSquashMultEnd = om2.MObject() outChain = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return IKVChainSolver() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to IKVChainSolver class. Instances of IKVChainSolver will use these attributes to create plugs for use in the compute() method. """ mAttr = om2.MFnMatrixAttribute() nAttr = om2.MFnNumericAttribute() eAttr = om2.MFnEnumAttribute() uAttr = om2.MFnUnitAttribute() IKVChainSolver.inRoot = mAttr.create("root", "root", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) IKVChainSolver.inHandle = mAttr.create("handle", "handle", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) IKVChainSolver.inPoleVector = mAttr.create( "poleVector", "pole", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) offX = uAttr.create("offsetX", "offx", om2.MFnUnitAttribute.kAngle, 0.0) offY = uAttr.create("offsetY", "offy", om2.MFnUnitAttribute.kAngle, 0.0) offZ = uAttr.create("offsetZ", "offz", om2.MFnUnitAttribute.kAngle, 0.0) IKVChainSolver.inOffset = nAttr.create("offset", "off", offX, offY, offZ) nAttr.array = True INPUT_ATTR(nAttr) jntOriX = uAttr.create("jointOrientX", "jox", om2.MFnUnitAttribute.kAngle, 0.0) jntOriY = uAttr.create("jointOrientY", "joy", om2.MFnUnitAttribute.kAngle, 0.0) jntOriZ = uAttr.create("jointOrientZ", "joz", om2.MFnUnitAttribute.kAngle, 0.0) IKVChainSolver.inJntOri = nAttr.create("jointOrient", "jo", jntOriX, jntOriY, jntOriZ) nAttr.array = True INPUT_ATTR(nAttr) IKVChainSolver.inParInvMtx = mAttr.create( "parentInverseMatrix", "pim", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) IKVChainSolver.inRestLenStart = nAttr.create("restLengthStart", "rls", om2.MFnNumericData.kFloat, 1.0) # nAttr.setMin(0.001) INPUT_ATTR(nAttr) nAttr.channelBox = True IKVChainSolver.inRestLenEnd = nAttr.create("restLengthEnd", "rle", om2.MFnNumericData.kFloat, 1.0) # nAttr.setMin(0.001) INPUT_ATTR(nAttr) nAttr.channelBox = True IKVChainSolver.inPreferredAngle = uAttr.create( "preferredAngle", "pa", om2.MFnUnitAttribute.kAngle, 0.0) uAttr.setMin(0.0) uAttr.setMax(2.0 * math.pi) INPUT_ATTR(uAttr) uAttr.channelBox = True IKVChainSolver.inTwist = uAttr.create("twist", "twist", om2.MFnUnitAttribute.kAngle, 0.0) INPUT_ATTR(uAttr) IKVChainSolver.inPvMode = eAttr.create("pvMode", "pvm", 0) eAttr.addField("Manual", 0) eAttr.addField("Auto", 1) INPUT_ATTR(eAttr) IKVChainSolver.inHierarchyMode = nAttr.create( "hierarchyMode", "hm", om2.MFnNumericData.kBoolean, True) INPUT_ATTR(nAttr) IKVChainSolver.inFlip = nAttr.create("flipOrientation", "fori", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) nAttr.channelBox = True IKVChainSolver.inUseScale = nAttr.create("useStretchAsScale", "usca", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) IKVChainSolver.inCompressionLimit = nAttr.create( "compressionLimit", "cl", om2.MFnNumericData.kFloat, 0.1) nAttr.setMin(0.001) nAttr.setMax(0.4) INPUT_ATTR(nAttr) IKVChainSolver.inSnapUpVector = nAttr.create("snapUpVector", "supv", om2.MFnNumericData.kFloat, 0.0) nAttr.setMin(0.0) nAttr.setMax(1.0) INPUT_ATTR(nAttr) IKVChainSolver.inSnap = mAttr.create("snap", "snap", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) IKVChainSolver.inSoftness = nAttr.create("softness", "soft", om2.MFnNumericData.kFloat, 0.0) nAttr.setMin(0.0) nAttr.setSoftMax(0.2) nAttr.setMax(1.0) INPUT_ATTR(nAttr) IKVChainSolver.inStretch = nAttr.create("stretch", "st", om2.MFnNumericData.kDouble, 0.0) nAttr.setMin(0.0) nAttr.setMax(1.0) INPUT_ATTR(nAttr) IKVChainSolver.inClampStretch = nAttr.create( "clampStretch", "cst", om2.MFnNumericData.kDouble, 0.0) nAttr.setMin(0.0) nAttr.setMax(1.0) INPUT_ATTR(nAttr) IKVChainSolver.inClampValue = nAttr.create("clampValue", "cstv", om2.MFnNumericData.kDouble, 1.5) nAttr.setMin(1.0) nAttr.setSoftMax(1.8) INPUT_ATTR(nAttr) # IKVChainSolver.inStretchMultStart = nAttr.create("stretchMultStart", "stms", om2.MFnNumericData.kFloat, 1.0) # nAttr.setMin(0.001) # INPUT_ATTR(nAttr) # IKVChainSolver.inStretchMultEnd = nAttr.create("stretchMultEnd", "stme", om2.MFnNumericData.kFloat, 1.0) # nAttr.setMin(0.001) # INPUT_ATTR(nAttr) IKVChainSolver.inSquash = nAttr.create("squash", "sq", om2.MFnNumericData.kDouble, 0.0) nAttr.setMin(0.0) nAttr.setMax(1.0) INPUT_ATTR(nAttr) startSqX = nAttr.create("squashMultStartX", "sqmsx", om2.MFnNumericData.kFloat, 1.0) startSqY = nAttr.create("squashMultStartY", "sqmsy", om2.MFnNumericData.kFloat, 1.0) IKVChainSolver.inSquashMultStart = nAttr.create( "squashMultStart", "sqms", startSqX, startSqY) nAttr.setMin([0.001, 0.001]) INPUT_ATTR(nAttr) endSqX = nAttr.create("squashMultEndX", "sqmex", om2.MFnNumericData.kFloat, 1.0) endSqY = nAttr.create("squashMultEndY", "sqmey", om2.MFnNumericData.kFloat, 1.0) IKVChainSolver.inSquashMultEnd = nAttr.create("squashMultEnd", "sqme", endSqX, endSqY) nAttr.setMin([0.001, 0.001]) INPUT_ATTR(nAttr) IKVChainSolver.outChain = mAttr.create("outChain", "oc", om2.MFnMatrixAttribute.kDouble) mAttr.array = True OUTPUT_ATTR(mAttr) IKVChainSolver.addAttribute(IKVChainSolver.inRoot) IKVChainSolver.addAttribute(IKVChainSolver.inHandle) IKVChainSolver.addAttribute(IKVChainSolver.inPoleVector) IKVChainSolver.addAttribute(IKVChainSolver.inOffset) IKVChainSolver.addAttribute(IKVChainSolver.inJntOri) IKVChainSolver.addAttribute(IKVChainSolver.inParInvMtx) IKVChainSolver.addAttribute(IKVChainSolver.inRestLenStart) IKVChainSolver.addAttribute(IKVChainSolver.inRestLenEnd) IKVChainSolver.addAttribute(IKVChainSolver.inPreferredAngle) IKVChainSolver.addAttribute(IKVChainSolver.inTwist) IKVChainSolver.addAttribute(IKVChainSolver.inPvMode) IKVChainSolver.addAttribute(IKVChainSolver.inHierarchyMode) IKVChainSolver.addAttribute(IKVChainSolver.inFlip) IKVChainSolver.addAttribute(IKVChainSolver.inUseScale) IKVChainSolver.addAttribute(IKVChainSolver.inCompressionLimit) IKVChainSolver.addAttribute(IKVChainSolver.inSnapUpVector) IKVChainSolver.addAttribute(IKVChainSolver.inSnap) IKVChainSolver.addAttribute(IKVChainSolver.inSoftness) IKVChainSolver.addAttribute(IKVChainSolver.inStretch) IKVChainSolver.addAttribute(IKVChainSolver.inClampStretch) IKVChainSolver.addAttribute(IKVChainSolver.inClampValue) # IKVChainSolver.addAttribute(IKVChainSolver.inStretchMultStart) # IKVChainSolver.addAttribute(IKVChainSolver.inStretchMultEnd) IKVChainSolver.addAttribute(IKVChainSolver.inSquash) IKVChainSolver.addAttribute(IKVChainSolver.inSquashMultStart) IKVChainSolver.addAttribute(IKVChainSolver.inSquashMultEnd) IKVChainSolver.addAttribute(IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inRoot, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inHandle, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inPoleVector, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inOffset, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inJntOri, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inParInvMtx, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inRestLenStart, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inRestLenEnd, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inPreferredAngle, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inTwist, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inPvMode, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inHierarchyMode, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inFlip, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inUseScale, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inCompressionLimit, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inSnapUpVector, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inSnap, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inSoftness, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inStretch, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inClampStretch, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inClampValue, IKVChainSolver.outChain) # IKVChainSolver.attributeAffects(IKVChainSolver.inStretchMultStart, IKVChainSolver.outChain) # IKVChainSolver.attributeAffects(IKVChainSolver.inStretchMultEnd, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inSquash, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inSquashMultStart, IKVChainSolver.outChain) IKVChainSolver.attributeAffects(IKVChainSolver.inSquashMultEnd, IKVChainSolver.outChain) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ if plug != IKVChainSolver.outChain: return om2.kUnknownParameter # Get Basis Quaternion mRoot = dataBlock.inputValue(IKVChainSolver.inRoot).asMatrix() mHandle = dataBlock.inputValue(IKVChainSolver.inHandle).asMatrix() mPoleVector = dataBlock.inputValue( IKVChainSolver.inPoleVector).asMatrix() pvMode = dataBlock.inputValue(IKVChainSolver.inPvMode).asShort() prefAngle = dataBlock.inputValue( IKVChainSolver.inPreferredAngle).asAngle().asRadians() twist = dataBlock.inputValue( IKVChainSolver.inTwist).asAngle().asRadians() snap = dataBlock.inputValue(IKVChainSolver.inSnapUpVector).asFloat() mSnap = dataBlock.inputValue(IKVChainSolver.inSnap).asMatrix() flip = dataBlock.inputValue(IKVChainSolver.inFlip).asBool() vRoot = om2.MVector(mRoot[12], mRoot[13], mRoot[14]) vHandle = om2.MVector(mHandle[12], mHandle[13], mHandle[14]) vPoleVector = om2.MVector(mPoleVector[12], mPoleVector[13], mPoleVector[14]) vSnap = om2.MVector(mSnap[12], mSnap[13], mSnap[14]) primAxis = om2.MVector.kXaxisVector secAxis = om2.MVector.kYaxisVector if flip: primAxis = -om2.MVector.kXaxisVector secAxis = -om2.MVector.kYaxisVector binAxis = primAxis ^ secAxis qBasis = om2.MQuaternion() vAim = vHandle - vRoot nAim = vAim.normal() qAim = om2.MQuaternion(primAxis, nAim) qBasis *= qAim vStartSnap = vSnap - vRoot vEndSnap = vSnap - vHandle if pvMode == 0: vUp = vPoleVector - vRoot else: qTwist = om2.MQuaternion(prefAngle + twist, nAim) vUp = secAxis.rotateBy(qTwist) nNormalPole = vUp - ((vUp * nAim) * nAim) nNormalPole.normalize() if snap > 0.0: nNormalSnap = vStartSnap - ((vStartSnap * nAim) * nAim) nNormalSnap.normalize() nNormal = (1.0 - snap) * nNormalPole + snap * nNormalSnap else: nNormal = nNormalPole nUp = secAxis.rotateBy(qAim) angle = nUp.angle(nNormal) qNormal = om2.MQuaternion(angle, nAim) if not nNormal.isEquivalent(nUp.rotateBy(qNormal), 1.0e-5): angle = 2.0 * math.pi - angle qNormal = om2.MQuaternion(angle, nAim) qBasis *= qNormal # Solver Triangle restStartLen = dataBlock.inputValue( IKVChainSolver.inRestLenStart).asFloat() restEndLen = dataBlock.inputValue( IKVChainSolver.inRestLenEnd).asFloat() compressionLimit = dataBlock.inputValue( IKVChainSolver.inCompressionLimit).asFloat() softVal = dataBlock.inputValue(IKVChainSolver.inSoftness).asFloat() startSnapLen = vStartSnap.length() endSnapLen = vEndSnap.length() startLen = (1.0 - snap) * restStartLen + snap * startSnapLen endLen = (1.0 - snap) * restEndLen + snap * endSnapLen chainLen = (1.0 - snap) * (restStartLen + restEndLen) + snap * ( startSnapLen + endSnapLen) handleLen = vAim.length() rigidLen = max(min(handleLen, chainLen), chainLen * compressionLimit) dc = chainLen da = (1.0 - softVal) * dc if handleLen > da and softVal > 0.0: ds = dc - da softLen = ds * (1.0 - math.pow(math.e, (da - handleLen) / ds)) + da solverLen = (1.0 - snap) * softLen + snap * rigidLen else: solverLen = rigidLen # Pre Calculations startLenSquared = math.pow(startLen, 2.0) endLenSquared = math.pow(endLen, 2.0) solverLenSquared = math.pow(solverLen, 2.0) stretch = dataBlock.inputValue(IKVChainSolver.inStretch).asDouble() squashMultStart = dataBlock.inputValue( IKVChainSolver.inSquashMultStart).asFloat2() squashMultEnd = dataBlock.inputValue( IKVChainSolver.inSquashMultEnd).asFloat2() if stretch > 0.0: clampStretch = dataBlock.inputValue( IKVChainSolver.inClampStretch).asDouble() clampValue = dataBlock.inputValue( IKVChainSolver.inClampValue).asDouble() squash = dataBlock.inputValue(IKVChainSolver.inSquash).asDouble() if handleLen > da and softVal > 0.0: scaleFactor = handleLen / solverLen else: scaleFactor = handleLen / chainLen if handleLen >= da: clampFactor = ( 1.0 - clampStretch) * scaleFactor + clampStretch * min( scaleFactor, clampValue) stretchFactor = (1.0 - stretch) + stretch * clampFactor else: stretchFactor = 1.0 squashFactor = (1.0 - squash) + squash * (1.0 / math.sqrt(stretchFactor)) else: stretchFactor = 1.0 squashFactor = 1.0 hierarchyMode = dataBlock.inputValue( IKVChainSolver.inHierarchyMode).asBool() useScale = dataBlock.inputValue(IKVChainSolver.inUseScale).asBool() outChainHandle = dataBlock.outputArrayValue(IKVChainSolver.outChain) offsetHandle = dataBlock.inputArrayValue(IKVChainSolver.inOffset) jntOriHandle = dataBlock.inputArrayValue(IKVChainSolver.inJntOri) mParInv = dataBlock.inputValue(IKVChainSolver.inParInvMtx).asMatrix() srtList = [] offsetList = [] jntOriList = [] for i in range(len(offsetHandle)): offsetHandle.jumpToLogicalElement(i) eOff = om2.MEulerRotation(offsetHandle.inputValue().asDouble3()) qOff = eOff.asQuaternion() offsetList.append(qOff) for i in range(len(jntOriHandle)): jntOriHandle.jumpToLogicalElement(i) eOri = om2.MEulerRotation(jntOriHandle.inputValue().asDouble3()) qOri = eOri.asQuaternion() jntOriList.append(qOri) # First Output # Scale firstStretch = stretchFactor firstScaX = firstStretch if not useScale: firstStretch = 1.0 firstSquash = [ squashFactor * squashMultStart[0], squashFactor * squashMultStart[1] ] firstSca = [firstStretch, firstSquash[0], firstSquash[1]] # Rotation betaCosPure = (startLenSquared + solverLenSquared - endLenSquared) / (2.0 * startLen * solverLen) betaCos = min(max(betaCosPure, -1.0), 1.0) beta = math.acos(betaCos) qBeta = om2.MQuaternion(beta, binAxis) qFirstRotW = qBeta * qBasis qFirstRot = om2.MQuaternion() if len(offsetList) >= 1: qFirstRot *= offsetList[0].invertIt() qFirstRot *= qFirstRotW if len(jntOriList) >= 1: qFirstRot *= jntOriList[0].invertIt() # Translation vFirstPos = vRoot # Matrix Output mtxFn = om2.MTransformationMatrix() mtxFn.setScale(firstSca, om2.MSpace.kTransform) mtxFn.setRotation(qFirstRot) mtxFn.setTranslation(vFirstPos, om2.MSpace.kTransform) mFirst = mtxFn.asMatrix() mFirst *= mParInv srtList.append(mFirst) # Second Output # Scale secondStretch = stretchFactor secondScaX = secondStretch if not useScale: secondStretch = 1.0 secondSquash = [ squashFactor * squashMultEnd[0], squashFactor * squashMultEnd[1] ] secondSca = [secondStretch, secondSquash[0], secondSquash[1]] # Rotation gammaCosPure = (startLenSquared + endLenSquared - solverLenSquared) / (2.0 * startLen * endLen) gammaCos = min(max(gammaCosPure, -1.0), 1.0) gamma = math.acos(gammaCos) gammaCmp = gamma + beta - math.pi qGamma = om2.MQuaternion(gammaCmp, binAxis) qSecondRotW = qGamma * qBasis qSecondRot = om2.MQuaternion() if len(offsetList) >= 2: qSecondRot *= offsetList[1].invertIt() qSecondRot *= qSecondRotW if hierarchyMode: qSecondRot *= qFirstRotW.invertIt() if len(offsetList) >= 1: qSecondRot *= offsetList[0].invertIt() if len(jntOriList) >= 2: qSecondRot *= jntOriList[1].invertIt() # Translation if hierarchyMode: vSecondPos = primAxis * startLen if not useScale: vSecondPos *= firstScaX else: vSecondOri = nAim.rotateBy(om2.MQuaternion( beta, nAim ^ nNormal)) * startLen if not useScale: vSecondOri *= firstScaX vSecondPos = vRoot + vSecondOri # Matrix Output mtxFn = om2.MTransformationMatrix() mtxFn.setScale(secondSca, om2.MSpace.kTransform) mtxFn.setRotation(qSecondRot) mtxFn.setTranslation(vSecondPos, om2.MSpace.kTransform) mSecond = mtxFn.asMatrix() if not hierarchyMode: mSecond *= mParInv srtList.append(mSecond) # Third Output # Rotation qThirdRot = qBasis if hierarchyMode: qThirdRot *= qSecondRotW.invertIt() if len(offsetList) >= 2: qThirdRot *= offsetList[1].invertIt() # Translation if hierarchyMode: vThirdPos = primAxis * endLen if not useScale: vThirdPos *= secondScaX else: vThirdPos = vRoot + nAim * solverLen if not useScale: vThirdPos = vRoot + nAim * solverLen * stretchFactor # Matrix Output mtxFn = om2.MTransformationMatrix() mtxFn.setRotation(qThirdRot) mtxFn.setTranslation(vThirdPos, om2.MSpace.kTransform) mThird = mtxFn.asMatrix() if not hierarchyMode: mThird *= mParInv srtList.append(mThird) # Set outputs for i in range(len(outChainHandle)): outChainHandle.jumpToLogicalElement(i) resultHandle = outChainHandle.outputValue() if i < len(outChainHandle) and i < len(srtList): resultHandle.setMMatrix(srtList[i]) else: resultHandle.setMMatrix(om2.MMatrix.kIdentity) outChainHandle.setAllClean()
class EulerToVector(om2.MPxNode): """ Main class of gfUtilEulerToVector node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inEuler = om2.MObject() inEulerX = om2.MObject() inEulerY = om2.MObject() inEulerZ = om2.MObject() inToDegrees = om2.MObject() outVector = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return EulerToVector() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to EulerToVector class. Instances of EulerToVector will use these attributes to create plugs for use in the compute() method. """ uAttr = om2.MFnUnitAttribute() nAttr = om2.MFnNumericAttribute() EulerToVector.inEulerX = uAttr.create("eulerX", "ex", om2.MFnUnitAttribute.kAngle, 0.0) EulerToVector.inEulerY = uAttr.create("eulerY", "ey", om2.MFnUnitAttribute.kAngle, 0.0) EulerToVector.inEulerZ = uAttr.create("eulerZ", "ez", om2.MFnUnitAttribute.kAngle, 0.0) EulerToVector.inEuler = nAttr.create("euler", "e", EulerToVector.inEulerX, EulerToVector.inEulerY, EulerToVector.inEulerZ) INPUT_ATTR(nAttr) EulerToVector.inToDegrees = nAttr.create("convertToDegrees", "todeg", om2.MFnNumericData.kBoolean, True) INPUT_ATTR(nAttr) EulerToVector.outVector = nAttr.createPoint("outVector", "ov") OUTPUT_ATTR(nAttr) EulerToVector.addAttribute(EulerToVector.inEuler) EulerToVector.addAttribute(EulerToVector.inToDegrees) EulerToVector.addAttribute(EulerToVector.outVector) EulerToVector.attributeAffects(EulerToVector.inEuler, EulerToVector.outVector) EulerToVector.attributeAffects(EulerToVector.inToDegrees, EulerToVector.outVector) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use euler = dataBlock.inputValue(EulerToVector.inEuler).asDouble3() toDegrees = dataBlock.inputValue(EulerToVector.inToDegrees).asBool() if toDegrees: euler = [angle * (180.0 / math.pi) for angle in euler] vVector = om2.MFloatVector(euler) outVectorHandle = dataBlock.outputValue(EulerToVector.outVector) outVectorHandle.setMFloatVector(vVector) outVectorHandle.setClean()
class VectorAnglePSD(om2.MPxNode): """ Main class of gfRigPSDVectorAngle node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inBase = om2.MObject() inSource = om2.MObject() inTarget = om2.MObject() inTargetEnvelope = om2.MObject() inTargetFalloff = om2.MObject() inRampWeights = om2.MObject() outWeights = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) def postConstructor(self): """ Post Constructor. """ thisMob = self.thisMObject() attr = VectorAnglePSD.inRampWeights ramp = om2.MRampAttribute(thisMob, attr) pos = om2.MFloatArray() val = om2.MFloatArray() interp = om2.MIntArray() pos.append(1.0) val.append(1.0) interp.append(om2.MRampAttribute.kLinear) ramp.addEntries(pos, val, interp) @staticmethod def creator(): """ Maya creator function. """ return VectorAnglePSD() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to IKVChain class. Instances of IKVChain will use these attributes to create plugs for use in the compute() method. """ mAttr = om2.MFnMatrixAttribute() nAttr = om2.MFnNumericAttribute() rAttr = om2.MRampAttribute() VectorAnglePSD.inBase = mAttr.create("base", "base", om2.MFnMatrixAttribute.kFloat) INPUT_ATTR(mAttr) VectorAnglePSD.inSource = mAttr.create("source", "source", om2.MFnMatrixAttribute.kFloat) INPUT_ATTR(mAttr) VectorAnglePSD.inTarget = mAttr.create("target", "target", om2.MFnMatrixAttribute.kFloat) mAttr.array = True INPUT_ATTR(mAttr) VectorAnglePSD.inTargetEnvelope = nAttr.create( "targetEnvelope", "te", om2.MFnNumericData.kFloat, 1.0) nAttr.setMin(0.0) nAttr.setMax(1.0) nAttr.array = True INPUT_ATTR(nAttr) VectorAnglePSD.inTargetFalloff = nAttr.create( "targetFalloff", "tf", om2.MFnNumericData.kFloat, 90.0) nAttr.setMin(0.0) nAttr.setMax(180.0) nAttr.array = True INPUT_ATTR(nAttr) VectorAnglePSD.inRampWeights = rAttr.createCurveRamp( "rampWeights", "rw") VectorAnglePSD.outWeights = nAttr.create("outWeights", "ow", om2.MFnNumericData.kFloat, 0.0) nAttr.array = True OUTPUT_ATTR(nAttr) VectorAnglePSD.addAttribute(VectorAnglePSD.inBase) VectorAnglePSD.addAttribute(VectorAnglePSD.inSource) VectorAnglePSD.addAttribute(VectorAnglePSD.inTarget) VectorAnglePSD.addAttribute(VectorAnglePSD.inTargetEnvelope) VectorAnglePSD.addAttribute(VectorAnglePSD.inTargetFalloff) VectorAnglePSD.addAttribute(VectorAnglePSD.inRampWeights) VectorAnglePSD.addAttribute(VectorAnglePSD.outWeights) VectorAnglePSD.attributeAffects(VectorAnglePSD.inBase, VectorAnglePSD.outWeights) VectorAnglePSD.attributeAffects(VectorAnglePSD.inSource, VectorAnglePSD.outWeights) VectorAnglePSD.attributeAffects(VectorAnglePSD.inTarget, VectorAnglePSD.outWeights) VectorAnglePSD.attributeAffects(VectorAnglePSD.inTargetEnvelope, VectorAnglePSD.outWeights) VectorAnglePSD.attributeAffects(VectorAnglePSD.inTargetFalloff, VectorAnglePSD.outWeights) VectorAnglePSD.attributeAffects(VectorAnglePSD.inRampWeights, VectorAnglePSD.outWeights) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=R0201 if plug != VectorAnglePSD.outWeights: return om2.kUnknownParameter mBase = dataBlock.inputValue(VectorAnglePSD.inBase).asFloatMatrix() mSource = dataBlock.inputValue(VectorAnglePSD.inSource).asFloatMatrix() targetHandle = dataBlock.inputArrayValue(VectorAnglePSD.inTarget) targetEnvelopeHandle = dataBlock.inputArrayValue( VectorAnglePSD.inTargetEnvelope) targetFalloffHandle = dataBlock.inputArrayValue( VectorAnglePSD.inTargetFalloff) outWeightsHandle = dataBlock.outputArrayValue( VectorAnglePSD.outWeights) vBase = om2.MFloatVector(mBase[12], mBase[13], mBase[14]) vSource = om2.MFloatVector(mSource[12], mSource[13], mSource[14]) vCurPose = vSource - vBase nCurPose = vCurPose.normal() targetList = [] envelopeList = [] falloffList = [] for i in range(len(targetHandle)): targetHandle.jumpToLogicalElement(i) mtx = targetHandle.inputValue().asFloatMatrix() vec = om2.MFloatVector(mtx[12], mtx[13], mtx[14]) vTargetPose = vec - vBase nTargetPose = vTargetPose.normal() targetList.append(nTargetPose) for i in range(len(targetEnvelopeHandle)): targetEnvelopeHandle.jumpToLogicalElement(i) env = targetEnvelopeHandle.inputValue().asFloat() envelopeList.append(env) for i in range(len(targetFalloffHandle)): targetFalloffHandle.jumpToLogicalElement(i) fall = targetFalloffHandle.inputValue().asFloat() falloffList.append(fall) for i in range(len(outWeightsHandle)): outWeightsHandle.jumpToLogicalElement(i) resultHandle = outWeightsHandle.outputValue() if (i < len(outWeightsHandle) and i < len(targetHandle) and i < len(targetEnvelopeHandle) and i < len(targetFalloffHandle)): theta = math.acos(targetList[i] * nCurPose) ratio = min(max(theta / math.radians(falloffList[i]), -1.0), 1.0) if ratio == 0.0: weight = envelopeList[i] * 1.0 elif ratio > 0.0: weight = envelopeList[i] * (1.0 - ratio) elif ratio < 0.0: weight = envelopeList[i] * (1.0 + ratio) thisMob = self.thisMObject() rampAttr = om2.MRampAttribute(thisMob, VectorAnglePSD.inRampWeights) resultWeight = rampAttr.getValueAtPosition(weight) resultHandle.setFloat(resultWeight) else: resultHandle.setFloat(0.0)
class ParentConstraint(om2.MPxNode): """ Main class of gfUtilParentConstraint node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inConstraintJntOri = om2.MObject() inConstraintRotOrder = om2.MObject() inConstraintParInvMtx = om2.MObject() inConstraintParSca = om2.MObject() # inTargetJntOri = om2.MObject() # inTargetRotOrder = om2.MObject() inTargetWorldMatrix = om2.MObject() inTargetOffset = om2.MObject() inTargetWeight = om2.MObject() inTargetList = om2.MObject() outConstTrans = om2.MObject() outConstRot = om2.MObject() outConstSca = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return ParentConstraint() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to ParentConstraint class. Instances of ParentConstraint will use these attributes to create plugs for use in the compute() method. """ mAttr = om2.MFnMatrixAttribute() nAttr = om2.MFnNumericAttribute() uAttr = om2.MFnUnitAttribute() eAttr = om2.MFnEnumAttribute() cAttr = om2.MFnCompoundAttribute() constJntOriX = uAttr.create("constraintJointOrientX", "cjorx", om2.MFnUnitAttribute.kAngle, 0.0) constJntOriY = uAttr.create("constraintJointOrientY", "cjory", om2.MFnUnitAttribute.kAngle, 0.0) constJntOriZ = uAttr.create("constraintJointOrientZ", "cjorz", om2.MFnUnitAttribute.kAngle, 0.0) ParentConstraint.inConstraintJntOri = nAttr.create( "constraintJointOrient", "cjor", constJntOriX, constJntOriY, constJntOriZ) INPUT_ATTR(nAttr) ParentConstraint.inConstraintRotOrder = eAttr.create( "constraintRotateOrder", "croo", 0) eAttr.addField("xyz", 0) eAttr.addField("yzx", 1) eAttr.addField("zxy", 2) eAttr.addField("xzy", 3) eAttr.addField("yxz", 4) eAttr.addField("zyx", 5) INPUT_ATTR(eAttr) ParentConstraint.inConstraintParInvMtx = mAttr.create( "constraintParentInverseMatrix", "cpim", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) ParentConstraint.inConstraintParSca = nAttr.createPoint( "constraintParentScale", "cps") nAttr.default = (1.0, 1.0, 1.0) INPUT_ATTR(nAttr) ParentConstraint.inTargetWorldMatrix = mAttr.create( "targetWorldMatrix", "twmtx", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) ParentConstraint.inTargetOffset = mAttr.create( "targetOffset", "toff", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) # targetJntOriX = uAttr.create("targetJointOrientX", "tjorx", om2.MFnUnitAttribute.kAngle, 0.0) # targetJntOriY = uAttr.create("targetJointOrientY", "tjory", om2.MFnUnitAttribute.kAngle, 0.0) # targetJntOriZ = uAttr.create("targetJointOrientZ", "tjorz", om2.MFnUnitAttribute.kAngle, 0.0) # ParentConstraint.inTargetJntOri = nAttr.create("targetJointOrient", "tjor", targetJntOriX, targetJntOriY, targetJntOriZ) # INPUT_ATTR(nAttr) # ParentConstraint.inTargetRotOrder = eAttr.create("targetRotateOrder", "troo", 0) # eAttr.addField("xyz", 0) # eAttr.addField("yzx", 1) # eAttr.addField("zxy", 2) # eAttr.addField("xzy", 3) # eAttr.addField("yxz", 4) # eAttr.addField("zyx", 5) # INPUT_ATTR(eAttr) ParentConstraint.inTargetWeight = nAttr.create( "targetWeight", "twght", om2.MFnNumericData.kFloat, 1.0) INPUT_ATTR(nAttr) ParentConstraint.inTargetList = cAttr.create("targetList", "tlist") cAttr.addChild(ParentConstraint.inTargetWorldMatrix) cAttr.addChild(ParentConstraint.inTargetOffset) # cAttr.addChild(ParentConstraint.inTargetJntOri) # cAttr.addChild(ParentConstraint.inTargetRotOrder) cAttr.addChild(ParentConstraint.inTargetWeight) cAttr.array = True ParentConstraint.outConstTrans = nAttr.createPoint( "constraintTranslate", "ctrans") OUTPUT_ATTR(nAttr) outConstRotX = uAttr.create("constraintRotateX", "crox", om2.MFnUnitAttribute.kAngle, 0.0) outConstRotY = uAttr.create("constraintRotateY", "croy", om2.MFnUnitAttribute.kAngle, 0.0) outConstRotZ = uAttr.create("constraintRotateZ", "croz", om2.MFnUnitAttribute.kAngle, 0.0) ParentConstraint.outConstRot = nAttr.create("constraintRotate", "cro", outConstRotX, outConstRotY, outConstRotZ) OUTPUT_ATTR(nAttr) ParentConstraint.outConstSca = nAttr.createPoint( "constraintScale", "csca") nAttr.default = (1.0, 1.0, 1.0) OUTPUT_ATTR(nAttr) ParentConstraint.addAttribute(ParentConstraint.inConstraintJntOri) ParentConstraint.addAttribute(ParentConstraint.inConstraintRotOrder) ParentConstraint.addAttribute(ParentConstraint.inConstraintParInvMtx) ParentConstraint.addAttribute(ParentConstraint.inConstraintParSca) ParentConstraint.addAttribute(ParentConstraint.inTargetList) ParentConstraint.addAttribute(ParentConstraint.outConstTrans) ParentConstraint.addAttribute(ParentConstraint.outConstRot) ParentConstraint.addAttribute(ParentConstraint.outConstSca) ParentConstraint.attributeAffects(ParentConstraint.inConstraintJntOri, ParentConstraint.outConstTrans) ParentConstraint.attributeAffects( ParentConstraint.inConstraintRotOrder, ParentConstraint.outConstTrans) ParentConstraint.attributeAffects( ParentConstraint.inConstraintParInvMtx, ParentConstraint.outConstTrans) ParentConstraint.attributeAffects(ParentConstraint.inConstraintParSca, ParentConstraint.outConstTrans) ParentConstraint.attributeAffects(ParentConstraint.inTargetWorldMatrix, ParentConstraint.outConstTrans) ParentConstraint.attributeAffects(ParentConstraint.inTargetOffset, ParentConstraint.outConstTrans) # ParentConstraint.attributeAffects(ParentConstraint.inTargetJntOri, ParentConstraint.outConstTrans) # ParentConstraint.attributeAffects(ParentConstraint.inTargetRotOrder, ParentConstraint.outConstTrans) ParentConstraint.attributeAffects(ParentConstraint.inTargetWeight, ParentConstraint.outConstTrans) ParentConstraint.attributeAffects(ParentConstraint.inConstraintJntOri, ParentConstraint.outConstRot) ParentConstraint.attributeAffects( ParentConstraint.inConstraintRotOrder, ParentConstraint.outConstRot) ParentConstraint.attributeAffects( ParentConstraint.inConstraintParInvMtx, ParentConstraint.outConstRot) ParentConstraint.attributeAffects(ParentConstraint.inConstraintParSca, ParentConstraint.outConstRot) ParentConstraint.attributeAffects(ParentConstraint.inTargetWorldMatrix, ParentConstraint.outConstRot) ParentConstraint.attributeAffects(ParentConstraint.inTargetOffset, ParentConstraint.outConstRot) # ParentConstraint.attributeAffects(ParentConstraint.inTargetJntOri, ParentConstraint.outConstRot) # ParentConstraint.attributeAffects(ParentConstraint.inTargetRotOrder, ParentConstraint.outConstRot) ParentConstraint.attributeAffects(ParentConstraint.inTargetWeight, ParentConstraint.outConstRot) ParentConstraint.attributeAffects(ParentConstraint.inConstraintJntOri, ParentConstraint.outConstSca) ParentConstraint.attributeAffects( ParentConstraint.inConstraintRotOrder, ParentConstraint.outConstSca) ParentConstraint.attributeAffects( ParentConstraint.inConstraintParInvMtx, ParentConstraint.outConstSca) ParentConstraint.attributeAffects(ParentConstraint.inConstraintParSca, ParentConstraint.outConstSca) ParentConstraint.attributeAffects(ParentConstraint.inTargetWorldMatrix, ParentConstraint.outConstSca) ParentConstraint.attributeAffects(ParentConstraint.inTargetOffset, ParentConstraint.outConstSca) # ParentConstraint.attributeAffects(ParentConstraint.inTargetJntOri, ParentConstraint.outConstSca) # ParentConstraint.attributeAffects(ParentConstraint.inTargetRotOrder, ParentConstraint.outConstSca) ParentConstraint.attributeAffects(ParentConstraint.inTargetWeight, ParentConstraint.outConstSca) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use eConstJntOri = om2.MEulerRotation( dataBlock.inputValue( ParentConstraint.inConstraintJntOri).asDouble3()) mConstParInv = dataBlock.inputValue( ParentConstraint.inConstraintParInvMtx).asMatrix() constRotOrder = dataBlock.inputValue( ParentConstraint.inConstraintRotOrder).asShort() constParSca = dataBlock.inputValue( ParentConstraint.inConstraintParSca).asFloat3() targetListHandle = dataBlock.inputArrayValue( ParentConstraint.inTargetList) mTargetsAdded = om2.MMatrix() mtxFn = om2.MTransformationMatrix() mtxFn.scaleBy(constParSca, om2.MSpace.kTransform) mInvSca = mtxFn.asMatrix() for i in range(len(targetListHandle)): targetListHandle.jumpToLogicalElement(i) targetHandle = targetListHandle.inputValue() # targetJntOri = om2.MEulerRotation(targetHandle.child(ParentConstraint.inTargetJntOri).asDouble3()) # targetRotOrder = targetHandle.child(ParentConstraint.inTargetRotOrder).asShort() mTargetW = targetHandle.child( ParentConstraint.inTargetWorldMatrix).asMatrix() mOffset = targetHandle.child( ParentConstraint.inTargetOffset).asMatrix() targetWeight = targetHandle.child( ParentConstraint.inTargetWeight).asFloat() mTarget = mOffset * (mTargetW * targetWeight) if mTargetsAdded == om2.MMatrix(): mTargetsAdded = mTarget else: mTargetsAdded += mTarget mResult = mTargetsAdded * mConstParInv * mInvSca if plug == ParentConstraint.outConstTrans: outTransHandle = dataBlock.outputValue( ParentConstraint.outConstTrans) outTrans = om2.MFloatVector(mResult[12], mResult[13], mResult[14]) outTransHandle.setMFloatVector(outTrans) outTransHandle.setClean() if plug == ParentConstraint.outConstRot: outRotHandle = dataBlock.outputValue(ParentConstraint.outConstRot) mtxFn = om2.MTransformationMatrix(mResult) eRotMtx = mtxFn.rotation(asQuaternion=False) qRotMtx = eRotMtx.asQuaternion() qConstJntOri = eConstJntOri.asQuaternion() qOutRot = qRotMtx * qConstJntOri.invertIt() outRot = qOutRot.asEulerRotation().reorderIt(constRotOrder) outRotHandle.setMVector(outRot.asVector()) outRotHandle.setClean() if plug == ParentConstraint.outConstSca: outScaHandle = dataBlock.outputValue(ParentConstraint.outConstSca) mtxFn = om2.MTransformationMatrix(mResult) outSca = mtxFn.scale(om2.MSpace.kWorld) outScaHandle.set3Float(outSca[0], outSca[1], outSca[2]) outScaHandle.setClean()
class DebugMatrix(omui2.MPxLocatorNode): """ Main class of gfDebugMatrix node. """ kNodeName = "" kNodeClassify = "" kNodeRegistrantID = "" kNodeID = "" inMatrix = om2.MObject() inNameColor = om2.MObject() inMtxColor = om2.MObject() inDistance = om2.MObject() inLineHeight = om2.MObject() fNameColor = om2.MColor([0.90588, 0.41961, 0.41961]) fMtxColor = om2.MColor([0.93725, 0.87843, 0.69412]) def __init__(self): """ Constructor. """ omui2.MPxLocatorNode.__init__(self) def postConstructor(self): """ Post Constructor. """ thisMob = self.thisMObject() nodeFn = om2.MFnDependencyNode(thisMob) nodeFn.setName("%sShape#" % DebugMatrix.kNodeName) # localPosPlug = nodeFn.findPlug("localPosition", False) # localPosPlug.isChannelBox = False # localPosPlug @staticmethod def creator(): """ Maya creator function. """ return DebugMatrix() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to DebugMatrix class. Instances of DebugMatrix will use these attributes to create plugs for use in the compute() method. """ mAttr = om2.MFnMatrixAttribute() nAttr = om2.MFnNumericAttribute() DebugMatrix.inMatrix = mAttr.create("matrixInput", "mtxi", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) mAttr.storable = False mAttr.keyable = True DebugMatrix.inNameColor = nAttr.createColor("nameColor", "nColor") nAttr.default = (DebugMatrix.fNameColor.r, DebugMatrix.fNameColor.g, DebugMatrix.fNameColor.b) INPUT_ATTR(nAttr) DebugMatrix.inMtxColor = nAttr.createColor("matrixColor", "mColor") nAttr.default = (DebugMatrix.fMtxColor.r, DebugMatrix.fMtxColor.g, DebugMatrix.fMtxColor.b) INPUT_ATTR(nAttr) DebugMatrix.inDistance = nAttr.create("distance", "dist", om2.MFnNumericData.kFloat, 0.0) INPUT_ATTR(nAttr) DebugMatrix.inLineHeight = nAttr.create("lineHeight", "lHeight", om2.MFnNumericData.kFloat, 0.7) nAttr.setMin(0.001) nAttr.setMax(2.0) INPUT_ATTR(nAttr) DebugMatrix.addAttribute(DebugMatrix.inMatrix) DebugMatrix.addAttribute(DebugMatrix.inNameColor) DebugMatrix.addAttribute(DebugMatrix.inMtxColor) DebugMatrix.addAttribute(DebugMatrix.inDistance) DebugMatrix.addAttribute(DebugMatrix.inLineHeight) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=unused-argument return None def connectionMade(self, plug, otherPlug, asSrc): """This method gets called when connections are made to attributes of this node. * plug (MPlug) is the attribute on this node. * otherPlug (MPlug) is the attribute on the other node. * asSrc (bool) is this plug a source of the connection. """ if plug == DebugMatrix.inMatrix: thisMob = self.thisMObject() distancePlug = om2.MPlug(thisMob, DebugMatrix.inDistance) dagFn = om2.MFnDagNode(otherPlug.node()) bBox = dagFn.boundingBox offset = bBox.width / 2.0 distancePlug.setFloat(offset + 2.0) return omui2.MPxLocatorNode.connectionMade(self, plug, otherPlug, asSrc) def connectionBroken(self, plug, otherPlug, asSrc): """This method gets called when connections are broken with attributes of this node. * plug (MPlug) is the attribute on this node. * otherPlug (MPlug) is the attribute on the other node. * asSrc (bool) is this plug a source of the connection. """ if plug == DebugMatrix.inMatrix: thisMob = self.thisMObject() distancePlug = om2.MPlug(thisMob, DebugMatrix.inDistance) distancePlug.setFloat(0.0) return omui2.MPxLocatorNode.connectionBroken(self, plug, otherPlug, asSrc) def drawText(self, mtx, dist, colorList, status, cameraPath, lineH, view=None, drawManager=None): """Draw the matrix text. """ mCamera = cameraPath.inclusiveMatrix() camFn = om2.MFnCamera(cameraPath) thisMob = self.thisMObject() worldMtxPlug = om2.MPlug(thisMob, DebugMatrix.inMatrix) destPlugList = worldMtxPlug.connectedTo(True, False) if len(destPlugList) >= 1: node = destPlugList[0].node() attr = destPlugList[0].attribute() dagFn = om2.MFnDagNode(node) name = dagFn.name() attrName = "%s (%s)" % (" " * len(name), om2.MFnAttribute(attr).name) bBox = dagFn.boundingBox else: attrName = "" name = "NO MATRIX INPUT" bBox = om2.MBoundingBox() offsetY = bBox.height / 2.0 pntCamera = om2.MPoint(mCamera[12], mCamera[13], mCamera[14]) pntPos = om2.MPoint(mtx[12] + dist, mtx[13] + offsetY, mtx[14]) vCamera = pntCamera - pntPos distFromCamera = vCamera.length() pntLineOffset = om2.MPoint(0.0, (distFromCamera / camFn.focalLength) * lineH, 0.0) rowList = [] pntRow1 = om2.MPoint(mtx[0], mtx[1], mtx[2], mtx[3]) row1 = "%s | %s | %s | %s" % ("%.3f" % pntRow1.x, "%.3f" % pntRow1.y, "%.3f" % pntRow1.z, "%.3f" % pntRow1.w) rowList.append(row1) pntRow2 = om2.MPoint(mtx[4], mtx[5], mtx[6], mtx[7]) row2 = "%s | %s | %s | %s" % ("%.3f" % pntRow2.x, "%.3f" % pntRow2.y, "%.3f" % pntRow2.z, "%.3f" % pntRow2.w) rowList.append(row2) pntRow3 = om2.MPoint(mtx[8], mtx[9], mtx[10], mtx[11]) row3 = "%s | %s | %s | %s" % ("%.3f" % pntRow3.x, "%.3f" % pntRow3.y, "%.3f" % pntRow3.z, "%.3f" % pntRow3.w) rowList.append(row3) pntRow4 = om2.MPoint(mtx[12], mtx[13], mtx[14], mtx[15]) row4 = "%s | %s | %s | %s" % ("%.3f" % pntRow4.x, "%.3f" % pntRow4.y, "%.3f" % pntRow4.z, "%.3f" % pntRow4.w) rowList.append(row4) if status == omui2.M3dView.kActive: view.setDrawColor(om2.MColor([0.3, 1.0, 1.0])) elif status == omui2.M3dView.kLead: view.setDrawColor(om2.MColor([1.0, 1.0, 1.0])) if status == omui2.M3dView.kDormant: view.setDrawColor(colorList[0]) view.drawText(name, pntPos, omui2.M3dView.kLeft) if status == omui2.M3dView.kDormant: view.setDrawColor(colorList[1]) view.drawText(attrName, pntPos, omui2.M3dView.kLeft) if worldMtxPlug.isConnected: for i in range(1, 5): pos = om2.MPoint(pntPos - (pntLineOffset * i)) view.drawText(rowList[i - 1], pos, omui2.M3dView.kLeft) def draw(self, view, path, style, status): """ Draw custom geometry in the viewport using OpenGL calls. * view [M3dView] is a 3D view that is being drawn into. * path [MDagPath] to the parent (transform node) of this locator in the DAG. To obtain the locator shape node, use MDagPath::extendToShape() if there is only one shape node under the transform or MDagPath::extendToShapeDirectlyBelow(unsigned int index) with the shape index if there are multiple shapes under the transform. * style [M3dView.DisplayStyle] is the style to draw object in. * status [M3dView.DisplayStatus] is the selection status of the object. """ # pylint: disable=unused-argument thisMob = self.thisMObject() mInput = om2.MPlug(thisMob, DebugMatrix.inMatrix).asMDataHandle().asMatrix() nameColor = om2.MPlug( thisMob, DebugMatrix.inNameColor).asMDataHandle().asFloatVector() mtxColor = om2.MPlug( thisMob, DebugMatrix.inMtxColor).asMDataHandle().asFloatVector() dist = om2.MPlug(thisMob, DebugMatrix.inDistance).asFloat() lineHeight = om2.MPlug(thisMob, DebugMatrix.inLineHeight).asFloat() colorList = om2.MColorArray() colorList.append(om2.MColor([nameColor.x, nameColor.y, nameColor.z])) colorList.append(om2.MColor([mtxColor.x, mtxColor.y, mtxColor.z])) cameraPath = view.getCamera() view.beginGL() glRenderer = omr1.MHardwareRenderer.theRenderer() glFT = glRenderer.glFunctionTable() glFT.glPushAttrib(omr1.MGL_CURRENT_BIT) glFT.glDisable(omr1.MGL_CULL_FACE) self.drawText(mInput, dist, colorList, status, cameraPath, lineHeight, view) view.endGL() return None
class DistributeAlongSurface(om2.MPxNode): """ Main class of gfDistributeAlongSurface node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inSurface = om2.MObject() inDistributeAlong = om2.MObject() inDisplace = om2.MObject() inAlwaysUniform = om2.MObject() outTransform = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return DistributeAlongSurface() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to DistributeAlongSurface class. Instances of DistributeAlongSurface will use these attributes to create plugs for use in the compute() method. """ tAttr = om2.MFnTypedAttribute() eAttr = om2.MFnEnumAttribute() nAttr = om2.MFnNumericAttribute() mAttr = om2.MFnMatrixAttribute() DistributeAlongSurface.inSurface = tAttr.create("inputSurface", "isurf", om2.MFnData.kNurbsSurface) INPUT_ATTR(tAttr) DistributeAlongSurface.inDistributeAlong = eAttr.create("distributeAlong", "da", 0) eAttr.addField("U", 0) eAttr.addField("V", 1) INPUT_ATTR(eAttr) DistributeAlongSurface.inDisplace = nAttr.create("displaceTangent", "dtan", om2.MFnNumericData.kFloat, 0.0) nAttr.setMin(0.0) nAttr.setMax(1.0) INPUT_ATTR(nAttr) DistributeAlongSurface.inAlwaysUniform = nAttr.create("alwaysUniform", "auni", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) DistributeAlongSurface.outTransform = mAttr.create("outputTransform", "ot", om2.MFnMatrixAttribute.kDouble) mAttr.array = True OUTPUT_ATTR(mAttr) DistributeAlongSurface.addAttribute(DistributeAlongSurface.inSurface) DistributeAlongSurface.addAttribute(DistributeAlongSurface.inDistributeAlong) DistributeAlongSurface.addAttribute(DistributeAlongSurface.inDisplace) DistributeAlongSurface.addAttribute(DistributeAlongSurface.inAlwaysUniform) DistributeAlongSurface.addAttribute(DistributeAlongSurface.outTransform) DistributeAlongSurface.attributeAffects(DistributeAlongSurface.inSurface, DistributeAlongSurface.outTransform) DistributeAlongSurface.attributeAffects(DistributeAlongSurface.inDistributeAlong, DistributeAlongSurface.outTransform) DistributeAlongSurface.attributeAffects(DistributeAlongSurface.inDisplace, DistributeAlongSurface.outTransform) DistributeAlongSurface.attributeAffects(DistributeAlongSurface.inAlwaysUniform, DistributeAlongSurface.outTransform) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use if plug != DistributeAlongSurface.outTransform: return om2.kUnknownParameter surfaceHandle = dataBlock.inputValue(DistributeAlongSurface.inSurface) surface = surfaceHandle.asNurbsSurface() distAlong = dataBlock.inputValue(DistributeAlongSurface.inDistributeAlong).asShort() displace = dataBlock.inputValue(DistributeAlongSurface.inDisplace).asFloat() alwaysUniform = dataBlock.inputValue(DistributeAlongSurface.inAlwaysUniform).asBool() outTransHandle = dataBlock.outputArrayValue(DistributeAlongSurface.outTransform) numOutputs = len(outTransHandle) surfaceFn = om2.MFnNurbsSurface(surface) step = 1.0 / (numOutputs - 1) if numOutputs > 1 else 0.0 curveData = om2.MFnNurbsCurveData().create() curveFn = om2.MFnNurbsCurve(curveData) if alwaysUniform: numCVs = surfaceFn.numCVsInU if distAlong == 0 else surfaceFn.numCVsInV curvePnts = om2.MPointArray() curveKnots = surfaceFn.knotsInU() if distAlong == 0 else surfaceFn.knotsInV() curveDegree = surfaceFn.degreeInU if distAlong == 0 else surfaceFn.degreeInV curveForm = surfaceFn.formInU if distAlong == 0 else surfaceFn.formInV curveIs2d = False curveRational = False for i in range(numCVs): if distAlong == 0: curvePnts.append(surfaceFn.cvPosition(i, int(displace))) else: curvePnts.append(surfaceFn.cvPosition(int(displace), i)) curveFn.create(curvePnts, curveKnots, curveDegree, curveForm, curveIs2d, curveRational, curveData) curveLength = curveFn.length() for i in range(numOutputs): if alwaysUniform: parU = curveFn.findParamFromLength(curveLength * step * i) if distAlong == 0 else displace parV = displace if distAlong == 0 else curveFn.findParamFromLength(curveLength * step * i) else: parU = step * i if distAlong == 0 else displace parV = displace if distAlong == 0 else step * i pos = surfaceFn.getPointAtParam(parU, parV, om2.MSpace.kWorld) tangents = surfaceFn.tangents(parU, parV, om2.MSpace.kWorld) aim = tangents[0].normalize() if distAlong == 0 else tangents[1].normalize() normal = surfaceFn.normal(parU, parV, om2.MSpace.kWorld).normalize() binormal = tangents[1].normalize() if distAlong == 0 else tangents[0].normalize() mtx = [ aim.x, aim.y, aim.z, 0.0, normal.x, normal.y, normal.z, 0.0, binormal.x, binormal.y, binormal.z, 0.0, pos.x, pos.y, pos.z, 1.0 ] mOut = om2.MMatrix(mtx) outTransHandle.jumpToLogicalElement(i) resultHandle = outTransHandle.outputValue() resultHandle.setMMatrix(mOut) surfaceHandle.setClean() outTransHandle.setAllClean() cmds.refresh()
class VectorToEuler(om2.MPxNode): """ Main class of gfUtilVectorToEuler node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inVector = om2.MObject() outEuler = om2.MObject() outEulerX = om2.MObject() outEulerY = om2.MObject() outEulerZ = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return VectorToEuler() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to VectorToEuler class. Instances of VectorToEuler will use these attributes to create plugs for use in the compute() method. """ uAttr = om2.MFnUnitAttribute() nAttr = om2.MFnNumericAttribute() VectorToEuler.inVector = nAttr.createPoint("vector", "vec") INPUT_ATTR(nAttr) VectorToEuler.outEulerX = uAttr.create("outEulerX", "oex", om2.MFnUnitAttribute.kAngle, 0.0) VectorToEuler.outEulerY = uAttr.create("outEulerY", "oey", om2.MFnUnitAttribute.kAngle, 0.0) VectorToEuler.outEulerZ = uAttr.create("outEulerZ", "oez", om2.MFnUnitAttribute.kAngle, 0.0) VectorToEuler.outEuler = nAttr.create("outEuler", "oe", VectorToEuler.outEulerX, VectorToEuler.outEulerY, VectorToEuler.outEulerZ) OUTPUT_ATTR(nAttr) VectorToEuler.addAttribute(VectorToEuler.inVector) VectorToEuler.addAttribute(VectorToEuler.outEuler) VectorToEuler.attributeAffects(VectorToEuler.inVector, VectorToEuler.outEuler) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use vector = dataBlock.inputValue(VectorToEuler.inVector).asFloat3() vVector = om2.MVector([unit * (math.pi / 180.0) for unit in vector]) eEuler = om2.MEulerRotation(vVector) outEulerHandle = dataBlock.outputValue(VectorToEuler.outEuler) outEulerHandle.set3Double(eEuler.x, eEuler.y, eEuler.z) outEulerHandle.setClean()
class AngularTrigMath(om2.MPxNode): """ Main class of gfUtilAngularTrigMath node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inAngle1 = om2.MObject() inAngle2 = om2.MObject() inOperation = om2.MObject() outAngle = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return AngularTrigMath() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to AngularTrigMath class. Instances of AngularTrigMath will use these attributes to create plugs for use in the compute() method. """ uAttr = om2.MFnUnitAttribute() eAttr = om2.MFnEnumAttribute() AngularTrigMath.inAngle1 = uAttr.create("angle1", "a1", om2.MFnUnitAttribute.kAngle, 0.0) INPUT_ATTR(uAttr) AngularTrigMath.inAngle2 = uAttr.create("angle2", "a2", om2.MFnUnitAttribute.kAngle, 0.0) INPUT_ATTR(uAttr) AngularTrigMath.inOperation = eAttr.create("operation", "operation", 0) eAttr.addField("No Operation", 0) eAttr.addField("Cosine", 1) eAttr.addField("Sine", 2) eAttr.addField("Tangent", 3) eAttr.addField("Arccos", 4) eAttr.addField("Arcsin", 5) eAttr.addField("Arctan", 6) eAttr.addField("Arctan2", 7) INPUT_ATTR(eAttr) AngularTrigMath.outAngle = uAttr.create("outAngle", "oa", om2.MFnUnitAttribute.kAngle, 0.0) OUTPUT_ATTR(uAttr) AngularTrigMath.addAttribute(AngularTrigMath.inOperation) AngularTrigMath.addAttribute(AngularTrigMath.inAngle1) AngularTrigMath.addAttribute(AngularTrigMath.inAngle2) AngularTrigMath.addAttribute(AngularTrigMath.outAngle) AngularTrigMath.attributeAffects(AngularTrigMath.inAngle1, AngularTrigMath.outAngle) AngularTrigMath.attributeAffects(AngularTrigMath.inAngle2, AngularTrigMath.outAngle) AngularTrigMath.attributeAffects(AngularTrigMath.inOperation, AngularTrigMath.outAngle) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use if plug != AngularTrigMath.outAngle: return om2.kUnknownParameter angle1 = dataBlock.inputValue(AngularTrigMath.inAngle1).asAngle() operation = dataBlock.inputValue(AngularTrigMath.inOperation).asShort() outAngleHandle = dataBlock.outputValue(AngularTrigMath.outAngle) if operation == 0: outAngleHandle.setMAngle(angle1) elif operation == 1: outAngle = math.cos(angle1.asRadians()) outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kDegrees)) elif operation == 2: outAngle = math.sin(angle1.asRadians()) outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kDegrees)) elif operation == 3: outAngle = math.tan(angle1.asRadians()) outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kDegrees)) elif operation == 4: resAngle = angle1.asDegrees() if resAngle > 1.0: resAngle = 1.0 elif resAngle < -1.0: resAngle = -1.0 outAngle = math.acos(resAngle) outAngleHandle.setMAngle(om2.MAngle(outAngle)) elif operation == 5: resAngle = angle1.asDegrees() if resAngle > 1.0: resAngle = 1.0 elif resAngle < -1.0: resAngle = -1.0 outAngle = math.asin(resAngle) outAngleHandle.setMAngle(om2.MAngle(outAngle)) elif operation == 6: outAngle = math.atan(angle1.asRadians()) outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kRadians)) elif operation == 7: angle2 = dataBlock.inputValue(AngularTrigMath.inAngle2).asAngle() outAngle = math.atan2(angle1.asRadians(), angle2.asRadians()) outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kRadians)) outAngleHandle.setClean()
class AngularScalarMath(om2.MPxNode): """ Main class of gfUtilAngularScalarMath node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inAngle = om2.MObject() inScalar = om2.MObject() inOperation = om2.MObject() outAngle = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return AngularScalarMath() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to AngularScalarMath class. Instances of AngularScalarMath will use these attributes to create plugs for use in the compute() method. """ uAttr = om2.MFnUnitAttribute() nAttr = om2.MFnNumericAttribute() eAttr = om2.MFnEnumAttribute() AngularScalarMath.inAngle = uAttr.create("angle", "angle", om2.MFnUnitAttribute.kAngle, 0.0) INPUT_ATTR(uAttr) AngularScalarMath.inScalar = nAttr.create("scalar", "scalar", om2.MFnNumericData.kDouble, 0.0) INPUT_ATTR(nAttr) AngularScalarMath.inOperation = eAttr.create("operation", "operation", 0) eAttr.addField("No Operation", 0) eAttr.addField("Add", 1) eAttr.addField("Subtract", 2) eAttr.addField("Multiply", 3) eAttr.addField("Divide", 4) eAttr.addField("Power", 5) eAttr.addField("Min", 6) eAttr.addField("Max", 7) INPUT_ATTR(eAttr) AngularScalarMath.outAngle = uAttr.create("outAngle", "oa", om2.MFnUnitAttribute.kAngle, 0.0) OUTPUT_ATTR(uAttr) AngularScalarMath.addAttribute(AngularScalarMath.inOperation) AngularScalarMath.addAttribute(AngularScalarMath.inAngle) AngularScalarMath.addAttribute(AngularScalarMath.inScalar) AngularScalarMath.addAttribute(AngularScalarMath.outAngle) AngularScalarMath.attributeAffects(AngularScalarMath.inAngle, AngularScalarMath.outAngle) AngularScalarMath.attributeAffects(AngularScalarMath.inScalar, AngularScalarMath.outAngle) AngularScalarMath.attributeAffects(AngularScalarMath.inOperation, AngularScalarMath.outAngle) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use if plug != AngularScalarMath.outAngle: return om2.kUnknownParameter angle = dataBlock.inputValue( AngularScalarMath.inAngle).asAngle().asDegrees() scalar = dataBlock.inputValue(AngularScalarMath.inScalar).asDouble() operation = dataBlock.inputValue( AngularScalarMath.inOperation).asShort() outAngleHandle = dataBlock.outputValue(AngularScalarMath.outAngle) if operation == 0: outAngleHandle.setMAngle(om2.MAngle(angle, om2.MAngle.kDegrees)) elif operation == 1: outAngle = angle + scalar outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kDegrees)) elif operation == 2: outAngle = angle - scalar outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kDegrees)) elif operation == 3: outAngle = angle * scalar outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kDegrees)) elif operation == 4: if scalar != 0.0: outAngle = angle / scalar else: outAngle = 9999.999 outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kDegrees)) elif operation == 5: outAngle = math.pow(angle, scalar) outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kDegrees)) elif operation == 6: outAngle = min(angle, scalar) outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kDegrees)) elif operation == 7: outAngle = max(angle, scalar) outAngleHandle.setMAngle(om2.MAngle(outAngle, om2.MAngle.kDegrees)) outAngleHandle.setClean()
class AimConstraint(om2.MPxNode): """ Main class of gfUtilAimConstraint node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inUpVecType = om2.MObject() inOffset = om2.MObject() inWorldUpVector = om2.MObject() inWorldUpMtx = om2.MObject() inAngleUp = om2.MObject() inTargetWMtx = om2.MObject() inTargetWeight = om2.MObject() inConstWMtx = om2.MObject() inConstParInvMtx = om2.MObject() inConstJntOri = om2.MObject() inConstRotOrder = om2.MObject() outConstraint = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return AimConstraint() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to AimConstraint class. Instances of AimConstraint will use these attributes to create plugs for use in the compute() method. """ eAttr = om2.MFnEnumAttribute() mAttr = om2.MFnMatrixAttribute() nAttr = om2.MFnNumericAttribute() uAttr = om2.MFnUnitAttribute() AimConstraint.inUpVecType = eAttr.create("upVectorType", "upt", 0) eAttr.addField("None", 0) eAttr.addField("World Up", 1) eAttr.addField("Object Up", 2) eAttr.addField("Angle Up", 3) INPUT_ATTR(eAttr) eAttr.channelBox = True offsetX = uAttr.create("offsetX", "offsetX", om2.MFnUnitAttribute.kAngle, 0.0) offsetY = uAttr.create("offsetY", "offsetY", om2.MFnUnitAttribute.kAngle, 0.0) offsetZ = uAttr.create("offsetZ", "offsetZ", om2.MFnUnitAttribute.kAngle, 0.0) AimConstraint.inOffset = nAttr.create("offset", "offset", offsetX, offsetY, offsetZ) INPUT_ATTR(nAttr) AimConstraint.inWorldUpVector = nAttr.createPoint( "worldUpVector", "wuv") nAttr.default = (0.0, 1.0, 0.0) INPUT_ATTR(nAttr) AimConstraint.inWorldUpMtx = mAttr.create( "worldUpMatrix", "wum", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) AimConstraint.inAngleUp = uAttr.create("angleUp", "angle", om2.MFnUnitAttribute.kAngle, 0.0) uAttr.setMin(0.0) uAttr.setMax(2.0 * math.pi) INPUT_ATTR(uAttr) AimConstraint.inTargetWMtx = mAttr.create( "targetWorldMatrix", "twmtx", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) AimConstraint.inTargetWeight = nAttr.create("targetWeight", "tw", om2.MFnNumericData.kDouble, 1.0) INPUT_ATTR(nAttr) AimConstraint.inConstWMtx = mAttr.create( "constraintWorldMatrix", "cwmtx", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) AimConstraint.inConstParInvMtx = mAttr.create( "constraintParentInverseMatrix", "cpim", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) jntOriX = uAttr.create("constraintJointOrientX", "cjorx", om2.MFnUnitAttribute.kAngle, 0.0) jntOriY = uAttr.create("constraintJointOrientY", "cjory", om2.MFnUnitAttribute.kAngle, 0.0) jntOriZ = uAttr.create("constraintJointOrientZ", "cjorz", om2.MFnUnitAttribute.kAngle, 0.0) AimConstraint.inConstJntOri = nAttr.create("constraintJointOrient", "cjor", jntOriX, jntOriY, jntOriZ) INPUT_ATTR(nAttr) AimConstraint.inConstRotOrder = eAttr.create("constraintRotateOrder", "cro", 0) eAttr.addField("xyz", 0) eAttr.addField("yzx", 1) eAttr.addField("zxy", 2) eAttr.addField("xzy", 3) eAttr.addField("yxz", 4) eAttr.addField("zyx", 5) INPUT_ATTR(eAttr) outConstraintX = uAttr.create("constraintX", "cx", om2.MFnUnitAttribute.kAngle, 0.0) outConstraintY = uAttr.create("constraintY", "cy", om2.MFnUnitAttribute.kAngle, 0.0) outConstraintZ = uAttr.create("constraintZ", "cz", om2.MFnUnitAttribute.kAngle, 0.0) AimConstraint.outConstraint = nAttr.create("constraint", "const", outConstraintX, outConstraintY, outConstraintZ) OUTPUT_ATTR(nAttr) AimConstraint.addAttribute(AimConstraint.inUpVecType) AimConstraint.addAttribute(AimConstraint.inOffset) AimConstraint.addAttribute(AimConstraint.inWorldUpVector) AimConstraint.addAttribute(AimConstraint.inWorldUpMtx) AimConstraint.addAttribute(AimConstraint.inAngleUp) AimConstraint.addAttribute(AimConstraint.inTargetWMtx) AimConstraint.addAttribute(AimConstraint.inTargetWeight) AimConstraint.addAttribute(AimConstraint.inConstWMtx) AimConstraint.addAttribute(AimConstraint.inConstParInvMtx) AimConstraint.addAttribute(AimConstraint.inConstJntOri) AimConstraint.addAttribute(AimConstraint.inConstRotOrder) AimConstraint.addAttribute(AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inUpVecType, AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inOffset, AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inWorldUpVector, AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inWorldUpMtx, AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inAngleUp, AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inTargetWMtx, AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inTargetWeight, AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inConstWMtx, AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inConstParInvMtx, AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inConstJntOri, AimConstraint.outConstraint) AimConstraint.attributeAffects(AimConstraint.inConstRotOrder, AimConstraint.outConstraint) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use if plug != AimConstraint.outConstraint: return om2.kUnknownParameter upVecType = dataBlock.inputValue(AimConstraint.inUpVecType).asShort() eOffset = om2.MEulerRotation( dataBlock.inputValue(AimConstraint.inOffset).asDouble3()) qOffset = eOffset.asQuaternion() mTargetW = dataBlock.inputValue(AimConstraint.inTargetWMtx).asMatrix() targetWeight = dataBlock.inputValue( AimConstraint.inTargetWeight).asDouble() mConstW = dataBlock.inputValue(AimConstraint.inConstWMtx).asMatrix() mConstParInv = dataBlock.inputValue( AimConstraint.inConstParInvMtx).asMatrix() eConstJntOri = om2.MEulerRotation( dataBlock.inputValue(AimConstraint.inConstJntOri).asDouble3()) qConstJntOri = eConstJntOri.asQuaternion() constRotOrder = dataBlock.inputValue( AimConstraint.inConstRotOrder).asShort() vTarget = om2.MVector(mTargetW[12], mTargetW[13], mTargetW[14]) vConst = om2.MVector(mConstW[12], mConstW[13], mConstW[14]) mtxFn = om2.MTransformationMatrix(mConstParInv) qConstParInv = mtxFn.rotation(asQuaternion=True) primAxis = om2.MVector.kXaxisVector secAxis = om2.MVector.kYaxisVector qAimConst = om2.MQuaternion() nAim = vTarget - vConst nAim.normalize() qAim = om2.MQuaternion(primAxis, nAim) qAimConst *= qAim if upVecType != 0: if upVecType == 1: # World Up nWorldUp = om2.MVector( dataBlock.inputValue( AimConstraint.inWorldUpVector).asFloat3()) nWorldUp.normalize() vUp = nWorldUp elif upVecType == 2: # Object Up mWorldUp = dataBlock.inputValue( AimConstraint.inWorldUpMtx).asMatrix() vWorldUp = om2.MVector(mWorldUp[12], mWorldUp[13], mWorldUp[14]) vUp = vWorldUp - vConst elif upVecType == 3: # Angle Up angleUp = dataBlock.inputValue( AimConstraint.inAngleUp).asAngle().asRadians() qTwist = om2.MQuaternion(angleUp, nAim) vUp = secAxis.rotateBy(qTwist) nNormal = vUp - ((vUp * nAim) * nAim) nNormal.normalize() nUp = secAxis.rotateBy(qAim) angle = nUp.angle(nNormal) qNormal = om2.MQuaternion(angle, nAim) if not nNormal.isEquivalent(nUp.rotateBy(qNormal), 1.0e-5): angle = 2.0 * math.pi - angle qNormal = om2.MQuaternion(angle, nAim) qAimConst *= qNormal qResult = om2.MQuaternion() qResult *= qOffset.invertIt() qResult *= qAimConst qResult *= qConstParInv qResult *= qConstJntOri.invertIt() eResult = qResult.asEulerRotation() eResult.reorderIt(constRotOrder) eResult *= targetWeight vResult = eResult.asVector() outConstraintHandle = dataBlock.outputValue( AimConstraint.outConstraint) outConstraintHandle.setMVector(vResult) outConstraintHandle.setClean()
class EulerMath(om2.MPxNode): """ Main class of gfUtilEulerMath node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inOperation = om2.MObject() inEuler1 = om2.MObject() inEuler1X = om2.MObject() inEuler1Y = om2.MObject() inEuler1Z = om2.MObject() inEuler1RotOrder = om2.MObject() inEuler2 = om2.MObject() inEuler2X = om2.MObject() inEuler2Y = om2.MObject() inEuler2Z = om2.MObject() inEuler2RotOrder = om2.MObject() inResRotOrder = om2.MObject() outEuler = om2.MObject() outEulerX = om2.MObject() outEulerY = om2.MObject() outEulerZ = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return EulerMath() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to EulerMath class. Instances of EulerMath will use these attributes to create plugs for use in the compute() method. """ eAttr = om2.MFnEnumAttribute() uAttr = om2.MFnUnitAttribute() nAttr = om2.MFnNumericAttribute() EulerMath.inOperation = eAttr.create("operation", "operation", 0) eAttr.addField("No Operation", 0) eAttr.addField("Add", 1) eAttr.addField("Subtract", 2) eAttr.addField("Multiply", 3) INPUT_ATTR(eAttr) EulerMath.inEuler1X = uAttr.create("euler1X", "e1x", om2.MFnUnitAttribute.kAngle, 0.0) EulerMath.inEuler1Y = uAttr.create("euler1Y", "e1y", om2.MFnUnitAttribute.kAngle, 0.0) EulerMath.inEuler1Z = uAttr.create("euler1Z", "e1z", om2.MFnUnitAttribute.kAngle, 0.0) EulerMath.inEuler1 = nAttr.create("euler1", "e1", EulerMath.inEuler1X, EulerMath.inEuler1Y, EulerMath.inEuler1Z) INPUT_ATTR(nAttr) EulerMath.inEuler1RotOrder = eAttr.create("rotateOrderEuler1", "roe1", 0) eAttr.addField("xyz", 0) eAttr.addField("yzx", 1) eAttr.addField("zxy", 2) eAttr.addField("xzy", 3) eAttr.addField("yxz", 4) eAttr.addField("zyx", 5) INPUT_ATTR(eAttr) EulerMath.inEuler2X = uAttr.create("euler2X", "e2x", om2.MFnUnitAttribute.kAngle, 0.0) EulerMath.inEuler2Y = uAttr.create("euler2Y", "e2y", om2.MFnUnitAttribute.kAngle, 0.0) EulerMath.inEuler2Z = uAttr.create("euler2Z", "e2z", om2.MFnUnitAttribute.kAngle, 0.0) EulerMath.inEuler2 = nAttr.create("euler2", "e2", EulerMath.inEuler2X, EulerMath.inEuler2Y, EulerMath.inEuler2Z) INPUT_ATTR(nAttr) EulerMath.inEuler2RotOrder = eAttr.create("rotateOrderEuler2", "roe2", 0) eAttr.addField("xyz", 0) eAttr.addField("yzx", 1) eAttr.addField("zxy", 2) eAttr.addField("xzy", 3) eAttr.addField("yxz", 4) eAttr.addField("zyx", 5) INPUT_ATTR(eAttr) EulerMath.outEulerX = uAttr.create("outEulerX", "oex", om2.MFnUnitAttribute.kAngle, 0.0) EulerMath.outEulerY = uAttr.create("outEulerY", "oey", om2.MFnUnitAttribute.kAngle, 0.0) EulerMath.outEulerZ = uAttr.create("outEulerZ", "oez", om2.MFnUnitAttribute.kAngle, 0.0) EulerMath.outEuler = nAttr.create("outEuler", "oe", EulerMath.outEulerX, EulerMath.outEulerY, EulerMath.outEulerZ) OUTPUT_ATTR(nAttr) EulerMath.inResRotOrder = eAttr.create("rotateOrderOutEuler", "rooe", 0) eAttr.addField("xyz", 0) eAttr.addField("yzx", 1) eAttr.addField("zxy", 2) eAttr.addField("xzy", 3) eAttr.addField("yxz", 4) eAttr.addField("zyx", 5) INPUT_ATTR(eAttr) EulerMath.addAttribute(EulerMath.inOperation) EulerMath.addAttribute(EulerMath.inEuler1) EulerMath.addAttribute(EulerMath.inEuler1RotOrder) EulerMath.addAttribute(EulerMath.inEuler2) EulerMath.addAttribute(EulerMath.inEuler2RotOrder) EulerMath.addAttribute(EulerMath.inResRotOrder) EulerMath.addAttribute(EulerMath.outEuler) EulerMath.attributeAffects(EulerMath.inOperation, EulerMath.outEuler) EulerMath.attributeAffects(EulerMath.inEuler1, EulerMath.outEuler) EulerMath.attributeAffects(EulerMath.inEuler1RotOrder, EulerMath.outEuler) EulerMath.attributeAffects(EulerMath.inEuler2, EulerMath.outEuler) EulerMath.attributeAffects(EulerMath.inEuler2RotOrder, EulerMath.outEuler) EulerMath.attributeAffects(EulerMath.inResRotOrder, EulerMath.outEuler) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use if (plug != EulerMath.outEuler and plug != EulerMath.outEulerX and plug != EulerMath.outEulerY and plug != EulerMath.outEulerZ): return om2.kUnknownParameter operation = dataBlock.inputValue(EulerMath.inOperation).asShort() euler1 = dataBlock.inputValue(EulerMath.inEuler1).asDouble3() euler2 = dataBlock.inputValue(EulerMath.inEuler2).asDouble3() euler1RotOrder = dataBlock.inputValue( EulerMath.inEuler1RotOrder).asShort() euler2RotOrder = dataBlock.inputValue( EulerMath.inEuler2RotOrder).asShort() outRotOrder = dataBlock.inputValue(EulerMath.inResRotOrder).asShort() eEuler1 = om2.MEulerRotation(euler1, euler1RotOrder) eEuler2 = om2.MEulerRotation(euler2, euler2RotOrder) outEulerHdle = dataBlock.outputValue(EulerMath.outEuler) if operation == 0: eEuler1.reorderIt(outRotOrder) outEulerHdle.set3Double(eEuler1.x, eEuler1.y, eEuler1.z) elif operation == 1: eEuler1.reorderIt(outRotOrder) eEuler2.reorderIt(outRotOrder) eOutEuler = eEuler1 + eEuler2 outEulerHdle.set3Double(eOutEuler.x, eOutEuler.y, eOutEuler.z) elif operation == 2: eEuler1.reorderIt(outRotOrder) eEuler2.reorderIt(outRotOrder) eOutEuler = eEuler1 - eEuler2 outEulerHdle.set3Double(eOutEuler.x, eOutEuler.y, eOutEuler.z) elif operation == 3: eEuler1.reorderIt(outRotOrder) eEuler2.reorderIt(outRotOrder) eOutEuler = eEuler1 * eEuler2 outEulerHdle.set3Double(eOutEuler.x, eOutEuler.y, eOutEuler.z) outEulerHdle.setClean()
class HelperJoint(om2.MPxNode): """ Main class of gfRigHelperJoint node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inSource = om2.MObject() inSourceParent = om2.MObject() inParInvMtx = om2.MObject() inSourceParSca = om2.MObject() inPositionOffset = om2.MObject() inRotationOffset = om2.MObject() inRotAngle = om2.MObject() inRestAngle = om2.MObject() inRotInterp = om2.MObject() inPosMult = om2.MObject() inNegMult = om2.MObject() inTargetList = om2.MObject() outTransform = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return HelperJoint() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to HelperJoint class. Instances of HelperJoint will use these attributes to create plugs for use in the compute() method. """ nAttr = om2.MFnNumericAttribute() mAttr = om2.MFnMatrixAttribute() uAttr = om2.MFnUnitAttribute() cAttr = om2.MFnCompoundAttribute() HelperJoint.inSource = mAttr.create("source", "s", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) HelperJoint.inSourceParent = mAttr.create("sourceParent", "sp", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) HelperJoint.inParInvMtx = mAttr.create("targetParentInverseMatrix", "tpimtx", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) HelperJoint.inSourceParSca = nAttr.createPoint("sourceParentScale", "spsca") nAttr.default = (1.0, 1.0, 1.0) INPUT_ATTR(nAttr) HelperJoint.inPositionOffset = nAttr.createPoint("positionOffset", "posoff") INPUT_ATTR(nAttr) rotOffX = uAttr.create("rotationOffsetX", "rotoffx", om2.MFnUnitAttribute.kAngle, 0.0) rotOffY = uAttr.create("rotationOffsetY", "rotoffy", om2.MFnUnitAttribute.kAngle, 0.0) rotOffZ = uAttr.create("rotationOffsetZ", "rotoffz", om2.MFnUnitAttribute.kAngle, 0.0) HelperJoint.inRotationOffset = nAttr.create("rotationOffset", "rotoff", rotOffX, rotOffY, rotOffZ) INPUT_ATTR(nAttr) HelperJoint.inRotAngle = uAttr.create("rotationAngle", "rotangle", om2.MFnUnitAttribute.kAngle, 0.0) INPUT_ATTR(uAttr) HelperJoint.inRestAngle = uAttr.create("restAngle", "rang", om2.MFnUnitAttribute.kAngle, 0.0) INPUT_ATTR(uAttr) HelperJoint.inRotInterp = nAttr.create("rotationInterpolation", "roti", om2.MFnNumericData.kFloat, 0.5) nAttr.setMin(0.0) nAttr.setMax(1.0) INPUT_ATTR(nAttr) HelperJoint.inPosMult = nAttr.create("positiveMultiplier", "posmult", om2.MFnNumericData.kFloat, 0.0) INPUT_ATTR(nAttr) HelperJoint.inNegMult = nAttr.create("negativeMultiplier", "negmult", om2.MFnNumericData.kFloat, 0.0) INPUT_ATTR(nAttr) HelperJoint.inTargetList = cAttr.create("targetList", "tgtl") cAttr.addChild(HelperJoint.inPositionOffset) cAttr.addChild(HelperJoint.inRotationOffset) cAttr.addChild(HelperJoint.inRotAngle) cAttr.addChild(HelperJoint.inRestAngle) cAttr.addChild(HelperJoint.inRotInterp) cAttr.addChild(HelperJoint.inPosMult) cAttr.addChild(HelperJoint.inNegMult) cAttr.array = True HelperJoint.outTransform = mAttr.create("outTransform", "outtrans", om2.MFnMatrixAttribute.kDouble) mAttr.array = True OUTPUT_ATTR(mAttr) HelperJoint.addAttribute(HelperJoint.inSource) HelperJoint.addAttribute(HelperJoint.inSourceParent) HelperJoint.addAttribute(HelperJoint.inParInvMtx) HelperJoint.addAttribute(HelperJoint.inSourceParSca) HelperJoint.addAttribute(HelperJoint.inTargetList) HelperJoint.addAttribute(HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inSource, HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inSourceParent, HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inParInvMtx, HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inSourceParSca, HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inPositionOffset, HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inRotationOffset, HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inRotAngle, HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inRestAngle, HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inRotInterp, HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inPosMult, HelperJoint.outTransform) HelperJoint.attributeAffects(HelperJoint.inNegMult, HelperJoint.outTransform) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use if plug != HelperJoint.outTransform: return om2.kUnknownParameter mSource = dataBlock.inputValue(HelperJoint.inSource).asMatrix() mtxFn = om2.MTransformationMatrix(mSource) mtxFn.setScale([1.0, 1.0, 1.0], om2.MSpace.kTransform) mSource = mtxFn.asMatrix() mSourceParent = dataBlock.inputValue(HelperJoint.inSourceParent).asMatrix() mParInv = dataBlock.inputValue(HelperJoint.inParInvMtx).asMatrix() sourceParSca = dataBlock.inputValue(HelperJoint.inSourceParSca).asFloat3() mtxFn = om2.MTransformationMatrix() mtxFn.scaleBy(sourceParSca, om2.MSpace.kTransform) mInvSca = mtxFn.asMatrix() targetListHandle = dataBlock.inputArrayValue(HelperJoint.inTargetList) outputList = [] for i in range(len(targetListHandle)): targetListHandle.jumpToLogicalElement(i) targetHandle = targetListHandle.inputValue() vPosOffset = om2.MVector(targetHandle.child(HelperJoint.inPositionOffset).asFloat3()) eRotOffset = om2.MEulerRotation(targetHandle.child(HelperJoint.inRotationOffset).asDouble3()) angle = targetHandle.child(HelperJoint.inRotAngle).asAngle().asRadians() restAngle = targetHandle.child(HelperJoint.inRestAngle).asAngle().asRadians() rotInterp = targetHandle.child(HelperJoint.inRotInterp).asFloat() posMult = targetHandle.child(HelperJoint.inPosMult).asFloat() negMult = targetHandle.child(HelperJoint.inNegMult).asFloat() mPositionOffset = om2.MMatrix() mPositionOffset[12] = vPosOffset.x mPositionOffset[13] = vPosOffset.y mPositionOffset[14] = vPosOffset.z multTranslation = abs(angle) * posMult if angle < restAngle: multTranslation = abs(angle) * negMult vPosOffset.normalize() mMultiplier = om2.MMatrix() mMultiplier[12] = vPosOffset.x * multTranslation mMultiplier[13] = vPosOffset.y * multTranslation mMultiplier[14] = vPosOffset.z * multTranslation mTargetPoint = mMultiplier * mPositionOffset * mSource mTargetOrient = mInvSca * (mSource * (1.0 - rotInterp)) + (mSourceParent * rotInterp) vResultPos = om2.MVector(mTargetPoint[12], mTargetPoint[13], mTargetPoint[14]) mtxFn = om2.MTransformationMatrix(mTargetOrient) eResultOri = eRotOffset + mtxFn.rotation(asQuaternion=False) mtxFn = om2.MTransformationMatrix() mtxFn.setRotation(eResultOri) mtxFn.setTranslation(vResultPos, om2.MSpace.kTransform) mResult = mtxFn.asMatrix() * mParInv outputList.append(mResult) outTransHandle = dataBlock.outputArrayValue(HelperJoint.outTransform) for i in range(len(outTransHandle)): outTransHandle.jumpToLogicalElement(i) resultHandle = outTransHandle.outputValue() if i < len(outTransHandle) and i < len(outputList): resultHandle.setMMatrix(outputList[i]) else: resultHandle.setMMatrix(om2.MMatrix.kIdentity)
class DebugVector(omui2.MPxLocatorNode): """ Main class of gfDebugVector node. """ kNodeName = "" kNodeClassify = "" kNodeRegistrantID = "" kNodeID = "" inLineWidth = om2.MObject() inColor = om2.MObject() inRadius = om2.MObject() inTipSize = om2.MObject() inSubdivisions = om2.MObject() inXRay = om2.MObject() inDashed = om2.MObject() inOperation = om2.MObject() inVec1 = om2.MObject() inVec2 = om2.MObject() inNormalize = om2.MObject() outVector = om2.MObject() def __init__(self): """ Constructor. """ omui2.MPxLocatorNode.__init__(self) def postConstructor(self): """ Post Constructor. """ thisMob = self.thisMObject() om2.MFnDependencyNode(thisMob).setName("%sShape#" % DebugVector.kNodeName) @staticmethod def creator(): """ Maya creator function. """ return DebugVector() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to DebugVector class. Instances of DebugVector will use these attributes to create plugs for use in the compute() method. """ eAttr = om2.MFnEnumAttribute() nAttr = om2.MFnNumericAttribute() DebugVector.inLineWidth = nAttr.create("lineWidth", "lw", om2.MFnNumericData.kFloat, 3.0) nAttr.setMin(1.0) nAttr.setSoftMax(5.0) INPUT_ATTR(nAttr) DebugVector.inColor = nAttr.createColor("color", "color") nAttr.default = (1.0, 1.0, 0.0) INPUT_ATTR(nAttr) DebugVector.inTipSize = nAttr.create("tipSize", "tipSize", om2.MFnNumericData.kFloat, 0.1) nAttr.setMin(0.1) nAttr.setMax(1.0) INPUT_ATTR(nAttr) DebugVector.inSubdivisions = nAttr.create("subdivisions", "subd", om2.MFnNumericData.kInt, 4) nAttr.setMin(2) nAttr.setMax(12) INPUT_ATTR(nAttr) DebugVector.inRadius = nAttr.create("radius", "radius", om2.MFnNumericData.kFloat, 1.0) nAttr.setMin(0.0) nAttr.setSoftMax(5.0) INPUT_ATTR(nAttr) DebugVector.inXRay = nAttr.create("XRay", "xray", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) DebugVector.inDashed = nAttr.create("dashed", "dashed", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) DebugVector.inOperation = eAttr.create("operation", "op", 0) eAttr.addField("No Operation", 0) eAttr.addField("Add", 1) eAttr.addField("Subtract", 2) eAttr.addField("Cross Product", 3) eAttr.addField("Vector Project", 4) INPUT_ATTR(eAttr) DebugVector.inVec1 = nAttr.createPoint("vector1", "v1") INPUT_ATTR(nAttr) DebugVector.inVec2 = nAttr.createPoint("vector2", "v2") INPUT_ATTR(nAttr) DebugVector.inNormalize = nAttr.create("normalizeOutput", "no", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) DebugVector.outVector = nAttr.createPoint("outVector", "ov") OUTPUT_ATTR(nAttr) DebugVector.addAttribute(DebugVector.inLineWidth) DebugVector.addAttribute(DebugVector.inColor) DebugVector.addAttribute(DebugVector.inTipSize) DebugVector.addAttribute(DebugVector.inSubdivisions) DebugVector.addAttribute(DebugVector.inRadius) DebugVector.addAttribute(DebugVector.inXRay) DebugVector.addAttribute(DebugVector.inDashed) DebugVector.addAttribute(DebugVector.inOperation) DebugVector.addAttribute(DebugVector.inVec1) DebugVector.addAttribute(DebugVector.inVec2) DebugVector.addAttribute(DebugVector.inNormalize) DebugVector.addAttribute(DebugVector.outVector) DebugVector.attributeAffects(DebugVector.inOperation, DebugVector.outVector) DebugVector.attributeAffects(DebugVector.inVec1, DebugVector.outVector) DebugVector.attributeAffects(DebugVector.inVec2, DebugVector.outVector) DebugVector.attributeAffects(DebugVector.inNormalize, DebugVector.outVector) @staticmethod def drawArrow(startPnt, endPnt, size, radius, subd, lineW, vp2=False, glFT=None, lineList=None): """Draw an aim arrow Args: startPnt (MFloatVector): The base of the vector. endPnt (MFloatVector): The end of the vector. size (float): The size of the arrow. radius (float): The radius of the arrow. subd (int): The number of subdivisions of the arrow. lineW (float): The width of the lines. vp2 (bool: False [Optional]): Draw inside of drawing override for viewport 2.0. glFT (instance: None [Optional]): The GL Function Table to draw in viewport 1.0. lineList (MPointArray: None [Optional]): The line list to append for drawing override. """ tipSize = 1.0 - size step = 2.0 * math.pi / subd vAim = endPnt - startPnt vBaseOrigin = vAim * tipSize nAim = vAim.normal() nWorld = om2.MFloatVector(0.0, 1.0, 0.0) nBinormal = nWorld ^ nAim nBinormal.normalize() nNormal = nAim ^ nBinormal nNormal.normalize() aim = [ nAim.x, nAim.y, nAim.z, 0.0, nNormal.x, nNormal.y, nNormal.z, 0.0, nBinormal.x, nBinormal.y, nBinormal.z, 0.0, startPnt.x, startPnt.y, startPnt.z, 1.0 ] mBase = om2.MMatrix(aim) mOrigin = om2.MMatrix() mOrigin[12] = vBaseOrigin.length() mBaseOrigin = mOrigin * mBase if vp2: lineList.append(om2.MPoint(startPnt)) lineList.append(om2.MPoint(endPnt)) else: glFT.glLineWidth(lineW) glFT.glBegin(omr1.MGL_LINES) glFT.glVertex3f(startPnt.x, startPnt.y, startPnt.z) glFT.glVertex3f(endPnt.x, endPnt.y, endPnt.z) glFT.glEnd() for i in range(subd): theta = step * i mPoint = om2.MMatrix() mPoint[13] = math.cos(theta) * radius mPoint[14] = math.sin(theta) * radius mArrow = mPoint * mBaseOrigin if vp2: lineList.append( om2.MPoint(mBaseOrigin[12], mBaseOrigin[13], mBaseOrigin[14])) lineList.append(om2.MPoint(mArrow[12], mArrow[13], mArrow[14])) lineList.append(om2.MPoint(mArrow[12], mArrow[13], mArrow[14])) lineList.append(om2.MPoint(endPnt)) else: glFT.glBegin(omr1.MGL_LINES) glFT.glVertex3f(mBaseOrigin[12], mBaseOrigin[13], mBaseOrigin[14]) glFT.glVertex3f(mArrow[12], mArrow[13], mArrow[14]) glFT.glVertex3f(mArrow[12], mArrow[13], mArrow[14]) glFT.glVertex3f(endPnt.x, endPnt.y, endPnt.z) glFT.glEnd() return None def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ if plug == DebugVector.outVector: operation = dataBlock.inputValue(DebugVector.inOperation).asShort() vVector1 = dataBlock.inputValue(DebugVector.inVec1).asFloatVector() vVector2 = dataBlock.inputValue(DebugVector.inVec2).asFloatVector() normalize = dataBlock.inputValue(DebugVector.inNormalize).asBool() if operation == 0: vEnd = vVector1 elif operation == 1: vFinal = vVector1 + vVector2 vEnd = vFinal elif operation == 2: vFinal = vVector1 - vVector2 vEnd = vFinal elif operation == 3: vFinal = vVector1 ^ vVector2 vEnd = vFinal elif operation == 4: if vVector2.length() < 0.001: vFinal = om2.MFloatVector(0.0, 0.0, 0.0) else: vFinal = ((vVector1 * vVector2) / math.pow(vVector2.length(), 2.0)) * vVector2 vEnd = vFinal if normalize: vEnd.normalize() outVectorHandle = dataBlock.outputValue(DebugVector.outVector) outVectorHandle.setMFloatVector(vEnd) outVectorHandle.setClean() def draw(self, view, path, style, status): """ Draw custom geometry in the viewport using OpenGL calls. * view [M3dView] is a 3D view that is being drawn into. * path [MDagPath] to the parent (transform node) of this locator in the DAG. To obtain the locator shape node, use MDagPath::extendToShape() if there is only one shape node under the transform or MDagPath::extendToShapeDirectlyBelow(unsigned int index) with the shape index if there are multiple shapes under the transform. * style [M3dView.DisplayStyle] is the style to draw object in. * status [M3dView.DisplayStatus] is the selection status of the object. """ # pylint: disable=unused-argument thisMob = self.thisMObject() lineW = om2.MPlug(thisMob, DebugVector.inLineWidth).asFloat() color = om2.MPlug(thisMob, DebugVector.inColor).asMDataHandle().asFloatVector() tipSize = om2.MPlug(thisMob, DebugVector.inTipSize).asFloat() subd = om2.MPlug(thisMob, DebugVector.inSubdivisions).asInt() radius = om2.MPlug(thisMob, DebugVector.inRadius).asFloat() xray = om2.MPlug(thisMob, DebugVector.inXRay).asBool() dashed = om2.MPlug(thisMob, DebugVector.inDashed).asBool() operation = om2.MPlug(thisMob, DebugVector.inOperation).asShort() vVector1 = om2.MPlug( thisMob, DebugVector.inVec1).asMDataHandle().asFloatVector() vVector2 = om2.MPlug( thisMob, DebugVector.inVec2).asMDataHandle().asFloatVector() normalize = om2.MPlug(thisMob, DebugVector.inNormalize).asBool() if operation == 0: vEnd = vVector1 elif operation == 1: vFinal = vVector1 + vVector2 vEnd = vFinal elif operation == 2: vFinal = vVector1 - vVector2 vEnd = vFinal elif operation == 3: vFinal = vVector1 ^ vVector2 vEnd = vFinal elif operation == 4: if vVector2.length() < 0.001: vFinal = om2.MFloatVector(0.0, 0.0, 0.0) else: vFinal = ((vVector1 * vVector2) / math.pow(vVector2.length(), 2.0)) * vVector2 vEnd = vFinal vStart = om2.MFloatVector() if normalize: vEnd.normalize() view.beginGL() glRenderer = omr1.MHardwareRenderer.theRenderer() glFT = glRenderer.glFunctionTable() glFT.glPushAttrib(omr1.MGL_CURRENT_BIT) glFT.glDisable(omr1.MGL_CULL_FACE) if dashed: glFT.glEnable(omr1.MGL_LINE_STIPPLE) if xray: glFT.glEnable(omr1.MGL_DEPTH_TEST) glFT.glClear(omr1.MGL_DEPTH_BUFFER_BIT) if status == omui2.M3dView.kActive: glFT.glColor3f(0.3, 1.0, 1.0) elif status == omui2.M3dView.kLead: glFT.glColor3f(1.0, 1.0, 1.0) elif status == omui2.M3dView.kDormant: glFT.glColor3f(color.x, color.y, color.z) if dashed: glFT.glLineStipple(1, 0x00FF) DebugVector.drawArrow(vStart, vEnd, tipSize, radius, subd, lineW, glFT=glFT) if dashed: glFT.glDisable(omr1.MGL_LINE_STIPPLE) if xray: glFT.glDisable(omr1.MGL_DEPTH_TEST) glFT.glPopAttrib() glFT.glLineWidth(1.0) view.endGL()
class FindParamFromLength(om2.MPxNode): """ Main class of gfFindParamFromLength node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inCurve = om2.MObject() inArcLength = om2.MObject() outParam = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return FindParamFromLength() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to DistributeAlongSurface class. Instances of DistributeAlongSurface will use these attributes to create plugs for use in the compute() method. """ tAttr = om2.MFnTypedAttribute() nAttr = om2.MFnNumericAttribute() uAttr = om2.MFnUnitAttribute() FindParamFromLength.inCurve = tAttr.create("inputCurve", "icrv", om2.MFnData.kNurbsCurve) INPUT_ATTR(tAttr) FindParamFromLength.inArcLength = uAttr.create( "arcLength", "length", om2.MFnUnitAttribute.kDistance, 0.0) INPUT_ATTR(uAttr) FindParamFromLength.outParam = nAttr.create("outParam", "param", om2.MFnNumericData.kFloat, 0.0) OUTPUT_ATTR(nAttr) FindParamFromLength.addAttribute(FindParamFromLength.inCurve) FindParamFromLength.addAttribute(FindParamFromLength.inArcLength) FindParamFromLength.addAttribute(FindParamFromLength.outParam) FindParamFromLength.attributeAffects(FindParamFromLength.inCurve, FindParamFromLength.outParam) FindParamFromLength.attributeAffects(FindParamFromLength.inArcLength, FindParamFromLength.outParam) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use if plug == FindParamFromLength.outParam: curveHandle = dataBlock.inputValue(FindParamFromLength.inCurve) curveMob = curveHandle.asNurbsCurve() arcLen = dataBlock.inputValue( FindParamFromLength.inArcLength).asDouble() curveFn = om2.MFnNurbsCurve(curveMob) param = curveFn.findParamFromLength(arcLen) outParamHandle = dataBlock.outputValue( FindParamFromLength.outParam) outParamHandle.setFloat(param) outParamHandle.setClean()
class PoleVectorConstraint(om2.MPxNode): """ Main class of gfUtilPoleVectorConstraint node. """ kNodeName = "" kNodeClassify = "" kNodeID = "" inRootWMtx = om2.MObject() inTargetWMtx = om2.MObject() inTargetWeight = om2.MObject() inConstParInvMtx = om2.MObject() inRestPosition = om2.MObject() inNormalizeOutput = om2.MObject() outConstraint = om2.MObject() def __init__(self): """ Constructor. """ om2.MPxNode.__init__(self) @staticmethod def creator(): """ Maya creator function. """ return PoleVectorConstraint() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to PoleVectorConstraint class. Instances of PoleVectorConstraint will use these attributes to create plugs for use in the compute() method. """ mAttr = om2.MFnMatrixAttribute() nAttr = om2.MFnNumericAttribute() PoleVectorConstraint.inRootWMtx = mAttr.create( "rootWorldMatrix", "rootm", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) PoleVectorConstraint.inTargetWMtx = mAttr.create( "targetWorldMatrix", "tgtm", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) PoleVectorConstraint.inTargetWeight = nAttr.create( "targetWeight", "tw", om2.MFnNumericData.kDouble, 1.0) nAttr.setMin(0.0) nAttr.setMax(1.0) INPUT_ATTR(nAttr) PoleVectorConstraint.inConstParInvMtx = mAttr.create( "constraintParentInverseMatrix", "cpim", om2.MFnMatrixAttribute.kDouble) INPUT_ATTR(mAttr) PoleVectorConstraint.inRestPosition = nAttr.createPoint( "restPosition", "rest") nAttr.writable = True nAttr.readable = True nAttr.storable = True nAttr.keyable = False PoleVectorConstraint.inNormalizeOutput = nAttr.create( "normalizeOutput", "normalize", om2.MFnNumericData.kBoolean, False) INPUT_ATTR(nAttr) PoleVectorConstraint.outConstraint = nAttr.createPoint( "constraint", "const") OUTPUT_ATTR(nAttr) PoleVectorConstraint.addAttribute(PoleVectorConstraint.inRootWMtx) PoleVectorConstraint.addAttribute(PoleVectorConstraint.inTargetWMtx) PoleVectorConstraint.addAttribute(PoleVectorConstraint.inTargetWeight) PoleVectorConstraint.addAttribute( PoleVectorConstraint.inConstParInvMtx) PoleVectorConstraint.addAttribute(PoleVectorConstraint.inRestPosition) PoleVectorConstraint.addAttribute( PoleVectorConstraint.inNormalizeOutput) PoleVectorConstraint.addAttribute(PoleVectorConstraint.outConstraint) PoleVectorConstraint.attributeAffects( PoleVectorConstraint.inRootWMtx, PoleVectorConstraint.outConstraint) PoleVectorConstraint.attributeAffects( PoleVectorConstraint.inTargetWMtx, PoleVectorConstraint.outConstraint) PoleVectorConstraint.attributeAffects( PoleVectorConstraint.inTargetWeight, PoleVectorConstraint.outConstraint) PoleVectorConstraint.attributeAffects( PoleVectorConstraint.inConstParInvMtx, PoleVectorConstraint.outConstraint) PoleVectorConstraint.attributeAffects( PoleVectorConstraint.inRestPosition, PoleVectorConstraint.outConstraint) PoleVectorConstraint.attributeAffects( PoleVectorConstraint.inNormalizeOutput, PoleVectorConstraint.outConstraint) def connectionMade(self, plug, otherPlug, asSrc): """This method gets called when connections are made to attributes of this node. * plug (MPlug) is the attribute on this node. * otherPlug (MPlug) is the attribute on the other node. * asSrc (bool) is this plug a source of the connection. """ if plug == PoleVectorConstraint.inTargetWMtx: thisMob = self.thisMObject() restPosPlug = om2.MPlug(thisMob, PoleVectorConstraint.inRestPosition) targetMob = otherPlug.asMObject() mtxDataFn = om2.MFnMatrixData(targetMob) mTarget = mtxDataFn.matrix() vTarget = om2.MFloatVector(mTarget[12], mTarget[13], mTarget[14]) restPosHdle = restPosPlug.asMDataHandle() restPosHdle.setMFloatVector(vTarget) restPosPlug.setMDataHandle(restPosHdle) return om2.MPxNode.connectionMade(self, plug, otherPlug, asSrc) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ # pylint: disable=no-self-use if plug != PoleVectorConstraint.outConstraint: return om2.kUnknownParameter mRoot = dataBlock.inputValue( PoleVectorConstraint.inRootWMtx).asMatrix() mTarget = dataBlock.inputValue( PoleVectorConstraint.inTargetWMtx).asMatrix() mConstParInv = dataBlock.inputValue( PoleVectorConstraint.inConstParInvMtx).asMatrix() targetWeight = dataBlock.inputValue( PoleVectorConstraint.inTargetWeight).asDouble() vRest = om2.MVector( dataBlock.inputValue( PoleVectorConstraint.inRestPosition).asFloat3()) normalize = dataBlock.inputValue( PoleVectorConstraint.inNormalizeOutput).asBool() vRoot = om2.MVector(mRoot[12], mRoot[13], mRoot[14]) vTarget = om2.MVector(mTarget[12], mTarget[13], mTarget[14]) vPoleDirection = (vTarget - vRoot) * mConstParInv vRestDirection = (vRest - vRoot) * mConstParInv vPole = (1.0 - targetWeight) * vRestDirection + targetWeight * vPoleDirection vResult = om2.MFloatVector(vPole) if normalize: vResult.normalize() outConstHdle = dataBlock.outputValue( PoleVectorConstraint.outConstraint) outConstHdle.setMFloatVector(vResult) outConstHdle.setClean()
class TestLocator(omui2.MPxLocatorNode): """ Main class of gfTestLocator node. """ kNodeName = "" kNodeClassify = "" kNodeRegistrantID = "" kNodeID = "" inSize = om2.MObject() inAttr = om2.MObject() outAttr = om2.MObject() def __init__(self): """ Constructor. """ omui2.MPxLocatorNode.__init__(self) def postConstructor(self): """ Post Constructor. """ thisMob = self.thisMObject() om2.MFnDependencyNode(thisMob).setName("%sShape#" % TestLocator.kNodeName) @staticmethod def creator(): """ Maya creator function. """ return TestLocator() @staticmethod def initialize(): """ Defines the set of attributes for this node. The attributes declared in this function are assigned as static members to TestLocator class. Instances of TestLocator will use these attributes to create plugs for use in the compute() method. """ nAttr = om2.MFnNumericAttribute() TestLocator.inSize = nAttr.create("size", "size", om2.MFnNumericData.kDouble, 1.0) INPUT_ATTR(nAttr) TestLocator.inAttr = nAttr.createPoint("inAttr", "inAttr") INPUT_ATTR(nAttr) TestLocator.outAttr = nAttr.createPoint("outAttr", "outAttr") OUTPUT_ATTR(nAttr) TestLocator.addAttribute(TestLocator.inSize) TestLocator.addAttribute(TestLocator.inAttr) TestLocator.addAttribute(TestLocator.outAttr) TestLocator.attributeAffects(TestLocator.inAttr, TestLocator.outAttr) def compute(self, plug, dataBlock): """ Node computation method: * plug is a connection point related to one of our node attributes (either an input or an output). * dataBlock contains the data on which we will base our computations. """ if plug == TestLocator.outAttr: inAttrValue = dataBlock.inputValue(TestLocator.inAttr).asVector() outAttrHandle = dataBlock.outputValue(TestLocator.outAttr) outAttrHandle.set3Float(inAttrValue.x, inAttrValue.y, inAttrValue.z) outAttrHandle.setClean() def draw(self, view, path, style, status): """ Draw custom geometry in the viewport using OpenGL calls. * view [M3dView] is a 3D view that is being drawn into. * path [MDagPath] to the parent (transform node) of this locator in the DAG. To obtain the locator shape node, use MDagPath::extendToShape() if there is only one shape node under the transform or MDagPath::extendToShapeDirectlyBelow(unsigned int index) with the shape index if there are multiple shapes under the transform. * style [M3dView.DisplayStyle] is the style to draw object in. * status [M3dView.DisplayStatus] is the selection status of the object. """ # pylint: disable=unused-argument thisMob = self.thisMObject() size = om2.MPlug(thisMob, TestLocator.inSize).asDouble() view.beginGL() glRenderer = omr1.MHardwareRenderer.theRenderer() glFT = glRenderer.glFunctionTable() glFT.glPushAttrib(omr1.MGL_CURRENT_BIT) glFT.glDisable(omr1.MGL_CULL_FACE) if status == omui2.M3dView.kActive: glFT.glColor3f(0.3, 1.0, 1.0) elif status == omui2.M3dView.kLead: glFT.glColor3f(1.0, 1.0, 1.0) elif status == omui2.M3dView.kDormant: glFT.glColor3f(1.0, 1.0, 0.0) vPoint1 = om2.MVector(1.0, 0.0, -1.0) * size vPoint2 = om2.MVector(-1.0, 0.0, -1.0) * size vPoint3 = om2.MVector(-1.0, 0.0, 1.0) * size vPoint4 = om2.MVector(1.0, 0.0, 1.0) * size glFT.glBegin(omr1.MGL_QUADS) glFT.glVertex3f(vPoint1.x, vPoint1.y, vPoint1.z) glFT.glVertex3f(vPoint2.x, vPoint2.y, vPoint2.z) glFT.glVertex3f(vPoint3.x, vPoint3.y, vPoint3.z) glFT.glVertex3f(vPoint4.x, vPoint4.y, vPoint4.z) glFT.glEnd() glFT.glPopAttrib() view.endGL() def isBounded(self): """isBounded?""" return True def boundingBox(self): """Return the boundingBox""" thisMob = self.thisMObject() size = om2.MPlug(thisMob, TestLocator.inSize).asDouble() corner1 = om2.MPoint(1.0, 0.0, -1.0) * size corner2 = om2.MPoint(-1.0, 0.0, 1.0) * size return om2.MBoundingBox(corner1, corner2)