OGRE动画实现(模型自己动)

来源:互联网 发布:知无涯者电影下载 编辑:程序博客网 时间:2024/04/30 20:06

由于基础教程在帧监听之后开始了GUI的讲解,那部分我们可以放到最后界面优化时在学习,所以还是先进入到OGRE的动画设计中

类似的,开始之前还是首先给出框架代码:

#include "ExampleApplication.h"
#include <deque>
using namespace std;
class MoveDemoListener : public ExampleFrameListener
{
public:
MoveDemoListener(RenderWindow* win, Camera* cam, SceneNode *sn,
Entity *ent, deque<Vector3> &walk)
 : ExampleFrameListener(win, cam, false, false), mNode(sn), mEntity(ent),
mWalkList( walk )
{
} // MoveDemoListener
bool nextLocation( )
{
return true;
} // nextLocation( )
bool frameStarted(const FrameEvent &evt)
{
return ExampleFrameListener::frameStarted(evt);
}
protected:
Real mDistance; // The distance the object has left to travel
Vector3 mDirection; // The direction the object is moving
Vector3 mDestination; // The destination the object is moving
towards
AnimationState *mAnimationState; // The current animation state of the object
Entity *mEntity; // The Entity we are animating
SceneNode *mNode; // The SceneNode that the Entity is attached
to
std::deque<Vector3> mWalkList; // The list of points we are walking to
Real mWalkSpeed; // The speed at which the object is moving
};
class MoveDemoApplication : public ExampleApplication
{
protected:
public:
MoveDemoApplication()
{
}
~MoveDemoApplication()
{
}
protected:
Entity *mEntity; // The entity of the object we are animating
SceneNode *mNode; // The SceneNode of the object we are moving
std::deque<Vector3> mWalkList; // A deque containing the waypoints
void createScene(void)
{
}
void createFrameListener(void)
{
mFrameListener= new MoveDemoListener(mWindow, mCamera, mNode, mEntity,
mWalkList);
mFrameListener->showDebugOverlay(true);
mRoot->addFrameListener(mFrameListener);
}
};
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
// Create application object
MoveDemoApplication app;
try {
app.go();
} catch( Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
MessageBox( NULL, e.getFullDescription().c_str(), "An exception has
occured!",
MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
fprintf(stderr, "An exception has occured: %s\n",
e.getFullDescription().c_str());
#endif
}
return 0;
}
 
稍加解释一下框架代码:
在MoveDemoApplication中预先定义的三个变量。
    Entity *mEntity;                // The entity of the object we are animating    SceneNode *mNode;               // The SceneNode of the object we are moving    std::deque<Vector3> mWalkList;  // A deque containing the waypoints
我们创建的entity实例保存在变量mEntity中。
我们创建的node实例保存在mNode中。
mWalkList包含了所有我们希望对象行走到的节点,他是deque类型,你可以把它想象成是一种双向链表的结构。书中解释为deque对象是一个高效的双端对列。他的基本使用函数有:
1、push_front和push_back方法分别将对象放入队列的前端和后端。
2、front和back方法分别返回当前队列前端和后端的元素
(注意:这里最好有判空的习惯,用if( empty() ) )pop_front和pop_back两个方法分别从队列两端移除对象。最后,empty方法返回该队列是否为空。
 
 
一、创建场景(createScene()函数中添加代码)
// Set the default lighting.创建环境光很亮,能够看到所有物体
mSceneMgr->setAmbientLight( ColourValue( 1.0f, 1.0f, 1.0f ) );
// Create the entity创建实体
mEntity = mSceneMgr->createEntity( "Robot", "robot.mesh" );
// Create the scene node创建场景节点
mNode = mSceneMgr->getRootSceneNode( )->
createChildSceneNode( "RobotNode", Vector3( 0.0f, 0.0f, 25.0f ) );
mNode->attachObject( mEntity );

