Plugin slow down help

hey everyone,

I stumbled on this excellent vertexConstraint scripted plug-in awhile back and am using it for our surface rigs. Collars, shirts etc. The rig itself uses three of these nodes, and we have 40 rigs going around a neck. Sooo, the full surface rig brings maya to its knees if I use the entire mesh. If I duplicate the mesh, delete all accept the faces I need then copy the weighting to the mesh and create the rig on that mesh its way faster. So I’m thinking the plug must slow down when it’s asking for vertex…

Any ideas on how it could be optimized in the code? I have a little plug-in understanding…not much though.


"""#////////////////////////////////////////////////////////////////////////////////////////////
#//
#//   Plug Name:             vertexConstraint Node 2.0 beta
#//   orignal Author:        Chad Robert Morgan
#//                          unfortunately Chad's site with the source code...
#//                          http://members.aol.com/docmorgan/plugins.htm
#//                          doesn't exist anymore...
#//
#/////////////////////////////////////////////////////////////////////////////////////////////
#//
#//   converted Chad's c++ code to a Python Plugin, changed a little bit to the original code...
#//   Alex V. U. Strarup... http://www.strarup.net ... april 2009
#//   3d@strarup.net
#//
#/////////////////////////////////////////////////////////////////////////////////////////////
#////////////////////////////////////////////////////////////////////////////////////////////
#//
#//  DESCRIPTION :	this plug is a Node which make it posible to constrain
#//                     objects to vertices...
#//                     first select the vertex you want your object constrained to
#//                     then select your object... and type the command...
#//                     vertexConstraint;
#//
#//-////////////////////////////////////////////////////////////////////////////////////////////
#//
#//  Installation: put this script in the Maya plugin folder...
#//                to activate the plugin, type loadPlugin vertexConstraint;
#//                or load via Plugin Manager...
#//
#//-////////////////////////////////////////////////////////////////////////////////////////////
#//
#//  Conditions : Use at your own risk, No Banana throwing or whatever if it doesn't work...
#//               NO RESPONSABILITIES DUE TO MALFUNCTION, JOB LOST, WHATEVER...
#//               copy... steal... modify it... do whatever you want...
#//               as long you don't call me up early in the morning... :-D
#//
#//-/////////////////////////////////////////////////////////////////////////////////////////////
#//-///////////////////////////////////   Start of Code /////////////////////////////////////////
#//-////////////////////////////////////////////////////////////////////////////////////////////
"""
import math, sys
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx

kPluginNodeName = "vertexConstraint"
kPluginCmdName="vertexConstraint"
# The id is a 32bit value used to identify this type of node in the binary file format.
# please don't use this number in any other plugin's...
# but get your own by contacting Autodesk... 
# original id was ( 0x000c1 ) however not sure if this was an unique value...
# so added one of the Id's I got from Alias back in 2004...
kPluginNodeId = OpenMaya.MTypeId(0x0010A781) # id nr. 2 from Alias...

