次のトピックでは、ディペンデンシー グラフ プラグインの基本的なコード構造を示します。
次の Python コードは、ディペンデンシー グラフ プラグインのサンプルです。このサンプルは、myNodeName
というユーティリティ(utility/general
)ハイパーシェード ノードを定義します。このノードには、浮動小数点数(十進数)を受け取る 1 つの入力アトリビュートと、入力された浮動小数点数を出力する 1 つの出力アトリビュートがあります。このノードは、cmds.createNode( 'myNodeName' )
または cmds.shadingNode( 'myNodeName', asUtility=True )
使用して作成されます。このノードが後者のコマンドで作成された場合、または Maya ハイパーシェード ユーザ インタフェース(テキスト ボックスに「My Node Name
」と入力してフィルタ)を介して手動で作成された場合は、ノードのインスタンスがウィンドウ > レンダリング エディタ > ハイパーシェード > 'ユーティリティ' タブ(Window > Rendering Editors > Hypershade > 'Utilities' Tab)に表示されます。
Python API 2.0:
# pySampleDGNode.py
import sys
import maya.api.OpenMaya as OpenMaya
# ... additional imports here ...
def maya_useNewAPI():
"""
The presence of this function tells Maya that the plugin produces, and
expects to be passed, objects created using the Maya Python API 2.0.
"""
pass
# Plug-in information:
kPluginNodeName = 'myNodeName' # The name of the node.
kPluginNodeClassify = 'utility/general' # Where this node will be found in the Maya UI.
kPluginNodeId = OpenMaya.MTypeId( 0x87EFE ) # A unique ID associated to this node type.
# Default attribute values
sampleDefaultValue = 1
##########################################################
# Plug-in
##########################################################
class myNode(OpenMaya.MPxNode):
# Static variables which will later be replaced by the node's attributes.
sampleInAttribute = OpenMaya.MObject()
sampleOutAttribute = OpenMaya.MObject()
def __init__(self):
''' Constructor. '''
OpenMaya.MPxNode.__init__(self)
def compute(self, pPlug, pDataBlock):
'''
Node computation method.
- pPlug: A connection point related to one of our node attributes (could be an input or an output)
- pDataBlock: Contains the data on which we will base our computations.
'''
if( pPlug == myNode.sampleOutAttribute ):
# Obtain the data handles for each attribute
sampleInDataHandle = pDataBlock.inputValue( myNode.sampleInAttribute )
sampleOutDataHandle = pDataBlock.outputValue( myNode.sampleOutAttribute )
# Extract the actual value associated to our sample input attribute (we have defined it as a float)
sampleInValue = sampleInDataHandle.asFloat()
# ... perform the desired computation ...
# Set the output value.
sampleOutDataHandle.setFloat( sampleInValue ) # As an example, we just set the output value to be equal to the input value.
# Mark the output data handle as being clean; it need not be computed given its input.
sampleOutDataHandle.setClean()
else:
return OpenMaya.kUnknownParameter
##########################################################
# Plug-in initialization.
##########################################################
def nodeCreator():
''' Creates an instance of our node class and delivers it to Maya as a pointer. '''
return myNode()
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)
#==================================
global sampleDefaultValue
myNode.sampleInAttribute = numericAttributeFn.create( 'myInputAttribute', 'i',
OpenMaya.MFnNumericData.kFloat,
sampleDefaultValue )
numericAttributeFn.writable = True
numericAttributeFn.storable = True
numericAttributeFn.hidden = False
myNode.addAttribute( myNode.sampleInAttribute ) # Calls the MPxNode.addAttribute function.
#==================================
# OUTPUT NODE ATTRIBUTE(S)
#==================================
myNode.sampleOutAttribute = numericAttributeFn.create( 'myOutputAttribute', 'o',
OpenMaya.MFnNumericData.kFloat )
numericAttributeFn.storable = False
numericAttributeFn.writable = False
numericAttributeFn.readable = True
numericAttributeFn.hidden = False
myNode.addAttribute( myNode.sampleOutAttribute )
#==================================
# NODE ATTRIBUTE DEPENDENCIES
#==================================
# If sampleInAttribute changes, the sampleOutAttribute data must be recomputed.
myNode.attributeAffects( myNode.sampleInAttribute, myNode.sampleOutAttribute )
def initializePlugin( mobject ):
''' Initialize the plug-in '''
mplugin = OpenMaya.MFnPlugin( mobject )
try:
mplugin.registerNode( kPluginNodeName, kPluginNodeId, nodeCreator,
nodeInitializer, OpenMaya.MPxNode.kDependNode, kPluginNodeClassify )
except:
sys.stderr.write( 'Failed to register node: ' + kPluginNodeName )
raise
def uninitializePlugin( mobject ):
''' Uninitializes the plug-in '''
mplugin = OpenMaya.MFnPlugin( mobject )
try:
mplugin.deregisterNode( kPluginNodeId )
except:
sys.stderr.write( 'Failed to deregister node: ' + kPluginNodeName )
raise
##########################################################
# Sample usage.
##########################################################
'''
# Copy the following lines and run them in Maya's Python Script Editor:
import maya.cmds as cmds
cmds.loadPlugin( 'pySampleDGNode.py' )
cmds.createNode( 'myNodeName' )
# ...
'''
Python API 1.0:
# sampleDGNode.py
import sys
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMaya as OpenMaya
# ... additional imports here ...
# Plug-in information:
kPluginNodeName = 'myNodeName' # The name of the node.
kPluginNodeClassify = 'utility/general' # Where this node will be found in the Maya UI.
kPluginNodeId = OpenMaya.MTypeId( 0x87EFE ) # A unique ID associated to this node type.
# Default attribute values
sampleDefaultValue = 1
##########################################################
# Plug-in
##########################################################
class myNode(OpenMayaMPx.MPxNode):
# Static variables which will later be replaced by the node's attributes.
sampleInAttribute = OpenMaya.MObject()
sampleOutAttribute = OpenMaya.MObject()
def __init__(self):
''' Constructor. '''
OpenMayaMPx.MPxNode.__init__(self)
def compute(self, pPlug, pDataBlock):
'''
Node computation method.
- pPlug: A connection point related to one of our node attributes (could be an input or an output)
- pDataBlock: Contains the data on which we will base our computations.
'''
if( pPlug == myNode.sampleOutAttribute ):
# Obtain the data handles for each attribute
sampleInDataHandle = pDataBlock.inputValue( myNode.sampleInAttribute )
sampleOutDataHandle = pDataBlock.outputValue( myNode.sampleOutAttribute )
# Extract the actual value associated to our sample input attribute (we have defined it as a float)
sampleInValue = sampleInDataHandle.asFloat()
# ... perform the desired computation ...
# Set the output value.
sampleOutDataHandle.setFloat( sampleInValue ) # As an example, we just set the output value to be equal to the input value.
# Mark the output data handle as being clean; it need not be computed given its input.
sampleOutDataHandle.setClean()
else:
return OpenMaya.kUnknownParameter
##########################################################
# Plug-in initialization.
##########################################################
def nodeCreator():
''' Creates an instance of our node class and delivers it to Maya as a pointer. '''
return OpenMayaMPx.asMPxPtr( myNode() )
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)
#==================================
global sampleDefaultValue
myNode.sampleInAttribute = numericAttributeFn.create( 'myInputAttribute', 'i',
OpenMaya.MFnNumericData.kFloat,
sampleDefaultValue )
numericAttributeFn.setWritable( True )
numericAttributeFn.setStorable( True )
numericAttributeFn.setHidden( False )
myNode.addAttribute( myNode.sampleInAttribute ) # Calls the MPxNode.addAttribute function.
#==================================
# OUTPUT NODE ATTRIBUTE(S)
#==================================
myNode.sampleOutAttribute = numericAttributeFn.create( 'myOutputAttribute', 'o',
OpenMaya.MFnNumericData.kFloat )
numericAttributeFn.setStorable( False )
numericAttributeFn.setWritable( False )
numericAttributeFn.setReadable( True )
numericAttributeFn.setHidden( False )
myNode.addAttribute( myNode.sampleOutAttribute )
#==================================
# NODE ATTRIBUTE DEPENDENCIES
#==================================
# If sampleInAttribute changes, the sampleOutAttribute data must be recomputed.
myNode.attributeAffects( myNode.sampleInAttribute, myNode.sampleOutAttribute )
def initializePlugin( mobject ):
''' Initialize the plug-in '''
mplugin = OpenMayaMPx.MFnPlugin( mobject )
try:
mplugin.registerNode( kPluginNodeName, kPluginNodeId, nodeCreator,
nodeInitializer, OpenMayaMPx.MPxNode.kDependNode, kPluginNodeClassify )
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
##########################################################
# Sample usage.
##########################################################
'''
# Copy the following lines and run them in Maya's Python Script Editor:
import maya.cmds as cmds
cmds.loadPlugin( 'sampleDGNode.py' )
cmds.createNode( 'myNodeName' )
# ...
'''
ディペンデンシー グラフ プラグインは、コマンド プラグインと同じエントリ/エグジット ポイント関数を必要とします。すなわち、initializePlugin()
と uninitializePlugin()
です。Maya がプラグインをロード/アンロードするときに、これらの 2 つの関数がそれぞれ呼び出されます。これらの 2 つの関数がファイルにない場合は、プラグインはロードに失敗します。ディペンデンシー グラフ プラグインを使用して、MPxNode
またはその多くのサブクラスのいずれかを継承し、新しいノード タイプを作成します。このトピックに含まれるサンプルでは、MPxNode
を直接継承しているので、MFnPlugin.registerNode()
を介してノードを登録する必要があります。
def initializePlugin( mobject ):
''' Initialize the plug-in '''
mplugin = OpenMayaMPx.MFnPlugin( mobject )
try:
mplugin.registerNode( kPluginNodeName, kPluginNodeId, nodeCreator,
nodeInitializer, OpenMayaMPx.MPxNode.kDependNode, kPluginNodeClassify )
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 )
MFnPlugin.registerNode()
への呼び出しに 6 つのパラメータが含まれていることが分かります。これらのパラメータの説明は次のとおりです。
ノード名: ノードの名前です。任意の名前の付いた変数 kPluginNodeName
でこの値を指定します。この変数は、uninitializePlugin()
の他に、sys.stderr.write()
へのエラー レポート呼び出しでも使用されます。
kPluginNodeName = 'myNodeName' # The name of the node.
このノードの名前を登録することにより、次の Python コマンドを使用してノードのインスタンスを作成することができます。
import maya.cmds as cmds
cmds.createNode( 'myNodeName' )
ノード ID: Maya で登録されている他のすべてのノード タイプからノードのタイプ(MTypeId
)を識別する一意の数値です。任意の名前の付いた変数 kPluginNodeId
でこの値を指定します。
kPluginNodeId = OpenMaya.MTypeId( 0x87EFE ) # A unique ID associated to this node type.
この MTypeId
値は一意である必要があります。Maya がノードを含むファイルを保存およびロードしようとするときは、この一意の ID を使用して、ノードがラベル付けされます。ロードされたノードの一意の ID が Maya に登録されていない場合(プラグインがロードされていない可能性がある場合)は、これらのノードがディペンデンシー グラフに表示されません。MTypeId
API ドキュメントで詳細に説明したように、MTypeId
の特定の数値範囲は別の目的のために予約されています。次の表に、これらの範囲の概要を示します。
予約済み MTypeId 範囲 | 修正される問題 |
---|---|
0x00000 - 0x7ffff | 広く分配するように意図されていないプラグインです。この範囲は内部で開発されたプラグインに適しています。 |
0x80000 - 0xfffff | Maya および API ドキュメントで提供されるサンプル プラグインです。 |
注: 作成したディペンデンシー グラフ プラグインをパブリックに分配する場合は、グローバルに一意の ID が必要になります。オートデスク デベロッパー ネットワークでは、こうした ID を 64、128、256、または 512 のブロックで提供します。1 つまたは複数の 24 ビットのプリフィックスが割り当てられます。これを取得したら、2 つの
unsigned int
パラメータを受け取るMTypeId
コンストラクタを使用します。プリフィックスが最初のパラメータになりますが、2 番目のパラメータになる ID の割り当てを管理する必要があります。
ノードの作成関数リファレンス: 任意の名前が付けられた nodeCreator()
関数へのリファレンスです。この関数は、ノードのインスタンスを作成し、有効な Maya オブジェクト ポインタとしてそのインスタンスを返します。
def nodeCreator():
''' Creates an instance of our node class and delivers it to Maya as a pointer. '''
return OpenMayaMPx.asMPxPtr( myNode() )
ノードのアトリビュートの初期化関数リファレンス: 任意の名前が付けられた nodeInitializer()
関数へのリファレンスです。この関数は、ノードの入力/出力アトリビュートのセットを作成します。
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)
#==================================
global sampleDefaultValue
myNode.sampleInAttribute = numericAttributeFn.create( 'myInputAttribute', 'i',
OpenMaya.MFnNumericData.kFloat,
sampleDefaultValue )
numericAttributeFn.setWritable( True )
numericAttributeFn.setStorable( True )
numericAttributeFn.setHidden( False )
myNode.addAttribute( myNode.sampleInAttribute ) # Calls the MPxNode.addAttribute function.
#==================================
# OUTPUT NODE ATTRIBUTE(S)
#==================================
myNode.sampleOutAttribute = numericAttributeFn.create( 'myOutputAttribute', 'o',
OpenMaya.MFnNumericData.kFloat )
numericAttributeFn.setStorable( False )
numericAttributeFn.setWritable( False )
numericAttributeFn.setReadable( True )
numericAttributeFn.setHidden( False )
myNode.addAttribute( myNode.sampleOutAttribute )
#==================================
# NODE ATTRIBUTE DEPENDENCIES
#==================================
# If sampleInAttribute changes, the sampleOutAttribute data must be recomputed.
myNode.attributeAffects( myNode.sampleInAttribute, myNode.sampleOutAttribute )
MFnNumericAttribute
関数セットを使用して、数値アトリビュートをカプセル化する MObject
のインスタンスを作成します。これらのアトリビュートには、ロング ネームとショート ネームの他、タイプおよびオプションの既定値が必要です。作成したアトリビュートを myNode
クラスのスタティック変数、たとえば、myNode.sampleInAttribute
として割り当てます。これらのオブジェクトをスタティック変数として割り当てると、myNode.compute()
メソッドでオブジェクトを参照することができます。
作成したアトリビュートは、MPxNode.addAttribute()
によってノードのベース クラスに追加されます。これにより、ディペンデンシー グラフでノードのアトリビュートにアクセスできるようになります。
MFnNumericAttribute.setWritable()
関数を使用すると、入力ソースの宛先としてアトリビュートに接続することができます。MFnNumericAttribute.setStorable()
を使用すると、アトリビュートに関連付けられた値をファイルに保存することができます。MFnNumeric.setReadable()
関数を使用すると、このアトリビュートに関連付けられた値を他のノードの書き込み可能なアトリビュートに対する入力として使用することができます。MFnNumeric.setHidden()
アトリビュートは、ユーザ インタフェースのアトリビュートを非表示にするので、このアトリビュートが誤って接続されることはありません。
MPxNode.attributeAffects()
関数は、指定された入力に関連付けられた値が変更された場合に、ノードの compute()
関数が呼び出され、出力アトリビュートの値が再計算されるようにします。詳細については、「ディペンデンシー グラフ」を参照してください。
ノード タイプ: プラグインによって定義されているノード タイプです。既定では、MPxNode.kDependNode
に設定されていますが、MPxNode.kLocatorNode
、MPxNode.kDeformerNode
、MPxNode.kSurfaceShape
などの他のタイプを指定して、ディペンデンシー グラフのさまざまな動作を定義できます。
ノードの分類: ノードの分類は、レンダー ノードの作成(Create Render Node)やハイパーシェード ウィンドウなどのさまざまなレンダリング関連のユーザ インタフェースでノードが分類される方法を決定する文字列です。文字列が提供されていない場合、ノードはレンダリング ノードとみなされないので、これらのユーザ インタフェースに表示されません。
kPluginNodeClassify = 'utility/general' # Where this node will be found in the Maya UI.
次の表は、有効なレンダリング ノード分類のセットを示しています。
カテゴリ | 区分文字列 |
---|---|
2D テクスチャ | 'texture/2d' |
3D テクスチャ | 'texture/3d' |
環境テクスチャ | 'texture/environment' |
サーフェス マテリアル | 'shader/surface' |
ボリューム マテリアル | 'shader/volume' |
ディスプレイスメント マテリアル | 'shader/displacement' |
ライト | 'light' |
一般ユーティリティ | 'utility/general' |
カラー ユーティリティ | 'utlity/color' |
パーティクル ユーティリティ | 'utility/particle' |
イメージ プレーン | 'imageplane' |
グロー | 'postprocess/opticalFX' |
ディペンデンシー グラフ ノード(myNode
)の動作は、MPxNode
を継承するクラスで定義されます。
class myNode(OpenMayaMPx.MPxNode):
# Static variables which will later be replaced by the node's attributes.
sampleInAttribute = OpenMaya.MObject()
sampleOutAttribute = OpenMaya.MObject()
def __init__(self):
''' Constructor. '''
OpenMayaMPx.MPxNode.__init__(self)
def compute(self, pPlug, pDataBlock):
'''
Node computation method.
- pPlug: A connection point related to one of our node attributes (could be an input or an output)
- pDataBlock: Contains the data on which we will base our computations.
'''
if( pPlug == myNode.sampleOutAttribute ):
# Obtain the data handles for each attribute
sampleInDataHandle = pDataBlock.inputValue( myNode.sampleInAttribute )
sampleOutDataHandle = pDataBlock.outputValue( myNode.sampleOutAttribute )
# Extract the actual value associated to our sample input attribute (we have defined it as a float)
sampleInValue = sampleInDataHandle.asFloat()
# ... perform the desired computation ...
# Set the output value.
sampleOutDataHandle.setFloat( sampleInValue ) # As an example, we just set the output value to be equal to the input value.
# Mark the output data handle as being clean; it need not be computed given its input.
sampleOutDataHandle.setClean()
else:
return OpenMaya.kUnknownParameter
Maya は、nodeInitializer()
の MPxNode.addAttribute()
で追加されたノード アトリビュートを使用して、ノードがディペンデンシー グラフでインスタンス化されたときにノードの入力/出力接続ポイントを定義します。
これらの接続ポイントはプラグと呼ばれ、MPlug
のインスタンスによって表されます。nodeInitializer()
の MPxNode.attributeAffects()
への呼び出しは、これらのプラグ間の原因となる入力 > 出力リンクを決定します。最終的に、ディペンデンシー グラフは、すべてのノード間で接続されているプラグの有向エッジを追跡します。Maya のディペンデンシー グラフの継続している評価プロセス中に、プラグに関連付けられたデータが変更された場合は、このプラグがダーティとしてマークされます。つまり、キャッシュされた値が古いので、値を再計算する必要があります。プラグ階層が再帰的にトラバースされ、残りの影響を受けるプラグがダーティとしてマークされます。
次に、Maya は、ダーティなプラグを含む各ノードで compute()
メソッドを呼び出し、MPlug
と MDataBlock
のインスタンスを渡します。渡された MPlug
の関連するアトリビュートをチェックし、何を計算するか決定する必要があります。これは、MPlug
のオーバーロードされた等価演算子「==
」を使用して行います。
渡された MDataBlock
インスタンスには、MDataHandle
オブジェクトが含まれます。このオブジェクトには、それぞれのアトリビュートに関連付けられた値が格納されます。float
入力値は、次のように取得されます。
sampleInDataHandle = pDataBlock.inputValue( myNode.sampleInAttribute )
sampleInValue = sampleInDataHandle.asFloat()
compute()
メソッドは、新たに計算された値を正しい出力 MDataHandle
に設定することによって終了する必要があります。MDataHandle.setClean()
への呼び出しによって、対応するプラグからダーティなフラグが除去されます。
compute()
関数に渡されたプラグが計算可能な出力アトリビュートに関連していない場合は、関数が OpenMaya.kUnknownParameter
を返す必要があります。これにより、ベース クラスのいずれかの compute()
関数を使用してプラグの値を計算する必要があることが Maya に通知されます。