API ガイド > 付録 >

# 付録 D: よくある質問

このリストは、Maya API を使用しているプログラマから寄せられた質問を編集したものです。質問は、いくつかカテゴリを設けて(下記を参照)、カテゴリ別に論理的に編成してあります。

現在のところ、次のカテゴリに分類されています。

- 一般的な質問
- マニュアルに関する質問
- ディペンデンシー グラフに関する質問
- GUI に関する質問
- アニメーションに関する質問
- Windows に関する質問

一般的な質問

Q:

API が返す単位はどうやって分かりますか?

A:

別途指定しない限り、すべての API メソッドで Maya の内部単位(cm およびラジアン)が使用されます。

Q:

OpenModel のようなスタンドアローン モードはありますか? それとも、Avid® Softimage® のようなスタンドアローン モードですか(Softimage では基本ソフトウェア パッケージ(およびライセンス)が必要)?

A:

はい。『API ガイド』の「プラグインのビルド環境を設定する」と MLibrary クラスのドキュメントで、セットアップ方法と使用方法について説明しています。スタンドアローン アプリケーションのサンプルもいくつかあります。これらの説明については、API マニュアルの「サンプル プラグイン」を参照してください。

Q:

OpenModel には OpenAlias と同じ API インタフェースが用意されていますが、OpenModel の関数コールの多くは(UI コール以外のコールであっても)異なる動作をしたり、まったく動作しなかったりします。たとえば、同一の関数セットの中で動作する部分と動作しない部分があるために、レンダリングの制御ができないことがあります。特にレンダラはバッチ モードで処理されることが多いので、かなりストレスを感じます。Maya でもバッチモードやインタラクティブ モードで同じ不具合が生じるのであれば、非常に期待はずれに感じます。

A:

Maya API ではこのような問題は発生しないはずです。確かに、UI コールをライブラリ モードで実行すると UI コールは動作しませんが、それ以外のコールはすべてまったく同じように動作するはずです。

Q:

使用されている角度単位が矛盾しています。Maya のアトリビュート ウィンドウでは、回転を度単位で指定します。ところが API では、回転のアトリビュートにアタッチするのに、ラジアン単位で記述する必要があります。キーフレームの値を記述するときに、UI で使われている単位と同じ単位を使用できませんか?

A:

UI を処理するときに、API は MAngle クラスを使用します。このクラスには、ユーザが UI で選択した単位を返す "uiUnit" というメソッドが含まれています。この値はユーザ プリファレンスであり、いつでも変更できます。MAngle クラスは、現在有効になっている UI プリファレンスに合わせてプラグイン コードを調整しなくても済むように、どんな場合でもラジアンに既定として設定されています。次のコードを使用すると、UI ユーザが設定した単位に合わせてプラグイン コードを調整することができます。

    MAngle foo;
    foo.setInternalUnit( foo.uiUnit() );

このコードを追加したあとは、UI ユーザがプリファレンスを再び変更するまで、新しい MAngle インスタンスはすべて UI と同じ単位で動作します。

また、効率上の理由から、トランスフォーム クラスの回転関数はラジアンを表す double 型で処理されます(実装上の要求です)。UI から角度データを取得する場合は、MArgList の asMAngle メソッドを使用してデータにアクセスし、asRadians メソッドを使用してトランスフォーム クラスで必要な値を抽出する必要があります。

Q:

blind_data のサンプルを変更し、ダイナミック アトリビュートとして多次元配列を追加したいのですが、どの MFnAttribute クラスがこの操作に最も適切か分かりません。

A:

任意の型のアトリビュートを作成し、MFnAttribute クラスの setArray メソッドをコールして、そのアトリビュートを配列に変換することができます。多次元配列が必要な場合は、何らかの形式のインデックス変換を使用して配列の上に多次元配列を構築する必要があります。

これでも不十分な場合は、多次元配列を直接格納できる MPxData から新しいデータ型全体を派生させる方法もあります。そのためには、新しいデータ型で配列の保存および復元が正しく行われるように、MPxData から派生した readASCII、writeASCII、readBinary、writeBinary の各仮想メソッドを実装する必要があります。これはやや手間のかかる作業です。blindShortDataCmd.cc、blindDoubleDataCmd.cc、blindComplexDataCmd.cc の 3 つのサンプルプラグインは、これがどのように行われるかを示します。

Q:

ノードのアトリビュートが変わった場合、どのように通知されますか?

A:

API には MMessage クラスをルートとするクラスの階層があり、これらのクラスによって、特定の Maya イベントが発生したときに起動されるコールバック関数の登録方法が提供されます。ここには、アトリビュートが変わったことを知らせる多数のメッセージがあります。

Q:

API を通じてデータをベイク処理するにはどうすればよいですか?

A:

「bakeResults」コマンドを使用すると、アニメーション データをベイク処理できます。エクスプレッション、モーション パス、アニメーション カーブなどはすべてディペンデンシー グラフで単一の animCurve に置き換えられ、同じモーションが生成されます。この機能は、「MGlobal::executeCommand」メソッドからアクセスできます。

さらに、MEL コマンド delete (-ch オプション)を実行すると、オブジェクトのコンストラクション ヒストリが除去されます。この機能にアクセスするには、同じく MGlobal::executeCommand メソッドを使用します。UI の編集 > 種類ごとに削除 > ヒストリ(Edit > Delete by Type > History)メニュー項目からこの機能にアクセスすることも可能です。

Q:

MFnNurbsSurface::cv() メソッドは MObject を返しますが、返された情報へのアクセスに使用できる関数セットが分かりません。CV を表すクラス上で動作する関数セット、または MPoints 上で動作する関数セットがありません。

A:

