Open Dynamics Engine 入門
【1日目】球の描画と衝突判定
Open Dynamics Engine の利用方法
ODEのインストールから、コンパイル方法は、次のページに非常に詳しく紹介されていますので参照ください。
2. インストールと開発/demura.net: ロボットの開発と教育
球の描画と衝突判定
ボールを空から落下させるプログラムです。
プログラムソース
以下のプログラムソースは、demura.net: ロボットの開発と教育にて公開されているソースを利用させていただいております。
#include <ode/ode.h>
#include <drawstuff/drawstuff.h>
#include <time.h>
#ifdef dDOUBLE
#define dsDrawSphere dsDrawSphereD
#endif
int WindowWidth = 352; //ウィンドウの幅
int WindowHeight = 288; //ウィンドウの高さ
static dWorldID world; //ODE世界のID
static dSpaceID space; //衝突空間のID(同じ衝突空間内のオブジェクトのみが衝突する)
static dGeomID ground;//地面との衝突判定のID
static dJointGroupID contactgroup; //ジョイントグループID
dsFunctions fn;
const int N = 200;//描画する球の数
const dReal radius = 0.2;//球の半径
const dReal mass = 1.0;//球の質量
typedef struct {//球の構造体
dBodyID body; //動力学計算用のボディ
dGeomID geom; //衝突計算用のジオメトリ
} MyObject;
MyObject ball[N];//球を描画するための配列
static void nearCallback(void *data, dGeomID o1, dGeomID o2)//2つのオブジェクトに衝突可能性がある場合に呼び出される
{
const int N = 10;
dContact contact[N];
//地面との衝突であるかどうかの判定
int isGround = ((ground == o1) || (ground == o2));
//衝突の判定を実際に行う関数 3つ目の引数に衝突の情報を格納する
int n = dCollide(o1,o2,N,&contact[0].geom,sizeof(dContact));
//if (isGround) {//もし地面との衝突だけを考える場合にはコメントアウトをはずす
for (int i = 0; i < n; i++) {
contact[i].surface.mode = dContactBounce; //衝突モード
contact[i].surface.mu = dInfinity; // 摩擦係数
contact[i].surface.bounce = 0.5; // 反発係数(0 ~ 1)
contact[i].surface.bounce_vel = 0.0; // 最低反射速度
dJointID c = dJointCreateContact(world,contactgroup,&contact[i]);
dJointAttach (c,dGeomGetBody(contact[i].geom.g1),dGeomGetBody(contact[i].geom.g2));
}
//}
}
static void simLoop (int pause)//無限ループ
{
const dReal *pos,*R;
dSpaceCollide(space,0,&nearCallback); //衝突判定用の関数
dWorldStep(world, 0.01);//世界の時間を進める
dJointGroupEmpty(contactgroup);//ジョイントグループを初期化
//球の描画を行う
for(int i=0;i<N;i++){
dsSetColor(1.0, 0.0, 0.0); //オブジェクトの色の設定
pos = dBodyGetPosition(ball[i].body); //オブジェクトの位置情報(posは配列)
R = dBodyGetRotation(ball[i].body); //オブジェクトの回転情報(Rは配列)
dsDrawSphere(pos,R,radius); //オブジェクトの描画
}
}
void start() //初期設定
{
static float xyz[3] = {0.0,-3.0,1.0}; //カメラの位置
static float hpr[3] = {90.0,0.0,0.0}; //カメラの方向
//(z軸, y軸, x軸)における回転角度({0.0,0.0,0.0}でx軸方向を向いている)
//上の場合は、y軸の方向を向いている
dsSetViewpoint (xyz,hpr); //カメラの設定
}
void prepDrawStuff() { //描画の初期化
fn.version = DS_VERSION;
fn.start = &start;
fn.step = &simLoop;
fn.command = NULL;
fn.stop = NULL;
fn.path_to_textures = "C:/ode-0.11.1/drawstuff/textures";
}
//いよいよメイン関数
int main (int argc, char *argv[])
{
srand(time(NULL)); //rand()の種
dReal x0 = 0.0, y0 = 0.0, z0 = 3.0;
dMass m1;
prepDrawStuff();
dInitODE();
world = dWorldCreate(); //世界の誕生(IDをworldに代入)
space = dHashSpaceCreate(0); //衝突空間の生成(IDをspaceに代入)
contactgroup = dJointGroupCreate(0); //ジョイント空間の生成(IDをcontactgroupに代入)
dWorldSetGravity(world,0,0,-0.5);//世界の重力加速度を指定
//地面との衝突判定用ジオメトリの生成(描画なし)
ground = dCreatePlane(space,0,0,1,0);
//ボールの生成
for(int i=0;i<N;i++){
ball[i].body = dBodyCreate(world); //動力学計算用ボディのIDを取得
dMassSetZero(&m1); //基本形状構造体の初期化
dMassSetSphereTotal(&m1, mass ,radius);//基本形状構造体に重心、慣性テンソルのパラメータをセットする(この場合は球)
dBodySetMass(ball[i].body,&m1); //生成したオブジェクトと基本形状構造体とリンクさせる
//生成したオブジェクトの初期位置を設定
dBodySetPosition(ball[i].body, x0+ double(rand()-rand())/double(RAND_MAX*10) , y0 + double(rand()-rand())/double(RAND_MAX*10) , z0 + double(i)/2.0);
ball[i].geom = dCreateSphere(space,radius); //衝突計算用ジオメトリのIDを取得
dGeomSetBody(ball[i].geom,ball[i].body); //衝突計算用ジオメトリのIDと動力学計算用ボディのIDをリンク
}
dsSimulationLoop (argc,argv,WindowWidth, WindowHeight,&fn); //シミュレーション用の無限ループ
dWorldDestroy (world);
dCloseODE();
return 0;
}
参考ページ
■demura.net: ロボットの開発と教育(出村 公成 氏)
■ODE (Open Dynamics Engine) プログラミング解説(Crystal-Creation 氏)




