Ogre Tutorial 4: Animation in Ogre

Ogre Animation Basics

Animation Definition

An animation defines the variation of a series of parameters for a set of objects during a specified time span (e.g. an object performing a circular movement in 10 seconds). An animation is described by a set of tracks (each controlling specific parameters) and for each a set of keyframes associated at pre-defined time instants within the animation duration.

  • Animation
    • Interface for an animation sequence for a mesh, a path along a spline, or composed animation. It consists of a set of AnimationTrack's
  • AnimationTrack
    • A sequence of keyframes that affects a given animable object. There are different types of AnimationTracks, being one of the most common the NodeAnimationTrack
  • KeyFrame
    • A set of parameter values corresponding to a given time in an animation. (e.g. an object position at 3 seconds of the sequence). Depending on the type of AnimationTrack, there are also different types of KeyFrame's (e.g. NodeAnimationTrack's have TransformKeyFrame's)

Animation Execution

The actual execution of an animation is controlled by an animation state, which transforms the animable object's parameters for the values defined in the Animation, according to a given time instant.

  • AnimationState
    • An instance of an Animation, that represents a state associated to a given time. Time instants between keyframes are interpolated. The type of interpolation can be configured (linear, spline, …)

The same animation can be used in similar objects, each with its AnimationState. The programmer has to update the animations (typically in an update or frame listener), but to do so he/she only needs to update the AnimationState with the corresponding time instant.

Example: a circling light

Let's create a rotating light using the animation facilities provided by Ogre (this example has been loosely based on this Xadeck tutorial).

  • In the CreateScene method
    • Create a Light
      		Light* alight = mSceneMgr->createLight("AnimLight");
       
      		alight->setType(Light::LT_SPOTLIGHT);
      		alight->setDiffuseColour(ColourValue(0.25f,0.25f,0.0f));
      		alight->setSpecularColour(ColourValue(0.25f,0.25f,0.0f));
      		alight->setAttenuation(8000,1,0.0005,0);
      		alight->setSpotlightRange(Ogre::Degree(60), Ogre::Degree(70));
      		alight->setDirection(Vector3::NEGATIVE_UNIT_Y);
    • Create a geometry to represent the light (in this case a Billboard, for a simple and nice effect)
      		BillboardSet* lightbillboardset = mSceneMgr->createBillboardSet("lightbbs", 1);
      		lightbillboardset->setMaterialName("Examples/Flare");
      		Billboard* lightbillboard = lightbillboardset->createBillboard(0,0,0,ColourValue(0.5,0.3,0.0f));
    • Create a SceneNode that will be holding the light and its representation
      		SceneNode* lightNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("AnimLightNode");
       
      		lightNode->attachObject(alight);
      		lightNode->attachObject(lightbillboardset);
       
      		Real x = 20, y = 20, z = 100;
       
      		lightNode->setPosition(x,y,z);
      		Real s = 0.05f;
      		lightNode->setScale(s,s,s);
    • Create an Animation associated to the scene node, with spline interpolation, and add a NodeAnimationTrack to it
      		Real duration=4.0;
      		Real step=duration/4.0;
      		Animation* animation = mSceneMgr->createAnimation("LightAnim",duration);
      		animation->setInterpolationMode(Animation::IM_SPLINE);
      		NodeAnimationTrack* track = animation->createNodeTrack(0,lightNode);
    • Add five KeyFrame's to the track, corresponding to the cardinal points plus the repetition of the first
      		TransformKeyFrame* key;
       
      		key = track->createNodeKeyFrame(0.0f);
      		key->setTranslate(Vector3(-x, -y,z));
      		key->setScale(Vector3(s,s,s));
       
      		key = track->createNodeKeyFrame(step);
      		key->setTranslate(Vector3( -x, y,z));
      		key->setScale(Vector3(s,s,s));
       
      		key = track->createNodeKeyFrame(2.0*step);
      		key ->setTranslate(Vector3( x, y, z));
      		key->setScale(Vector3(s,s,s));
       
      		key = track->createNodeKeyFrame(3.0*step);
      		key->setTranslate(Vector3(x, -y, z));
      		key->setScale(Vector3(s,s,s));
       
      		key = track->createNodeKeyFrame(4.0*step);
      		key->setTranslate(Vector3(-x, -y,z));
      		key->setScale(Vector3(s,s,s));
    • Still in CreateScene, create an AnimationState (it should be accessible by a frame listener that will update it)
      // Declare it so that it is accessible in a frame listener (e.g. as an object you pass to the frame listener)
      AnimationState * mLightAnimationState;
      // ...
      		mLightAnimationState = mSceneMgr->createAnimationState("LightAnim");
      		mLightAnimationState->setEnabled(true);
      		mLightAnimationState->setLoop(true);
  • In a frame listener
    • Update the animation state in the frameStarted method
          bool frameStarted(const FrameEvent& evt)
          {
      	mLightAnimationState->addTime(evt.timeSinceLastFrame);
       
            return true;
          }