// Create the walking list创建总动路线上的关键点放到高效的两端队列里


       mWalkList.push_back( Vector3( 550.0f,  0.0f,  50.0f  ) );


       mWalkList.push_back( Vector3(-100.0f,  0.0f, -200.0f ) );


// Create objects so we can see movement创建关键点的标记物


       Entity *ent;


       SceneNode *node;

 

       ent = mSceneMgr->createEntity( "Knot1", "knot.mesh" );


       node = mSceneMgr->getRootSceneNode( )->createChildSceneNode( "Knot1Node",


           Vector3(  0.0f, -10.0f,  25.0f ) );


       node->attachObject( ent );


       node->setScale( 0.1f, 0.1f, 0.1f );

 

 

       ent = mSceneMgr->createEntity( "Knot2", "knot.mesh" );


       node = mSceneMgr->getRootSceneNode( )->createChildSceneNode( "Knot2Node",


           Vector3( 550.0f, -10.0f,  50.0f ) );


       node->attachObject( ent );


       node->setScale( 0.1f, 0.1f, 0.1f );

 

 

       ent = mSceneMgr->createEntity( "Knot3", "knot.mesh" );


       node = mSceneMgr->getRootSceneNode( )->createChildSceneNode( "Knot3Node",


           Vector3(-100.0f, -10.0f,-200.0f ) );


       node->attachObject( ent );


       node->setScale( 0.1f, 0.1f, 0.1f );

 


       // Set the camera to look at our handywork创建摄像机


       mCamera->setPosition( 90.0f, 280.0f, 535.0f );


       mCamera->pitch( Degree(-30.0f) );


       mCamera->yaw( Degree(-15.0f) );

 

至此你可以运行一下,看看基本的环境。

 

 

二、动画制作

1、你需要从实体对象里获取AnimationState。

2、设置它的选项,并激活它。

3、你还必须在每一帧后给它添加时间,才能让动画动起来。

MoveDemoListener的构造函数中添加代码(这只是个小练习,之后会删除这些代码)

      // Set idle animation
      mAnimationState = ent->getAnimationState("Idle");
      mAnimationState->setLoop(true);
      mAnimationState->setEnabled(true);

第二行从实体中获取到了AnimationState。

第三行我们调用setLoop( true ),让动画不停地循环。

第四行才把这个动画真正激活。

但是,Idle是从哪里来的呢?每个mesh都有它们自己定义的动画集。为了能够查看某个mesh的全部动画,你需要下载OgreMeshViewer才能看到,我们可以在maya中将这些动作定义好,导出的时候,给相应的clip起好名字就可以了。

最后,还需要在每一帧里根据时间来更新这个动画的状态。找到MoveDemoListener::frameStarted方法,在方法的开头添加这一行:

mAnimationState->addTime(evt.timeSinceLastFrame);
根据Idle这个动作,机器人至此已经可以原地踏步了!

——————————————————————————-————————————————

三、移动机器人

变量说明

机器人移动的方向保存到mDirection里面。

当前机器人前往的目的地保存在mDestination里。

mDistance保存机器人离目的地的距离。

mWalkSpeed里保存机器人的移动速度。

MoveDemoListener中添加代码:

清空MoveDemoListener构造器(第二步的代码只是想让我们知道如何调用基本的动画的流程)

速度设为每秒35个单位,把mDirection设成零向量,因为后面会用它来判断机器人是否正在行走。

       // Set default values for variables
       mWalkSpeed = 35.0f;
       mDirection = Vector3::ZERO;

