2013年10月27日 星期日

ODE教學<一>初始化ODE



目前正在翻譯這篇,要轉載或幫忙翻譯請跟我說....
原文http://www.alsprogrammingresource.com/basic_ode.html


ODE是個物理的函數庫可以在虛擬世界模擬真實的物理現象,ODE的全名是Open Dynamics Engine而作者是Russell Smith,
是個開放程式碼的計畫,在此的ODE範例將會用免費的Dev-cpp來編譯。


這是個簡單的一個物件-一個盒子掉在一個平坦的表面,我們晚點再來講關節的部份,先來講ode include的部份。
#inlcude <ode/ode.h>

// 我們需要的global variables. 
MATRIX GeomMatrix;
MyObject Object;
dWorldID World;
dSpaceID Space;
dJointGroupID contactgroup;

第一個變數叫作GeomMatrix是個陣列將3x3 rotation matrix和position vector用來給OpenGL成像。 在這邊是用一個1x12的float陣列,在之後會講轉換函數。 

第二個變數型態是MyObject,簡單的結構包含兩個物件ID, 這些獨特的物件ID是用來給ODE library的reference用來在模擬時區分不同的物件。 

#define GEOMSPERBODY 1  // maximum number of geometries per body

struct MyObject
{
    dBodyID Body;  // the dynamics body
    dGeomID Geom[GEOMSPERBODY];  // geometries representing this body
};

在結構中第一個物件ID是物件dynamics body的identifier,物件的dynamics body包含物件的位置、速度等資訊。 在結構中第二個物件ID是指標到一個陣列包含基本的幾何形狀,像是矩形、球形等來組成物件。 在這個範例中只有一個'幾何(geom)',在這個盒子群組中,但是通常的一個物件是由一堆不同形狀的幾何所組成的 在模擬中的body則是結合dynamics body和他的幾何所組成。 

最後三個變數也是物件ID給下列的物件型態:
  • dWorld物件包含所有的body跟其節點
  • dSpace物件類似world container不過他應用了碰撞而非dynamics.
  • dJointGroup物件則是包含了一系列的節點
如果我們沒有要連結任何的物件在一起,為什麼我們需要dJointGroup物件? 因為在物理模擬中任何的物件body的接觸(包含接觸地面)都是以暫時節點來加以計算的, 當一個盒子掉落到一個平面最多會有四個接觸節點,每個盒子的四角都有可能產生接觸節點。 

ODE初始化

在講完global variables之後,接著講ODE模擬初始化,建立一個InitODE函數, 

