Created
December 5, 2021 23:28
-
-
Save leixingyu/78352732cb52c6e797244755ade0bbfe to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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