MoveDemoListener::frameStarted()中add之前添加代码:

       if (mDirection == Vector3::ZERO)
      {
          if (nextLocation())
          {
              // Set walking animation
              mAnimationState = mEntity->getAnimationState("Walk");
              mAnimationState->setLoop(true);
              mAnimationState->setEnabled(true);
          }
      }
       else   //mDirection != Vector3::ZERO
       {
           Real move = mWalkSpeed * evt.timeSinceLastFrame;
           mDistance -= move;//路程=速度*时间

//检测是否“走过”了目标地点。如果现在mDistance小于0,则“跳”到这点上并设置移动到下一个地点

//把mDirection设置成零向量。如果nextLocation方法不改变mDirection(即没有其它地方可去)

//则不再四处移动了
           if (mDistance <= 0.0f)
           {
               mNode->setPosition(mDestination);
               mDirection = Vector3::ZERO;
// Set animation based on if the robot has another point to walk to.
              if (! nextLocation())
              {
                  // Set Idle animation                    
                  mAnimationState = mEntity->getAnimationState("Idle");
                  mAnimationState->setLoop(true);
                  mAnimationState->setEnabled(true);
              }
              else
              {
                  // Rotation Code will go here later
              }//如果queue里已经没有更多的地点要走的话,我们没有必要再次设置行走动画。

               //既然机器人已经在行走了,没有必要再告诉他这么做。

               //如果机器人还要走向另一个地点,我们就要把它旋转以面对那个地点。

               //现在,我们在else括号旁边留下注释占位符;记住这个地点,后面还要回来。
          }
           else
           {
               mNode->translate(mDirection * move);
           } // else
       } // if
MoveDemoListener::nextLocation()添加代码return语句之前:

       //如果我们用完了所有的地点,它返回false。

       if (mWalkList.empty())
           return false;
      

 

mDestination = mWalkList.front(); // this gets the front of the deque
mWalkList.pop_front(); // this removes the front of the deque
mDirection = mDestination - mNode->getPosition();
//首先我们从双端队列里取出一个向量。通过目标向量减去场景节点的当前向量,我们得取方向向量。 mDistance = mDirection.normalise(); //还记得我们要在frameStarted方法里用mDirection乘以移动量吗?如果我们这么做,我们必须把方向
//向量转换成单位向量(即,它的长度等于一)。normalise函数为我们做了这些事,并返回向量的原始长
//度。
// Rotation Code will go here later的地方添加如下代码,实现机器人的转向:
Vector3 src = mNode->getOrientation() * Vector3::UNIT_X;//getOrientation方法,返回了一个表示机器人在空间里面向方向的四元组。因为Ogre不知道机器人的
//哪一面才是它的正面,所以我们必须用UNIT_X方向乘以这个朝向,以取得机器人当前的朝向。
//有一种特殊情况将会使SceneNode::rotate失败。如果我们正试图让机器人旋转180度,旋转代码会因
//为除以0的错误而崩掉。为了解决这个问题,我们需要测试我们是否执行180度的旋转。如果是,我们只
//要用yaw来将机器人旋转180度,而不是用rotate。

  if ((1.0f + src.dotProduct(mDirection)) < 0.0001f)

// 如果两个向量是互相反向的(即,它们之间的角度是180度),它们的点乘就将是-1。所以,如果我们把

//两个向量点乘而且结果等于-1.0f,则我们用yaw旋转180度,否则我们用rotate代替。为什么我加上

//1.0f,并检查它是否小于0.0001f? 不要忘了浮点舍入错误。你应该从来不直接比较两个浮点数的大小

//最后,需要注意的是,这两个向量的点乘永远是落在[-1,1]这个区域之间的。

        {

          mNode->yaw(Degree(180));

        }

        else

       {

          Ogre::Quaternion quat = src.getRotationTo(mDirection);

//四元组就是在三维空间里对旋转的表示,getRotationTo方法返回给我们一个四元组,它
//表 示机器人从目前的朝向到我们想让它朝向方向的旋转 mNode->rotate(quat);//实现旋转 } // else
——————————————————————————————————————————

完工!你会看见一个机器人朝着指定的地点走动 ,由于大半部分代码是直接从教程中复制过来的,所以

格式有些问题,望大家见谅,同时望大家维护“OGRE3D开放资源地带”之权益。如果有需要,我可以将我

按教程编写的最终代码拷贝下来,供大家参考!

原创粉丝点击