次のトピックでは、ディペンデンシー グラフ プラグインの基本的なコード構造を示します。
次の Python コードは、ディペンデンシー グラフ プラグインのサンプルです。このサンプルは、myNodeName というユーティリティ(utility/general)ハイパーシェード ノードを定義します。このノードには、浮動小数点数(十進数)を受け取る 1 つの入力アトリビュートと入力された浮動小数点数を出力する 1 つの出力アトリビュートがあります。このノードは、cmds.createNode( 'myNodeName' )または cmds.shadingNode( 'myNodeName', asUtility=True )を使用して作成されます。このノードが後者のコマンドで作成された場合、または Maya ハイパーシェード ユーザ インタフェース(テキスト ボックスに「My Node Name」と入力してフィルタ)を介して手動で作成された場合は、ノードのインスタンスがウィンドウ > レンダリング エディタ > ハイパーシェード > 'ユーティリティ' タブ(Window > Rendering Editors > Hypershade > 'Utilities' Tab)に表示されます。
# 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' ) # ... '''
# 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 ドキュメントで提供されるサンプル プラグインです。 |
ノードの作成関数リファレンス: 任意の名前が付けられた 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 に通知されます。