Skip to content

Instantly share code, notes, and snippets.

@leixingyu
Created December 5, 2021 23:28
Show Gist options
  • Save leixingyu/78352732cb52c6e797244755ade0bbfe to your computer and use it in GitHub Desktop.
Save leixingyu/78352732cb52c6e797244755ade0bbfe to your computer and use it in GitHub Desktop.
import sys
import math
import maya.cmds as cmds
import maya.OpenMaya as om
import maya.OpenMayaMPx as mpx
nodeName = 'myFirstDeformer'
nodeID = om.MTypeId(0x55555)
class MyDeformer(mpx.MPxDeformerNode):
amplitudeAttr = om.MObject()
displacementAttr = om.MObject()
matrixAttr = om.MObject()
def __init__(self):
mpx.MPxDeformerNode.__init__(self)
def deform(self, dataBlock, geomIterator, localToWorldMatrix, geomIndex):
# step 1: access built-in attribute value using attribute name and attribute handle
envelopeAttr = mpx.cvar.MPxGeometryFilter_envelope
envelopeHandle = dataBlock.inputValue(envelopeAttr)
envelopeValue = envelopeHandle.asFloat()
# step 1.5: access custom attribute value
amplitudeHandle = dataBlock.inputValue(MyDeformer.amplitudeAttr)
amplitudeValue = amplitudeHandle.asFloat()
displacementHandle = dataBlock.inputValue(MyDeformer.displacementAttr)
displacementValue = displacementHandle.asFloat()
# step 1.55: access custom attribute value connected to an accessory node
matrixHandle = dataBlock.inputValue(MyDeformer.matrixAttr)
matrixValue = matrixHandle.asMatrix()
transMatrix = om.MTransformationMatrix(matrixValue) # matrix type
translateValue = transMatrix.getTranslation(om.MSpace.kObject) # vector type
# step 2: access input mesh
inputMesh = self.getDeformerInputGeom(dataBlock, geomIndex)
# step 2.5: access mesh normals
meshFn = om.MFnMesh(inputMesh)
normalVectorArray = om.MFloatVectorArray() # create float vector array to store normal vector
meshFn.getVertexNormals(False, normalVectorArray, om.MSpace.kObject) # (average normal or not?, the array to store, normal space)
# step 3: iterate the mesh vertices and deform it
newVertexPosArray = om.MPointArray() # to store new vertices position
while not geomIterator.isDone():
vertexPos = geomIterator.position()
vertexIndex = geomIterator.index()
normalVector = om.MVector(normalVectorArray[vertexIndex])
# get weight from weightPainting and multiply it to the deform, this is done inside
# built-in function weightValue(dataBlock, geomIndex, vertexIndex)
weight = self.weightValue(dataBlock, geomIndex, vertexIndex)
vertexPos.x = vertexPos.x + math.sin(vertexIndex + displacementValue + translateValue[0]) * normalVector.x * amplitudeValue * envelopeValue * weight
vertexPos.y = vertexPos.y + math.sin(vertexIndex + displacementValue + translateValue[0]) * normalVector.y * amplitudeValue * envelopeValue * weight
vertexPos.z = vertexPos.z + math.sin(vertexIndex + displacementValue + translateValue[0]) * normalVector.z * amplitudeValue * envelopeValue * weight
newVertexPosArray.append(vertexPos)
geomIterator.next()
geomIterator.setAllPositions(newVertexPosArray)
# override built-in function that allows to create accessory node along with deformer
def accessoryNodeSetup(self, dagModifier):
# step1: create the accessory node using the supplied dagModifier
locator = dagModifier.createNode('locator')
# step2: access accessory node's attribute(can't use mplug type, has to be mobject type)
# access dependency node function set
dependNodeFn = om.MFnDependencyNode(locator)
matrixPlug = dependNodeFn.findPlug('worldMatrix') # this returns mplug type attribute, we need mobject type attribute
matrixAttr = matrixPlug.attribute()
# step3: connect mobject type(required) together
# param: accessory node(mobject), accessory attr(mobject), deformer node(mobject: using self.thisMObject()), deformer attr(mobject)
mConnectStatus = dagModifier.connect(locator, matrixAttr, self.thisMObject(), MyDeformer.matrixAttr)
'''now the accessory node's worldMatrix is driving to the in-matrix of the deformer node'''
return mConnectStatus
def accessoryAttribute(self):
# returns the deformer node attribute connected
return MyDeformer.matrixAttr
def getDeformerInputGeom(self, dataBlock, geomIndex):
inputAttr = mpx.cvar.MPxGeometryFilter_input
inputHandle = dataBlock.outputArrayValue(inputAttr) # use outputArray instead of inputArray to avoid re-computation
inputHandle.jumpToElement(geomIndex)
inputElementHandle = inputHandle.outputValue()
inputGeomAttr = mpx.cvar.MPxGeometryFilter_inputGeom
inputGeomHandle = inputElementHandle.child(inputGeomAttr) # this is different from how we usually get handler
inputGeomMesh = inputGeomHandle.asMesh()
return inputGeomMesh
def nodeCreator():
# return pointer to instance of our deformer node class
return mpx.asMPxPtr(MyDeformer())
def nodeInitializer():
# step1: define attribute function set (numericAttr & matrixAttr)
numericAttrFn = om.MFnNumericAttribute()
matrixAttrFn = om.MFnMatrixAttribute()
# step2: create custom attribute and set its property
MyDeformer.amplitudeAttr = numericAttrFn.create('ampplitude', 'amp', om.MFnNumericData.kFloat, 0.0)
numericAttrFn.setMin(-1.0)
numericAttrFn.setMax(1.0)
numericAttrFn.setReadable(False)
MyDeformer.displacementAttr = numericAttrFn.create('displacement', 'dis', om.MFnNumericData.kFloat, 0.0)
numericAttrFn.setMin(-5.0)
numericAttrFn.setMax(5.0)
numericAttrFn.setReadable(False)
MyDeformer.matrixAttr = matrixAttrFn.create('matrix', 'm')
matrixAttrFn.setStorable(False)
matrixAttrFn.setConnectable(True)
# step2.5: access built-in attribute using OpenMayaMpx.cvar.MPxGeometryFilter_outputGeom
outputGeom = mpx.cvar.MPxGeometryFilter_outputGeom
# step3: attach attribute
MyDeformer.addAttribute(MyDeformer.amplitudeAttr)
MyDeformer.addAttribute(MyDeformer.displacementAttr)
MyDeformer.addAttribute(MyDeformer.matrixAttr)
# step4: add circuit (relationship)
MyDeformer.attributeAffects(MyDeformer.amplitudeAttr, outputGeom)
MyDeformer.attributeAffects(MyDeformer.displacementAttr, outputGeom)
MyDeformer.attributeAffects(MyDeformer.matrixAttr, outputGeom)
cmds.makePaintable(nodeName, 'weights', attrType='multiFloat', shapeMode='deformer')
def initializePlugin(mobject):
mplugin = mpx.MFnPlugin(mobject)
try:
# Parameter: node_name, node_id, node_creatorFunc, node_initFuc, nodeType(common type include-kDeformerNode, kDependNode)
mplugin.registerNode(nodeName, nodeID, nodeCreator, nodeInitializer, mpx.MPxNode.kDeformerNode)
except:
sys.stderr('fail to register node: ' + nodeName)
raise
def uninitializePlugin(mobject):
mplugin = mpx.MFnPlugin(mobject)
try:
mplugin.deregisterNode(nodeID)
except:
sys.stderr('fail to de-register node: ' + nodeName)
raise
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment