# defTwist.py
import sys
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMaya as OpenMaya
import maya.cmds as cmds
import math
# Plug-in information:
kPluginNodeName = "twistDeformer" # The name of the node.
kPluginNodeId = OpenMaya.MTypeId( 0xBEEF8 ) # A unique ID associated to this node type.
# Some global variables were moved from MPxDeformerNode to MPxGeometryFilter.
# Set som constants to the proper C++ cvars based on the API version
kApiVersion = cmds.about(apiVersion=True)
if kApiVersion < 201600:
kInput = OpenMayaMPx.cvar.MPxDeformerNode_input
kInputGeom = OpenMayaMPx.cvar.MPxDeformerNode_inputGeom
kOutputGeom = OpenMayaMPx.cvar.MPxDeformerNode_outputGeom
kEnvelope = OpenMayaMPx.cvar.MPxDeformerNode_envelope
else:
kInput = OpenMayaMPx.cvar.MPxGeometryFilter_input
kInputGeom = OpenMayaMPx.cvar.MPxGeometryFilter_inputGeom
kOutputGeom = OpenMayaMPx.cvar.MPxGeometryFilter_outputGeom
kEnvelope = OpenMayaMPx.cvar.MPxGeometryFilter_envelope
class MyDeformerNode(OpenMayaMPx.MPxDeformerNode):
# Static variable(s) which will later be replaced by the node's attribute(s).
twistYAttr = OpenMaya.MObject()
def __init__(self):
''' Constructor. '''
# (!) Make sure you call the base class's constructor.
OpenMayaMPx.MPxDeformerNode.__init__(self)
def deform(self, pDataBlock, pGeometryIterator, pLocalToWorldMatrix, pGeometryIndex):
''' Deform each vertex using the geometry iterator. '''
# The envelope determines the overall weight of the deformer on the mesh.
# The envelope is obtained via the OpenMayaMPx.cvar.MPxDeformerNode_envelope (pre Maya 2016) or
# OpenMayaMPx.cvar.MPxGeometryFilter_envelope (Maya 2016) variable.
# This variable and others like it are generated by SWIG to expose variables or constants declared in C++ header files.
envelopeAttribute = kEnvelope
envelopeValue = pDataBlock.inputValue( envelopeAttribute ).asFloat()
# Get the value of the mesh twist node attribute.
twistYAttribute = pDataBlock.inputValue( MyDeformerNode.twistYAttr )
twistYAttributeType = meshInflationHandle.asDouble()
# Get the input mesh from the datablock using our getDeformerInputGeometry() helper function.
inputGeometryObject = self.getDeformerInputGeometry(pDataBlock, pGeometryIndex)
# Obtain the list of normals for each vertex in the mesh.
normals = OpenMaya.MFloatVectorArray()
meshFn = OpenMaya.MFnMesh( inputGeometryObject )
meshFn.getVertexNormals( True, normals, OpenMaya.MSpace.kObject )
def getDeformerInputGeometry(self, pDataBlock, pGeometryIndex):
'''
Obtain a reference to the input mesh. This mesh will be used to compute our bounding box, and we will also require its normals.
We use MDataBlock.outputArrayValue() to avoid having to recompute the mesh and propagate this recomputation throughout the
Dependency Graph.
OpenMayaMPx.cvar.MPxDeformerNode_input and OpenMayaMPx.cvar.MPxDeformerNode_inputGeom (for pre Maya 2016) and
OpenMayaMPx.cvar.MPxGeometryFilter_input and OpenMayaMPx.cvar.MPxGeometryFilter_inputGeom (Maya 2016) are SWIG-generated
variables which respectively contain references to the deformer's 'input' attribute and 'inputGeom' attribute.
'''
inputAttribute = OpenMayaMPx.cvar.MPxGeometryFilter_input
inputGeometryAttribute = OpenMayaMPx.cvar.MPxGeometryFilter_inputGeom
inputHandle = pDataBlock.outputArrayValue( inputAttribute )
inputHandle.jumpToElement( pGeometryIndex )
inputGeometryObject = inputHandle.outputValue().child( inputGeometryAttribute ).asMesh()
return inputGeometryObject
##########################################################
# Plug-in initialization.
##########################################################
def nodeCreator():
''' Creates an instance of our node class and delivers it to Maya as a pointer. '''
return OpenMayaMPx.asMPxPtr( MyDeformerNode() )
def nodeInitializer():
''' Defines the input and output attributes as static variables in our plug-in class. '''
# The following MFnNumericAttribute function set will allow us to create our attributes.
numericAttributeFn = OpenMaya.MFnNumericAttribute()
#==================================
# INPUT NODE ATTRIBUTE(S)
#==================================
# Define a mesh inflation attribute, responsible for actually moving the vertices in the direction of their normals.
MyDeformerNode.meshInflationAttribute = numericAttributeFn.create( 'ytwist', 'yt', OpenMaya.MFnNumericData.kDouble, 10.0 )
numericAttributeFn.setDefault(0.0)
numericAttributeFn.setKeyable(True)
MyDeformerNode.addAttribute( MyDeformerNode.twistYAttribute )
''' The input geometry node attribute is already declared in OpenMayaMPx.cvar.MPxGeometryFilter_inputGeom '''
#==================================
# OUTPUT NODE ATTRIBUTE(S)
#==================================
''' The output geometry node attribute is already declared in OpenMayaMPx.cvar.MPxGeometryFilter_outputGeom '''
#==================================
# NODE ATTRIBUTE DEPENDENCIES
#==================================
# If any of the inputs change, the output mesh will be recomputed.
print dir(OpenMayaMPx.cvar)
MyDeformerNode.attributeAffects( MyDeformerNode.twistYAttr, kOutputGeom )
def initializePlugin( mobject ):
''' Initialize the plug-in '''
mplugin = OpenMayaMPx.MFnPlugin( mobject )
try:
mplugin.registerNode( kPluginNodeName, kPluginNodeId, nodeCreator,
nodeInitializer, OpenMayaMPx.MPxNode.kDeformerNode )
except:
sys.stderr.write( 'Failed to register node: ' + kPluginNodeName )
raise
def uninitializePlugin( mobject ):
''' Uninitializes the plug-in '''
mplugin = OpenMayaMPx.MFnPlugin( mobject )
try:
mplugin.deregisterNode( kPluginNodeId )
except:
sys.stderr.write( 'Failed to deregister node: ' + kPluginNodeName )
raise