2015年8月8日土曜日

ode いもむし

Open Dynamics Engine 入門
【4日目】デモ「いもむし」


デモ「いもむし」

 

http://www.natural-science.or.jp/article/20110213201106.php

任意のボディに力を加えることで、運動させることができます。

プログラムを考察してみる 余分なコードあり。

素人がしたもの

// 2015/08/02
// 芋虫が動く


#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
#define dsDrawSphere dsDrawSphereD
#define dsDrawCylinder dsDrawCylinderD
#define dsDrawCapsule dsDrawCapsuleD
#endif

#define NUM 10            //いもむしの長さ
#define NN 5            //いもむしの数
#define SIDE (0.2)        //
#define MASS (1.0)
#define RADIUS (0.1732f)

static dWorldID world;
static dSpaceID space;
//dGeomID ground;
static dBodyID body[NN][NUM];    //いもむしを作っている球の配列
static dJointID joint[NN][NUM-1];//上の球同士のジョイントの配列
static dJointGroupID contactgroup;
static dGeomID sphere[NN][NUM];

//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のサイズ変更
//    for (int i=oldcards; i<ncards; ++i)
//        cards[i] = new Card;                //メモリの確保
//
//    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日かかる。はじめは?????なんでだったが。
//   
//    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){
//            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 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)のコードにより実行しない
//        }
//    }
//}

