Ogre動畫

 原文地址:


http://blog.csdn.net/onejavaer/article/details/6704724

1.動畫基礎
      實現動畫的基本原理:多個關鍵幀組成一個動畫軌跡,多個動畫軌跡組成一個動畫。在各個關鍵幀之間通過插值算法(Spline插值或線性插值)進行插值來生成最終的動畫。一個動畫不只包括關鍵幀,還有其它的一些屬性(動畫名、動畫長度、動畫的權重、是否被啓用等)。一個動畫軌跡可以指定其控制的節點。

 2.OGRE中與基本動畫相關的類
根據動畫原理,我們抽象出以下幾個類:

 
 a.SimpleSpline與RotationSpline類
      實現了樣條的插值算法。SimpleSpline用來對Translate和Scale進行插值,RotationSpline用來對Rotation進行插值。
 b.關鍵幀類(KeyFrame)
      組成動畫最基本的元素。一個動畫軌跡裏有多個關鍵幀,每個關鍵幀具有自己的位置、縮放比例和旋轉角,同時每個關鍵幀還保存有自己在整個動畫軌跡裏所處的時間點。在實際運行時,根據當前時間,通過對兩個關鍵幀的插值可以得到當前幀(當前位置、縮放比例和旋轉角)。隨着時間的變化,插值得到的當前幀也是變化的,動畫就產生了。由於關鍵幀包括位置信息、縮放比例信息和旋轉信息,所以可以實現運動動畫、縮放動畫和旋轉動畫以及混合動畫。  
d.動畫軌跡類(AnimationTrack)
   動畫軌跡由多個關健幀組成,負責對相鄰的兩個關鍵幀之間插值。
   每個動畫軌跡保存關鍵幀列表、本動畫軌跡所屬的動畫(Animation)、本動畫軌跡所控制的Node,又由於動畫軌跡負責關鍵幀的插值,所以它保存SimpleSpline(簡單樣條插值計算器)和RotationSpline(旋轉樣條插值計算器)對象以實現插值功能。
     一個動畫軌跡可以對一個物體產生作用,使物體按照動畫軌跡運動。這個物體可以是一個Node(一塊骨頭或場景中的一個結點)。如果骨頭受動畫軌跡控制,可以實現骨骼動畫,如果場景節點受動畫軌跡控制,可以直接實現場景節點的動畫運動。
    重要函數
創建一個關鍵幀,傳入這一幀的所在的時間點。
       KeyFrame* createKeyFrame(Real timePos);
根據當前時間,得到插值計算出來的的當前幀。
KeyFrame getInterpolatedKeyFrame(Real timeIndex) const;
      使當前動畫軌跡對其控制的節點產生作用,參數是當前時間點、權重和是否累計權重。
void apply(Real timePos, Real weight = 1.0, bool accumulate = false);
 e.動畫類(Animation)
一個動畫由多個動畫軌跡(AnimationTrack)組成。而一個動畫軌跡可以控制一個節點,這樣一個動畫可以使多個節點沿着自己的軌跡運動。 設想一下一個人的行走動畫,人身上的關節點都沿着自己的軌跡運動,如果把兩個關節點連接起來,就形成骨頭,再讓表面的網格受骨頭影響而運動,骨骼動畫的基礎有了!
    每個動畫保存自己的AnimationTrack列表,保存動畫名稱和長度(時間)。
重要函數
       設置關鍵幀間的插值方式。參數IM_LINEAR 代表線性插值、IM_SPLINE代表樣條插值。
        void setInterpolationMode(InterpolationMode im);
       創建一個動畫軌跡。第一個參數是這個動畫軌跡的唯一標識,第二個參數指定應用這個動畫軌跡的節點。