That's it. With these basic concepts you can already elaborate on a series of animations and effects, such as pulsing objects, magic flares around a character, moving an object along a path, etc.

Character / Mesh Animation

The same concepts described above apply to the animation of characters. The main difference is that you do not create the Animation's and animation tracks in code, but these are created interactively by the artists modelling the characters in a 3D modelling tool such as Blender or 3DS Max, and then exported to Ogre.

Character Rigging

Characters are first created as meshes in such modelling tools, and then rigged with a bone or skeleton. The skeleton consists of a set of segments (the bones) and joints, and the rigging consists of mapping different parts of the mesh to different bones (e.g. the arm polygons are linked to the arm bone).

Creating skeletal animation and exporting

The modeler can then animate the character by manipulating the skeleton's bones, and the mesh parts will deform to follow the corresponding bones. By creating a sequence of bone positions and storing each position as a keyframe, an animation is created.

For the same character, multiple animations can be created (e.g. “walking”, “running”, “falling”). When exporting a rigged character to Ogre format, you will get the associated mesh, materials and textures, and a .skeleton file that holds the information on the skeleton and all the animations associated to it.

Skeleton loading and animation playing

When a mesh is loaded in Ogre, the associated skeleton is also made available, and the corresponding Animation objects are created. You may then create one or more entities based on that mesh, and for each of them obtain the corresponding animation state, so that you can animate them independently (AnimationState updates are again performed in a frame listener).

Skeletal animation example

Ogre provides a series of sample characters that you may use. Below is an example on how to add a “Jaiqua” to your scene:

  • In the CreateScene method
                    // Setup animation defaults
                    Animation::setDefaultInterpolationMode(Animation::IM_LINEAR);
                    Animation::setDefaultRotationInterpolationMode(Animation::RIM_LINEAR);
     
    		// Create entity (Loads mesh and associated animations, if it is first reference to mesh)
    		Entity *ent = mSceneMgr->createEntity("jaiquaent", "jaiqua.mesh");
     
    		// Create a scene node and add the entity to it
    		SceneNode *mCharSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    		mCharSceneNode->attachObject(ent);
    		mCharSceneNode->pitch(Degree(90));
    		mCharSceneNode->yaw(Degree(-128));
    		mCharSceneNode->setPosition(-152, 38, 33);
     
     
    		// Gets an animation state for the animation "sneak" for the created entity, and sets it up
                    // (mCharAnimState must be accessible in the frame listener
    		mCharAnimState = ent->getAnimationState("Sneak");
    		mCharAnimState->setEnabled(true);
    		mCharAnimState->setLoop(true); 
  • In a frame listener, update the animation state in the frameStarted method
        bool frameStarted(const FrameEvent& evt)
        {
    	mCharAnimState->addTime(evt.timeSinceLastFrame);
            return true;
        }