ODE講座6:衝突検出

 sample2

ODE(Open Dynamics Engine)講座の第6回目です。衝突検出機能について勉強します。今日、テレビでかわいいお化けの映画キャスパーをやっていました。前回の赤玉はキャ スパーのように大地を突き抜けていったわけですが、今回はそれを止めてご覧にいれましょう。

以下にソースコードを示します。 前回のプログラムと違うところだけコメントを入れています。APIについては概要しか説明しませんので、詳細についてはODEの本家ウェブサイトのドキュメントをご覧ください。

なお、以下のコード上に(新党?)大地が出てきます。 昔、党首の鈴木宗男に大きな会合でお会いしたことがあります。会場には数百人いましたが、全員に大きな声をかけながら握手をしたものすごいパワーを今でも覚えています。やっぱり国会議員は違うのだと。

さて、ソースコードをさっそく読みましょう。なお、前回のsample1 と全く同じstart関数は掲載していません。

 // Sample2.cpp  by  でむ                     
 #include <ode/ode.h>
#include <drawstuff/drawstuff.h>

static dWorldID world;
static dSpaceID space;  // 衝突検出用のスペース                                                   
static dGeomID  ground; //  (新党?)大地                                                  
static dJointGroupID contactgroup; // コンタクトグループ 詳細はODEマニュアルを                                       
dsFunctions fn;
const dReal   radius = 0.2,  mass   = 1.0;

// 前回は動力学計算用のbodyだけでしたが、今回は衝突検出用のgeomが加わったので

// ボールオブジェクトを構造体で定義しました。
typedef struct {                                                             
  dBodyID body;                                                                       
  dGeomID geom;     // 衝突検出用                                                      
} MyObject;                                                             
MyObject ball;      //  ボールオブジェクト  

 //  衝突検出のコールバック関数
static void nearCallback(void *data, dGeomID o1, dGeomID o2)
{
  static const int N = 4;    // 接触点数の上限は4個  staticを忘れずにつけてください.

  dContact contact[N];

  int isGround = ((ground == o1) || (ground == o2)); // 衝突する2つのうちどちらかが大地なら大地の旗を立てます。
  int n =  dCollide(o1,o2,N,&contact[0].geom,sizeof(dContact)); // nは衝突点数
  if (isGround)  { // 大地の旗が立っていたら衝突検出機能を働かせる
    for (int i = 0; i < n; i++) {
      contact[i].surface.mode = dContactBounce; // 地面の反発係数を設定
      contact[i].surface.bounce = 0.0; // (0.0~1.0)   反発係数は0から1まで

   // コンタクトジョイント生成                        
      dJointID c = dJointCreateContact(world,contactgroup,&contact[i]);
      // 接触している2つのgeometryをコンタクトジョイントで拘束
  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); // ジョイントグループを空に(詳細はODEマニュアルを)   
  dsSetColor(1.0,0.0,0.0);
  pos = dBodyGetPosition(ball.body);
  R   = dBodyGetRotation(ball.body);
  dsDrawSphereD(pos,R,radius);
}

void  prepDrawStuff() {
  fn.version = DS_VERSION;
  fn.start   = &start;
  fn.step    = &simLoop;
  fn.command = NULL;
  fn.stop    = NULL;
  fn.path_to_textures = “../../drawstuff/textures”;
}

int main (int argc, char **argv)
{
  dReal x0 = 0.0, y0 = 0.0, z0 = 2.0;
  dMass m1;

   prepDrawStuff();

  world = dWorldCreate();
  space = dHashSpaceCreate(0);
  contactgroup = dJointGroupCreate(0); // コンタクトグループの生成                                  

  dWorldSetGravity(world,0,0,-0.5);

  // Create a ground                                                           
  ground = dCreatePlane(space,0,0,1,0); // (新党?)大地の結成                                

  // Create a ball                                                             
  ball.body = dBodyCreate(world);                                   
  dMassSetZero(&m1);
  dMassSetSphereTotal(&m1,mass,radius);
  dBodySetMass(ball.body,&m1);                                     
  dBodySetPosition(ball.body, x0, y0, z0);                        

  ball.geom = dCreateSphere(space,radius); // 玉ジオメトリの生成                              
  dGeomSetBody(ball.geom,ball.body);       //  bodyにgeomをセット                              

  dsSimulationLoop (argc,argv,352,288,&fn);

  dWorldDestroy (world);
  return 0;
}

  ODEでは動力学計算と衝突検出計算とは別々に実装されています。今回は衝突検出機能が必要なので、まず、衝突検出計算用の空間spaceを作らなければ なりません。オブジェクトも動力学計算用のbody(ボディ)の他に衝突検出計算用にgeom(ジオメトリ)を設定する必要があります。MyObject 構造体ではメンバをそのよ うに定義していますね。

main関数の中で玉オブジェクトのgeomをdCreateSphere()で作り、dGeomSetBodyでbodyとgeomを関連付けていますのでオブジェクトの位置と姿勢はbodyだけで設定すればOKです。これをしないと幽体離脱現象に陥ってしまいます。

衝 突検出関数dSpaceCollideはシミュレーションの各ステップで実行されるsimLoop関数の中で呼び出されています。注意する点としては、必 ずsimLoopの一番始めで呼び出してください。これを後のほうにもっていくと玉が大地を突き抜けてしまいますよ。dSpaceCollideではコー ルバックを呼び出しています。これを説明すると長くなりすぎるので今回はこの辺にします。

では、ここからソースコードをダウンロードして実行してください。今度は大地を突き抜けないはずです。 実行の仕方は前回とほぼ同じです。sample1をsample2と読み替えてください。

課題を1つ出します。sample2を実行すると大地が好きなのかどうかよくわかりませんがボールがまったく弾みません。 この呪縛を解いてください。

では、また次回(目標10月30日)!

コメントを残す

メールアドレスが公開されることはありません。