AnimationTrack* createTrack(unsigned short handle, Node* node);
使當前動畫對其控制的節點產生作用(委託AnimationTrack進行),參數是當前時間點、權重和是否累計權重。
void apply(Real timePos, Real weight = 1.0, bool accumulate = false);

  f.動畫狀態類(AnimationState)
    一個動畫狀態類的對象對應一個動畫類的對象,看上面的類圖,AnimationState的數據成員mAnimationName與Animation類的數據成員mName是相對應的。它保存相應動畫的狀態。 動畫狀態包括動畫名、當前時間點,動畫長度(總時間)、動畫權重和動畫的enable開關。
   重要函數
    設置動畫是否啓用。
      void setEnabled(bool enabled);
    移動當前時間點,讓動畫狀態在動畫時間線上向前移動。參數爲移動量。
      void addTime(Real offset);
 g.場景管理器類(SceneManager)
      場景管理器負責動畫和動畫狀態的創建與維護。在場景管理器裏保存有mAnimationsList(動畫列表)和mAnimationStates(動畫狀態列表),其中每個動畫和動畫狀態通過名字一一對應。 場景管理器中有一個很重要的方法_applySceneAnimations ,它在每次渲染時(_renderScene函數裏)都會被調用(自動調用不需要程序員控制),它的任務是在每一幀渲染前根據動畫狀態更新動畫,完成動作。_applySceneAnimations方法遍歷全部的動畫狀態,並根據這些狀態找出與之一一對應的動畫,再找出每個動畫中的全部動畫軌跡,在每個軌跡裏都保存有該軌跡控制的節點。_applySceneAnimations方法先將這些被動畫控制的節點都還原爲初始狀態,而後再調用動畫的apply方法將全部節點更新到新的位置、大小或方向,從而使動畫向前進行。

    通過FrameLisener調用動畫狀態的addTime方法,可以完成動畫狀態的更新。場景管理器的_applySceneAnimations方法會根據新的動畫狀態將對應動畫的相關節點的位置、大小和方向也更新,這就是OGRE中實現動畫的基本原理和方法。
   重要函數
    創建動畫Animation,參數爲動畫名和長度(時間)
       virtual Animation* createAnimation(const String& name, Real length);
    創建動畫狀態AnimationState,參數是與動畫對應的名稱。
       virtual AnimationState* createAnimationState(const String& animName);
    基本動畫實例:
      定義一個10秒種的動畫,這個動畫包含一個動畫軌跡(上下翻轉)。讓這個動畫應用到當前攝像機上去,程序運行時,我(第一人稱攝像機)應該在上下翻轉。

      在createScene函數裏做初始化工作。
       首先我們考慮怎樣可以把動畫應用到當前攝像機上。因爲一個動畫可以應用到一個節點上,所以可以創建一個節點並將當前攝像機attach到這個節點上去,代碼如下:
        SceneNode* camNode = mSceneMgr->getRootSceneNode()->createChild();
        camNode->attachObject(mCamera);

    下面定義動畫、動畫軌跡以及關鍵幀:
        // 定義動畫,指定動畫的名稱及長度(這裏爲10秒)
        Animation* anim = mSceneMgr->createAnimation("CameraTrack", 10);
        // 指定動畫關鍵幀之間的插值方式(包括線性插值和樣條插值)
        anim->setInterpolationMode(Animation::IM_SPLINE);
        // 定義動畫的一個動畫軌跡,並指定這個軌跡是作用到camNode節點上的
        AnimationTrack* track = anim->createTrack(0, camNode);
        // 定義動畫軌跡包含的關鍵幀,下面定義了四個關鍵幀,加上起始幀
       // 五個關健幀形成了一個翻轉的動畫。
        KeyFrame* key = track->createKeyFrame(0); // startposition
        key = track->createKeyFrame(2.5);
        key->setTranslate(Vector3(500,500,-1000));
        key = track->createKeyFrame(5);
        key->setTranslate(Vector3(-1500,1000,-600));
        key = track->createKeyFrame(7.5);
        key->setTranslate(Vector3(0,-100,0));
        key = track->createKeyFrame(10);
        key->setTranslate(Vector3(0,0,0));  

       然後定義AnimationState類的對象,它和剛纔定義的動畫類相對應。設置動畫的狀態爲啓用:
        mAnimState = mSceneMgr->createAnimationState("CameraTrack");
        mAnimState->setEnabled(true); // 啓用該動畫
    到此,初始化工作就做完了。

       最後,要想使動畫動起來,我們需要重載ExampleFrameLisener類的frameStarted函數,並調用下面的函數,根據傳入的時間來設置動畫的狀態:    

         mAnimState->addTime(evt.timeSinceLastFrame);
