2015年8月1日土曜日

カードタワー ode  コードの解析 多分間違いだらけ 素人がしたもので...

20110212-2.gif
 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 件のコメント:

コメントを投稿

About

参加ユーザー

連絡フォーム

名前

メール *

メッセージ *

ページ

Featured Posts