Icarus Scene Engine

前幾天無意間發現的引擎。
Icarus Scene Engine」是一款在C#環境底下開發的。
之所以會提到,是因為它也是使用「Tao」包裝的「ODE」!
而且該引擎還是OpenSource,雖然它更新資料似乎停在2007年底就是…
正好作為目前程式的範本,也順便見習見習別人的寫法XDD
另外,它主要是使用OpenGL、OpenAL,所以就算不是用XNA,也是個不錯的範本吧?
有興趣的請用力吃阿~甭客氣!

–End

用 foreach 走訪(遍歷) Dictionary

之前使用Dictionary時,都盡可能避免走訪所有元素(也不知道為啥XDD)
當然,走訪是不可避免的,方式也很多種…
選擇最簡便使用的就對了!

4.10 对泛型Dictionary类型使用foreach

 foreach (KeyValuePair<int, string> item in myDict)
 {
     Console.WriteLine("key " + item.Key);
     Console.WriteLine("Value " + item.Value);
 }

也就是使用KeyValuePair<(Of <(TKey, TValue>)>) 結構
據說效率並非很好就是~

另一種類似的DictionaryEntry 結構也是可以的,只不過要多做轉型的動作,浪費效能又要多寫code。

 foreach (DictionaryEntry entry in dictionary)
 {
     Object key = entry.Key;
     Object val = entry.Value;
 }

–End

Open Dynamics Engine v0.5 User Guide

只有逐字翻譯才不會漏看細節…
雖然有找到像是『ODE v0.5 用户指南- -!』『ODE 0.5 版使用指南 v0.1』等部分的翻譯。
除了不齊全外,用詞上也不太習慣,最後還是照原文的慢慢看,上面的翻譯就當參考用。

上面兩篇和接下來翻譯的都是ODE的user guide(Html版本)(PDF版本)。
這篇為索引,點選連結會到各自的文章裡,
有翻譯的就會附上連結
如果看我的版本還是覺得怪怪的,請對照原文後提醒一下怎麼翻比較好,感謝!
目錄的部分就不做翻譯,方便對照



1. Introduction

2. How to Install and Use ODE
3. Concepts
4. Data Types and Conventions
5. World
6. Rigid Body Functions
7. Joint Types and Joint Functions
8. StepFast
9. Support Functions
10. Collision Detection
11. How To Make Good Simulations
12. FAQ
13. Known Issues
14. ODE Internals



–End

ODE – Chapter 6 《Rigid Body Functions》 Part I

