http://www.natural-science.or.jp/article/20110213154150.php
デモ「カードタワー」
//カード状のオブジェクトを組み立てて、タワーを作っています。
//構造体
#include <vector>
#include <ode/ode.h>
#include <drawstuff/drawstuff.h>
int WindowWidth = 640; //ウィンドウの幅
int WindowHeight = 480; //ウィンドウの高さ
#ifndef DRAWSTUFF_TEXTURE_PATH
#define DRAWSTUFF_TEXTURE_PATH "C:/ode-0.11.1/drawstuff/textures"
#endif
#ifdef dDOUBLE
#define dsDrawBox dsDrawBoxD
#endif
static int levels = 3;
static int ncards = 0;
static dSpaceID space;
static dWorldID world;
static dJointGroupID contactgroup;
dGeomID ground;
struct Card {
dBodyID body;
dGeomID geom;
static const dReal sides[3];
Card()
{
body = dBodyCreate(world);
geom = dCreateBox(space, sides[0], sides[1], sides[2]);
dGeomSetBody(geom, body);
dGeomSetData(geom, this);
//dGeomSetData geomのデータをセットしているみただが、
//今まで使わなくてもだいじょうぶだったが
dMass mass;
//以下のコードがはいっていないがいいのか?
//dMassSetZero(&mass); // 全質量パラメータを0にする
//mass.setBox(1, sides[0], sides[1], sides[2]);
dMassSetBox(&mass, 1, sides[0], sides[1], sides[2]);
//Boxに質量を与える
//void dMassSetBox (dMass *, dReal density, dReal lx, dReal ly, dReal lz);
//void dMassSetBoxTotal (dMass *, dReal total_mass, dReal lx, dReal ly, dReal lz);
// density : 密度
// lx, ly, lz : サイズ(x, y, z)
// total_mass : 剛体全体での質量
dBodySetMass(body, &mass);
}
~Card()
{
dBodyDestroy(body);
dGeomDestroy(geom);
}
void draw() const
{
dsDrawBox(dBodyGetPosition(body),
dBodyGetRotation(body), sides);
}
};
//カードの大きさ
static const dReal cwidth=.5, cthinkness=.02, clength=1;
//static const dReal cwidth=.1, cthinkness=.1, clength=1.0;
const dReal Card::sides[3] = {cwidth, cthinkness, clength};
std::vector<Card*> cards;
// Card*(構造体のポインタ)の可変長配列の宣言
// ポインタを使わないとだめなのか?
//カードの総数を計算
int getncards(int levels)
{
return (3*levels*levels + levels) / 2;
// 1段 2段 3段 床枚数
//levels=1 2枚 (3*1*1+1)/2=2 正解 3枚 -1枚
//levels=2 7枚 (3*2*2+2)/2=7 正解 3枚+3枚*2個 -2枚
//levels=3 15枚 (3*3*3+3)/2=15正解 3枚+3枚*2個+3枚*3個 -3枚
//グーグル先生によると 1+2+3+...+n=1/2n(n+1)だから
//3*(1+2+...levels)-levels
// =3*(1/2*levels*(levels+1))-levels
// =(levels*levels+levels)*3/2-levels*2/2
// =(3*levels*levels+levels)/2
}
//カードを設置(再配置)
//地面に黄色の点が7つ 赤の点が一個 青が一個ある で真ん中の黄点が原点
//赤がx軸+方向 青がy軸+方向 上がz軸+方向
void place_cards()
{
ncards = getncards(levels);
int oldcards = cards.size();
for (int i=ncards; i<oldcards; ++i)
delete cards[i]; //メモリの開放
//最初のカードの数より少ないカード数を設定したら余分なカードの
//メモリを開放
cards.resize(ncards); //cardsのサイズ変更
//ここでresizeしているのでnewとかdeleteとかはいらないのでは?
//多分上の設定をしないと cards[i] などとできないのではないか?
//kouzoutai_test.cppでこれを省いたコードではだんまり実行となった。
for (int i=oldcards; i<ncards; ++i)
cards[i] = new Card; //メモリの確保
//最初のカードの数より多いカード数を設定したら余分なカードの
//メモリを確保
//newを使ったら必ずdeleteで開放する。
//下でcards[c]->bodyのようにアロー演算子がつかわれているが
//以下のコードのようにすぐ->で代入しようとするとエラーとなる
//その前に p=&a などと値を代入できる領域を確保しなければいけ
//ない。 cards[i]ではnewでそれをしている。
//struct cell{
// int value;
//};
//struct cell a, *p;
//p->value=10;
int c = 0;
dMatrix3 right, left, hrot; //回転行列用の変数
dReal angle = 20*M_PI/180.; //円周率 odeのほうのヘッダーファイルでインクルードされているみたい
//dRFromAxisAndAngleの引数angleの単位がラジアンだから、角度20度を
//ラジアンに変換
dRFromAxisAndAngle(right, 1, 0, 0, -angle);//x軸右回転行列の取得 rightに代入
dRFromAxisAndAngle(left, 1, 0, 0, angle); //左/回転行列の取得 leftに代入
dRFromAxisAndAngle(hrot, 1, 0, 0, 91*M_PI/180.);
//①void dRFromAxisAndAngle(dMatrix3 R, dReal ax, dReal ay, dReal
//az, dReal angle);
// 回転軸ベクトル(ax, ay, az)の回りを反時計方向にangle[rad]回転した
//ときの回転変換行列Rを取得する。
//②void dBodySetRotation(dBodyID, const dMatrix3 R);
//剛体dBodyIDの姿勢を回転行列Rに設定する。
//①と②は対になっているのだろう
//dReal eps = 0.2; //余スペース
dReal eps = 0.05; //余スペース
//dReal vstep = 2.0;
//dReal hstep = 0.5;
dReal vstep = cos(angle)*clength + eps; //横方向への移動幅 0.9896926...
dReal hstep = sin(angle)*clength + eps; //縦方向への移動幅 0.3920201....
//clengthはカードの縦の長さ それをカードを20度に傾けて積んだら横方向の幅はvstepとなる
//hstepも同様の考え方
//注意 原文ではcosを横(y軸方向)sinを縦(z軸方向)とかいてある
//がz軸垂直方向からの回転角みたいなので、縦横の表示が逆と思う。こ
//の結論にほぼ1日かかる。はじめは?????なんでだったが。
//以下では->でbodyに値を代入しているがbodyは構造体Cardの中のクラスCard
//の中の関数なのでこんな形式でアクセスできるのが不思議
//Cardのオブジェクトを作成しなくてもいいのか?
//std::vector<Card*> cards; から cards.resize(ncards) .. newコマ
//ンドまでの一連のコードが構造体Cardにも、その中のコンストラクタCard
//にも、オブジェクトを作成するのに作用しているみたい。
for (int lvl=0; lvl<levels; ++lvl){ //各階層ごとにカードを配置
int n = (levels-lvl);
dReal height = (lvl)*vstep + vstep/2;
//カードの位置をレベル(階層)があがるごとにvstepプラスするん
//だけれどカードの位置はvstep/2の引数となるかな。
for (int i=0; i<2*n; ++i, ++c){ //cはカードの数までずーと++か?
dBodySetPosition(cards[c]->body,//カードの位置を設定
0, //x座標
-n*hstep + hstep*i, //y座標 なんでここで横方向と縦方向の移動パラメ
//ータがx座標とz座標でいれかわっているのか
//上の結論のごとくok
height //z座標
);
if (i%2)
dBodySetRotation(cards[c]->body, left);
else
dBodySetRotation(cards[c]->body, right);
//どうしても カードの位置の決定の仕方が不明
//いや 以下でいいのか
//levels=3とすると
//for (int lvl=0; lvl<levels; ++lvl) でlvl=0~2に変化
//だから int n = (levels-lvl) は lvl=0~2に変化するなら
//各lvlの時
// lvl=0 n=3 height=0*0.98+0.98/2=0.49
// for (int i=0; i<2*n; ++i, ++c)
// i=0~5
// -n*hstep+hstep*i -3*hstep~2*hstep
// lvl=1 n=2 height=1*0.98+0.98/2=1.47
// for (int i=0; i<2*n; ++i, ++c)
// i=0~3
// -n*hstep+hstep*i -2*hstep~1*hstep
//
// lvl=2 n=1 height=2*0.98+0.98/2=2.45
// for (int i=0; i<2*n; ++i, ++c)
// i=0~1
// -n*hstep+hstep*i -1*hstep~0
//
//level=1とすると
//for (int lvl=0; lvl<levels; ++lvl) lvl=0~0
//int n = (levels-lvl); n=1
//height=1
//for (int i=0; i<2*n; ++i, ++c) i=0~1
// i=0 i=1
// -n*hstep+hstep*i -hstep 0
}
if (n==1)
break;
//n==1の時は以下のコードを実行しないと言うことか
//床のカードの座標計算
//アバウトな感じがする
for (int i=0; i<n-1; ++i, ++c){
dBodySetPosition(cards[c]->body,
0,
-(n-1 - (clength-hstep)/2)*hstep + 2*hstep*i,
height + vstep/2);
dBodySetRotation(cards[c]->body, hrot);
//levels=3とすると
//for (int lvl=0; lvl<levels; ++lvl) lvl=0~2
//int n = (levels-lvl);
// for (int i=0; i<n-1; ++i, ++c)
// lvl=0 n=3 i=0~1
// -(n-1 - (clength-hstep)/2)*hstep + 2*hstep*i
// -(2-(c-h)/2)*h
// -n*hstep + hstep*i
// -3*hstep+hstep/2
// lvl=1 n=2 i=0~0
// lvl=2 n=1 if (n==1)のコードにより実行しない
}
}
}
void start()
{
static float xyz[3] = {3.0, 0.0, 1.0}; //カメラの位置
static float hpr[3] = {-180.0, 0.0, 0.0}; //カメラの方向
// 上の場合はカメラはy軸の方向を向いている
dsSetViewpoint(xyz, hpr);
puts("Controls");
puts(" SPACE - reposition cards");
puts(" - - one less level");
puts(" - - one more level");
}
static void nearCallback(void *data, dGeomID o1, dGeomID o2)
{
dBodyID b1 = dGeomGetBody(o1);
dBodyID b2 = dGeomGetBody(o2);
const int MAX_CONTACTS = 8;
dContact contact[MAX_CONTACTS];
int numc = dCollide(o1, o2, MAX_CONTACTS,
&contact[0].geom,
sizeof(dContact));
int isGround = ((ground==o1) || (ground==o2));
for (int i=0; i<numc; i++){
contact[i].surface.mode = dContactApprox1;
if(isGround) contact[i].surface.mu =5; //カードと地面との摩擦係数
else contact[i].surface.mu = 5; //カード同士の摩擦係数
//0.1だとカードが滑ってタワーが壊れる
dJointID c = dJointCreateContact(world, contactgroup, contact+i);
dJointAttach(c, b1, b2);
}
}
void simLoop(int pause)
{
if (!pause) {
//下3行のコードはどうしてもいる。きまり文句
dSpaceCollide(space, 0, &nearCallback);
dWorldQuickStep(world, 0.01);
dJointGroupEmpty(contactgroup);
}
dsSetColor(1, 1, 0);
for(int i=0; i<ncards; ++i){
//ncards カードの総数
dsSetColor(1, dReal(i)/ncards, 0);
//1番目は赤,2番目は緑,3番目は青成分で0以上1以下の値となりま
//す.ちなみに,全部0だと黒,全部1.0だと白です.
cards[i]->draw(); //各カードを描いている
}
}
//キーボード「-」「=」で押すと、タワーの階層を「減らす」「増やす」し、
//「(スペース)」でタワーをリセットできます。
void command(int c)
{
switch(c){
case '=':
levels++;
place_cards();
break;
case '-':
levels--;
if(levels <= 0)
levels++;
place_cards();
break;
case ' ':
place_cards();
break;
}
}
int main(int argc, char **argv)
{
dInitODE();
dsFunctions fn;
//dsFunctionsではイニシャライズ関数、シミュレーションループ関数やコ
//マンドを受け付ける関数の指定を
fn.version = DS_VERSION;
fn.start = &start;
fn.step = &simLoop;
fn.command = &command;
fn.stop = 0;
fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH;
world = dWorldCreate();
dWorldSetGravity(world, 0, 0, -0.5); //世界にz方向に-0.5の重力
dWorldSetQuickStepNumIterations(world, 50);
//dWorldStep(dWorldID, dReal stepsize);で世界をすすめるとメモリをたくさん必要とするらしい。
//そこでdWorldQuickStep (dWorldID, dReal stepsize);を使うが
//早くなる分、正確さにかける。で
//dWorldSetQuickStepNumIterations(dWorldID, int num);を使うみたい
//mumでステップ毎にQuickStep方式を実行する繰り返し数の取得と設定を
//しているようだ
space = dSimpleSpaceCreate(0);
contactgroup = dJointGroupCreate(0);
ground = dCreatePlane(space, 0, 0, 1, 0);
//平面のジオメトリを作成 地面の剛体はつくらなくてもいいみたい
place_cards();
dsSimulationLoop(argc, argv, WindowWidth, WindowHeight, &fn);
////シミュレーション用の無限ループ
levels = 0;
place_cards();
//最後にlevels = 0としてplace_cards()でcards[]のメモリ全てをdelete
//で開放する
dJointGroupDestroy(contactgroup);
dWorldDestroy(world);
dSpaceDestroy(space);
dCloseODE();
}
//dWorldID world; // 動力学の世界
//dBodyID apple; // リンゴ
//dReal r = 0.2, m = 1.0; // リンゴの半径,質量
//dsFunctions fn; // ドロースタッフ用の構造体
//
//void simLoop(int pause) /*** シミュレーションループ ***/
//{
// dWorldStep(world,0.01); // シミュレーションを1ステップ進める
//
// dsSetColor(1.0,0.0,0.0); // 赤色の設定(r,g,b)
// const dReal *pos = dBodyGetPosition(apple); // リンゴの位置を取得
// const dReal *R = dBodyGetRotation(apple); // リンゴの姿勢を取得
// dsDrawSphereD(pos,R,r); // リンゴの描画
//}
//
//void start() /*** 前処理 ***/
//{
// static float xyz[3] = {3.0,0.0,1.0}; // 視点の位置
// static float hpr[3] = {-180, 0, 0}; // 視線の方向
// dsSetViewpoint(xyz,hpr); // カメラの設定
//}
//
//void setDrawStuff() /*** 描画関数の設定 ***/
//{
// fn.version = DS_VERSION; // ドロースタッフのバージョン
// fn.start = &start; // 前処理 start関数のポインタ
// fn.step = &simLoop; // simLoop関数のポインタ
// fn.path_to_textures = "../../drawstuff/textures"; // テクスチャ
//}
//
//int main(int argc, char **argv) /*** main関数 ***/
//{
// setDrawStuff(); // 描画関数の設定
// world = dWorldCreate(); // 世界の創造
// dWorldSetGravity(world,0,0,-0.2); // 重力設定
//
// apple = dBodyCreate(world); // ボールの生成
// dMass mass; // 構造体massの宣言
// dMassSetZero(&mass); // 構造体massの初期化
// dMassSetSphereTotal(&mass,m,r); // 構造体massに質量を設定
// dBodySetMass(apple,&mass); // ボールにmassを設定
// dBodySetPosition(apple, 0.0, 0.0, 2.0); // ボールの位置(x,y,z)を設定
//
// dsSimulationLoop(argc,argv,320, 240,&fn); // シミュレーションループ
// dWorldDestroy(world); // 世界の終焉
// return 0;
//}
0 件のコメント:
コメントを投稿