MayaとLocatorとViewport2.0
2015/4/27
こんにちは。
開発部の齊藤です
今回は、カスタムLocatorのViewport2.0対応版のサンプル解説しながら
Viewport1.0とViewport2.0で何が変わったのか見ていこうと思います
Maya2015からViewportは2.0が標準となって、Maya2014まで標準だったものはLegacyという扱いになりました
何が変わったのでしょうか?
Locatorからすると、描画の流れが変わります。
Viewport2.0対応していないロケーターは、Viewport2.0上では表示されません。
それは困るので、今回は簡単なサンプルを作りながらViewport2.0対応するにはどこを変更すればいいのか見ていきます
以下の文章では、Viewport1.0と2.0を下記のように定義して記述します
- Maya2014まで標準だったViewport: Viewport1.0
- Maya2015から標準のViewport: Viewport2.0
Viewport2.0で変わる事
Viewport1.0と2.0では、大きく2つ変わります。
1つは描画の流れ、もう1つがinitializePlugin, uninitializePlugin関数の内容です
■ 描画の流れの違い
まずはじめに、描画の流れがどのように変わったのかを見ていきます
各関数の詳しいことは後述します。
まずはViewport1.0の、Locator作成から描画までの流れです
次にViewport2.0の描画です
Viewport2.0では、新たにMUserDataクラスとMPxDrawOverrideクラスの2つが加わります
MPxDrawOverrideクラスは描画の処理担当し、MUserDataは描画に必要な情報を格納します
描画の処理は、Viewport1.0ではMPxLocatorのdraw関数1つで処理が終わっていました。
しかしViewport2.0では、描画の事前準備をMPxDrawOverrideのprepareForDraw関数が、実行を同クラスのdraw関数と2つに分かれます
Viewport1.0と2.0では描画する流れが違う為、両方に処理を書く必要があります。
■ initializePlugin, uninitializePluginの違い
次に、initializePluginの登録です。
下記がViewport1.0のコードです
plugin.registerNode(ノードのタイプ名, ノードのID, ノード作成関数, ノード初期化関数, MPxNode::kLocatorNode);
次に、Viewport2.0対応のコードです
plugin.registerNode(ノードのタイプ名, ノードのID, ノードを作成する関数, ノードを初期化する関数, MPxNode::kLocatorNode, Viewport2.0用の分類文字列); MHWRender::MDrawRegistry::registerDrawOverrideCreator(Viewport2.0用の分類文字列, Viewport2.0用の描画クラスを示す固有ID文字列, Viewport2.0用の描画クラスを作成する関数);
Locatorを登録するregisterNodeの引数に、”Viewport2.0用の分類文字列”が追加され、
Viewport2.0用の描画クラスの登録が別途必要になります
“Viewport2.0用の分類文字列”によって、ロケータと描画は紐付けられ、Viewport2.0描画時にはこの登録した描画クラスが呼ばれるようになります
登録するものが増えるということは、アンロード時に削除する処理も増える
ということで、uninitializePlugin関数の内容も変わります
viewport1.0の時はLocatorのみ
plugin.deregisterNode(ノードのID);
viewport2.0では、登録した描画クラスも消す必要があります
MHWRender::MDrawRegistry::deregisterDrawOverrideCreator(Viewport2.0用の分類文字列, Viewport2.0用の描画クラスを示す固有ID文字列); plugin.deregisterNode(ノードのID);
サンプルコード
■ 準備
コードは下記をダウンロードして下さい
-
dfCustomLocator.zip
- zipファイルの中に、VisualStudio2010のプロジェクトファイルとソースコードが入っています
■ ビルド
- dfCustomLocator.slnを開きます
- Configurationを目的のMayaのバージョンに合わせます
- ビルド
- Mayaバージョン/dfCustomLocator.mll が出来ていることを確認します
(Maya2015なら、Maya2015/dfCustomLocator.mll)
■ 動かしてみる
- 環境変数: MAYA_PLUG_IN_PATHをビルドした、mllがある場所に設定する
- Mayaを起動
- scriptEditorで、下記のMELを実行する
loadPlugin "dfCustomLocator"; createNode "DFCustomLocator";
Viewport1.0と2.0両方とも描画されれば大丈夫です
Wireframe, Shade mode描画で、表示が切り替わります
解説
プラグインがロードされてからの流れに沿って見ていきます
■ initializePlugin
まずは登録をプラグインをロードした時に呼ばれる部分です
MStatus initializePlugin(MObject obj) { /* プラグインロード時に呼ばれる関数 */ MStatus status; // プラグイン情報 MFnPlugin plugin(obj, "DigitalFrontier.Inc", "1.0", "DigitalFrontier.Inc" ); status = plugin.registerNode(DFCustomLocator::typeName, DFCustomLocator::typeId, DFCustomLocator::creator, DFCustomLocator::initialize, MPxNode::kLocatorNode, &DFCustomLocator::drawDbClassification); if(!status){ std::cout << "Failed registerNode" << std::endl; return status; } status = MHWRender::MDrawRegistry::registerDrawOverrideCreator(DFCustomLocator::drawDbClassification, DFCustomLocator::drawRegistrantId, LocatorDrawOverride::Creator); if(!status){ std::cout << "Failed registerDrawOverride" << std::endl; return status; } return status; }
DFCustomLocator固有の情報として下記があります
- ノードタイプ名: typeName
- ノードID: typeId
- Viewport2.0用の分類文字列: drawDbClassification
- Viewport2.0用の描画クラス登録ID: drawRegistrantId
それぞれ下記のように定義しています
MTypeId DFCustomLocator::typeId(0x70200); MString DFCustomLocator::typeName("DFCustomLocator"); MString DFCustomLocator::drawDbClassification("drawdb/geometry/DFCustomLocator"); MString DFCustomLocator::drawRegistrantId("DFCustomLocatorNodePlugin");
重要なのが、分類文字列と呼ばれるものを定義しているdrawDbClassification変数です。Locator登録とDrawOverride登録の2箇所で登場しています。
前述の通り、これによりLocatorとDrawOverrideは紐付けられ、Locator描画時に同じ文字列を持ったDrawOverrideが使われるようになります。
この分類文字列は必ず、”drawdb/geometry/”から始まる必要があります。
■ DrawOverrideのCreator, supportedDrawAPIs, boundingBox
MHWRender::MPxDrawOverride* LocatorDrawOverride::Creator(const MObject& obj) { /* Viewport2.0描画の初回に呼ばれる */ return new LocatorDrawOverride(obj); }
CreatorはViewport2.0描画の初回のみ呼ばれる関数です
素直にCreatorの名前の通り、LocatorDrawOverrideをインスタンスして返します
MHWRender::DrawAPI LocatorDrawOverride::supportedDrawAPIs() const { /* サポートするAPIを返す */ // 今回はOpenGLのみ対応なのでOpenGLのみを返す return MHWRender::kOpenGL; }
supportedDrawAPIsは、LocatorDrawOverrideがサポートAPIを返します
OpenGLなのか、DirectXなのか。Viewportの描画設定は、設定で変更可能です
今回はOpenGLのみ対応なのでkOpenGLで返していますので、DirectX描画に変更すると描画されなくなります
MBoundingBox LocatorDrawOverride::boundingBox( const MDagPath& objPath, const MDagPath& cameraPath) const { /* BoundingBoxを返す このBoundingBoxを用いて、描画するかしないかを決める */ return MBoundingBox(MPoint(-1.0f, -1.0f, -1.0f), MPoint(1.0f, 1.0f, 1.0f)); }
BoundingBoxのサイズを返します。描画するかしないかに用いられます。
描画サイズが変わるようなものの場合、ここを工夫してあげる必要があります
■ prepareForDraw
いよいよ、描画の前に呼ばれるprepareForDraw関数です。
MUserData* LocatorDrawOverride::prepareForDraw(const MDagPath& objPath, const MDagPath& cameraPath, const MHWRender::MFrameContext& frameContext, MUserData* oldData) { LocatorDrawUserData* draw_data = dynamic_cast(oldData); if(draw_data == NULL) { draw_data = new LocatorDrawUserData(); } // ワイヤーフレーム表示かどうか draw_data->is_wireframe = frameContext.getDisplayStyle() & MHWRender::MDrawContext::kWireFrame; // ロケータは選択されているかどうか const MHWRender::DisplayStatus displayStatus = MHWRender::MGeometryUtilities::displayStatus(objPath); draw_data->is_selected = MHWRender::kActive == displayStatus || MHWRender::kLead == displayStatus; return draw_data; }
Viewport1.0との違いが、この描画の準備と実行が分かれていることです
Viewport2.0では、描画メソッドでDGからデータを取得することは、Mayaが不安定になるという理由からここを分けています。
描画に必要なデータは、MUserDataを継承したクラスに格納して返します
この返したデータがそのまま、draw関数の引数に渡されます
関数の引数にあるMUserDataは、前回のMUserDataです
ただ初回は、前回の画無い為、新しく作ってあげる必要が有ります
今回はこの関数内で描画に必要な情報として、
ワイヤーフレーム表示状態か否かと、ロケータが選択されているか否かを判別し、それぞれ変数に入れています
判別に使うのも、Viewport2.0では1.0と違い、M3dViewを用いずMHWRenderを用いて判別します
■ draw , display
いよいよ実際の描画処理です
void LocatorDrawOverride::draw(const MHWRender::MDrawContext& context, const MUserData* userdata) { MStatus status; const LocatorDrawUserData* draw_data = dynamic_cast(userdata); const MMatrix transform = context.getMatrix(MHWRender::MFrameContext::kWorldViewMtx, &status); if (status != MStatus::kSuccess) { return; } const MMatrix projection = context.getMatrix(MHWRender::MFrameContext::kProjectionMtx, &status); if (status != MStatus::kSuccess) { return; } glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadMatrixd(transform.matrix[0]); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadMatrixd(projection.matrix[0]); glPushAttrib(GL_ALL_ATTRIB_BITS); DFCustomLocator::display(draw_data); glPopAttrib(); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); }
Viewport1.0と違いは、
- M3dViewのbeginGLとengGLが無い
- WorldMatrixとProjectionMatrix両方とも取得してセットしてあげる必要がある
というところです。
今回Viewport1.0と両立する為、drawにはViewport2.0で描画するのに必要な準備を書き
実際の描画は、DFCusotmeLocator::display関数内に書いています
こうすることで、Viewport1.0の時はViewport1.0で描画をするのに必要な処理を書いて
DFCusotmeLocator::display関数内を呼び出すことで、目的の描画が出来ます。
最後に
駆け足でしたが、なんとなくViewport2.0におけるLocator描画の流れを掴めましたでしょうか。
今回は、OpenGLのみの対応で書きましたが、DirectXでの書き方も知りたい場合はdevkitのfootPrintNodeプラグインに方法が書かれています
これがLocatorを書いたり、サンプルを読み解く一助になればと思います
ではまた。
※免責事項※
本記事内で公開している全ての手法・コードの有用性、安全性について、当方は一切の保証を与えるものではありません。
これらのコードを使用したことによって引き起こる直接的、間接的な損害に対し、当方は一切責任を負うものではありません。
自己責任でご使用ください。
コメント
コメントフォーム