|
|
チュートリアル
第二回 モデルを出してみよう
前回、真っ白(真っ青というべきか)なウィンドウを出すまではこぎつけましたが、あれではどこが3Dエンジンなのかわかりませんね?そこで今回は3Dプログラムらしく、モデルの一つも出してみようかと思います。
新しいお友達、3D空間の偉い人
まずは前回同様、Win32アプリケーションとして新しいプロジェクトを作りましょう。
プロジェクトを作ったら、前回作ったmain.cppをプロジェクトディレクトリにコピーして、プロジェクトに追加してください。
必要ならIrrlicht.dllもコピーしておきましょう。
ISceneManager登場
準備ができたら、main.cppに以下のコードを追加します。
/*
* モデルを出してみよう
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <irrlicht.h>
using namespace irr;
using namespace core;
using namespace video;
using namespace scene;
#pragma comment(lib, "Irrlicht.lib")
#ifdef WIN32
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main()
#endif
{
IrrlichtDevice *Device = createDevice(EDT_OPENGL, dimension2d<s32>(512, 384), 16, false, false, 0);
IVideoDriver *Driver = Device->getVideoDriver();
ISceneManager *Scene = Device->getSceneManager();
while(Device->run())
{
Driver->beginScene(true, true, SColor(0,100,100,160));
Driver->endScene();
}
Device->drop();
return 0;
}
まず、今回新たにネームスペースsceneのサービスを使うことになるので、それを宣言しておきます。
続いて、IVideoDriverに続いてISceneManagerオブジェクトを取得します。
前回、IVideoDriverは低レベルの描画を管理するクラスだという話をしましたが、
今回追加するISceneManagerは高レベルの描画を管理します。
もう少し噛み砕くと、IVideoDriverは板一枚、線一本という単位で描画を行いますが、
ISceneManagerはモデル、マップ、カメラといった単位で描画を行います。
irr::scene::ISceneManager* irr::IrrlichtDevice::getSceneManager ()
|
| IrrlichtDeviceが内包するISceneManagerオブジェクトへのポインタを返します |
どんどんNodeを入れようね
カメラがなくちゃ始まらない
3D空間を画面に表示する場合、空間のどこかにカメラが置かれており、そのカメラを覗いた風景を画面に描画する、という概念が基本になります。
Irrlichtの場合もその原則は同じですので、3D空間にカメラを配置しましょう。
以下のコードを追加してください
IrrlichtDevice *Device = createDevice(EDT_OPENGL, dimension2d<s32>(512, 384), 16, false, false, 0);
IVideoDriver *Driver = Device->getVideoDriver();
ISceneManager *Scene = Device->getSceneManager();
Scene->addCameraSceneNode(0, vector3df(0,10,-40), vector3df(0,0,0));
while(Device->run())
ISceneManagerは、内部にNodeと呼ばれる3D部品を追加していくことで空間情報を定義していきます。
ここでは、座標(0,10,-40)の位置に座標(0,0,0)の方向を向いたカメラ、というNodeを追加します。
ICameraSceneNode * irr::scene::ISceneManager::addCameraSceneNode (
ISceneNode * parent = 0,
const core::Vector3df & position = core::vector3df(0,0,0),
const core::Vector3df & lookat = core::vector3df(0,0,100),
s32 id = -1
)
|
| ISceneManagerに新しくカメラをNodeを追加します |
| ISceneNode *parent |
このカメラNodeの「親」となるNodeを指定します。特に指定しない場合は0とします。
「親」として設定されたNodeが移動するとそれにあわせてこのNodeも移動します。
カメラNodeの場合、普通は0でよいでしょう。 |
| Vector3df & position |
このカメラの3D空間中の座標位置をベクトル形式で設定します。 |
| Vector3df & lookat |
このカメラがどの方向を見ているか(どの座標を見つめているのか)をベクトル形式で指定します。 |
| s32 id |
コリジョン判定の時使いますので、詳しくはその時説明します。今はデフォルトでOKです。 |
|
モデルさんいらっしゃい
カメラを配置したら、今度は被写体の用意をします。
今回はIrrlichtのサンプルに入っているデータを流用させてもらいましょう。
Irrlichtを解凍したディレクトリないのmediaというディレクトリにサンプルデータが入っていますので、そこからsydney.md2をプロジェクトのディレクトリにコピーしてください。
拡張子md2のファイルというのはQuake2形式のキャラクタモデルファイルになります。
コピーが終わったら、以下のコードを追加しましょう
Scene->addCameraSceneNode(0, vector3df(0,10,-40), vector3df(0,0,0));
IAnimatedMesh *SydneyMesh = Scene->getMesh("sydney.md2");
IAnimatedMeshSceneNode *Sydney = Scene->addAnimatedMeshSceneNode( SydneyMesh );
while(Device->run())
Irrlichtでは、一般に3DモデルはIAnimatedMeshオブジェクトにロードします。
モデルファイルをロードしてIAnimatedMeshオブジェクトを作るサービス関数getMesh()がISceneManegerに用意されています。
IAnimatedMeshが出来上がったら、ISceneManager::addAnimatedMeshSceneNode()関数を使用してNodeとして追加します。
IAnimatedMesh * irr::scene::ISceneManager::getMesh (
const c8 * filename
)
|
モデルファイルを読み込んで、IAnimatedMeshオブジェクトを生成し、そのポインタを返します。
失敗した場合は0(NULL)を返します。 |
| c8 * filename |
読み込むべきモデルファイル名を指定します。
読込可能なファイル形式は、.obj、.3ds、.md2、.ms3d、.x、.bsp形式です(0.6.0の時点)。 |
|
IAnimatedMeshSceneNode * irr::scene::ISceneManager::addAnimatedMeshSceneNode (
IAnimatedMesh * mesh,
ISceneNode * parent = 0,
s32 id = -1,
const core::Vector3df & position = core::vector3df(0,0,0),
const core::Vector3df & rotation = core::vector3df(0,0,0),
const core::Vector3df & scale = core::vector3df(1.0f,1.0f,1.0f)
)
|
| ISceneManagerにIAnimatedMeshを新しいNodeとして追加します |
| IAnimatedMesh * mesh |
Nodeとして設定するIAnimatedMeshを指定します。 |
| ISceneNode *parent |
このNodeの「親」となるNodeを指定します。特に指定しない場合は0とします。
「親」として設定されたNodeが移動するとそれにあわせてこのNodeも移動します。
キャラクタに武器モデルを持たせたい場合に、キャラクタモデルを武器モデルの「親」として設定したりとか、そのように使います。 |
| s32 id |
コリジョン判定の時使いますので、詳しくはその時説明します。今はデフォルトでOKです。 |
| Vector3df & position |
このNodeの3D空間中の座標位置をベクトル形式で設定します。 |
| Vector3df & rotation |
このNodeがどちらを向いているのか、X,Y,Zの角度表現で指定します。 |
| Vector3df & scale |
このNodeの縮尺をX,Y,Zのベクトル表現で指定します。
(1.0f,1.0f,1.0f)で等倍になり、(0.5f,0.5f,0.5f)で半分の大きさになります。 |
|
役者は揃った
カメラと被写体が揃いましたから、いよいよ描画をしてみましょう。
以下のコードを追加してください
while(Device->run())
{
Driver->beginScene(true, true, SColor(0,100,100,160));
Scene->drawAll();
Driver->endScene();
}
beginScene()とendScene()関数の間に、ISceneManager::DrawAll()関数を記述します。
これを実行することで、ISceneManagerに属する全てのNodeの描画処理が行われます
void irr::scene::ISceneManager::drawAll ()
|
ISceneManager中の全てのNodeに関しての描画処理を行います
IVideoDriver::begenScene()とendScene()の間に記述してください |
これでひとまずはモデルの表示に必要な最低限の手続きが整いましたから、一度実行してみましょう。
DLLの準備はOKですね?

うまくいけば、画面中央に影絵状態でお姉ちゃんが表示され、アニメーションするはずです。
テクスチャでお化粧
現状の影絵モード(?)もこれはこれで味がありますが、やはりちゃんと顔やら服やら見えて欲しいですね。
というわけで、テクスチャマッピングをしましょう。
まず、mediaディレクトリからテクスチャファイル、sydney.bmpをコピーしてきてください。
コピーが終わったら以下のコードを追加しましょう。
IAnimatedMesh *SydneyMesh = Scene->getMesh("sydney.md2");
IAnimatedMeshSceneNode *SydneyNode = Scene->addAnimatedMeshSceneNode( SydneyMesh );
SydneyNode->setMaterialFlag(EMF_LIGHTING, false);
SydneyNode->setMaterialTexture( 0, Driver->getTexture("sydney.bmp") );
SydneyNode->setMD2Animation(EMAT_RUN);
while(Device->run())
まず、キャラクタが真っ黒な理由は、3D空間中に光源がどこにもないからです。光源についてはは次回以降に取り扱うことにして、今回はモデルの光源処理自体をOFFにしてしまうことで対処しましょう。
その後、テクスチャを貼り付けます。テクスチャをファイルからロードして生成するためには、IVideoDriver::getTexture()関数を使います。
void irr::scene::ISceneNode::setMaterialFlag (
video::E_MATERIAL_FLAG flag,
bool newvalue
)
|
| ISceneNodeの表面処理についての各種設定をON/OFFします |
| video::E_MATERIAL_FLAG flag |
設定する表面処理を指定します。設定できるのは以下のものです。
| EMF_WIREFRAME |
ONだとワイヤーフレーム描画になります。デフォルトOFFです。 |
| EMF_GOURAUD_SHADING |
グロー/フラットシェーディングを選択します。ONでグローになります。デフォルトONです。 |
| EMF_LIGHTING |
ONで光源計算を有効にします。デフォルトONです |
| EMF_ZBUFFER |
ONの場合Zバッファ比較を行います。デフォルトONです。 |
| EMF_ZWRITE_ENABLE |
ONだとZバッファ書込みを行います。デフォルトONです。半透明の物体を描くときはOFFにしましょう。 |
| EMF_BACK_FACE_CULLING |
ONの場合裏面を向いたポリゴンを描画しません。デフォルトONです。 |
| EMF_BILINEAR_FILTER |
ONだとテクスチャのピクセル計算にバイリニアフィルタを使用します。デフォルトONです。 |
| EMF_TRILINEAR_FILTER |
ONだとテクスチャのピクセル計算にトライリニアフィルタを使用します。これがONの場合、バイリニアフィルタより優先されます。デフォルトOFFです。 |
| EMF_FOG_ENABLE |
ONだとフォッグ処理を有効にします。デフォルトOFFです。 |
|
| bool newvalue |
この値がtrueの場合、指定した設定をONに、falseだとOFFにします。 |
|
void irr::scene::ISceneNode::setMaterialTexture (
s32 textureLayer,
video::ITexture * texture
)
|
| ISceneNodeのテクスチャに指定したITextureオブジェクトを割り当てます。 |
| s32 textureLayer |
設定するテクスチャ番号を指定します。
テクスチャ番号として有効なのは、0からMATERIAL_MAX_TEXTUREまでです。
通常は0番しか使いませんが、特殊なマッピングを行う場合に1番以降を指定する場合があります。 |
| video::ITexture |
割り当てるITextureオブジェクトのポインタを指定します。 |
|
irr::video::ITexture * irr::video::IVideoDriver::getTexture (
const c8 filename
)
|
| テクスチャファイルを読み込んで、ITextureオブジェクトを生成し、そのポインタを返します。 |
| c8 filename |
読み込むテクスチャファイル名を指定します。
読み込み可能なフォーマットは.bmp、.jpg、.tga、.pcx、.psd形式になります。 圧縮bmp及び圧縮tga形式には対応していません。 |
|
ついでに、現状はアニメーションが立ちパターンから死にパターンまで一気に再生される状態でしたが、走りパターンだけをループするように変更しましょう。Irrlichtでは、Quake2仕様に準拠したMD2ファイルであれば、selMD2Animation()関数を使用して、状態を示す定数を一発与えるだけで簡単にアニメーションパターンを指定できます。が、これはあくまでMD2形式限定ですので、普段はフレーム番号を指定してアニメーションパターンを決めるのが一般的です。これについては次回以降で取り扱います。
bool irr::scene::IAnimatedMeshSceneNode::setMD2Animation (
EMD2_ANIMATION_TYPE anim
)
|
md2形式を読み込んだIAnimatedMeshSceneNodeのアニメーションパターンを設定します。
md2形式以外を読み込んでいる場合には何も起こりません。
設定に成功したらtrueを、md2形式でない等の理由で設定に失敗したらfalseを返します。 |
| EMD2_ANIMATION_TYPE anim |
設定するアニメーションパターンを以下の中から設定します。
各パターンの意味合いについて私自身がわかってない部分が結構ありますので
md2形式について詳しい方教えてくださいorz
| EMAT_STAND |
立ち静止 |
| EMAT_RUN |
走り |
| EMAT_ATTACK |
攻撃 |
| EMAT_PAIN_A |
やられパターンA |
| EMAT_PAIN_B |
やられパターンB |
| EMAT_JUMP |
ジャンプ |
| EMAT_FLIP |
宙返り(?) |
| EMAT_SALUTE |
屈伸(?) |
| EMAT_FALLBACK |
落下(?) |
| EMAT_WAVE |
手を振る(?) |
| EMAT_POINT |
指し示す |
| EMAT_CROUCH_STAND |
しゃがみ静止 |
| EMAT_CROUCH_WALK |
しゃがみ移動 |
| EMAT_CROUCH_ATTACK |
しゃがみ攻撃 |
| EMAT_CROUCH_PAIN |
しゃがみやられ |
| EMAT_CROUCH_DEATH |
しゃがみダウン |
| EMAT_DEATH_FALLBACK |
後方ダウン |
| EMAT_DEATH_FALLFORWARD |
前方ダウン |
| EMAT_DEATH_FALLBACKSLOW |
ダウン(ゆっくり) |
| EMAT_BOOM |
倒れ(?) |
|
|
それでは実行してみましょう。うまくいけば、真っ黒じゃない(って、この服黒ですねorz)Sydney姐さんが走っているはずです。

今回はここまでにしておきましょう。ここまでのソースの最終状態を再掲しておきます。
/*
* モデルを出してみよう
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <irrlicht.h>
using namespace irr;
using namespace core;
using namespace video;
using namespace scene;
#pragma comment(lib, "Irrlicht.lib")
#ifdef WIN32
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main()
#endif
{
IrrlichtDevice *Device = createDevice(EDT_OPENGL, dimension2d<s32>(512, 384), 16, false, false, 0);
IVideoDriver *Driver = Device->getVideoDriver();
ISceneManager *Scene = Device->getSceneManager();
Scene->addCameraSceneNode(0, vector3df(0,10,-40), vector3df(0,0,0));
IAnimatedMesh *SydneyMesh = Scene->getMesh("sydney.md2");
IAnimatedMeshSceneNode *SydneyNode = Scene->addAnimatedMeshSceneNode( SydneyMesh );
SydneyNode->setMaterialFlag(EMF_LIGHTING, false);
SydneyNode->setMaterialTexture( 0, Driver->getTexture("sydney.bmp") );
SydneyNode->setMD2Animation(EMAT_RUN);
while(Device->run())
{
Driver->beginScene(true, true, SColor(0,100,100,160));
Scene->drawAll();
Driver->endScene();
}
Device->drop();
return 0;
}
サンプルと同じモデルじゃつまんねぇ、という人はこのへんからでもフリーのmd2モデルをダウンロードしてみて、差し替えてみるといいでしょう。pk3形式になっているかもしれませんが、pk3の正体は実はただのzipファイルですから、zipに対応した解凍ツールで解凍できるはずです。
もちろん自分でMD2ファイルを作っても全然かまいません。が、MD2モデルを作るのはかなり大変です。また、ボーンを持っていないので武器を持たせたりとかそういうことが苦手ですので、番外編としてXファイルの作成について解説していきたいと思います。(探せばQuake2用のモデル作成について取り扱っているサイトがあるかもしれません、てか、知ってたら教えてください)
トップページへ 第一回へ 第三回へ
|