ディペンデンシー ノードの基本例

次の例では、基本的な MPxNode 親クラスから派生する単純なカスタム ディペンデンシー グラフ ノードについて説明します。単一の浮動小数値を入力値として取り、この数値の正弦値を計算し、結果を出力します。

#include <string.h>
#include <iostream.h>
#include <math.h>
#include <maya/MString.h> 
#include <maya/MFnPlugin.h>

これはプラグインでもあるため、MFnPlugin.h も必要です。ただし、コマンド以外でノードを登録するには、別の方法を使用します。

#include <maya/MPxNode.h> 
#include <maya/MTypeId.h> 
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>

これらのヘッダ ファイルは、ほとんどのプラグイン ディペンデンシー グラフ ノードで使用されます。

#include <maya/MFnNumericAttribute.h>

さまざまなアトリビュート型が数多くあります(これらについては後で説明します)。必要なアトリビュート型は、作成するノードの種類によって決まります。このサンプルでは数値データのみを使用します。

class sine : public MPxNode
{

ユーザ定義ディペンデンシー グラフ ノードは、MPxNode クラスから派生します。

コンストラクタ:

    public:
        sine();

このノードのインスタンスが作成されるたびに、ノードのコンストラクタがコールされます。createNode コマンドをコールするときや MFnDependencyNode::create() メソッドを起動するときなどがこれにあたります。

デストラクタ:

        virtual ~sine();

ノードを本当に削除する場合のみ、デストラクタがコールされます。Maya には元に戻す(undo)待ち行列があるので、ノードを削除しても実際にノードのデストラクタはコールされません。削除を元に戻した場合に、ノードを再作成せずに復元できるようにするためです。一般的に削除したノードのデストラクタは、元に戻す待ち行列がフラッシュされた場合にのみコールされます。

compute() 関数:

        virtual MStatus compute( const MPlug& plug,
            MDataBlock& data );

compute() メソッドはノードの頭脳です。ノードの入力を使用してノードの実際の作業を実行し、出力を生成します。

creator() 関数

        static void* creator();

creator() メソッドは、コマンドの creator メソッドと同じ目的で動作します。つまり、Maya でこのノードのインスタンスをインスタンス化できるようにします。createNode コマンドまたは MFnDependencyNode::create() メソッドがノードの新しいインスタンスを要求するたびに、creator() メソッドがコールされます。

initialize() 関数:

        static MStatus initialize();

initialize() メソッドは、ディペンデンシー ノードのための登録メカニズムでコールされます。このため、ユーザ定義ノードを含むプラグインがロードされた直後に一度コールされます。ノードの入出力(たとえばアトリビュート)を定義するために使用されます。

ノード アトリビュート:

    public:
        static MObject input;
        static MObject output;

この 2 つの MObject は正弦(sine)ノードのアトリビュートです。ノードのアトリビュートには任意の名前を自由に使用できます。ここでは分かりやすくするため、入力(input)と出力(output)を使用します。

ノード タイプ:

        static MTypeId id;

それぞれのノードには固有の識別子が必要です。MFnDependencyNode::create() は、この識別子を使用して作成するノードを識別します。Maya ファイル フォーマットでも、この識別子が使用されます。

ノードをローカルでテストする場合は、0x00000000 から 0x0007ffff までの識別子を任意に使用できますが、永続的な目的でノードを使用する場合は、世界で固有な識別子を http://mayaid.autodesk.io/ から取得してください。ユーザごとに独自で管理できる、固有の範囲が割り当てられます。

};
MTypeId sine::id( 0x80000 );

ここでは、ノードの識別子を一意のタグに初期化します。これらのタグは、すべてのノードで一意にする必要があります(タグはノードを再作成するためにファイル フォーマットで使用されます)。タグは Autodesk から API ユーザに割り当てます。

MObject sine::input; 
MObject sine::output; 

ノードのアトリビュートは NULL 値に初期化されます。

void* sine::creator() {
    return new sine;
}

前に説明したように、creator() メソッドは、このノードの新しいインスタンスを単に返します。複数のノードを相互に接続する必要がある、より複雑な状況では、接続される複数のノード用に 1 つの creator を定義し、この creator を使って割り当てすべてのノードを相互に接続することができます。