void InitODE()
{
    // 產生一個新的、空的world然後指定他的ID,大部分的程式只需要一個world.

    World = dWorldCreate();

    // 產生一個新的碰撞空間並指定ID,傳入0或是現有的dSpaceID。
    // 根據物件數量有三種不同碰撞空間,dSimpleSpaceCreate用於少量的物件,
    // 如果有很多物件,則考慮用dHashSpaceCreate或dQuadTreeSpaceCreate(參照ODE文件) 
    Space = dSimpleSpaceCreate(0);

    // 產生一個碰撞群組並指定ID. dJointGroupCreate需要一個
    // max_size參數,但我們沒有使用時就傳入0
    contactgroup = dJointGroupCreate(0);

    // 在碰撞空間中產生一個平坦地面,在dCreatePlane中傳入我們的碰撞空間space參數
    // 接下來四個參數則是平面的法向量 (a, b, c) 和距離(d) 根據平面公式
    // a*x+b*y+c*z=d並且長度必須為1
    dCreatePlane(Space, 0, 1, 0, 0);

    // 現在我們可以設定world的地心引力,在dWorldSetGravity傳入world參數
    // 地球地心引力(0, -9.81, 0)假設+Y向上,不過我覺得輕一點引力在這個範例中看起來更真實 
    dWorldSetGravity(World, 0, -1.0, 0);

    //接下來兩個函數用來控制多少的error correcting和constraint force混合發生在這個world。
    // 現在先別管這些,暫時就用預設就對了,如果需要更精確的控制物件碰撞時,建議參照ODE文件

    dWorldSetERP(World, 0.2);
    dWorldSetCFM(World, 1e-5);
    
    // 這個函數會設定兩個物體交互作用分離時的速度,預設是無窮大
    dWorldSetContactMaxCorrectingVel(World, 0.9);
    // 這個函數會設定world物件的平面深度,接觸的物件可以陷入所設定的深度
    // 設定一個小的數值可以減低兩個物件接觸時的不穩定狀態,預設是0  
    dWorldSetContactSurfaceLayer(World, 0.001);

    // 爲了節省CPU運算時間,我們將自動停用旗標設為1,這代表當物件停止後(根據物件的線性與角速度),
    // 將不會在參與模擬運算,直到有新的動作外加於物件使之移動才會重新啟用運算
    // 如果不想啟用這個功能的話,可以將旗標設定為 0 ,也可以手動啟用或停用物件(使用dBodyEnable 和
    // dBodyDisable),參閱文件關於更多這方面的資料
    dWorldSetAutoDisableFlag(World, 1);
    // 以上的設定完成了我們虛擬世界所需要設定,接著我們必須對物件進行初始化

    // 在我們的虛擬世界中建立一個新的物體並且取得ID

    Object.Body = dBodyCreate(World);

    // 接著設定新物體的位置
    dBodySetPosition(Object.Body, 0, 10, -5);

    // 接著設定初始的線性速度為固定不動,讓地心引力作功,
    // 但妳可以試著改變速度向量來改變一開始的物體行為, 
    //妳也可以用一樣的參數為新物體設定角速度(dBodySetAngularVel)
    VECTOR tempVect(0.0, 0.0, 0.0);
    dBodySetLinearVel(Object.Body, tempVect.x, tempVect.y, tempVect.z);

    // 為了讓每次執行程式,物件一開始時有不同的旋轉方向,我們建立一個新的矩陣叫做 R 並且用
    // dRFromAxisAndAngle這個函數來產生隨機參數 R ,之後將 R 傳入dBodySetRotation來產生隨機的
    //初始旋轉方向
    dMatrix3 R;
    dRFromAxisAndAngle(R, dRandReal() * 2.0 - 1.0,
                          dRandReal() * 2.0 - 1.0,
                          dRandReal() * 2.0 - 1.0,
                          dRandReal() * 10.0 - 5.0);
    dBodySetRotation(Object.Body, R);

    // 在此我們可以用dBodySetData來新增相關的資料,不過在這個範例中並未使用
    size_t i = 0;
    dBodySetData(Object.Body, (void*)i);
    
    // 現在我們必須建立一個四方體的質量來結合他的幾何,首先我們先建立一個新的dMass 結構(其
    // 中的構造目前並不重要) 然後建立一個3個浮點數(dReal)的陣列,並且設定四方體的長寬高(分別對
    // 應於x,y,z軸)然後我們兩這兩個參數傳入dMassSetBox中並且用預先定義的密度(DENSITY) 0.5來設定
    dMass m;
    dReal sides[3];
    sides[0] = 2.0;
    sides[1] = 2.0;
    sides[2] = 2.0;
    dMassSetBox(&m, DENSITY, sides[0], sides[1], sides[2]);
    // 現在我們可以將這個質量套用到我們的物體上
    dBodySetMass(Object.Body, &m);

    // 在此我們用dCreateBox建立一個真正的幾何物件,注意這也把這個幾何物件加入到我們的碰撞空間中
    // 並且將這個幾何大小設定為我們所設定的質量
    Object.Geom[0] = dCreateBox(Space, sides[0], sides[1], sides[2]);
    // 最後我們需要用dGeomSetBody連結物體和他的幾何在一起,在幾何物件上設定物體會自動結合
    // 他的位置向量和旋轉矩陣,所以當我們改變物體(body)和幾何(geom)其中一個的位置或方向,會同時
    // 作用到兩者ODE文件對於幾何函數有更詳細的說明
    dGeomSetBody(Object.Geom[0], Object.Body);
}

沒有留言:

張貼留言