MItSurfaceCV 関数セットを使用してください。これはやや直観には反するかもしれませんが、CV メソッドによって返される MObject が実際に返すのは、オブジェクトの複数の CV を含めることができるコンポーネント構造です。そのため、それを展開してCV データを取得するには、CV イテレータが必要です。

Q:

グループ ノードを選択すると、グループ内の全オブジェクトがハイライト表示され、それらがすべて選択されているかのように見えますが、グローバルのセレクション リストに表示されるのはグループ ノードだけです。これは想定された動作でしょうか?グループ内のオブジェクトがすべて選択されているのだから、リストにも全オブジェクトが表示されるはずではないかと思われます (グローバルのアクティブ セレクション リストから MItSelectionList を構築しており、フィルタとして MFn::kInvalid を使用しています)。

A:

想定された動作です。これは API や MItSelectionList の問題ではなく、Maya の動作に関する問題です。ウィンドウ > ハイパーグラフ: 階層(Window > Hypergraph: Hierarchy)でハイパーグラフ(Hypergraph)を開き、次の操作を実行すると、同一の動作を確認できます。

- プリミティブを 2 つまたは 3 つ作成します。
- すべてのプリミティブを選択します。
- 編集 > グループ化(Edit > Group)を選択します。

ハイパーグラフでは、グループのすべてのプリミティブが「ハイライト」されていても、グループのトランスフォーム(Transform)(ノード)である「group1」だけが「選択されている」ことに注目してください。Maya に関するかぎり、ノードは 1 つだけしか選択されず、その単一のノードが MItSelectionList によって返される唯一のノードになります。もちろん、ハイパーグラフ(Hypergraph)で個々のプリミティブを選択したり、MEL を使ってプリミティブを名前で選択したりできますが、UI ではグループのトランスフォーム(Transform)ノードだけが選択されます。

ただし、トランスフォーム ノードが選択されているときに API コールの MGlobal::getHiliteList を使用すると、「ハイライト表示」されているオブジェクトのリストを取得することができます。

Q:

MPointArray など、API の簡単な配列構造に関する質問です。MPointArray に格納されているデータは隣接していますか、またはリンク リストとして格納されているのですか?

つまり、append() メソッドは(リンク リストの場合と同様)要素を追加するだけですか、それとも、recalloc() 関数(連続する新しいデータ ブロックと 1 つの要素を割り当て、古いデータをコピーする)と同等の動作を行っているのでしょうか?

A:

リンク リストとして格納されているわけではありませんが、追加(または挿入)が行われるたびに realloc が実行されているわけでもありません。append() は論理/物理空間モデルを管理し、さらに空間が必要な場合にユーザ設定可能な要素数によって物理空間を拡張するものです。

すべての「配列」には、メソッド 「sizeIncrement」および「setSizeIncrement」が必ず含まれています。sizeIncrement は、必要なときにいくつの要素まで配列ができるかを示し、setSizeIncrement はその値を変更できるようにします。

Maya 2.0 から、すべての配列クラスのコンストラクタが初期サイズのパラメータを受け取るようになりました。したがって、配列のサイズがあらかじめわかっている場合は、配列を大きくしたりエレメントをコピーしたりしてオーバーヘッドが生じるのを完全に回避することができます。

Q:

コマンド ウィンドウからコマンドを起動するたびにコマンドの新しいインスタンスが作成されるのは何故ですか。これは Maya の元に戻す(undo)機能と関連がありますか? コマンド オブジェクトのこれらのインスタンスはいつ削除されますか?

A:

Maya では、無限の元に戻す機能を次のように実装しています。

コマンドまたはツールが起動されると、そのオブジェクトの creator 関数がコールされて、新しいインスタンスが作成されます。このインスタンスには、状態を保存するのに十分な数のローカル データ メンバが含まれている必要があります。そうすれば、doIt メソッドがコールされたときに、次の操作に備えて十分な状態を保存しておくことが可能です。

- 実行しようとしていることを元に戻す(undo)
- 実行しようとしていることのやり直し(redo)

一般に、コマンドの doIt メソッドは、変更が取り消された場合に備えて変更前の現在の状態を保存し、ユーザが「実行しようとしている」操作のパラメータをキャッシュして、redoIt をコールするだけです。

redoIt は、キャッシュされたパラメータをもとに動作し、元に戻すマネージャからコールされた場合、それ以上ユーザと対話せずに操作を「再実行」できます。

元に戻すも、キャッシュされたデータをもとに動作し、それ以上ユーザと対話せずに動作することができます。

さらに、ツールが「終了」すると、(MPxToolCommand ベース クラスで提供される)仮想メソッド「finalize」がコールされます。このルーチンで、操作を「再実行」するコマンドを含む MArgList が構築されます。このコマンド文字列が Maya Journal に書き込まれると、発生した操作はすべて記録されます。

仮想メソッド MPxCommand::isUndoable がオーバーライドされ、メソッドから「false」が返される場合(ベース クラスの既定は「true」です)、doIt メソッドがコールされた直後に、Maya はコマンド インスタンスのデストラクタをコールします。そうでなければ、インスタンスは元に戻すマネージャに渡され、元に戻すマネージャは undoIt メンバと redoIt メンバをコールして undo および redo 要求を実装します。元に戻す待ち行列がフラッシュされると、コマンドまたはツールのすべてのインスタンスが破棄され、その結果、元に戻すまたはやり直しに必要なパラメータをキャッシュしているローカル メモリは解放されます。

Q:

UI を介して単一の CV が選択されたか複数の CV が選択されたかを問わず、MItSelectionList イテレータは 1 つの選択項目だけを返します。複数の CV が選択された場合、どの CV が選択されたかはどのように確認できますか?

A:

複数の CV を選択した後で API の MItSelectionList イテレータ クラスを使用すると、選択されたすべての CV が単一のコンポーネントとして返されます。