MStatus sine::initialize() {

initialize メソッドは、ノードを初めて Maya に登録する際、一度だけコールされます。このメソッドでは、ノードのアトリビュート、つまりその他のノードからの接続の対象となる、ノードの入出力データを定義します。

    MFnNumericAttribute nAttr;

このサンプルでは数値データしか使用しないので、すべてのアトリビュートは数値であり MFnNumericAttribute のみが必要になります。

    output = nAttr.create( "output", "out",
        MFnNumericData::kFloat, 0.0 );
    nAttr.setWritable(false);
    nAttr.setStorable(false);

この 3 行の最初の行は、正弦ノードの出力アトリビュートを定義します。アトリビュートを定義する場合は、アトリビュートのロング ネーム(4 文字以上)とショート ネーム(3 文字以下)を指定する必要があります。この名前は、MEL スクリプトと UI エディタで特定アトリビュートを識別するために使用されます。必須ではありませんが、一般的にはロング ネームをアトリビュートの C++ 識別子と同じにします。このサンプルでは、両方とも出力(output)です。

create メソッドではアトリビュートの型も指定します。この場合は float (MFnNumbericData::kFloat)です。また既定値をゼロに設定します。アトリビュートの名前を一意にする必要があるのは同一ノード内のみです。別のノードでは同じ名前のアトリビュートを使用できます。

次の 2 行は、このアトリビュートの特定の特性を設定します。これは正弦ノードの出力であるため、その他のノードからは書き込めません。つまり、別のノードの出力アトリビュートは、このアトリビュートに接続できません。またこれは出力であるため、ファイルの書き込み時に保存する必要はありません。出力は入力から生成できるからです(出力を保存しても問題にはなりませんが、スペースの浪費になります)。

新しいノードをインスタンス化すると、以下の特性は true に設定されます。

以下の特性は false に設定されます。

    input = nAttr.create( "input", "in",
        MFnNumericData::kFloat, 0.0 );
    nAttr.setStorable(true);

入力アトリビュートの初期化は出力アトリビュートと同じですが、入力の値はノードで計算できないので、ノードを保存する際に保存する必要があります。

    addAttribute( input );
    attributeAffects( input, output );

入力アトリビュートが正弦ノードのアトリビュートとして追加されます。attributeAffects() メソッドは、入力アトリビュートが出力アトリビュートに影響するタイミングを示すために使用されます。これを認識すると、Maya は、複数の入出力がある、より複雑なノードのグラフでディペンデンシーを最適化できるようになりますが、すべての入力がすべての出力に影響するとは限りません。

    addAttribute( output );

出力アトリビュートが正弦ノードに追加されます。出力アトリビュートが生成されても入力アトリビュートは影響されないので、attributeAffects() メソッドは必要ありません。

    return MS::kSuccess;

成功を返し、ノードが問題なく初期化されたことを Maya に通知します。エラーが返されると初期化は停止します。initialize メソッドは一度しかコールされないので、ノードはこのセッションで使用できなくなります。ノードが必要とするリソースを使用できない場合は、常にエラーが返されます。

}
sine::sine() {}
sine::~sine() {}

非常に単純なノードなので、コンストラクタとデストラクタは何も実行しません。

MStatus sine::compute( const MPlug& plug, MDataBlock& data ) {

compute() メソッドはディペンデンシー グラフ ノードの頭脳で、ノードの実際の作業をすべて実行します。このメソッドは 2 つの引数を取ります。最初の引数は、計算を要求するプラグへの参照で、次の引数はノードのデータ ブロックです。プラグとデータ ブロックについては、後のセクションで詳しく説明します。

    MStatus returnStatus;
    if( plug == output )
    {

プラグ

プラグは、再計算されたアトリビュートと考えることができます。このテストでは、再計算が要求されている出力アトリビュートがチェックされます。

この単純なサンプルでは出力アトリビュートしか該当しませんが、より複雑なノードでは、任意の出力になることがあります。プラグが認識されているアトリビュートを表しているかどうか、常にテストしてください。たとえば、誰かが接続のあるノードにダイナミック アトリビュートをアタッチしたとします。これにより、入力が変更されていないのに compute メソッドがコールされることがあります。

        MDataHandle inputData = data.inputValue( input,
            &returnStatus );

データ ブロック

データ ブロックには、ノードのこのインスタンスのデータがすべて含まれています。効率上の理由から、このデータは 1 つのブロックとして保存されます。ブロックの一部を参照するには、データ ハンドルを使用します。このケースでは、入力アトリビュートを参照するためにデータ ハンドルが設定されています。

        if( returnStatus != MS::kSuccess )
            cerr << "ERROR getting data" << endl;
        else
        {
            float result = sin( inputData.asFloat() );

MFnDataHandleas*() メソッドによって、データ ハンドルからデータが取り出されます。as*() メソッドは必ず、宣言されたアトリビュートの型と一致させる必要があります。入力アトリビュートを MFnNumericAttribute::kFloat として宣言したので、データは asFloat() で取り出す必要があります。型と取り出しメソッドが混在すると、重大なエラーの原因となります。たとえばアトリビュートを MFnNumericAttribute::kDouble として宣言し、asFloat() で取り出すと重大なエラーとなります。

             MDataHandle outputHandle = data.outputValue(
                output );

出力アトリビュートのデータ ブロックの一部を参照するために、新しいデータ ハンドルを割り当てます。

            outputHandle.set( result );

出力アトリビュートに計算結果が割り当てられます。

            data.setClean(plug);

再計算を引き起こしたデータ ブロックのプラグが clean にマークされ、新しく計算し直されたことが示されます。

        }
    }
    return MS::kSuccess;
}

成功ステータスが返ると、計算が適切に終了したことが分かります。

MStatus initializePlugin( MObject obj ) { 
    MStatus status;
    MFnPlugin plugin( obj, "My plug-in", "1.0", "Any");
    status = plugin.registerNode( "sine", sine::id, sine::creator, sine::initialize );

プラグインでは、コマンド プラグインと同じように initializePlugin()uninitializePlugin() が必要ですが、registerCommand() の代わりに registerNode() を使用し、Maya のノードのデータベースにノードを追加します。ノードの initialize メソッドは、registerNode をコールした結果としてコールされます。initialize メソッドがエラー コードを返すと、registerNode もエラーになり、ノードはロードされません。

    return status;
}
MStatus uninitializePlugin( MObject obj) {
     MStatus status;
     MFnPlugin plugin( obj );
     status = plugin.deregisterNode( sine::id );
     return status;
}

単純なディペンデンシー グラフ ノードが完成しました。