骨骼動畫
       基本概念
     什麼是骨骼動畫
          骨骼動畫用骨架(由一系列骨頭構成的繼承體系)來單獨保存動作信息,這樣就將動作信息與網絡、皮膚信息分割成兩種數據結構分別進行處理,從而比以住的動畫技術效率更高。 每塊骨頭都有自己的位置和旋轉方向,這些骨頭以樹的形式組織起來構成骨骼。例如:腕關節是肘關節的子節點,肘關節又是肩關節的子節點。肩關節旋轉會自動帶動肘關節運動,腕關節同樣也會運動。
       那麼怎麼使一個網格產生動作效果呢?我們可以使網格上的每個頂點都對應於一塊或多塊骨頭,當這些骨頭移動時便會影響網格的位置。如果一個點與多塊骨關聯,則必須通過指定權重來決定每塊骨頭對此頂點的影響程度(一個頂點對應一塊骨頭時,該點的權重爲1.0 )。
       與關鍵幀動畫相比,使用骨骼動畫有很多優點。

       首先,骨骼動畫需要保存的動作數據量非常小。

       其次,通過指定不同的權重,可以很容易的將多個動作綁定在一起形成新的動作。還可以實現動作間的平滑過渡等等。 當然骨骼動畫的實現中,骨骼本身的運動還是需要關鍵幀來完成,但信息量已經很小了。
    Ogre的骨骼動畫
        Ogre骨骼信息和動畫信息保存到後綴名爲.skeleton的文件中,你可以通過OGRE提供的導出插件工具(Milkshape和3Dmax的exporter)來導出該類型的文件。當你創建基於.Mesh文件的Entity時,.Skeleton文件將會自動被系統加載進來。爲了操作方便,Entity自動給每一個動作指定一個AnimationState類(請參考基本動畫)的對象,你可以通過Entity::getAnimationState函數來得到具體的動作。 
       示例一:行走的機器人
     
     

       該實例創建基於robot.mesh文件的實體(Entity)對象,指定其“走”動作(動作信息都在.skeleton文件裏),並將其顯示到屏幕上。robot.mesh文件中保存機器人的網格信息,Entity會自動加載保存有機器人骨骼信息的robot.skeleton文件。我們重載ExampleApplication類的createScene函數,其部分代碼如下:
void createScene(void)
{
 // ….
        // 設置關鍵幀之間的插值方法爲樣條插值
        Animation::setDefaultInterpolationMode(Animation::IM_SPLINE);
        // 創建基於網格文件robot.mesh的Entity 
        Entity *ent = mSceneMgr->createEntity("robot", "robot.mesh");
        // 將實體附屬到場景根結點上
        mSceneMgr->getRootSceneNode()->createChild()->attachObject(ent);
        // 得到實體“走”動作的AnimationState類對象
        mAnimState = ent->getAnimationState("Walk");
       // “Enable”(起始)該動作
        mAnimState->setEnabled(true);
    } 
       在createScene函數裏成功的指定了機器人的當前動作爲“走”,下一步要讓動畫“動”起來,還需要根據時間跨度計算當前幀的骨骼位置。重載ExampleFrameListener類的frameStarted函數:
    bool frameStarted(const FrameEvent& evt)
{
          //將兩幀之間的時間差傳入AnimationState::addTime函數,該函數內部會計算出動// 畫的當前時間點。
        mAnimState->addTime(evt.timeSinceLastFrame);

          // 調用父類的frameStarted函數
        return ExampleFrameListener::frameStarted(evt);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章