夏休みに入り時間ができたので,ODE初級講座の4回目を開講します.今回はODE(Open Dynamics Engine)の3Dグラフィクスについて学びます.ODEのデモを実行すると棚引く雲や地面のテクスチャの質が良く,シミュレーションの見栄えが良いですね.これは,ODEにもれなく付いてくる3Dグラフィクスライブラリdrawstuffの仕業です.ODEではdrawstuffは単なるデモプログラム表示用という位置づけなので簡単な機能しかありませんし,ポリゴンの描画も遅いのでdrawstuffで不十分な場合は他のライブラリを使うと良いでしょう.Windowsの他にLinuxでも利用したいならirrlichtやOgre3Dが候補に上ると思います.
drawstuffはOpenGLと呼ばれる3Dグラフィクスライブラリを使って書かれており,とてもシンプルなライブラリです.簡単なのでOpenGLの勉強にもピッタリです.なお,drawstuffに関するAPI(関数)は小文字のdsで始まり,ODEに関するAPIは小文字のdで始まります.
drawstuffの使い方
- ヘッダファイルをインクルードする
[code] #include <drawstuff/drawstuff.h> [/code]
- 設 定
drawstuffを使うためには下のdsFunctions構造体の各メンバを設定しなければなりません.
// drawstuffで使われるdsFunctions構造体の定義
typedef struct dsFunctions
{
int version; /* DS_VERSIONを入れる */
void (*start)(); /* シミュレーションループが始まる前に呼び出される */
void (*step) (int pause); /* シミュレーションループ毎呼び出される */
void (*command) (int cmd); /* キーが押されると呼び出される */
void (*stop)(); /* シミュレーションループの後に呼び出される */
const char *path_to_textures; /* テクスチャへのパス */
}
dsFunctions;
-
- バージョン: DS_VERSIONを代入する
- fn.version = DS_VERSION
- 前処理関数: シミュレーションループが始まる前に呼び出されます.ここで,カメラの視点と視線を設定するのが一般的です.
- fn.start = &start;
- カメラの視点,視線
dsSetViewpoint(視点,視線);
- シミュレーションループ関数:シミュレーションの各ループで毎回呼び出されます.この関数に動力学計算,衝突検出計算や描画に関する処理を書きます.サンプルプログラムでは描画に関する処理だけを書いています.
- fn.step = &simLoop;
- 描画に関するAPIを書く
- キー処理関数: キーが押される度に呼び出されます.必要がない場合はNULL(ヌルポインタ)を代入します.NULLは0番地を指す特別なポインタでしたね.
- fn.command = &command;
- 後処理関数: シミュレーションループが終わった後に呼び出されます.必要がない場合はNULL(ヌルポインタ)を代入します.
- fn.stop = &stop;
- テクスチャのパス
- fn.path_to_textures = “パス”;
- バージョン: DS_VERSIONを代入する
- 実 行
- 以下のAPIを一度だけ呼び出すと描画されます.内部はwhileループになっています.dsSimulationLoop(argc, argv, witdh, height, &fn);
- argc, argv: main関数の引数
- width: ウインドウの幅
- height: ウインドウの高さ
- fn: ドロースタッフ構造体dsFunctionsの変数
- 以下のAPIを一度だけ呼び出すと描画されます.内部はwhileループになっています.dsSimulationLoop(argc, argv, witdh, height, &fn);
サンプルプログラム
/* ODE tutorial by Kosei Demura */
/* Lesson 4 3D Graphics */
// 球,円柱,カプセル,ボックス,線を描画するプログラム
// 衝突検出,動力学計算はしていません.
#include <ode/ode.h>
#include <drawstuff/drawstuff.h>
#ifdef _MSC_VER
#pragma warning(disable:4244 4305) // VC++用警告を止める
#endif
#ifdef dDOUBLE
#define dsDrawBox dsDrawBoxD
#define dsDrawSphere dsDrawSphereD
#define dsDrawCylinder dsDrawCylinderD
#define dsDrawCapsule dsDrawCapsuleD
#define dsDrawLine dsDrawLineD
#endif
#define DENSITY (5.0) // 密度
struct MyObject {
dBodyID body; // ボディ(剛体)
};
dReal radius = 0.25; // 半径
dReal length = 1.0; // 長さ
dReal sides[3] = {0.5,0.5,1.0}; // 辺の長さ
static dWorldID world; // 動力学計算ワールド
static MyObject sphere, box, capsule, cylinder; // 物体
// start simulation
static void start()
{
static float xyz[3] = {5,3,0.5}; // 視点[m]
static float hpr[3] = {-180, 0, 0}; // 視線[°]
dsSetViewpoint (xyz,hpr); // 視点と視線の設定
}
// シミュレーションループ
static void simLoop (int pause)
{
const dReal *pos1,*R1,*pos2,*R2,*pos3,*R3; // 球の描画
dsSetColor(1,0,0); // 色の設定 (赤,緑,青)各成分は0から1
dsSetSphereQuality(3); // 球の質を高める
pos1 = dBodyGetPosition(sphere.body); // 位置の取得
R1 = dBodyGetRotation(sphere.body); // 姿勢(回転行列)の取得
dsDrawSphere(pos1,R1,radius); // 球の描画
// 円柱の描画
dsSetColorAlpha (0,1,0,1);
pos2 = dBodyGetPosition(cylinder.body);
R2 = dBodyGetRotation(cylinder.body);
dsDrawCylinder(pos2,R2,length,radius); // 円柱の描画
// カプセルの描画
dsSetColorAlpha (1,1,1,1);
pos2 = dBodyGetPosition(capsule.body);
R2 = dBodyGetRotation(capsule.body);
dsDrawCapsule(pos2,R2,length,radius); // カプセルの描画
// ボックスの描画
dsSetColorAlpha (0,0,1,1);
pos3 = dBodyGetPosition(box.body);
R3 = dBodyGetRotation(box.body);
dsDrawBox(pos3,R3,sides); // ボックスの描画
// 線の描画
dReal posA[3] = {0, 5, 0}, posB[3]={0, 5, 1.9};
dsDrawLine(posA,posB); // 線の描画
}
int main (int argc, char **argv)
{
// drawstuffの設定
dsFunctions fn;
fn.version = DS_VERSION;
fn.start = &start;
fn.step = &simLoop;
fn.command = NULL;
fn.stop = NULL;
fn.path_to_textures = "../../drawstuff/textures";
dInitODE(); // ODEの初期化
world = dWorldCreate(); // 動力学計算用ワールドの生成
dMass m; // 質量パラメータ
dMassSetZero (&m); // 質量パラメータの設定
// 球
sphere.body = dBodyCreate (world); // ボディの生成
dReal radius = 0.5; // 半径 [m]
dMassSetSphere (&m,DENSITY,radius);
// 質量パラメータの計算
dBodySetMass (sphere.body,&m); // ボディに質量パラメータを設定する
dBodySetPosition (sphere.body,0,1, 1); // ボディの位置を設定
// ボックス
box.body = dBodyCreate (world);
dMassSetBox (&m,DENSITY,sides[0],sides[1],sides[2]);
dBodySetMass (box.body,&m); dBodySetPosition (box.body,0,2,1);
// カプセル capsule.body = dBodyCreate (world);
dMassSetCapsule(&m,DENSITY,3,radius,length);
dBodySetMass (capsule.body,&m);
dBodySetPosition (capsule.body,0,4,1);
// 円柱
cylinder.body = dBodyCreate (world);
dMassSetCylinder(&m,DENSITY,3,radius,length);
dBodySetMass (cylinder.body,&m);
dBodySetPosition (cylinder.body,0,3,1);
// シミュレーションの実行
dsSimulationLoop (argc,argv,960,480,&fn);
dWorldDestroy (world); // ワールドの破壊
dCloseODE(); // ODEの終了
return 0;
}
ダウンロード
次のサンプルプログラム4をダウンロードして実行しよう。