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.
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.
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.
Let's create a rotating light using the animation facilities provided by Ogre (this example has been loosely based on this Xadeck tutorial).
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);
BillboardSet* lightbillboardset = mSceneMgr->createBillboardSet("lightbbs", 1); lightbillboardset->setMaterialName("Examples/Flare"); Billboard* lightbillboard = lightbillboardset->createBillboard(0,0,0,ColourValue(0.5,0.3,0.0f));
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);
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);
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));
// 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);
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.
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.
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).
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.
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).
Ogre provides a series of sample characters that you may use. Below is an example on how to add a “Jaiqua” to your scene:
// 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);
bool frameStarted(const FrameEvent& evt) { mCharAnimState->addTime(evt.timeSinceLastFrame); return true; }