Maya Python API Training (Part.2 - Custom Node)

mac2024-02-22  34

General Format

Custom node Sample

API 2.0

import maya.api.OpenMaya as om import sys def maya_useNewAPI(): pass nodeName = 'myFirstNode' nodeID = om.MTypeId(0x55555) nodeClassify = 'utility/general' class MyNode(om.MPxNode): inRadiusAttr = om.MObject() inTranslateAttr = om.MObject() outRotateAttr = om.MObject() def __init__(self): om.MPxNode.__init__(self) # plug is MPlug type object, dataBlock is MDataBlock type object, refers to the entire datablock of the node def compute(self, plug, dataBlock): if plug == MyNode.outRotateAttr: # step one: get datablock handle (inputValue returns MDataHandle type object) inRadiusHandle = dataBlock.inputValue(MyNode.inRadiusAttr) inTranslateHandle = dataBlock.inputValue(MyNode.inTranslateAttr) outRotateHandle = dataBlock.outputValue(MyNode.outRotateAttr) # step two: extract input value from the handle inRadius = inRadiusHandle.asFloat() inTranslate = inTranslateHandle.asFloat() # step three: create logic and set output value outRotate = inTranslate/(6.18*inRadius) * -360 outRotateHandle.setFloat(outRotate) # step four: mark output plug as clean outRotateHandle.setClean() # unknown plug, unknown error else: return om.kUnknownParameter def nodeCreator(): return MyNode() def nodeInitializer(): defaultRadius = 2.0 defaultTranslate = 0.0 # step one: create reference to attribute function set such as numericAttribute numericAttrFn = om.MFnNumericAttribute() # step two: create attribute using the function set MyNode.inRadiusAttr = numericAttrFn.create('radius', 'r', om.MFnNumericData.kFloat, defaultRadius) numericAttrFn.readable = True # API1.0: numericAttrFn.setReadable(True) numericAttrFn.writable = True numericAttrFn.storable = True numericAttrFn.hidden = False numericAttrFn.keyable = True MyNode.inTranslateAttr = numericAttrFn.create('translate', 't', om.MFnNumericData.kFloat, defaultTranslate) numericAttrFn.readable = True numericAttrFn.writable = True numericAttrFn.storable = True numericAttrFn.hidden = False numericAttrFn.keyable = True MyNode.outRotateAttr = numericAttrFn.create('rotation', 'rot', om.MFnNumericData.kFloat) numericAttrFn.readable = True numericAttrFn.writable = False numericAttrFn.storable = False numericAttrFn.hidden = False # step three: attach attribute MyNode.addAttribute(MyNode.inRadiusAttr) MyNode.addAttribute(MyNode.inTranslateAttr) MyNode.addAttribute(MyNode.outRotateAttr) # step four: add circuit (relationship in->out) MyNode.attributeAffects(MyNode.inRadiusAttr, MyNode.outRotateAttr) MyNode.attributeAffects(MyNode.inTranslateAttr, MyNode.outRotateAttr) def initializePlugin(mobject): mplugin = om.MFnPlugin(mobject) try: mplugin.registerNode(nodeName, nodeID, nodeCreator, nodeInitializer, om.MPxNode.kDependNode, nodeClassify) except: sys.stderr.write('fail to register node: ' + nodeName) raise def uninitializePlugin(mobject): mplugin = om.MFnPlugin(mobject) try: mplugin.deregisterNode(nodeID) except: sys.stderr.write('fail to de-register node: ' + nodeName) raise

Data block (MDataBlock)

Data block refers the entire container of a node. This container stores all the values for each attribute of the node.

The datablock of this node is provided in the compute() function.

Plug (MPlug)

Plug is the outer plug of the node which provides the connections to other node’s plug. The MPlug usually connects the plug from another node and the value of a certain attribute of this node.

The plug is also provided in the compute() function

Data handle (MDataHandle)

Data handle usually stores an attribute’s value.

We can set a value to an attribute using MDataHandle.setType(value), and get an attribute’s value using value = MDataHandle.asType().

