Open Dynamics Engine 入門
【5日目】デモ「こま」
物理シミュレータ ODE(Open Dynamics Engine)の入門編の5日目です。
ODEに付属のデモプログラムをいじって、より深く理解していきます。
ただ、今回のサンプルプログラムでは、各種のパラメータなどを設定する際に、ODEのAPIを利用せずに、それぞれの型のオブジェクトにメンバ関数を利用しています。
慣れると、VisualC++などで利用できるIntellisenseが使えるので、こちらのほうがプログラミングし易くなりそうです。
これまでの履歴
■基本形1:【1日目】球の描画と衝突判定
■基本形2:【2日目】オブジェクトのジョイント
■デモ1:【3日目】デモ「カードタワー」
■デモ2:【4日目】デモ「いもむし」
デモ「こま」
ODEで用意されているシリンダー型とカプセル型の剛体でこまを表現しています。
2つのこまの違いは、姿勢制御モードのある・なしです。
キーボードでの操作もできます。
「(スペース)」:リスタート
「A」:軸にトルクを与える(軸が傾いている時は逆効果)
「T」:衝突判定を行った結果、衝突計算が行われている場所を表示する
「1」:dWorldExportDIF関数を利用して、内部情報をdifファイルとして保存する
プログラムソース
以下のプログラムソースは、C:/ode-0.11.1/ode\demo/demo_gyroscopic.cpp を利用させていただいております。
#include <ode/ode.h>
#include <drawstuff/drawstuff.h>
int WindowWidth = 480; //ウィンドウの幅
int WindowHeight = 320; //ウィンドウの高さ
#ifndef DRAWSTUFF_TEXTURE_PATH
#define DRAWSTUFF_TEXTURE_PATH "C:/ode-0.11.1/drawstuff/textures"
#endif
#ifdef dDOUBLE
#define dsDrawBox dsDrawBoxD
#define dsDrawSphere dsDrawSphereD
#define dsDrawCylinder dsDrawCylinderD
#define dsDrawCapsule dsDrawCapsuleD
#define dsDrawConvex dsDrawConvexD
#endif
bool write_world = false;
bool show_contacts = false;
dWorld *world; //dWorld型のポインタを宣言
dBody *top1, *top2;//dBody型のポインタを宣言
dSpace *space; //dSpace型のポインタを宣言
dJointGroup contactgroup;
const dReal pinradius = 0.05;
const dReal pinlength = 1.5;
const dReal topradius = 1.0;
const dReal toplength = 0.25;
const dReal topmass = 1.0;
#define MAX_CONTACTS 4
static void nearCallback (void *data, dGeomID o1, dGeomID o2)
{
// for drawing the contact points
dMatrix3 RI; //3×3行列の変数RIを宣言
dRSetIdentity (RI); //行列RIを単位行列にセットする
const dReal ss[3] = {0.1,0.1,0.1};
int i;
dBodyID b1 = dGeomGetBody(o1);
dBodyID b2 = dGeomGetBody(o2);
dContact contact[MAX_CONTACTS];
int numc = dCollide (o1,o2,MAX_CONTACTS,&contact[0].geom,
sizeof(dContact));
for (i=0; i<numc; i++) {
contact[i].surface.mode = dContactApprox1;
contact[i].surface.mu = 2;
dJointID c = dJointCreateContact (*world,contactgroup,contact+i);
dJointAttach (c,b1,b2);
if (show_contacts)
dsDrawBox (contact[i].geom.pos, RI, ss);
}
}
// start simulation - set viewpoint
static void start()
{
static float xyz[3] = {4.777f, -2.084f, 2.18f};
static float hpr[3] = {153.0f, -14.5f, 0.0f};
dsSetViewpoint (xyz,hpr);
printf ("SPACE to reset\n");
printf ("A to tilt the tops.\n");
printf ("T to toggle showing the contact points.\n");
printf ("1 to save the current state to 'state.dif'.\n");
}
char locase (char c) //大文字を小文字に変換
{
if (c >= 'A' && c <= 'Z') return c - ('a'-'A');
else return c;
return c;
}
// called when a key pressed
static void reset();//関数のプロトタイプ宣言
static void tilt();
static void command (int cmd)
{
cmd = locase (cmd);
if (cmd == ' ')
{
reset();
}
else if (cmd == 'a') {
tilt();
}
else if (cmd == 't') {
show_contacts = !show_contacts;
}
else if (cmd == '1') {
write_world = true;
}
}
// simulation loop
static void simLoop (int pause)
{
dsSetColor (0,0,2);
space->collide(0,&nearCallback);
if (!pause)
//world->quickStep(0.02);
world->step(0.02);
if (write_world) {
FILE *f = fopen ("state.dif","wt");
if (f) {
dWorldExportDIF (*world,f,"X");
fclose (f);
}
write_world = false;
}
// remove all contact joints
dJointGroupEmpty (contactgroup);
dsSetTexture (DS_WOOD);
dsSetColor (1,0.5f,0);
dsDrawCylinder(top1->getPosition(),
top1->getRotation(),
toplength, topradius);
dsDrawCapsule(top1->getPosition(),
top1->getRotation(),
pinlength, pinradius);
dsSetColor (0.5f,1,0);
dsDrawCylinder(top2->getPosition(),
top2->getRotation(),
toplength, topradius);
dsDrawCapsule(top2->getPosition(),
top2->getRotation(),
pinlength, pinradius);
}
static void reset()
{
dMatrix3 R;
dRSetIdentity(R);
top1->setRotation(R);
top2->setRotation(R);
top1->setPosition(0.8f, -2, 2);
top2->setPosition(0.8f, 2, 2);
top1->setAngularVel(0,0,5);
top2->setAngularVel(0,0,5);
top1->setLinearVel(0,0.2f,0);
top2->setLinearVel(0,0.2f,0);
}
static void tilt()
{
top1->addTorque(0, 10, 0);
top2->addTorque(0, 10, 0);
}
int main (int argc, char **argv)
{
// setup pointers to drawstuff callback functions
dsFunctions fn;
fn.version = DS_VERSION;
fn.start = &start;
fn.step = &simLoop;
fn.command = &command;
fn.stop = 0;
fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH;
// create world
dInitODE();
world = new dWorld(); //dWorld型のオブジェクトを生成(ODEの世界を生成)
world->setGravity(0,0,-0.5f); //重力の設定
world->setCFM(1e-5f); //CFMを設定
world->setLinearDamping(0.00001f);//速度減衰率を設定
world->setAngularDamping(0.0001f);//角速度減衰率を設定
space = new dSimpleSpace(0);//dSpace型のオブジェクトを生成(衝突空間の生成)
dPlane *floor = new dPlane(*space, 0,0,1,0);//dPlane型のオブジェクトを生成(平面をspace衝突空間に生成)
top1 = new dBody(*world); //dbody型のオブジェクトを生成(動力学計算用ボディをODE世界のworldに生成)
top2 = new dBody(*world);
dMass m; //dMass型のオブジェクト m を宣言
m.setCylinderTotal(1, 3, topradius, toplength); //シリンダー型の質量をセット //void OdeMass::set_cylinder_total(float total_mass, int direction, float radius, float length);
top1->setMass(m);//dbody型のオブジェクト top1 に
top2->setMass(m);
dGeom *g1, *g2, *pin1, *pin2;//dGeom型のポインタを宣言
g1 = new dCylinder(*space, topradius, toplength); //dGeom型のポインタにシリンダーの新規アドレスをセット
g1->setBody(*top1); //ボディとジオメトリとの関連付けを行う
g2 = new dCylinder(*space, topradius, toplength);
g2->setBody(*top2);
pin1 = new dCapsule(*space, pinradius, pinlength);
pin1->setBody(*top1);
pin2 = new dCapsule(*space, pinradius, pinlength);
pin2->setBody(*top2);
top2->setGyroscopicMode(false); //姿勢制御モード
reset();
// run simulation
dsSimulationLoop (argc,argv,WindowWidth,WindowHeight,&fn);
//dWorldDestroy (world) を利用しないで、手動でオブジェクトを捨てる
delete g1;
delete g2;
delete pin1;
delete pin2;
delete floor;
contactgroup.empty();
delete top1;
delete top2;
delete space;
delete world;
dCloseODE();
}
サンプルプログラムで理解したこと
姿勢制御モード
まずはじめに、姿勢制御モードについてですが、dBody型のオブジェクトにメンバ関数setGyroscopicModeで指定しています。
top2->setGyroscopicMode(false); //姿勢制御モード
計算の安定性を確保するために、デフォルトで姿勢制御モードは「オン」になっています。 こまのシミュレーションをするのであれば、当然「オフ」にしなければいけないでしょう。
dWorld型のメンバ関数
今回のサンプルでは、これまでのようにID型の変数を宣言して、ODEのAPIでパラメータを設定することを行っていません。 dWorld型のオブジェクトを生成し、メンバ関数を利用してパラメータを設定しています。
dWorld *world; //dWorld型のオブジェクトをポインタとして生成 dSpace *space; //dSpace型のオブジェクトをポインタとして生成 world = new dWorld(); //dWorld型のオブジェクトを生成(ODEの世界を生成) world->setGravity(0,0,-0.5f); //重力の設定 world->setCFM(1e-5f); //CFMを設定 world->setLinearDamping(0.00001f);//速度減衰率を設定 world->setAngularDamping(0.0001f);//角速度減衰率を設定
dBody型のメンバ関数
dBody *top1;//dBody型のオブジェクトをポインタとして生成 top1 = new dBody(*world); //dbody型のオブジェクトを生成(動力学計算用ボディをODE世界のworldに生成) dMass m; //dMass型のオブジェクト m を宣言 m.setCylinderTotal(1, 3, topradius, toplength); //シリンダー型の質量をセット //void OdeMass::set_cylinder_total(float total_mass, int direction, float radius, float length); top1->setMass(m);//dbody型のオブジェクト top1 に質量型のオブジェクトと関連付けを行う
dGeom型のメンバ関数
dGeom *g1;//dGeom型のオブジェクトをポインタとして生成 g1 = new dCylinder(*space, topradius, toplength); //dGeom型のポインタにシリンダーの新規アドレスをセット g1->setBody(*top1); //ボディとジオメトリとの関連付けを行う
次回、このデモ「こま」を改造して遊んでみたいと思います。




