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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值