TOP > DF TALK > はじめてC++で書くMayaプラグイン

DF TALK

はじめてC++で書くMayaプラグイン

2012/8/13

8月はSIGGRAPH、CEDECと大きなイベントがあり、あっという間に過ぎ去りそうです。
どうも久しぶりです、開発室の齊藤です。

今回は初めてC++でMayaプラグインを書くにあたって
1つの参考になればと、プロジェクト作成からプラグインでカスタムコマンドを作成するまでの話です。

以前、Python版のプラグイン話もありましたが今回はC++です。
サンプルは多い方が良い!というのもあり書いてみました。

最小限のPlugin

最初に、Mayaへ登録のみを行う最小限のプラグイン作成を通して
プロジェクト作成からプラグインロードまでの流れを見てみましょう。

今回Maya Plug-in Wizardを使いません。プロジェクト作成から行う利点は

・Plugi-in Wizardを使うよりファイルが少なくすっきりする
・最初の設定から行うので不安感が弱くなる

こんなところです。また、今回の最小限のプラグインプロジェクトを作成すれば
今後のプラグインを作成する場合のテンプレートになります。

環境

今回の環境は以下を前提としています

OS: Windows7 64bit
Maya: 2012
VisualStudio: 2008 Standard

VisualStudio “Express”の場合、デフォルトは64bit向けのコンパイルは出来ません。
この話については今回割愛させて頂きます。

プロジェクト作成

さっそくプロジェクトを作成しましょう

1. ファイル -> 新規作成 -> プロジェクト

2. プロジェクト作成の為の作成は以下のようになります

項目
プロジェクトの種類 Visual C++
テンプレート 空のプロジェクト
プロジェクト名 mayaBasePlugin
場所 好きな場所を指定して下さい
ソリューション名 そのまま

3. 設定が終わったら”OK”ボタンを押してプロジェクトを作成します

プロジェクト設定

1. ファイルの作成

ファイルが無いと必要な設定項目が出てこないので、先にファイルを作成します

1. ソースファイル 右クリック

2. 追加 -> 新しい項目

3. 以下の設定でファイルを作成する

項目
カテゴリ Visual C++
テンプレート C++ファイル(.cpp)
ファイル名 pluginMain
場所 変更しない

4. “追加”ボタンを押しファイルを追加します

注) ファイルの中身については後で記述するので、今は空のままで大丈夫です。

2. 64bitOS向けプラグインの設定

デフォルトのプロジェクトでは64bit向けにコンパイルする項目がないので作成します

1. ソリューションを右クリック

2. “構成マネージャ”を選択

3. “アクティブソリューション構成”のプルダウンを開く

4. “Release”を選択

5. “アクティブ ソリューション プラットフォーム”のプルダウンを開く

6. “新規作成”を選択

7. “新しいプラットフォームを入力または選択して下さい”のプルダウンを開く

8. “x64″を選択

9. “OK”ボタンを押す

10. “アクティブ ソリューション プラットフォーム”が”x64″になっていることを確認

11. “閉じる”ボタンを押す

12. プロジェクトを目的の構成とプラットフォームに設定します

3. プロジェクトをMayaプラグイン向けに設定

Mayaプラグインのビルドに向けて、8か所の設定項目を変更していきます

1. プロジェクトを右クリック

2. “プロパティ”を選択

3. “全般”をクリック

4. 以下の設定項目を変更

項目
構成の種類 ダイナミック ライブラリ(.dll)

5. “C/C++” => “全般”をクリック

6. 以下の設定項目を変更

項目
追加のインクルードディレクトリ “C:\Program Files\Autodesk\Maya2012\include”
MayaAPIのincludeディレクトリがどこにあるかを指定します。
プラグインを使用するMayaのバージョンによりここは変化します。

7. “C/C++” => “プリプロセッサ”をクリック

8. 以下の設定項目を変更

項目
プロプロセッサの定義 NDEBUG;NT_PLUGIN;REQUIRE_IOSTREAM;_WIN64
ざっくり定義した4つについて。
[NDEBUG](Cの標準マクロ)
assertマクロというものを使用しないことを表します 。
assertはデバックで用いられるもので、今回はリリース版で使用しないため定義しています。
コードに登場しないのでいらないのでは?と思うかもしれませんが、
MayaAPIのコードにNDEBUGが定義されている場合の分岐が存在しています。

[NT_PLUGIN](Mayaの仕様)
Windows向けのプラグインですよと定義しています。

[REQUIRE_IOSTREAM](Mayaの仕様)
REQUIRE_IOSTREAMが定義されている場合に、STLの入出力を使用するコードがMayaAPIに記述されています。
STLはStandardTemplateLibraryというC++の標準ライブラリの1つです。
今回のコードにSTLは登場しませんが、MayaAPIで使用しているため必要になります。

[_WIN64](Windowsの仕様)
Windowsの64ビットプラットフォームであることを定義しています。

9. “C/C++” => “コード生成”をクリック

10. 以下の項目の値になっているかを確認