static void start()
{
    static float xyz[3] = {2.1640f, -1.3079f, 1.7600f};    //カメラの位置
    static float hpr[3] = {125.5000f, -17.0000f, 0.0000f};    //カメラの方向
    // 上の場合はカメラは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];
    dContact contact;
    if (b1 && b2 && dAreConnected(b1,b2)) return;
    //dAreConnected 2つの剛体がジョイントによって接続されているとき1を
    //返して、そうで//ない場合は0を返す接続されているボディ同士は衝突
    //検出しない
    //return文でこの関数を抜ける
    contact.surface.mode = 0;
    contact.surface.mu   = 1;
    contact.surface.mu2  = 0;
    if (dCollide(o1, o2, 1, &contact.geom, sizeof(dContactGeom))){
        dJointID c = dJointCreateContact(world, contactgroup, &contact);
        dJointAttach(c, b1, b2);
    //2接触情報の作成
    //dColide()によって、衝突する可能性のある2つのジオメトリの接触情報
    //を作成できます。その接触情報は、dContactGeom構造体の配列として取
    //得できます。
    //
    //戻り値は接触点の数であり、衝突していない場合には0が返されます。
    //なおこの関数は、対象のジオメトリが属するスペースを考慮しません。
    //
    //int dCollide (
    //    dGeomID o1,            // 検査対象のジオメトリ(スペース)1
    //    dGeomID o2,            // 検査対象のジオメトリ(スペース)2
    //    int flags,             // 接触情報の作成を指示するフラグ (接触点の最大数)
    //    dContactGeom *contact, // 接触情報を格納するdContactGeom構造体の配列
    //    int skip               // dContactGeom構造体の大きさ
    //    );
   
    //3 dJointCreateContact 接触ジョイントの作成
    //4 dJointAttach 接触したボディを接触ジョイントで接続
    }

//    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)
{
    int i;
    if (!pause){
        for (int j=0; j<NN; j++){
            //NN いもむしの数 NUM いもむしの長さ
            static double angle = 0;
            angle += 0.01;
            dBodyAddForce( body[j][NUM-1], 0, 0, 5.0*( sin(angle*(j+1))+1.0) );
            //ボディに力を加える
            //力はボディごとに加算されて作用します。累積された力は、シ
            //ミュレーションのステップごとに0にクリアされます。
            //5.0*( sin(angle*(j+1))+1.0)をpythonで計算
            // >>> import math
            // >>> angle = 0
            // >>> for j in range(5):
            // ...     angle = angle + 0.01
            // ...     ans=5.0*(math.sin(angle*(j+1)+1.0))
            // ...     print ans
            // ...
            // 4.23415922309
            // 4.31202113622
            // 4.43313457225
            // 4.58401554386
            // 4.74492309678

            dSpaceCollide(space, 0, &nearCallback);
            dWorldStep(world, 0.05);
            dJointGroupEmpty(contactgroup);
        }
    }
    //オブジェクトのテクスチャを設定する.texture_number はDS_WOOD(木
    //目) やDS_NONE(テクスチャなし) などのテクスチャ定数でなければいけ
    //ない.現在のテクスチャは現在の色で塗られる
    //NN いもむしの数 いもむしごとに色分けしている
    for (int j=0; j<NN; j++){
        for (int i=0; i<NUM; i++){
            switch(j){
                case 0:
                    dsSetTexture(DS_CHECKERED);
                    break;
                case 1:
                    dsSetTexture(DS_GROUND);
                    break;
                case 2:
                    dsSetTexture(DS_SKY);
                    break;
                case 3:
                    dsSetTexture(DS_WOOD);
                    break;
                default:
                    dsSetColor(1, 1, 0);
            }
            dsDrawSphere(dBodyGetPosition(body[j][i]),
                    dBodyGetRotation(body[j][i]),
                    RADIUS);
        }
    }
    // if (!pause) {
    //     //下3行のコードはどうしてもいる。きまり文句
    //     dSpaceCollide(space, 0, &nearCallback);
    //     //衝突検出関数dSpaceCollide()を実行する。衝突が検出されると、
    //     //そのコールバック関数が呼ばれる。
    //     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)
{
    int i;
    dReal k;
    dMass m;

    dsFunctions fn;
    //dsFunctionsではイニシャライズ関数、シミュレーションループ関数やコ
    //マンドを受け付ける関数の指定を

    fn.version = DS_VERSION;
    fn.start = &start;
    fn.step = &simLoop;
    //fn.command = &command;
    fn.command = 0;
    fn.stop = 0;
    fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH;

    dInitODE();
    world = dWorldCreate();

    space = dSimpleSpaceCreate(0);
    //単純な衝突検出計算用スペースを生成し,そのID 番号を返す.
    //space = dHashSpaceCreate(0);
    //高速に計算可能なハッシュテーブルを備えた衝突検出計算用スペースの
    //生成し,そのID 番号を返す
    //どちらでも、かわらないような....
   
    dWorldSetGravity(world, 0, 0, -0.5);    //世界にz方向に-0.5の重力
    dCreatePlane(space, 0, 0, 1, 0);

    //ground = dCreatePlane(space, 0, 0, 1, 0);
    //dWorldSetQuickStepNumIterations(world, 50);
    //dWorldStep(dWorldID, dReal stepsize);で世界をすすめるとメモリをたくさん必要とするらしい。
    //そこでdWorldQuickStep (dWorldID, dReal stepsize);を使うが
    //早くなる分、正確さにかける。で
    //dWorldSetQuickStepNumIterations(dWorldID, int num);を使うみたい
    //mumでステップ毎にQuickStep方式を実行する繰り返し数の取得と設定を
    //しているようだ

    //contactgroup = dJointGroupCreate(0);
    ////平面のジオメトリを作成 地面の剛体はつくらなくてもいいみたい

    //place_cards();
   
    //NN いもむしの数   
    //NUM いもむしの長さ
    //SIDE 0.2
    //RADIUS (0.1732f)
    for (int j=0; j<NN; j++){
        contactgroup = dJointGroupCreate(0);
        for (i=0; i<NUM; i++){
            body[j][i] = dBodyCreate(world);
            k = i * SIDE;
            dBodySetPosition(body[j][i], k-j*0.1, k, k+0.4+j*1.0);
            dMassSetBox(&m, 1, SIDE, SIDE, SIDE);
            dMassAdjust(&m, MASS);
            //すでに密度で与えられたdMassに対して全質量を再度与えるのに有用
            //質量がnewmassになるように調整します。これはdMassSetXx()
            //によって密度から設定した場合に、その質量を設定するのに有
            //効です。
            dBodySetMass(body[j][i], &m);
            sphere[j][i] = dCreateSphere(space, RADIUS);   
            dGeomSetBody(sphere[j][i], body[j][i]);
            //sphereなどと書くのでややこしい。geomをセットしている
            //下のコードと比較 下のコードが私にとってなじんだコード
            // 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);
        }
        for (i=0; i<(NUM-1); i++){
            joint[j][i] = dJointCreateBall(world, 0);   
            //ボールジョイント
            dJointAttach(joint[j][i], body[j][i], body[j][i+1]);
            //ボールジョイントでつなで一匹のいもむしを作成
            k = (i + 0.5) * SIDE;
            dJointSetBallAnchor(joint[j][i], k-j*0.1, k, k+0.4+j*1.0);
            //軸の位置をセット
        }
    }

    dsSimulationLoop(argc, argv, WindowWidth, WindowHeight, &fn);
    //////シミュレーション用の無限ループ
   
    //levels = 0;
    //place_cards();

    dJointGroupDestroy(contactgroup);
    dWorldDestroy(world);
    dSpaceDestroy(space);
    dCloseODE();
    return 0;
}



//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