Procedure

step 1: Declare attribute (MObject) in the class

class MyNode(om.MPxNode): inAttr = om.MObject() outAttr = om.MObject() def __init__(self): om.MPxNode.__init__(self) def compute(self, plug, dataBlock): pass

inAttr refers to the input attribute that we are creating, outAttr refers to the output attribute that we are creating. Both are declared as MObject() type. We will later access them using MyNode.inAttr and MyNode.outAttr.

step 2: Initialize Node

Node Creator

def nodeCreator(): return MyNode()

Node Creator in API 2.0 directly returns an instance to the class, API 1.0 uses a pointer like in command plugin.

Node Initializer

def nodeInitializer(): '''1: create reference to attribute function set such as numericAttribute''' numericAttrFn = om.MFnNumericAttribute() ''' 2: create attribute using the function set''' MyNode.inAttr = numericAttrFn.create('in', 'i', om.MFnNumericData.kFloat, 1.0) numericAttrFn.readable = True numericAttrFn.writable = True MyNode.outAttr = numericAttrFn.create('out', 'o', om.MFnNumericData.kFloat) numericAttrFn.readable = True numericAttrFn.writable = False ''' 3: attach attribute''' MyNode.addAttribute(MyNode.inAttr) MyNode.addAttribute(MyNode.outAttr) ''' 4: add circuit (relationship in->out)''' MyNode.attributeAffects(MyNode.inAttr, MyNode.outAttr) MFnNumericAttribute provides the function sets to create attribute for numeric type attribute. There’s also MFnMatrixAttribute to create matrix type attribute and so on.–Using the function set’s create() function, we add parameters for attribute’s long name, short name, data type, and default value (optional). –This returns a MObject type attribute which is attached to the function set. We store it in the attribute declared earlier in the class. –Set the attribute’s property using function set, such as readable, writable, hidden, storable, connectable (In API1.0, use like this numericAttrFn.setReadable(True))Using the MyNode.addAttribute(MyNode.inAttr) to attach attribute to the nodeUsing the MyNode.attributeAffects(MyNode.inAttr, MyNode.outAttr) to design the affect relationship between attributes, in this case, change of inAttr will affect outAttr.

RegisterNode

mplugin.registerNode(nodeName, nodeID, nodeCreator, nodeInitializer, om.MPxNode.kDependNode, nodeClassify)

Parameters: name of the node, id of the node, node creator function, node initializer function, node type (DependNode or DeformNode…), node classification (utility, shading…)

Example:     nodeID = om.MTypeId(0x55555)     nodeClassify = ‘utility/general’

De-registerNode

mplugin.deregisterNode(nodeID)

step 3: Initialize Node

class MyNode(om.MPxNode): inAttr = om.MObject() outAttr = om.MObject() def __init__(self): om.MPxNode.__init__(self) def compute(self, plug, dataBlock): if plug == MyNode.outAttr: # 1: get datablock handle (inputValue returns MDataHandle type object) inHandle = dataBlock.inputValue(MyNode.inAttr) outHandle = dataBlock.outputValue(MyNode.outAttr) # 2: extract input value from the handle inValue = inHandle.asFloat() # 3: create logic and set output value outValue = inValue * 2 outHandle.setFloat(outValue) # 4: mark output plug as clean outHandle.setClean() else: return om.kUnknownParameter

When MyNode.outAttr is dirty (meaning it needs to recompute), we use if plug == MyNode.outAttr: to identify this certain plug. (this if statement will work, even if plug is MPlug type and MyNode.outAttr is MOjbect type)

We have already identify what input attribute is affecting this plug, in order to retrieve the value of this input, we need to attach a data handle on the data block specifying this certain input attribute we want to retrive. Therefore we have handle = dataBlock.input/outputValue(MyNode.attr)

Next, we use value = handle.asFloat() and handle.setFloat(value) to get and set the value from and to the attribute. (float type as example)

Last, we mark the current plug as clean, by setClean() to the outHandle

最新回复(0)