# da Node definition...
class vertexConstraintNode(OpenMayaMPx.MPxNode):
      inputShape = OpenMaya.MObject()
      output = OpenMaya.MObject()
      outputX = OpenMaya.MObject()
      outputY = OpenMaya.MObject()
      outputZ = OpenMaya.MObject()
      vertexIndex = OpenMaya.MObject()
      constraintParentInverseMatrix = OpenMaya.MObject()

      def __init__(self):
            OpenMayaMPx.MPxNode.__init__(self)

      #da compute function thingie...
      def compute(self,daPlug,daData):
            if daPlug == vertexConstraintNode.output or daPlug.parent() == vertexConstraintNode.output:
                  try:
                        inputData = daData.inputValue(vertexConstraintNode.inputShape)
                  except:
                        sys.stderr.write("Oh Mama %s failed to get da MDataHandle inputValue inputShape
") %kPluginNodeName; raise
                  try:
                        vertHandle = daData.inputValue(vertexConstraintNode.vertexIndex)
                  except:
                        sys.stderr.write("Oh Mama %s failed to get da MDataHandle inputValue vertexIndex
") %kPluginNodeName; raise
                  try:
                        inverseMatrixHandle = daData.inputValue(vertexConstraintNode.constraintParentInverseMatrix)
                  except:
                        sys.stderr.write("Oh Mama %s failed to get da MDataHandle inputValue constraintParentInverseMatrix
") %kPluginNodeName; raise
                  try:
                        outputHandle = daData.outputValue(vertexConstraintNode.output)
                  except:
                        sys.stderr.write("Oh Mama %s failed to get da MDataHandle outputValue output
") %kPluginNodeName; raise
                  nodeFn = OpenMaya.MFnDependencyNode()
                  mesh = OpenMaya.MObject()
                  mesh = inputData.asMesh()

                  index = vertHandle.asInt() 
                  vert = OpenMaya.MFnMesh(mesh)
                  inverseMatrix = OpenMaya.MMatrix()
                  inverseMatrix = inverseMatrixHandle.asMatrix()
                  pos = OpenMaya.MPoint()
                  try:
                        vert.getPoint(index, pos, OpenMaya.MSpace.kWorld)
                  except:
                        sys.stderr.write("Oh Mama %s Could not get vert position
") %kPluginNodeName; raise
                  pos *= inverseMatrix
                  #set output attrs
                  posX = outputHandle.child(vertexConstraintNode.outputX)
                  posX.setDouble(pos.x)
                  posY = outputHandle.child(vertexConstraintNode.outputY)
                  posY.setDouble(pos.y)
                  posZ = outputHandle.child(vertexConstraintNode.outputZ)
                  posZ.setDouble(pos.z)
                  daData.setClean(daPlug)
                  # everything is oki doki... :)
                  return OpenMaya.MStatus.kSuccess
            # spooky...
            else:
                  return OpenMaya.kUnknownParameter

#--------------------------------------------------------------------------------------
# da creator may he have mercy on us... :-D
# this method exists to give Maya a way to create new objects of this type...
#--------------------------------------------------------------------------------------
def nodeCreator():
      return OpenMayaMPx.asMPxPtr(vertexConstraintNode())

def nodeInitializer():
      nAttr = OpenMaya.MFnNumericAttribute()
      tAttr = OpenMaya.MFnTypedAttribute()
      mAttr = OpenMaya.MFnMatrixAttribute()
      cAttr = OpenMaya.MFnCompoundAttribute()
      try:
            vertexConstraintNode.inputShape = tAttr.create( "inputShape", "inS", OpenMaya.MFnMeshData.kMesh)
            tAttr.setStorable(0)
            tAttr.setKeyable(0)
      except:
            sys.stderr.write("Oh Mama failed to create da inputShape attribute
"); raise 
      try:
            vertexConstraintNode.vertexIndex = nAttr.create( "vertex", "vert", OpenMaya.MFnNumericData.kLong)
            nAttr.setWritable(1)
            nAttr.setStorable(1)
            nAttr.setArray(0)
      except:
            sys.stderr.write("Oh Mama failed to create da vertexIndex attribute
"); raise 
      try:
            vertexConstraintNode.constraintParentInverseMatrix = mAttr.create("constraintParentInverseMatrix", "pim",  OpenMaya.MFnMatrixAttribute.kDouble)
            mAttr.setStorable(0)
            mAttr.setKeyable(0)
      except:
            sys.stderr.write("Oh Mama failed to create da constraintParentInverseMatrix attribute
"); raise 
      try:
            vertexConstraintNode.outputX = nAttr.create( "outputX", "X", OpenMaya.MFnNumericData.kDouble)
      except:
            sys.stderr.write("Oh Mama failed to create da outputX attribute
"); raise 
      try:
            vertexConstraintNode.outputY = nAttr.create( "outputY", "Y", OpenMaya.MFnNumericData.kDouble)
      except:
            sys.stderr.write("Oh Mama failed to create da outputY attribute
"); raise 
      try:
            vertexConstraintNode.outputZ = nAttr.create( "outputZ", "Z", OpenMaya.MFnNumericData.kDouble)
      except:
            sys.stderr.write("Oh Mama failed to create da outputZ attribute
"); raise 
      try:
            vertexConstraintNode.output = cAttr.create("output", "out")
            cAttr.addChild(vertexConstraintNode.outputX)
            cAttr.addChild(vertexConstraintNode.outputY)
            cAttr.addChild(vertexConstraintNode.outputZ)
            cAttr.setStorable(0)
            cAttr.setKeyable(0)
            cAttr.setArray(0)
      except:
            sys.stderr.write("Oh Mama failed to create da output attribute
"); raise 
      try:
            vertexConstraintNode.addAttribute(vertexConstraintNode.inputShape)
      except:
            sys.stderr.write("Oh Mama failed to add da input attributes
"); raise 
      try:
            vertexConstraintNode.addAttribute(vertexConstraintNode.vertexIndex)
      except:
            sys.stderr.write("Oh Mama failed to add da input attributes
"); raise 
      try:
            vertexConstraintNode.addAttribute(vertexConstraintNode.constraintParentInverseMatrix)
      except:
            sys.stderr.write("Oh Mama failed to add da input attributes
"); raise 
      try:
            vertexConstraintNode.addAttribute(vertexConstraintNode.output)
      except:
            sys.stderr.write("Oh Mama failed to add da input attributes
"); raise 
      try:
            vertexConstraintNode.attributeAffects( vertexConstraintNode.inputShape, vertexConstraintNode.output )
      except:
            sys.stderr.write("Oh Mama failed setting da attributeAffects
"); raise 
      try:
            vertexConstraintNode.attributeAffects( vertexConstraintNode.vertexIndex, vertexConstraintNode.output )
      except:
            sys.stderr.write("Oh Mama failed setting da attributeAffects
"); raise 
      try:
            vertexConstraintNode.attributeAffects( vertexConstraintNode.constraintParentInverseMatrix, vertexConstraintNode.output )
      except:
            sys.stderr.write("Oh Mama failed setting da attributeAffects
"); raise 
      # everything is oki doki... :)
      return OpenMaya.MStatus.kSuccess
#--------------------------------------------------------------------------------
#--------------------------- da end of the nodeInitilizer -----------------------
#--------------------------------------------------------------------------------

# start off vertexConstraint command....
# command
class vertexConstraintCmd(OpenMayaMPx.MPxCommand):
      def __init__(self):
            OpenMayaMPx.MPxCommand.__init__(self)
            self.dgMod = OpenMaya.MDGModifier()

      def redoIt(self):
            try:
                  self.dgMod.doIt()
                  print( "vertexConstraint created......
")
            except:
                  sys.stderr.write( "Error creating vertexConstraint command
" ); raise

      def undoIt(self):
            try:
                  self.dgMod.undoIt()
                  print( "vertexConstraint removed....
")
            except:
                  sys.stderr.write( "Error removing vertexConstraint command
" ); raise

      def doIt(self, args):
            slist = OpenMaya.MSelectionList()
            OpenMaya.MGlobal.getActiveSelectionList(slist)
            component = OpenMaya.MObject()
            targetDagPath = OpenMaya.MDagPath()
            targetVertexFn = OpenMaya.MFnMesh()
            destDagPath = OpenMaya.MDagPath()
            destTransformFn = OpenMaya.MFnTransform()


            try:
                  vert = OpenMaya.MItSelectionList(slist, OpenMaya.MFn.kMeshVertComponent)
            except:
                  sys.stderr.write("Oh Mama %s Could not create iterator
") %kPluginCmdName; raise
            try:
                  vert.getDagPath(targetDagPath, component)
            except:
                  sys.stderr.write("Oh Mama %s Could not get dag path to vertex
") %kPluginCmdName; raise
            if(component.isNull()):
                  sys.stderr.write("Oh Mama no vertexes are selected
"); raise
            try:
                  targetVertexFn.setObject(targetDagPath)
            except:
                  sys.stderr.write("Oh Mama %s Could not set vertes to object
") %kPluginCmdName; raise
            try:
                  vertIndex = OpenMaya.MItMeshVertex(targetDagPath, component)
            except:
                  sys.stderr.write("Oh Mama %s Could not get the vertIndex
") %kPluginCmdName; raise
            try:
                  dest = OpenMaya.MItSelectionList(slist, OpenMaya.MFn.kTransform)
            except:
                  sys.stderr.write("Oh Mama %s Could not create iterator
") %kPluginCmdName; raise
            try:
                  dest.getDagPath(destDagPath)
            except:
                  sys.stderr.write("Oh Mama %s Could not get dag path to transform
") %kPluginCmdName; raise
            try:
                  destTransformFn.setObject(destDagPath)
            except:
                  sys.stderr.write("Oh Mama %s Could not set fn to transform
") %kPluginCmdName; raise
            # create vertexConstraint node
            try:
                  vertexConstraintObj = OpenMaya.MObject(self.dgMod.createNode("vertexConstraint"))
            except:
                  sys.stderr.write("Oh Mama %s Could not create vertexConstraint node
") %kPluginCmdName; raise
            vertexConstraintFn = OpenMaya.MFnDependencyNode(vertexConstraintObj)
            inMeshPlug = OpenMaya.MPlug()
            inMeshPlug = vertexConstraintFn.findPlug("inputShape")
            # get output worldmesh from shape
            worldMeshPlug = OpenMaya.MPlug()
            worldMeshPlug = targetVertexFn.findPlug("worldMesh")
            worldMeshPlug = worldMeshPlug.elementByLogicalIndex(targetDagPath.instanceNumber())
            inverseMatrix = OpenMaya.MPlug()
            inverseMatrix = destTransformFn.findPlug("parentInverseMatrix")
            inverseMatrix = inverseMatrix.elementByLogicalIndex(destDagPath.instanceNumber())
            vertexPlug = OpenMaya.MPlug()
            vertexPlug = vertexConstraintFn.findPlug("vertex")
            vertexPlug.setInt(vertIndex.index())
            try:
                  self.dgMod.connect((vertexConstraintFn.findPlug("output")), (destTransformFn.findPlug("translate")))
            except:
                  sys.stderr.write("Oh Mama %s Could not connect output to translation
") %kPluginCmdName; raise
            try:
                  self.dgMod.connect(worldMeshPlug, inMeshPlug)
            except:
                  sys.stderr.write("Oh Mama %s Could not connect world mesh to constraint
") %kPluginCmdName; raise
            try:
                  self.dgMod.connect(inverseMatrix, vertexConstraintFn.findPlug("constraintParentInverseMatrix"))
            except:
                  sys.stderr.write("Oh Mama %s Could not connect world mesh to constraint
") %kPluginCmdName; raise
            self.redoIt()

      def isUndoable(self):
            return True

# command Creator
def cmdCreator():
# Create the command
      return OpenMayaMPx.asMPxPtr( vertexConstraintCmd() )

# initialize the script plug-in
def initializePlugin(mobject):
      mplugin = OpenMayaMPx.MFnPlugin(mobject, "vertex Constraint Node", "2.0", "Any")
      try:
            mplugin.registerNode( kPluginNodeName, kPluginNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kDependNode)
      except:
            sys.stderr.write( "Failed to register node: %s" % kPluginNodeName ); raise
      try:
            mplugin.registerCommand( kPluginCmdName, cmdCreator )
      except:
            sys.stderr.write( "Failed to register command: %s
" % kPluginCmdName ); raise  

# uninitialize the script plug-in
def uninitializePlugin(mobject):
      mplugin = OpenMayaMPx.MFnPlugin(mobject)
      try:
            mplugin.deregisterNode( kPluginNodeId )
      except:
            sys.stderr.write( "Failed to deregister node: %s" % kPluginNodeName ); raise
      try:
            mplugin.deregisterCommand( kPluginCmdName )
      except:
            sys.stderr.write( "Failed to unregister command: %s
" % kPluginCmdName ); raise 


Try removing all your try/excepts, or combine them into one. Setting these up can be expensive

Gave it a try, no dice. Performance is about the same.

I think you have two problems here.

The first is the amount mesh data being propagated through the dependency graph. Every mesh, that is connected to a vertexConstraint node, has to send its data to the node whenever an update is requested. And if a mesh is connected to more than one node, a unique copy of the mesh data is sent to each node. This data is also cached locally per node. So the more meshes/constraint nodes, the more data is being passed around and slowing your scene down.

Now, I don’t think the amount of time taken to pass the data around is the culprit, but I think the amount of data that the plug-in is working with is more the issue. Which leads us into the next issue…

Secondly, in general a scripted python plug-in is slower than a compiled c++ plug-in. In this case I don’t think the actual computation is slow, but rather the amount of time taken by python to access the mesh data (which is rather large).

Solutions:

You already mentioned connecting a reduced version of the mesh to the constraint node. I would keep doing that since you’ve already noticed speed improvements and it doesn’t seem like too much work.

If you feel up to it, you might want to try modifying the plug-in to accept an array of vertex indices and outputting an array of positions. This way if multiple objects are constrained to the same mesh, then you’ll only need one constraint per mesh (less copies of the mesh being passed around).

Lastly, you might want to look into re-writing this in C++. It might not be necessary with the other solutions, but if you’re looking to squeeze every frame possible, this is the way. And just for comparison, I did a quick conversion and saw the node compute times improve anywhere between 30 and 70 percent over the python version (depending on the scene). I would definitely recommend writing plug-ins like this in C++ rather than in python, but that may not be an option for you.

Hope that helps some.

Neil

have you run in thru a profiler? converting to C++ would make a difference.

turning off the undo stack can help (undoState is the command i think)

will the psyco Python compiler work with maya? supposedly it makes some python scripts run faster.
make sure you’re not running out of ram to cause ram paging to the hard drive

there’s other vertex constraint type plugins out there, try one of those.

attach it to a simplified proxy mesh or maybe bake out the vertex locations

in the loops of your code, look for memory grabbing operations (or use a profiler to see if something else is happening more than it should or just taking too long) and do all of those at once before the loop begins

chris rogers (is looking for a job)
http://opototopo.com
scrogbot AT gmail.com

Thanks for the input guys. The reason I’m loving the scripted plug-in is that we’re on Linux and OSX, so I have to dev for both OS’s. Add to that the fact that I don’t really know my way around compiling c++ for OSX or Linux. So, I can feed a list potentially into the plug-in. or work with a smaller mesh.

Cheers,
Shawn