個々のCVにアクセスするときは、MItSelectionListのgetDagPath メソッドを使用する必要があります。getDagPath メソッドは、MDagPath と MObject の両方を返し、次にこれらを引数としてイテレータのコンストラクタに渡します。NURBS サーフェスの場合は、個々のサーフェス CV を抽出する MItSurfaceCV クラスが使用されます。MItCurveCV、MItMeshVertex、MItMeshEdge、MItMeshPolygon の各イテレータを使用すると、NURBS カーブ上およびポリゴン オブジェクトのさまざまなコンポーネント上で同じような操作を実行することができます。lassoTool プラグインは、これがどのように行われるかを示す好例です。

Q:

MItSurfaceCV::index() が返す値にはどういう意味がありますか?

A:

NURBS サーフェスのコンポーネントの 1 つは CV の配列です。index メソッドは、サーフェスによって維持される配列内の指定された CV の位置を返します。CV の位置は、UI では U および V の各インデックスに対応する CV の行と列を持つ、CV の 2 次元配列として表されます。内部的には 1 次元配列(row1、row2、row3 など)として格納され、インデックスでこのデータ構造内の CV の位置を返します(ちなみに、MFnNurbsSurface::create を使用してサーフェスを作成するのであれば、この方法で CV 配列を確保する必要があります)。CV の位置は、次の方法で 2D インデックスのペアに変換できます。

    sizeInV = MFnNurbsSurfaceInstance.numCVsInV();
    indexU = index() / sizeInV;
    indexV = index() % sizeInV;

MItSurfaceCV クラスの getIndex メソッドは、まさにこの計算を使用して indexU 値と indexV 値を返します。

Q:

オブジェクトの 2 つのコンポーネントが同じかどうか、どのように比較できますか? 具体的には NURBS サーフェス上の 2 つの CV を比較する必要がありますが、NURBS オブジェクトとポリゴン オブジェクトのあらゆる種類のコンポーネントに当てはまる問題だと思われます。

A:

これを行う簡単なメカニズムはありません。2 つのコンポーネントが同じ Dag パスのメンバであり(MDagPath クラスは==演算子を定義してこの比較を実行します)、2 つのコンポーネントのインデックス(コンポーネントの各種イテレータの index メソッドによって返されます)も同じである場合、それらのコンポーネントは同一です。

Q:

MFnNurbsSurface 関数セットのインスタンスを作成し、適切なデータを取得できました。その後、MGlobal::viewFrame をコールしてアニメーションを別のフレームに移動したところ、サーフェスは移動しましたが、データを取得したら最初のデータと同じでした。うまく動作させるにはどうすればよいですか?

A:

コードを少し再構築する必要があります。

MGlobal::viewFrame をコールした後で、関数セットがアクセスしているオブジェクトに関数セットを再バインドする必要があります。これは、次のようなコードで処理することができます。

     MDagPath path;
     // initialize path somehow to refer to the object in question
     MFnNurbsSurface surf;
     for (int i = 1; i <= maxFrames; ++i) {
         MGlobal::viewFrame(i);
         surf.setObject(path);
     }

MDagPath は全フレームで有効なので、MDagPath を使用して、各フレームのオブジェクトに関数セットを再バインドすることができます。

マニュアルに関する質問

Q:

CV がワールド空間に配置される際に CV 上で行われるすべての変換について、完全なマニュアルがあればと思います。たとえば、CV は行列、クラスタ、関数、アニメーションなどを多数使用して最終的にグローバル位置に配置されます。これらの変換に関するマニュアルがあれば非常に便利です。

A:

考えうる変換のセットがあまりに複雑でダイナミックなので、一般的なケースでマニュアル化することはできません。たとえば、クラスタについて考えてみます。Maya のクラスタはデフォーマとして実装されます。これは、オリジナルのサーフェスと新しい出力サーフェスの間にデフォーマが挿入されることを意味します。

したがって、変換された CV がワールド空間に配置されるまでのワールド変換情報は、デフォメーションの結果を保持する DAG シェイプを通じて取得することができ、ワールド空間でのポイントの位置を検索するのは簡単です。これは、現状手を加えずに取得できます。

しかし、ローカルからワールドへのポイントの変換は複雑になることがあります。ノードが、行列を全く必要としないプロシージャ変換を実装することは簡単です。これは概念的には、ローカル空間内のポイントが一連の「ブラック ボックス」の中を渡されるようなアーキテクチャです。各ボックスはポイントの位置に影響を与えますが、ボックスの中身が何であるかは全く分かりません。

これは同時に、ワールド空間でポイント(あるいは CV)の位置を正確に設定できないことを意味します。ワールド空間でポイントの位置を設定するには、ローカル空間からワールド空間への変換とは逆の計算が必要ですが、そのようなトランスフォームを一般的に定義する方法はありません。

したがって、Maya のトランスフォーム ノードがスケール、回転、移動、およびその他の変換をノードのアトリビュートからオブジェクトに適用する順序のみに関心がある場合は、MEL 『コマンド』 リファレンス オンライン マニュアルのトランスフォーム ノードに関するドキュメント(「コマンド xform」)を参照してください。

ディペンデンシー グラフに関する質問

Q:

DG で正しく処理される独自のカスタム クラス/ノードを標準クラス/ノードから派生させることはできますか?この機能は以前に言及されたことがありますが、改めてこの機能についての最新情報を確認させてください。

A:

Maya は、MObject の所有権を保持し、そのままデータとして提供します。したがって、これらのオブジェクトから独自のクラス/ノードを派生させることはできません。