項目
ランタイム ライブラリ マルチスレッド DLL(/MD)

10. “リンカ” => “全般”をクリック

11. 以下の設定項目を変更

項目
出力ファイル ./$(ConfigurationName)/$(ProjectName).mll
追加のライブラリディレクトリ “C:\Program Files\Autodesk\Maya2012\lib”
[出力ファイル]
設定したファイル名がプラグインのファイルになります
$(ConfigurationName)や$(ProjectName)はそれぞれ、構成名とプロジェクト名に展開されます。

[追加のライブラリディレクトリ]
MayaAPIのライブラリディレクトリを指定します。Includeディレクトリの指定同様に、
プラグインを使用するMayaのバージョンによって変わります

12. “リンカ” => “入力”をクリック

13. 以下の設定項目を変更

項目
追加の依存ファイル OpenMaya.lib Foundation.lib
この追加の依存ファイルは、作成するMayaプラグインの内容によって増えます
PythonでAPI使う場合でも、OpenMaya,OpenMayaUIと分かれているのと同じく
使うMayaAPIの種類によってライブラリが分かれています。
詳しく知りたい方は、
"Autodesk社MayaAPIガイドとリファレンス"のAPIガイド=>MayaAPIの基礎知識のライブラリ"
を参照をお願いします

14. “リンカ” => “コマンドライン” をクリック

15. 以下の設定項目を変更

項目
追加のオプション /export:initializePlugin /export:uninitializePlugin

コード

これで準備が出来ました。
実際にコードを作成していきます。 pluginMain.cppファイルに以下のコードを書きます

#include <maya/MFnPlugin.h>

// プラグインロード時に呼出される
MStatus initializePlugin(MObject obj)
{
    // プラグイン情報登録
    // (MObject, 作成者, Pluginバージョン, 要求するMayaAPIバージョンがあれば記載)
    MFnPlugin plugin(obj, "DigitalFrontier.Inc", "1.0", "any" );
    return MStatus::kSuccess;
}

// プラグインアンロード時に呼出される
MStatus uninitializePlugin( MObject obj )
{
    MFnPlugin plugin( obj );
    return MStatus::kSuccess;
}

これがほぼ最小限のMayaプラグインのコードになります。
ここで重要なのは下記の2点です。

・最低限必要な関数は2つ、initalizePlugin関数とuninitalizePlugin関数
・initalizePlugin関数内でプラグイン情報を決めている

この2つがどのように呼ばれ、使われているのかは後述します。

ビルドからロードまで

ビルドすると出力ファイルで指定した場所、今回は”Projectディレクトリ/構成名/プロジェクト名.mllが出来ています
(今回はmayaBasePlugin.mll)

このファイルをMayaで正常にロード出来るかを確認します

プラグインロード、アンロード時の流れ

プラグインがロード出来た所で、ユーザがプラグインをロードした時に何が起きているのか。
その流れが下記の図になります。

MayaはロードされたプラグインのinitalizePlugin関数を呼出します。
この関数でプラグインは必要な情報をMayaに登録します。
最後に正常に処理が終わったかのステータスを返します。

次にプラグインをアンロードした時の流れが下記の図になります

Mayaはアンロード時はuninitalizePluginを呼び出します。
この関数でプラグインはinitalizePluginで登録した内容を、削除します。
ここで全ての登録した情報を削除しておかないと、再度ロードした際にクラッシュする原因になります。

ひと休憩….

HelloMayaプラグイン

実際にプラグインでコマンドを作成します。
helloMayaとMELコマンドを打つとprint文を実行するだけのコマンドです。

プロジェクト作成

せっかくなので先ほど作成したmayaBasePluginを用いて、おうちゃくにプロジェクトを作成してみましょう。

1. helloMayaCmdディレクトリを作成します。

2. helloMayaCmdディレクトリ直下に、mayaBasePluginからslnファイルとvcprojファイル、pluginMain.cppをコピーします。

3. slnファイルの名前をhelloMayaCmd.slnに、vcprojファイルの名前をhelloMayaCmd.vcprojに変更します

5. slnファイルを適当なテキストエディタで開き、下記の部分を変更し保存します

変更前:
“mayaBasePlugin”, “mayaBasePlugin\mayaBasePlugin.vcproj”,

変更後:
“helloMayaCmd”, “helloMayaCmd.vcproj”,

6. helloMayaCmd.vcprojファイルをテキストエディタで開き、下記の部分を変更し保存します

変更前:
Name=”mayaBasePlugin”
RootNamespace=”mayaBasePlugin”

変更後:
Name=”helloMayaCmd”
RootNamespace=”helloMayaCmd”

7. ソースファイルに”helloMayaCmd.cpp”, ヘッダファイルに”helloMayaCmd.h”を新規作成します

準備が完了したのでコードを書いていきます

コード

プラグインがロードしてからコマンドを実行するまでの流れは後述します

1. “helloMayaCmd.h”に以下を記述します

