株式会社デジタル・フロンティア-Digital Frontier

Header

Main

MayaとLocatorとViewport2.0

2015/4/27

Tag: ,,,

こんにちは。
開発部の齊藤です

今回は、カスタム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のプロジェクトファイルとソースコードが入っています

■ ビルド

  1. dfCustomLocator.slnを開きます
  2. Configurationを目的のMayaのバージョンに合わせます
  3. ビルド
  4. Mayaバージョン/dfCustomLocator.mll が出来ていることを確認します
    (Maya2015なら、Maya2015/dfCustomLocator.mll)

■ 動かしてみる

  1. 環境変数: MAYA_PLUG_IN_PATHをビルドした、mllがある場所に設定する
  2. Mayaを起動
  3. scriptEditorで、下記のMELを実行する
  4. 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を書いたり、サンプルを読み解く一助になればと思います
ではまた。


※免責事項※
本記事内で公開している全ての手法・コードの有用性、安全性について、当方は一切の保証を与えるものではありません。
これらのコードを使用したことによって引き起こる直接的、間接的な損害に対し、当方は一切責任を負うものではありません。
自己責任でご使用ください。

コメント

コメントはありません

コメントフォーム

コメントは承認制ですので、即時に反映されません。ご了承ください。

CAPTCHA


 

*