また、Maya の内部ノードから派生させることもできません。たとえば、内部の回転(Revolve)ノードから派生させたいとします。回転ノードは、すべてのノードと同様に、アトリビュートのセットに対する計算関数にすぎません。このノードに変更を加えるには compute メソッドに対するソースコードが必要ですが、ソースコードは提供されていません。代わりに、ユーザ定義の新しいノード(次の質問を参照してください)を Maya の回転ノードのアトリビュートに接続し、これらの新しいノードを使用して回転ノードの入出力を変更することができます。あるいは、この場合、新しい回転ノードを作成し、それをシステム定義ノードの代わりに使用することも可能です。

Maya プロキシ オブジェクトは、派生用として明示的に設計されているオブジェクトです。Maya プロキシ オブジェクトを使用すると、新しいユーザ ノードを Maya に追加することができます。

また、関数セットから派生させて MObject 上に新しい操作を作成することもできます(MObject が不透明データであり、関数セットのメンバを実装するためのソースを使用できないということによってのみ制限されます)。

要約すると、Maya のノードから直接派生させることはできませんが、独自のノードを作成してディペンデンシー グラフに挿入し、新しいノードを既存の Maya ノードと置き換えるか、または新しいノードを使用して Maya ノードの入出力パラメータを変更することができます。

Q:

ユーザ定義ノードは既存のノードと基本的に異なっているように思われます。たとえば、MFnNurbsCurve は MFnBase から派生していますが、円のサンプルでは、「円」は MPxNode から派生しています。

A:

「関数」セットと「Maya オブジェクト」の間には根本的な違いがあります。Maya 内部オブジェクト(ディペンデンシー ノードを含んでいます)は MObjects と関数セットでカプセル化され、MFnBase から派生し、MObject と「一緒に」初期化されて MObject で動作します。これは、ユーザ記述コードが Maya オブジェクトの内部に作用できる「外部 -> 内部」のようなパラダイムです。

ユーザ定義のディペンデンシーノードを作成する方法は全く異なります。これが、MPxNode クラスが必要な理由です。つまり、Mayaの 新しい内部ノードを作成し、そのメソッドをカスタマ生成ノードに定義されているメソッドに「接続」します。たとえば、ディペンデンシー グラフの評価中にユーザ定義ノードを「再計算」する必要がある場合、次のことが起こります。

- ディペンデンシー グラフのエバリュエータによって Maya の「内部ノード」の compute メソッドがコールされます。
- その計算関数がユーザ記述ノードの計算関数をコールして結果を取得します。
- そして、結果が渡されます。

再計算が必要なノードのすべてのアトリビュートに対してこれが繰り返されます。これは、内部にある Maya オブジェクトが、ユーザ記述の計算関数をコールする必要があるという「内部 -> 外部」のようなパラダイムです。

したがって、確かに基本的な違いはありますが、それは意図されたものであり、問題が根本的に異なるということから生じるものです。

Q:

API によって「永続的な」効果を達成できるかどうかが分かりません。CV に変更が加えられるとアーク長が変化しますが、arclen アトリビュートにアタッチされているすべてのオブジェクトをそれに応じて移動またはサイズ変更する必要があります。

A:

ディペンデンシー グラフの接続を使用して値が伝播される限り、移動やサイズ変更は自動的に行われます。たとえば、次のようになります。

MayaNodeA.cvSet > customerNode.input > CustomerNode.output (アーク長からスケールを計算します) > MayaNodeB.scale

MayaNodeA で CV が変更されると、CustomerNode と MayaNodeB で自動的に再計算が行われ、それに応じてオブジェクトが移動またはサイズ変更されます。

Q:

あるオブジェクトのアトリビュートを、別のオブジェクトで派生可能な値によって駆動したい場合があります。たとえば、オブジェクトのスケールをカーブのアーク長によって駆動させる場合や、特定のパラメトリック値で評価されたカーブ値に応じてオブジェクトを移動する場合です。サンプルには、ディペンデンシー ノードをインスタンス化する方法が示されています(複数のオブジェクトの各アトリビュートを結合できるようになります)。さらにもう一歩進んで、駆動する値を評価値とすることはできますか(myNurbsCurve.arclen()やmyNurbsCurve.point(0.5)など)?

また、独自のノードを作成する際に、float や string ではなくノードまたは MObject を入力として取り込む入力アトリビュートを作成することはできますか?このような入力アトリビュートがあれば、MFnNurbsCurve に戻り、派生されたメソッドを必要に応じて使用することが可能になります。

A:

Maya アーキテクチャでは、MFnTypedAttribute を持つノードを作成することによって、そのような入力アトリビュートを非常に簡単に構築できます。アトリビュートの宣言で、アトリビュートが入力として受け取る「型」をパラメータとして指定します。「kNurbsCurve」型を使用して nurbsCurve を取るように指定することは可能です。ノードには実際のカーブが含まれることになるので、これを基にするものはすべて計算できます。

したがって、作成するノードは、nurbsCurve 入力アトリビュートと、3つの double (scale)出力パラメータを持つことになります。あとはこのノードの入力をカーブを生成するノードに接続し、ノードの出力をアニメートしたいノードのスケール入力に接続するだけで、残りの作業はディペンデンシー グラフが行ってくれます。カーブノードで加えた変更はすべてグラフを通じて自動的に伝播され、最終オブジェクトのスケールが更新されます。arcLenNode サンプル プラグインは、これを行う方法を示します。

さらに、ノード内に実際に nurbsCurve オブジェクトが取り込まれるので、MFnNurbsCurve 関数セットをそのオブジェクトにアタッチし、そのメソッドを使用して必要な値を計算することができます。「length」関数はカーブのアーク長を計算し、「pointAtParm」メソッドは特定のパラメータ値でポイントを返します。