#include <maya/MPxCommand.h>
#ifndef HELLOMAYA_H
#define HELLOMAYA_H

class helloMayaCmd : public MPxCommand
{
public:
    // コマンド名
    static const MString cmdName;

    helloMayaCmd();
    virtual ~helloMayaCmd();      

    // コマンド実行時に呼出
    static void *creator(void);
    MStatus doIt( const MArgList& );
};

#endif

2. “helloMayaCmd.cpp”に以下を記述します

#include <maya/MGlobal.h>

#include "helloMayaCmd.h"

const MString helloMayaCmd::cmdName("helloMaya");

helloMayaCmd::helloMayaCmd(){}
helloMayaCmd::~helloMayaCmd(){}

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

MStatus helloMayaCmd::doIt(const MArgList& args)
{
    MGlobal::executeCommand("print(\"-- Hello Maya CMD --\");");
	return MStatus::kSuccess;
}
includeの<>と”"の違い:
<>で括った場合は、プロジェクトのプロパティで設定した”追加のインクルードディレクトリ”で指定した場所を調べ、次に環境変数INCLUDEを調べます

“”で括った場合は、”"が記述されているファイルがある場所をまず調べます。無い場合は<>の場合と同じ場所を調べます。

3. pluginMain.cppに以下を記述します

#include <maya/MFnPlugin.h>

// 今回のコマンドのヘッダファイルを追加
#include "helloMayaCmd.h"

// プラグインロード時に呼出される
MStatus initializePlugin(MObject obj)
{
    // プラグイン情報登録
    // (MObject, 作成者, Pluginバージョン, 要求するMayaAPIバージョンがあれば記載)
    MFnPlugin plugin(obj, "DigitalFrontier.Inc", "1.0", "any" );

    // 今回のコマンドをMayaへ登録
    plugin.registerCommand(helloMayaCmd::cmdName, helloMayaCmd::creator);

    return MStatus::kSuccess;
}

// プラグインアンロード時に呼出される
MStatus uninitializePlugin(MObject obj)
{
    MFnPlugin plugin(obj);

    // 登録したコマンドを削除
    plugin.deregisterCommand(helloMayaCmd::cmdName);

    return MStatus::kSuccess;
}

これで完了です。
pluginMain.cppで重要なのはplugin.registerCommandの部分です。
今回はコマンドを登録するのでregisterCommandを用いますが、ノードの場合はregisterNodeを用います。
何かを登録して使うのが基本になります。

ファイル構成

今回のプラグインファイルの構成は、
pluginMain.cppにはプラグインのロード、アンロードに必要な部分を記述し、
helloMayaCmd.h、helloMayaCmd.cppにはコマンドの部分を記述しています。
このように分けることで、複数のノードやコマンドからなるプラグインを作成しても、
pluginMain.cppを見ればプラグインが何を登録しているのかがわかります。
また、コマンドやノードごとにファイルが分かれていれば後で修正する際にも楽になります。
ただし、どのようにファイルを分割するかはその時の流れ、人や文化によって異なります。

ビルドと実行

では構成”Release”プラットフォームが”x64″になっていることを確認した上でビルドを行います。

出来たプラグインをロードし、MELでhelloMayaを実行すると、ヒストリーに”— Hello Maya CMD —”と表示されれば成功です。

プラグインロードから実行までの流れ

プラグインロードしてから実行されるまで、何が起きているのか。
その流れを下記の図になります。

最初にbaseMayaPluginに比べ、initalizePluginでコマンドを登録する流れが追加されます。
登録したコマンド”helloMaya”を実行すると、MayaはhelloMayaCmdをインスタンス化します。
それからdoIt関数を呼び出すようになっています。
今回はdoItだけ実装しましたが、undoの場合はundoIt関数、redoの場合はredoIt関数が呼び出されるようになっていますので
undoやredoの処理を入れる場合は、各関数を実装します。

最後に

以上がかけ足でしたが、プロジェクト作成からプラグインでカスタムコマンドを作成までです。
今となってはPythonでAPI叩けますし、C++でプラグイン書くより楽なので、
C++でプラグイン!というのも少なくなるのかもわかりません。
しかし、C++でプラグインを作る方法を知っていれば、本当に速度を求める場合、C++で書かれたサンプルを読むなど
など役に立つものだと思います。

Mayaのプラグインサンプルを見たい方は、下記の場所にあります。
C:\Program Files\Autodesk\Mayaバージョン\devkit

それではまた!


Categories: TECH TALK Tag: , ,

コメント

[...] 過去に書いた、はじめてC++で書くMayaプラグインを覚えてる方もいらっしゃるでしょうか。 もう1年半前ですね。早いです。 今回は、C++ではなくPythonを用いてプラグインを書いていこうと思います。 必要なのはMayaとコードを書くためのテキストエディタ。身軽ですね。 何かの規則に基づいてオブジェクトを変形させるのがデフォーマ。 今回は、下記2つのデフォーマを実装を通して、デフォーマ入門してみましょう [...]

コメントフォーム

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

*

PAGE TOP