以下は、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)待ち行列があるので、ノードを削除しても実際にノードのデストラクタはコールされません。削除を元に戻した場合に、ノードを再作成せずに復元できるようにするためです。一般的に削除したノードのデストラクタは、元に戻す待ち行列がフラッシュされた場合にのみコールされます。
virtual MStatus compute( const MPlug& plug,
MDataBlock& data );
compute() メソッドはノードの頭脳です。ノードの入力を使用してノードの実際の作業を実行し、出力を生成します。
static void* creator();
creator() メソッドは、コマンドの creator メソッドと同じ目的で動作します。つまり、Maya でこのノードのインスタンスをインスタンス化できるようにします。createNode コマンドまたは MFnDependencyNode::create() メソッドがノードの新しいインスタンスを要求するたびに、creator() メソッドがコールされます。
static MStatus initialize();
initialize() メソッドは、ディペンデンシー ノードのための登録メカニズムでコールされます。このため、ユーザ定義ノードを含むプラグインがロードされた直後に一度コールされます。ノードの入出力(たとえばアトリビュート)を定義するために使用されます。
public:
static MObject input;
static MObject output;
この 2 つの MObject は正弦(sine)ノードのアトリビュートです。ノードのアトリビュートには任意の名前を自由に使用できます。ここでは分かりやすくするため、入力(input)と出力(output)を使用します。
static MTypeId id;
それぞれのノードには固有の識別子が必要です。MFnDependencyNode::create() は、この識別子を使用して作成するノードを識別します。Maya ファイル フォーマットでも、この識別子が使用されます。
ノードをローカルでテストする場合は、0x00000000 から 0x0007ffff までの識別子を任意に使用できますが、永続的な目的でノードを使用する場合は、世界で固有な識別子を Autodesk テクニカル サポートから取得してください。ユーザごとに独自で管理できる、固有の範囲が割り当てられます。
};
MTypeId sine::id( 0x80000 );
ここでは、ノードの識別子を一意のタグに初期化します。これらのタグは、すべてのノードで一意にする必要があります(タグはノードを再作成するためにファイル フォーマットで使用されます)。タグは Autodesk から API ユーザに割り当てます。
MObject sine::input;
MObject sine::output;
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 に設定されます。
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() );
MFnDataHandleのas*() メソッドによって、データ ハンドルからデータが取り出されます。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;
}