また、既存の複数のディペンデンシー ノードを 1 つに接続するだけで、同様の結果を得ることもできます。たとえば、subCurve ノードと curveInfo ノードを使用すると、下に示すようにサブカーブのアーク長を計算することができます。

    global float $arclen;
    // Create a curve
    curve -p -5 0 8 -p -9 0 2 -p -3 0 5 -p -6 0 -2
     -p 1 0 3 -p -4 0 -5 -p 4 0 1;
     // Create a node to extract part of a curve,
     // set the parts to keep, and attach it to
     // the curve created above.
     createNode -n subCurve1 subCurve;
     setAttr subCurve1.minValue 0.4;
     setAttr subCurve1.maxValue 1.4;
     connectAttr curveShape1.local subCurve1.inputCurve;
     // Create a curveInfo node, and connect
     // it to the output of the subcurve.
     createNode -n curveInfo1 curveInfo;
     connectAttr subCurve1.outputCurve curveInfo1.inputCurve;
     // Get the arclen of the subcurve from the
     // curveInfo node and print it.
     $arclen =`getAttr curveInfo1.arcLength`;
     print("curve[0.4:1.4] has arclen " + $arclen + "\n");
     // Change the part of the curve extracted by the
     // subCurve node and print the new arclen
     setAttr subCurve1.minValue 0.0;
     setAttr subCurve1.maxValue 5.0;
     $arclen =`getAttr curveInfo1.arcLength`;
     print("curve[0.0:5.0] has arclen " + $arclen + "\n");

Q:

DAG ノードとディペンデンシー ノードの違い、DAG ノードおよびディペンデンシー ノードと API オブジェクト クラスとの関連について詳しく教えてください。特に、MItSelectionList イテレータの getDagPath メソッドをいつ使用するかや、getDependNode をいつ使用するかは、どのように知ることができますか?

A:

DAG ノードは、オブジェクトのインスタンスがジオメトリからどのように構築されるかを表します。球を作成するときは、ジオメトリ ノード(球自体)と、球の位置やスケーリングなどを指定できるトランスフォーム ノードを作成します。1 つのジオメトリに複数のトランスフォーム ノードをアタッチすることもできます。たとえば、次のようになります。

一方、ディペンデンシー グラフには新しい要素があります。DAG ノードはすべてディペンデンシー ノードでもありますが、すべてのディペンデンシー ノードが DAG ノードであるとは限りません。たとえば、現在のアニメーションのフレーム番号を生成できる「time1」ディペンデンシー ノードがあります。サンプル プラグイン「circle.cc」と「sine.cc」によって作成される「circleNode」型と「sineNode」型は、ディペンデンシー ノードですが、DAG の一部ではありません。代わりに、複数のディペンデンシー ノードを接続することで、最終的に DAG ノードに影響を与える(したがって描画されたオブジェクトに影響を与える)ことができるダイナミックな評価グラフを作成することができます。たとえば、次のようになります。

この例で、Transform3 の x、y、z の各スケール パラメータはフレーム番号によって駆動されます。したがって、アニメーションが実行されると、球の 2 つのインスタンスが大きくなります。

次に、getDagPath や getDependNode をいつ使用するかをどのように知ることができるかという問題です。

画面上のオブジェクトをマウスで選択する場合、常にインスタンスを選択していることになります。したがって、DAG ノードが必ず使用可能になり、ユーザは getDagPath を使用しているはずです。

オブジェクトを名前で選択した場合は、DAG ノードが常に使用可能になるわけではありません。この場合は、DAG ノードを使用できるかどうかを確認する必要があります。MItSelectionList イテレータの 「itemType」メソッドは、2 つのノード型を区別する enum 型の要素を返します。その後で、必要に応じて getDagPath または getDependNode をコールすることができます。

しかしもう 1 つ重要なことは、ジオメトリの DAG ノードには変換行列がないということです。DAG ノードの変換情報は、上位のトランスフォーム ノードに依存しています。このため、3D ビューでジオメトリを選択すると、常に実際のジオメトリ ノードではなくジオメトリの上位のトランスフォーム ノードが選択されます。このため、変換ツールはすべて正しく機能します。

したがって、上図では、3D ビューで Sphere1 をクリックすると Transform2 が選択されます。

たとえば、セレクション リストで球を反復検索していて、球の CV 上で操作を実行したい場合は、最終的に Transform2 に到達します。

(getDependNode を使用して) Transform2 をディペンデンシー ノードとして取得すると、トランスフォーム ノードを取得します。このトランスフォーム ノードからは、球もその CV も見つけることはできません。さらに、(最初の図のように)オブジェクトがインスタンス化されている場合、どのインスタンスが選択されたかという情報を失います。

Transform2 を DAG ノードとして取得すると、DAG パス オブジェクトを取得します。DAG パス オブジェクトはもう少しインテリジェントです。DAG パス オブジェクトは、DAG 内のどこにトランスフォーム ノードが常駐するかを知っています。NURBS サーフェスの関数セット(またはイテレータ)に DAG パスを与えると、トランスフォーム ノードの下位にある球ノードが自動的に検索され、CV を変更できるようになります。

Q:

オブジェクトの変換行列の取得方法が理解できません。ディペンデンシー ノードと DAG パスに MFnTransform 関数セットをアタッチしようとしましたが、正しく実行されませんでした。

A:

正しい処理に近いですが、もう少し手順が必要です。これを実行するには、まずオブジェクトの DAG パス構造を取得し、次に MFnTransform 関数セットをそれにアタッチする必要があります。MFnTransform 関数セットを使用すると、MTransformationMatrix オブジェクトを取得できます。MTransformationMatrix オブジェクトにより、オブジェクトのトランスフォーム ノードを非常にたくさんの方法でアクセスし更新することができます。行列全体を返すこともこれで可能です。この問題を解決するには、次のコードを組み込みます。

     MDagPath mdagPath;
     MStatus status;
     MTransformationMatrix transform;
     MMatrix matrix;
     if ( mdagPath.hasFn(MFn::kTransform) ) {
         // Get the transform matrix via the Dag path.
         MFnTransform transformNode(mdagPath,&status);
         // Get the transform matrix via the function set
         transform = transformNode.transformation(&status);
         matrix = transform.asMatrix();
         ...
     }

