Step5ではボディ,ジオメトリ,位置,姿勢,質量,サイズなどの物体の属性をまとめて扱うために構造体を使いました.Step6では構造体の便利さを実感しましょう.
Step5のサンプルプログラムでは球1個のバウンドのシミュレーションをしましたが,ここでは9個のシミュレーションを実現します.そのためのStep5からの変更点を以下に示します.変更点はたったこれしかありません.
まず,構造体変数の変更部分です.appleをapple[NUM],つまり構造体の配列にしただけです.構造体の配列といえども恐れるにたらず普通の配列のように宣言すれば良いだけです.
#define NUM 9 MyObject apple[NUM];
main関数の変更部分です.リンゴを9個作るために,forループを9回しています.dmSphereCreate()の変更点は1番目の引数がappleからapple[i]に変わっている点です.
for (i = 0; i < NUM; i++) { p[1] += 0.5; dmSphereCreate(&apple[i],p,R,m,r,color); // リンゴの生成 }
simLoop関数の変更部分も同様です.NUM回描画するためにforループで回しています.
for (i = 0; i < NUM; i++) { dmDraw(apple[i]); // 物体の描画 }
これから構造体の配列とは関係ありません.Step5で説明を省略したコールバック関数を説明します.コールバック関数とは電話で「コールバックしてね」と同じように,必要になったら呼び出される関数です.ここでは,2つのジオメトリが衝突しそうになったらdSpaceCollide()関数により呼び出されます.つまり,ユーザーはnearCallback関数を使う時を考えなくて済むのでプログラムはとても楽です.
nearCallback関数の1番目の引数dataはdSpaceCollide()の2番目の引数です.ここではありません.2番目と3番目の引数は衝突する可能性のある2つのジオメトリです.4行目で接触点数の上限を設定しています.ポリゴンなど複雑なジオメトリを扱わない限り10で十分です.5行目のcontact[N]には接触点が格納されます.8行目のisGroundは衝突する可能性のある(つまり,まだ衝突しているとは限りません)ジオメトリのどちらかがground(地面)だったら1にセットされます.
11行目のdCollide()で接触点の情報を生成します.戻り値は接触点数です.つまり,この接触点数nが1以上だったら接触しているわけです.12行目のif文は,接触している2つの物体のうちどちらかが地面の場合だけ以下の処理をします.14行目で反発性を設定し,15行目で具体的な反発係数を設定しています.16行目のbounce_velは反発するために必要な最低速度です.ODEでは接触点がジョイント,つまり拘束と考えて計算します.そのため,接触ジョイントを作り,2つの剛体を接触ジョイントで結合しているのです.
// コールバック関数 void nearCallback(void *data, dGeomID o1, dGeomID o2) { static const int N = 10; // 接触点数の最大値 dContact contact[N]; // 接触点 // 接触している物体のどちらかが地面ならisGroundに非0をセット int isGround = ((ground == o1) || (ground == o2)); // 衝突情報の生成 nは衝突点数 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.bounce = 1.0; // 反発係数(0.0から1.0) contact[i].surface.bounce_vel = 0.0; // 反発に必要な最低速度 // 接触ジョイントの生成 dJointID c = dJointCreateContact(world,contactgroup, &contact[i]); // 接触している2つの剛体を接触ジョイントにより拘束 dJointAttach(c,dGeomGetBody(contact[i].geom.g1), dGeomGetBody(contact[i].geom.g2)); } } }
ホームワーク
- サンプルコードstep6-090717.zipをダウンロードして実行しよう.
- ドミノ倒し
- 球を細長い直方体に変更し,それを100個生成し,ドミノ倒しができるように地面の上に1列に並べる
- ヒント MyObject構造体のメンバーに直方体のサイズを表す*sidesを追加すると簡単.初期値の設定は同じメンバーである位置*pをまねること.
sidesをsides[3]としても良いが初期化の方法が違うので注意.その場合,apple[i].sides = sides;とするとエラーになります.配列はアドレスでコピーできませんでしたね.
- ヒント MyObject構造体のメンバーに直方体のサイズを表す*sidesを追加すると簡単.初期値の設定は同じメンバーである位置*pをまねること.
- 一番端のドミノに力を加えて転倒させる.ヒント ODE本101ページにある力やトルクを加えるにあるAPIを使ってください.
- ドミノ倒しを鑑賞し楽しむ!
- 球を細長い直方体に変更し,それを100個生成し,ドミノ倒しができるように地面の上に1列に並べる
コメント