Chapter 6 《Rigid Body Functions》

    6.1 Creating and Destroying Bodies
    
        dBodyID dBodyCreate (dWorldID);
        
        在指定的世界中創造剛體,並給予預設的mass和放置在座標(0, 0, 0)。回傳剛體的ID。

        void dBodyDestroy (dBodyID);

        摧毀剛體。所有附加在剛體上的關節都會被棄置(無附加目標和不影響模擬,但他們並沒有被刪除!)。


    6.2 Position and orientation

        void dBodySetPosition (dBodyID, dReal x, dReal y, dReal z);
        void dBodySetRotation (dBodyID, const dMatrix3 R);
        void dBodySetQuaternion (dBodyID, const dQuaternion q);
        void dBodySetLinearVel (dBodyID, dReal x, dReal y, dReal z);
        void dBodySetAngularVel (dBodyID, dReal x, dReal y, dReal z);
        const dReal * dBodyGetPosition (dBodyID);
        const dReal * dBodyGetRotation (dBodyID);
        const dReal * dBodyGetQuaternion (dBodyID);
        const dReal * dBodyGetLinearVel (dBodyID);
        const dReal * dBodyGetAngularVel (dBodyID);

        這些用來設定和取得剛體的位置、旋轉、直線速度和旋轉速度。在設定一個群組的剛體後,如果新的設定和關節或接觸點的設定不一致的話,模擬器會無法定義。
        取得時,方法會回傳內部資料結構的指標,所以可以合法的修改向量來改變剛體系統的結構。
        
        dBodyGetRotation 回傳的是 4×3 旋轉矩陣。

    6.3 Mass and force


        void dBodySetMass (dBodyID, const dMass *mass);

        void dBodyGetMass (dBodyID, dMass *mass);

        設定和取得剛體的質量(mass)。

        void dBodyAddForce (dBodyID, dReal fx, dReal fy, dReal fz);
        void dBodyAddTorque (dBodyID, dReal fx, dReal fy, dReal fz);
        void dBodyAddRelForce (dBodyID, dReal fx, dReal fy, dReal fz);
        void dBodyAddRelTorque (dBodyID, dReal fx, dReal fy, dReal fz);
        void dBodyAddForceAtPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
        void dBodyAddForceAtRelPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
        void dBodyAddRelForceAtPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
        void dBodyAddRelForceAtRelPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);

        施加(絕對或相對座標的)力量給剛體。這些力量會累積在每個剛體上,在每次時間推進後就會歸零。
        …RelForce和..RelTorque方法所用到的力量向量都是相對於剛體自身的座標。
        …ForceAtPos和…ForceAtRelPos方法會使用到一個額外的座標向量(相對於世界或剛體的座標),它是用來定義力量會被施加的所在位置。其他所有的方法都是將力量施加在質量的中心點。

        const dReal * dBodyGetForce (dBodyID);
        const dReal * dBodyGetTorque (dBodyID);

        回傳目前所累積的力量和力矩向量。回傳的指標是由三個dReals組成的陣列。回傳值是指向內部的資料,所以可在剛體改變之前透過指標合法地修改。

        void dBodySetForce (dBodyID b, dReal x, dReal y, dReal z);
        void dBodySetTorque (dBodyID b, dReal x, dReal y, dReal z);

        設定剛體的力量和力矩向量。通常用來將未啟用剛體的累積力量歸零,而後再啟用之。

    6.4 Utility

        void dBodyGetRelPointPos (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
        void dBodyGetRelPointVel (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
        void dBodyGetPointVel (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);

        Utility方法會產生一個剛體上位置(px, py, pz)的指標,並將該指標的位置或速度,轉換成相對於世界座標的結果放在result裡面。dBodyGetRelPointXXX方法會給與相對於剛體座標的結果,dBodyGetPointVel則是相對於世界座標的結果。

        void dBodyGetPosRelPoint (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);

        這是對應dBodyGetRelPointPos()。它會轉換一個世界的(px, py, pz)到相對於剛體的座標。

        void dBodyVectorToWorld (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
        void dBodyVectorFromWorld (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);

        將剛體(世界)座標的向量轉換成世界(剛體)座標的向量放在result裡面。

    6.5 Automatic Enabling and Disabling

        每個剛體都可以決定是否要啟動自動關閉的功能。啟用的剛體會參與模擬,而關閉中的剛體會在模擬時忽略。新的剛體剛創造時通常都是啟用狀態。
        關閉的剛體可以透過關節和啟用中的鋼體連結,在下一次模擬推進時自動啟用。
        關閉的剛體不會消耗CPU時間,所以為了增進效能,應該在剛體靜止時關閉它。這可以透過設定自動關閉的選項來達成。
        當剛體自動關閉的功能被啟動時,它會在以下的狀況自動關閉:

            1. 在一定次數的推進中維持閒置狀態。
            2. 在一定模擬器時間中維持閒置狀態。

        剛體在直線速度和旋轉速度遠小於門檻值時,認定為閒置狀態。
        每個剛體有五種自動關閉的參數:啟用旗標(flag)、閒置推進步數門檻、閒置時間門檻、直線速度門檻和旋轉速度門檻。新創造的剛體會從世界給予這些參數。
        接下來是設定和取得剛體的參數(啟用/關閉)。

        void dBodyEnable (dBodyID);
        void dBodyDisable (dBodyID);

        手動啟用和關閉剛體。注意,當關閉中的剛體透過關節與啟用中的剛體連接,會在下一次推進時自動啟用。

        int dBodyIsEnabled (dBodyID);
        
        當鋼體式啟用時回傳1,不然回傳0。

        void dBodySetAutoDisableFlag (dBodyID, int do_auto_disable);
        int dBodyGetAutoDisableFlag (dBodyID);

        設定和取得剛體的自動關閉旗標。如果do_auto
_disable為非零,那麼當剛體閒置夠久,就會自動關閉。

        void dBodySetAutoDisableLinearThreshold (dBodyID, dReal linear_threshold);
        dReal dBodyGetAutoDisableLinearThreshold (dBodyID);

        設定和取得剛體自動關閉的直線速度門檻。剛體的直線速度遠小於這個門檻時,就認定為『閒置』。將門檻設為dInfinity則直接忽略直線速度的判定。

        void dBodySetAutoDisableAngularThreshold (dBodyID, dReal angular_threshold);
        dReal dBodyGetAutoDisableAngularThreshold (dBodyID);

        設定和取得剛體自動關閉的旋轉速度門檻。剛體的旋轉速度遠小於這個門檻時,就認定為『閒置』。將門檻設為dInfinity則直接忽略旋轉速度的判定。

        void dBodySetAutoDisableSteps (dBodyID, int steps);
        int dBodyGetAutoDisableSteps (dBodyID);

        設定和取得剛體自動關閉的推進次數門檻。將門檻設為0,則忽略判定閒置次數。

        void dBodySetAutoDisableTime (dBodyID, dReal time);
        dReal dBodyGetAutoDisableTime (dBodyID);

        設定和取得剛體自動關閉的時間門檻。將門檻設為0,則忽略判定閒置的時間。

        void dBodySetAutoDisableDefaults (dBodyID);

        將剛體的自動關閉參數設定成世界的預設值(新剛體的預設值)。

Next – Chapter 6 《Rigid Body Functions》 Part II

–Next

ODE – Chapter 6 《Rigid Body Functions》 Part II

    6.6 Miscellaneous Body Functions

        void dBodySetData (dBodyID, void *data);
        void *dBodyGetData (dBodyID);

        取得和設定剛體使用者自訂資料的指標。

        void dBodySetFiniteRotationMode (dBodyID, int mode);

        設定剛體每次推進時控制旋轉的模式。
        mode參數可以是:

            • 0: 以『無窮小』的方式更新。計算快速,但在鋼體快速旋轉時偶爾會出現不準確的結果。特別是這些剛體還有連結到其他剛體。新的剛體預設都是使用這個模式。
            • 1: 以『有限』的方式更新。需要更多計算,但在剛體快速旋轉時有更好的準確度。儘管如此,高速旋轉的剛體還是會產生各種不同的模擬錯誤,這個模式只會修正最原始的剛體。

        int dBodyGetFiniteRotationMode (dBodyID);

        回傳目前剛體控制旋轉的模式(0或1)。

        void dBodySetFiniteRotationAxis (dBodyID, dReal x, dReal y, dReal z);

        設定剛體的無窮小旋轉軸。這個軸只有在使用『有限』模式會使用(詳見dBodySetFiniteRotationMode())。

        如果旋轉軸為零(0, 0, 0),所有的有限旋轉將套用到剛體上。
        如果旋轉軸為非零,剛體就會根據旋轉軸旋轉。而『無窮小』的模式就是直接沿著直角旋轉。
        這樣可以減少剛體在快速旋轉時所造成的錯誤。舉例來說,如果有個汽車輪胎在高速旋轉的時候,你可以呼叫這個方法讓輪胎延著輪胎的轉軸(wheel's hinge axis)轉動,用以改善它的旋轉方式。

        void dBodyGetFiniteRotationAxis (dBodyID, dVector3 result);

        回傳剛體目前的限制軸。

        int dBodyGetNumJoints (dBodyID b);

        回傳附加在剛體上的關節(Joint)數量。

        dJointID dBodyGetJoint (dBodyID, int index);

        依照給予的index,回傳剛體上的關節。有效值範圍從0到n-1,這裡的n可從dBodyGetNumJoints()取得。

        void dBodySetGravityMode (dBodyID b, int mode);
        int dBodyGetGravityMode (dBodyID b);

        設定和取得剛體是否被世界的重力所影響。如果mode為非零則影響,如果mode為零則不影響。新創造的剛體預設會被世界重力所影響。

–End

ODE – Chapter 5 《World》

Chapter 5 《World》

    世界裡的物件(object)包含剛體(rigid body)和關節(joint)。不同世界的物件是無法互相作用的,舉例來說,兩個世界的剛體是無法碰撞的。
    世界裡所有的物件都擁有相同的時間點(point in time),因此,為了模擬不同頻率(rate)的碰撞系統,就須使用不同的世界。
    大部分的狀況下,都只需要一個世界即可。

    dWorldID dWorldCreate();

    產生一個空的世界,並且回傳世界的ID。

    void dWorldDestroy (dWorldID);

    摧毀世界裡所有的東西(所有的剛體、關節,但不包括關節群組)。關節群組底下的關節將會被停用,且可以藉由呼叫dJointGroupEmpty()來摧毀之。

    void dWorldSetGravity (dWorldID, dReal x, dReal y, dReal z);
    void dWorldGetGravity (dWorldID, dVector3 gravity);

    設定和取得世界的全域重力(gravity)向量。單位為:「m/s/s(每公尺/秒平方)」。依照地球的重力來說,假設+Z軸為向上向量,就是(0, 0, -9.81)。預設的重力為(0, 0, 0)。

    void dWorldSetERP (dWorldID, dReal erp);
    dReal dWorldGetERP (dWorldID);
    
    設定和取得全域ERP,用來控制每個時間推進(time step)所能修正的錯誤程度。

    void dWorldSetCFM (dWorldID, dReal cfm);
    dReal dWorldGetCFM (dWorldID);

    設定和取得全域CFM(constraint force mixing)。CFM的值通常介於10-9e到1之間。
    如果是用單精準浮點數(float),則預設值為10-5e;如果是雙精準浮點數(double)則為10-10e。

    void dWorldSetAutoDisableFlag (dWorldID, int do_auto_disable);
    int dWorldGetAutoDisableFlag (dWorldID);
    void dWorldSetAutoDisableLinearThreshold (dWorldID, dReal linear_threshold);
    dReal dWorldGetAutoDisableLinearThreshold (dWorldID);
    void dWorldSetAutoDisableAngularThreshold (dWorldID, dReal angular_threshold);
    dReal dWorldGetAutoDisableAngularThreshold (dWorldID);
    void dWorldSetAutoDisableSteps (dWorldID, int steps);
    int dWorldGetAutoDisableSteps (dWorldID);
    void dWorldSetAutoDisableTime (dWorldID, dReal time);
    dReal dWorldGetAutoDisableTime (dWorldID);

    設定和取得新剛體創造時,預設自動關閉的參數。詳見6.5自動關閉的相關說明。
    預設的參數如下:

        • AutoDisableFlag = disabled
        • AutoDisableLinearThreshold = 0.01
        • AutoDisableAngularThreshold = 0.01
        • AutoDisableSteps = 10
        • AutoDisableTime = 0

    void dWorldImpulseToForce (dWorldID, dReal stepsize, dReal ix, dReal iy, dReal iz, dVector3 force);

    如果你需要在剛體上施加線性或有角度的推進力(impulse)而非力量(force)力矩(torque)。,那麼你可以在呼叫dBodyAdd…之類的方法之間,使用這個方法轉換將要施加的推進力,變成力量或力矩向量。
    這方法需要傳遞推進力(ix, iy, iz)和接收的力量向量force。方法只是簡單的依照1/stepsize縮放推進力,這裡的stepsize是指下一次的的推進時間。
    這個方法也須傳遞dWorldID,因為將來計算力量時,可能會需要使用到世界中的一些設定。

    void dCloseODE();

    有些ODE額外使用到的記憶體(內存;memory)是無法透過一些普通的方法來清除的(像是dWorldDestroy())。可以在程式最後呼叫這個方法來避免ODE回報記憶體洩漏(memory leak)的錯誤。

    
    5.1 Stepping Functions
        
        void dWorldStep (dWorldID, dReal stepsize);
        
        世界推進的腳步(step)。這個方法會使用「巨大矩陣」,耗費時間為m*m*m、記憶體消耗為m*m,這裡的m是約束條件(constraint)的總行數(row)。
        對於大型系統來說,這個方法會使用大量的記憶體,且計算緩慢。但這方法是目前最為準確的。

        void dWorldQuickStep (dWorldID, dReal stepsize);

        世界推進的腳步(step)。這裡會使用到遞迴方法,耗費時間為m*N、記憶體消耗為m,這裡的m是約束條件(constraint)的總行數(row),N為遞迴的數量。
        對於大型系統來說,速度會明顯地比dWorldStep()快速許多,但少些準確度。
        QuickStep對於堆疊(stack)物件是很好的,尤其是搭配適當的auto-disable設定。但是對於奇異(near-singular)的系統來說,準確度是非常差的。當使用高摩擦係數的接觸點、motors或某些連接結構,就會產生奇異系統。
        舉例來說,一個擁有多隻腳的機器人坐在地上,就有可能成為奇異系統。
        這裡有一些克服QuickStep不夠準確的方法:
            • 增加CFM。
            • 減少系統中的接觸點數量(像是使用機器人或生物最少的腿數)。
            • 避免使用過度摩擦係數的接觸點。
            • 恰當的使用可滑動的接觸點(contact slip)。
            • 避免運動循環(kinematic loops),然而有腿的生物無法避免運動循環。
            • 避免使用過度的動力(motor strength)。
            • 使用force的動力(force-based motors)取代速度的動力(velocity-based motors)。

        void dWorldSetQuickStepNumIterations (dWorldID, int num);
        int dWorldGetQuickStepNumIterations (dWorldID);

        設定和取得每次推進時QuickStep的重複次數。更密集的次數會讓結果更為準確,同時也需要更長時間的計算作為代價。預設值為20次。

    5.2 Contact Parameters
        
        void dWorldSetContactMaxCorrectingVel (dWorldID, dReal vel);
        dReal dWorldGetContactMaxCorrectingVel (dWorldID);

        設定和取得接觸點(contact)可允許的最大速度修正值,預設值為無限(也就是沒有限制)。減少這個值可以避免深處的內嵌物件『凸』出來。

        void dWorldSetContactSurfaceLayer (dWorldID, dReal depth);
        dReal dWorldGetContactSurfaceLayer (dWorldID);

        設定和取得圍繞在幾何體物件的外觀階層(surface layer)深度。這用來允許接觸點在停止之前可以深入表層的深度。預設值為0。稍微增加一點點(例如0.001)可以避免接觸點不斷產生和摧毀的抖動問題。

–End

ODE – Chapter 4 《Data Types and Conventions》

Chapter 4 《Data Types and Conventions》

     4.1 The basic data types
        ODE可以編譯成單倍或雙倍精確浮點數的版本。單精準版本比較快且較省記憶體,但是模擬時會產生較多的誤差;也就是得到較不準確的結果。
        [必須先說明哪些因素會影響準確度和穩定性]
        浮點數的資料形態為dReal。其他常用的像是dVector3, dVector4, dMatrix3, dMatrix4, dQuaternion。

    4.2 Objects and IDs
        在ODE裡面有下列幾個不同的物件可以產生:
            • dWorld – 動力世界。
            • dSpace – 碰撞空間。
            • dBody – 剛體。
            • dGeom – 用來碰撞的幾何體。
            • dJoint – 關節。
            • dJointGroup – 關節群組。
        相關處理這些物件的方法,都需要傳遞或回傳物件的ID。這些物件ID類別會是dWorldID、dBodyID等等。

    4.3 Argument conventions
        所有的三軸向量(x,y,z)都有『set』的方法,給予X、Y、Z個別的參數。
        所有的三軸向量也都可透過get()方法取得向量的dReal陣列指標。
        更多軸的向量也都支援且,可以回傳向量的dReal陣列指標。
        所有的座標都是相對於世界座標,除了特殊定義的。
        
    4.4 C versus C++
        ODE庫是用C++撰寫,但它的公開介面(Public interface)都是由C的方法構成,並非類別(class)。
        為什麼呢?
            • 使用C的介面只因為簡潔(簡單?) – 使用C++的介面並沒有對ODE有很大的助益。
            • 避免C++在各種編譯器之中不同的mangle(打亂function名稱的動作),以及run-time時的一些問題。
            • 使用者不需要熟稔(詭異的?)C++也可使用ODE。

    4.5 Debugging
        ODE庫可以編譯成『debugging』或『release』模式。除錯模式會比較慢,但是方法會檢查參數和run-time的檢測。釋出版本則會比較快,但會跳過檢查的步驟。

–End

ODE 運作流程

碰撞真是個麻煩的東西…
最終還是選擇使用現成的Lib來用
之後應該會有一系列ODE的筆記吧~

以下節錄自ODE user guide 3.10 章節。

  1. 創造動力世界(dynamics world)。
  2. 創造在世界中的Body。
  3. 設定所有Body的狀態(座標之類的)。
  4. 創造在世界中的Joint。
  5. 將Joint依附在Body上。
  6. 設定所有Joint的屬性。
  7. 如果需要,創造碰撞世界(collision world)和碰撞幾何體(collision geometry)物件。
  8. 創造一個JointGroup來存放接觸點(contact joint)
  9. 迴圈:
    (a) 如果需要,套用Force到Body上。
    (b) 如果需要,調整Joint的參數。
    (c) 呼叫碰撞偵測。
    (d) 為每個接觸點產生一個接觸點,並將之放進JointGroup。
    (e) 推動模擬器(simulation)。
    (f) 移除JointGroup裡面所有的接觸點。
  10. 摧毀動力世界和碰撞世界。

目前不是很確定Joint和Contact Joint是否有差別…

–End