Q:

リボルブ風のプラグインを作成して、カーブを取り、カーブからサーフェスを作成し、カーブが変更された場合はサーフェスを再生成させるには、どうすればよいですか?

A:

この「ヒストリ」機能を実装するには、独自の「回転」ノードを作成する必要があります。回転ノードは、(MFnTypedAttribute(arcLenNode プラグインを参照してください)を使用して)カーブを入力として取り込み、サーフェスを出力します。そこで、このノードの出力アトリビュートを、サーフェスを描画する nurbsSurface ノードの create アトリビュートに接続する必要があります。MELでアトリビュートをコネクトする一般的な方法は次のとおりです。

     createNode transform -n revolvedSurface1;
     createNode nurbsSurface -n revolvedSurfaceShape1 -p revolvedSurface1;
     createNode yourRevolveNode -n yourRevolveNode1;
     connectAttr yourRevolveNode1.outputSurface revolvedSurfaceShape1.create

simpleLoftNode サンプル プラグインは、これを行う方法を示す好例です。

Q:

Maya でデフォメーションをセットアップし、続いてプラグインから DAG ツリーを走査すると、変形されたサーフェスのトランスフォーム ノードより下位の部分に MFnNurbsSurface と解釈できる 2 つの Shape ノードが見つかります。1 つは変形されたシェイプで、もう1つは中間位置のシェイプです。どちらが中間位置か示すフラグはありますか。サーフェスが実際にはその位置にないことが分かれば、そこで操作を行う必要はなくなります。

A:

その 2 つのサーフェスは、ブーリアン型の intermediateObject アトリビュートによって区別されます。このアトリビュートの値が TRUE の場合、ノードはデフォメーション用の入力サーフェスであり、無視できます。

2 つのノードそれぞれに、このアトリビュート用のプラグを作成してアトリビュートの値をチェックし、プラグからアトリビュートの値を取得することができます。あるいは、この操作を実行する便利なルーチン、MFnDagNode 関数セットの isIntermediateObject ルーチンを使用します。

Q:

ユーザ定義のデータ型の配列を実装するために、Maya のマルチ アトリビュートをどのように使用できますか?

A:

これでは、ユーザ定義のデータ型の配列を実装できません。ユーザ定義データを処理するときに、Maya はデータの外観を考慮しません。質問のように、1 つのデータ型を作成してからマルチ プラグを使用してそのデータ型の複数のインスタンスを作成したくなりますが、マルチ プラグは DG 内の複数の接続のために設計されたものであり、データ保存のためのものではありません。したがって、これはうまくいきません。

代わりに、データの配列を実装するには、ユーザ定義のデータ型の内部に配列を作成し、blindComplexDataCmd のサンプルに示されているメソッドを使用してその配列にアクセスする必要があります。たとえば、次のようになります。

    class blindComplexData : class MPxData {
        public:
            // override methods like readBinary()....
            // define any data you want, for example, an array of integers
            int a[12];
    };

上記のユーザ定義データをダイナミック アトリビュートとして追加してから、通常の方法でそのデータにプラグをアタッチしてデータにアクセスし、getValue() を実行してデータに対するハンドルを取得します。ハンドルを取得したら、ハンドルをカスタム データ型のインスタンスへのポインタに変換します。

Q:

クラスタによって実装されている NURBS サーフェスの場合、MFnNurbsSurface 関数セットを使用してこのサーフェスの CV を移動しても変化がないように見えます。これは何故でしょうか。また、どうすれば CV を移動することができますか?

A:

クラスタがある場合、DAG 内に表示されるサーフェスのシェイプを計算するデフォーマ ネットワークが構築されます。その「最終かつ可視」のサーフェス上の CV を移動すると、そのサーフェスを作成しているのはデフォーマなので、CV はデフォーマによって元の位置に戻されます。これに対処する方法は 2 つあります。第一に、デフォーマに対する入力サーフェスである「中間オブジェクト」を見つけ、そこに CV を移動する方法です。この方法の問題点は、入力 CV を移動することによって出力サーフェス上の位置にどのような影響があるかを予測するのが困難なことです。

第二の方法は、微調整を使用することです。微調整を使用すると、デフォーマが CV の位置を決定した後で CV を移動する(調整する)ことができます。MFnNurbsSurface に微調整を処理するための特定の API はありません(Maya 1.0 でも用意されていないはずです)が、nurbsSurface ディペンデンシー ノードのアトリビュートを直接変更することによって微調整を作成できます。微調整を実行するには、次の操作を行う必要があります。

- ブーリアン型のアトリビュート「tweak」を true に設定します。
- nurbsSurface ノードに対する入力サーフェスと同じ次元の CV 配列を作成します。
- その配列内のすべての CV を [0,0,0] に初期化します。
- 調整したい CV について、CV データ構造内で対応する要素を設定し、次に setCVs を使用して配列を更新します。

ブーリアン型のアトリビュート 「relativeTweak」が true の場合、デフォーマが CV を挿入する位置からの相対位置で表される CV 配列の値を使って対応する CV を移動します。false の場合は、CV 配列の値は CV の絶対位置です。

GUI に関する質問

Q:

たとえば winopen() を使用して、プラグイン独自のグラフィックス ウィンドウを開き、Maya の他のウィンドウを妨げずにグラフィックスを実行することはできますか? できる場合、OpenGL を使用する必要がありますか? プラグインにイベント(ウィンドウでのマウスの動きなど)を処理させ、さらに Maya の他の部分と矛盾せず動作させるにはどうすればよいでしょうか。プラグインで使用でき、Maya の他のウィンドウと互換性があるイベント処理メカニズムは Maya に用意されていますか?

