Getting compute() to run on transform

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Hi all.
I'm writing a simple collision script and I'm having trouble getting its compute() method to run when I want it to.
The script takes a start point and an input mesh. If the start point is inside the mesh it computes the closest point on that mesh and sets the out point to that value.
This method works fine when I'm not using nodes but I'm having trouble getting it to work in real time. Right now all I'm taking as inputs are a kMesh (the outMesh attribute on a shape node) and a k3Double for the start point.
I'd like to have it so that the compute() method runs when either vertices of the geometry get transformed (say with a skin cluster) or when I move the transform node of that shape. I'm not sure if I'm missing an input attribute or not. I'm using an MpxNode.
Here's my rough code so far:
import maya.OpenMayaMPx as omMPx import maya.OpenMaya as om class CollisionNode(omMPx.MPxNode): # give it a unique id id = om.MTypeId(0x20218) # declare our input attributes inMesh = om.MObject() startPoint = om.MObject() # declare our output attributes outPoint = om.MObject() def compute(self, plug, data): if plug not in [CollisionNode.outPoint]: raise ValueError("kUnknownParameter") print "computing!" # only computes on init. Not running whenever the object or its verts are transformed... # make our MDataHandles inMeshHandle = data.inputValue(CollisionNode.inMesh) startPointHandle = data.inputValue(CollisionNode.startPoint) outPointHandle = data.outputValue(CollisionNode.outPoint) # check the data is the right type. If so, get it as its correct type inMeshVal = inMeshHandle.asMeshTransformed() startPointVal = startPointHandle.asDouble3() # now we can pass that data on to our function startPointVector = om.MVector(startPointVal[0], startPointVal[1], startPointVal[2]) isInside = self.pointInPrimitive(inMeshVal, startPointVector) if isInside: startPointAsPoint = om.MPoint(startPointVector.x, startPointVector.y, startPointVector.z) collisionPoint = self.simpleCollision(inMeshVal, startPointAsPoint) outPointHandle.set3Double(collisionPoint[0], collisionPoint[1], collisionPoint[2]) else: outPointHandle.set3Double(startPointVal[0], startPointVal[1], startPointVal[2]) data.setClean(plug) # Fast method to determine if a point is inside a convex polygon primitive def pointInPrimitive(self, meshDag, rayOrigin): """ :param meshDag: MDagPath(): the mesh we want to hit :param rayOrigin: MVector(): represents the start of our ray :return: Boolean: If the point is inside our polygon or not """ # use a MItMeshPolygon iterator. MUCH faster than a for loop itPolys = om.MItMeshPolygon(meshDag) # for every polygon in our mesh: while not itPolys.isDone(): # See if the vector from our origin point to a point in that poly dot the normal is > 0. # If they all are all greater than zero then the point is inside the mesh thisPolyNormal = om.MVector() itPolys.getNormal(thisPolyNormal, om.MSpace.kWorld) pointsOnPoly = om.MPointArray() itPolys.getPoints(pointsOnPoly, om.MSpace.kWorld) pointVector = om.MVector(pointsOnPoly[0].x, pointsOnPoly[0].y, pointsOnPoly[0].z) originToPointVector = pointVector - rayOrigin # if the dot product between these two is less then zero then the point is outside of our mesh if originToPointVector * thisPolyNormal < 0: return False itPolys.next() # if we made it through the loop without anything returning false then our point is within the mesh return True def simpleCollision(self, meshDag, p0): """ :param meshDag: MDagPath: Dag path of a polygon primitive :param p0: MPoint: The original point that is inside our mesh (detected using bm_pointInPrimitive :return: MVector: The new point that is the closest point outside of our mesh """ fnMesh = om.MFnMesh(meshDag) # find the closest point on our mesh from the point that's inside the mesh closestPoint = om.MPoint() fnMesh.getClosestPoint(p0, closestPoint, om.MSpace.kWorld) return om.MVector(closestPoint.x, closestPoint.y, closestPoint.z) def creator(): # standard creator function for a Maya plugin return omMPx.asMPxPtr(CollisionNode()) def initialize(): # shorten OpenMaya attributes as variables for readability tAttr = om.MFnTypedAttribute() nAttr = om.MFnNumericAttribute() """ Input Attributes """ # create our input attribute of type kFloat and a default value 0.0 CollisionNode.inMesh = tAttr.create("inputMesh", "inMesh", om.MFnData.kMesh) # set only to storable tAttr.setStorable(True) # add our attribute to the node CollisionNode.addAttribute(CollisionNode.inMesh) CollisionNode.startPoint = nAttr.create("startPoint", "sp", om.MFnNumericData.k3Double) # input values should be keyable, storable, readable, and writable nAttr.setKeyable(True) nAttr.setStorable(True) nAttr.setReadable(True) nAttr.setWritable(True) # add our attribute to the node CollisionNode.addAttribute(CollisionNode.startPoint) """ Output Attributes """ CollisionNode.outPoint = nAttr.create("outPoint", "op", om.MFnNumericData.k3Double) # input values should be keyable, storable, readable, and writable nAttr.setKeyable(False) nAttr.setStorable(False) nAttr.setReadable(True) nAttr.setWritable(False) # add our attribute to the node CollisionNode.addAttribute(CollisionNode.outPoint) CollisionNode.attributeAffects(CollisionNode.startPoint, CollisionNode.outPoint) def initializePlugin(obj): plugin = omMPx.MFnPlugin(obj, "Ben Morgan", "1.0", "Any") try: plugin.registerNode("collisionNode", CollisionNode.id, creator, initialize) except: raise RuntimeError("Failed to register node") def uninitializePlugin(obj): plugin = omMPx.MFnPlugin(obj) try: plugin.deregisterNode(CollisionNode.id) except: raise RuntimeError("Failed to register node")