A:

できます。helixMotifCmd サンプル プラグイン(Windows では使用不可)は、Motif ウィンドウを開く方法を示しています。このウィンドウで OpenGL を使用することもできます。Maya によってイベント ループの制御が保持されるという制限はありますが、ウィンドウでは UI 要素のコールバックを登録する必要があるだけで、コールバックは Maya によって起動されるので、この制限は特に問題にはなりません。

問題は「本当にしたいことは何か」ということです。自分が必要とするウィンドウを作成する方法は、ほかにも数通りあるからです。

第一に、Maya の MEL スクリプト言語を使用すると、新しいウィンドウを作成し、ほとんどすべての Maya の機能を利用することができます。ダイアログボックス用のウィンドウが必要な場合、MEL でウィンドウを作成するのが、ダイアログボックスの機能を実装する簡単で最も効率的な方法です。

Maya には MPxLocatorNode という名前の新しいクラスも含まれています。この MPxLocatorNode クラスを使用すると、DAG オブジェクトを作成し、OpenGL コールによって実装された描画ルーチンを DAG オブジェクトに提供することができます。DAG オブジェクトは、レンダリングされないので、ロケータと呼ばれます。したがって、DAG オブジェクトを画面のフィードバック用に使用することはできますが、レンダリング可能なオブジェクトの作成に使用することはできません。サンプル プラグイン footPrintNode と cvColorNode は、ロケータの作成方法と使用方法の例を示します。

入力イベントに関しては、現在のところ MPxContext という名前の API クラスが用意されています。MPxContext は、入力イベントの処理に使用します。サンプル プラグイン marqueeTool、helixTool、および lassoTool は、MPxContext の実装方法を示します。

Q:

左マウス ボタンと中マウス ボタンの両方が一度に押された場合、どのように識別できますか? 今は 1 つだけが通知されているように思えます。

A:

2 つ目のボタンが押された場合、イベントは生成されませんが、最初のボタンの hold イベントのモディファイアに入れられます。たとえば、ユーザが左マウス ボタンを押したとします。ユーザが左ボタンを押したまま中マウス ボタンを押したかどうかを確認するには、MEvent::isModifierMiddleMouseButton() を使用して doHold イベントのモディファイアをチェックします。

Q:

どの 3D ウィンドウがアクティブになっているかは、どのようにして識別できますか? カメラ名を使用することを考えましたが、カメラ名が変更された場合は、この方法では識別できません。(XY、XZ、YZ ごとに)どのウィンドウが 3D ビューであるかを返す関数が必要です。

A:

これはやや困難です。どのビューも任意にタンブルしたりパースビューに変更したりできるので、この問題を普遍的に解決するには多少の作業が必要です。

M3dView::active3dView で、カメラを取得できるアクティブ ビューが提供されます。そして、ユーザが見ているビューを判別するために、MFnCamera メソッドの upDirection と rightDirection を使用して対応するベクトルを取得し、ベクトルを MVector::xAxisや MVector::xNegAxis に対して比較することができます。

Q:

M3dView の viewToWorld メソッドは、正投影ビューでは 2D 座標を 3D 座標に正確にマッピングしますが、パース ビューではカーソル引数ででたらめな値を返します。

A:

パース ビューでは、2D ビューでポイントが与えられている Near および Far クリッピング プレーン上のポイントを返すバージョンの viewToWorld を使用する必要があります。両ポイントを接続するライン セグメント上のポイントはすべて、マッピングに対する有効な解です。どのポイントを使用するかは自分で決定する必要があります。

アニメーションに関する質問

Q:

ユーザ定義ノードを使用して DG で「作成した」アニメーションがアニメーション カーブのグラフに表示されません。つまり、プロシージャによって生成されたアニメーションの結果を確認する方法がありません。

アニメーション カーブのグラフには、キーフレーム アニメーションによって生成されたアニメーションしか表示されません。

A:

残念ながら、これは変更できません。キーフレーム アニメーションの場合、アトリビュートに接続されているノードが animCurve ノードかどうかをチェックするだけで済みます。ノードが animCurve ノードであれば、キーフレーム アトリビュートの値を抽出してグラフに表示するのは非常に簡単です。

その他の種類のアニメーションでは、特定のアトリビュートに接続されているノードが time ノードであると判明した場合にそれがアニメーションであると分かるだけです。

プロシージャ アニメーションを実行するノードの場合、アニメーション全体を実際に実行し、各フレームの出力アトリビュート値を表示用に保存しなければなりません。これでは、計算量が非常に大きくなる可能性があります。また、結果のカーブは、カーブの計算方法に関する情報にはアクセスせず出力値だけを表示したものなので、結果のカーブを編集することはできません。

Q:

任意のアニメーション フレームの任意のデータを何らかの方法で検索したいと思います。OM/OA では、viewFrame (x)を実行し、その後でデータをチェックします。しかしこれは非常に時間がかかります。知りたいのは、この特殊なノードの時間 x での位置です。OM では、サブノードでワイヤフレームを実行すると、アニメーションの設定方法によっては、だいたいの場合不正確な値が返されます。

A:

Maya ではディペンデンシー グラフによって「サブノード」の情報が常に正確であることが確認されるので、この点については Maya のほうがすぐれているはずです。

OM/OA の場合とよく似た方法で必要なデータを検索することができます。つまり、viewFrame を実行して時間を設定してから、MPlug クラスの getValue メソッドを使用して必要なノード アトリビュート(tx、ty、tz など)を検索します。ただし、ビューフレームはグローバル時間を変化させるので、やはり処理速度は少し遅くなります。

Maya API では別の方法を実行できます。希望の時間に初期化された MDGContext クラスのインスタンスを作成します。このインスタンスを MPlug クラスの getValue メソッドに渡すと、必要なアトリビュートが指定の時間に評価されます。正確な結果が確実に得られるように、ちょうど必要なだけのディペンデンシー ノードが再評価されます。その他のノード上にディペンデンシー ノードがたくさんあると、評価は遅くなります。そうでなければ、評価は高速になります。

また、Maya は全キーフレームのアニメーション オブジェクトの状態を保持するわけではないので、特定の時間におけるアニメーションの状態を判別するには、上記のいずれかの方法で特定の時間のオブジェクトの状態を検索する以外に方法はありません。

Q:

MGlobal::viewFrame() を使用して時間をランダムに設定することはできますか? この場合、パーティクルと IK の各部位はすべての場合に適切に更新されますか?

A:

時間はランダムに設定することができ、すべてが常に適切に更新されます。ただし、基本メカニズムは単調な時間増加に対して高度に最適化されています。時間をランダムにジャンプすると、パフォーマンスが大きく落ちる可能性があります。

Q:

MFnMotionPath がその動作を実行する方法が分かりません。具体的には、MFnMotionPath は DAG ツリーとどのように対話するのでしょうか?たとえば、子としてシェイプをペアレント化するトランスフォーム ノードがあるとします。キーフレームを設定してトランスフォーム チャネルをアニメートすると、トランスフォーム チャネルは緑色に変わります。あるいは、MotionPath を実行することもできますが、この場合はトランスフォーム チャネルに影響はありません。

MotionPath とトランスフォーム チャンネルの両方がある場合、MotionPath がトランスフォーム チャンネルをオーバーライドし、トランスフォーム チャンネルは無視されるようです。

MotionPath が動きに影響を与えるメカニズムは何ですか?

任意の時間における値を読み取ることはできますか? できるとしたら、どのようにして読み取るのですか?

DAG ツリーを走査しているとき、オブジェクトの位置はどのように識別できますか?

A:

キーフレームはアニメーション カーブのディペンデンシー ノードによって提供され、同様に、モーション パスはディペンデンシー ノードとして実装されます。これらのノードが機能するのは、ノードが DAG 内で親であるトランスフォーム ノードの変換アトリビュートに接続されているからです。トランスフォーム ノードで値が必要なときは、アニメーション カーブ ノードまたはモーション パス ノードから値を取得します。

したがって、モーション パス ノードを接続すると、アニメーション カーブ ノードは切断されて、トランスフォーム ノードはアニメーション カーブ ノードから影響を受けなくなります。トランスフォーム ノードに一度に接続できるのは 2 つのノードのうち 1 つだけで、最後に接続されたノードが優先します。

トランスフォーム ノードがアニメーション カーブノードまたはモーション パス ノードに接続されているかどうかにかかわらず、いつでもトランスフォーム ノードに変換情報を要求して、正しい値を取得することができます。

Q:

Maya でクオータニオン カーブに使用される補間システムを記述するマニュアルやサンプル コードが見つかりません。詳細についてどこを参照すればいいか教えてください。

A:

スプライン補間の場合は、次に挙げる最初の 2 つの参考図書で説明されている squad を使用します。

- Shoemake, K.『Animating Rotation with Quaternion Curves』SIGGRAPH 85 Proceedings, pp 245-254, 1985.
- Shoemake, K.『Quaternion Calculus for Animation』SIGGRAPH 91 course notes for "Math for SIGGRAPH".
- Eberly, David.『Quaternion Algebra and Calculus.』(この書籍には、squad の派生について記載されています。)
- Ramamoorthi, Ravi.『Fast Construction of Accurate Quaternion Splines.』SIGGRAPH 97 Proceedings, 1997.(この書籍には、ほかの補間手法についての説明が記載されています。)

Windows に関する質問

Q:

Windows 固有のコードをプラグインに追加する方法を教えてください。

A:

Windows 固有のコードの前に #ifdef _WIN32 を使用します。

Q:

プラグインのデバッグ方法を教えてください。

A:

プロジェクト > 設定(Project > Settings)を選択し、次にデバッグ(Debug)タブを選択します。デバッグ セッションのフィールドにある Executable で、Maya の実行ファイルへのフル パス名を入力します。たとえば、次のように入力します。

C:¥Program Files¥Autodesk¥Maya2013¥bin¥Maya.exe

F9 キーを使用して、プラグインのソースコード内のブレークポイントを切り替えます。デバッグを開始する準備ができたら、Build > Start Debug > Go を選択します。

Q:

プラグインのアプリケーション インスタンス(HINSTANCE)に対するハンドルは、どのように取得できますか?

A:

グローバル変数 MhInstPlugin にプラグインの HINSTANCE が保存されています。プラグインのインクルードファイルの標準セットを組み込んである場合は、MhInstPlugin を使用できるはずです。具体的には、この変数は MfnPlugin.h インクルード ファイルに定義されています。

Q:

次の警告メッセージが表示された場合はどうすればよいですか?

    warning C4190: ’initializePlugin’ has C-linkage specified, but returns UDT ’MStatus’ which is incompatible with C

A:

何も行う必要はありません。コンパイラで発生する警告ですが、処理は適切に行われます。この警告メッセージによる支障はありません。

Q:

変数「near」と「far」を使用するとコンパイラ エラーが発生するのは何故ですか?

A:

Microsoft コンパイラで予約されているキーワードに該当するためです。変数名を、nearClip と farClip などに変更してください。

Q:

次のエラーが発生した場合はどうすればよいですか?

    error C2065: ’uint’ : undeclared identifier

A:

Windows の UINT に相当するものです。この問題を解決するために MTypes.h に定義を追加してあります。

Q:

次のエラーが発生した場合はどうすればよいですか?

    error C2065: ’alloca’ : undeclared identifier

A:

ソース コードに次のコードを追加してください。

    #ifdef _WIN32
    #include "malloc.h"
    #endif