Loading and rendering an exported 3D animation

We're now in a position to start rendering a 3D animation, and as with rendering a static 3D model it's also surprisingly easy to do.

Adding the IwAnim API to a project

Before we can use Marmalade's animation functionality, we first need to add the IwAnim API to our project. This API builds on top of the IwGraphics API required for rendering static 3D models.

As with all such Marmalade APIs, we add support for IwAnim to a project by listing iwanim in the subprojects section of the MKB file. We must then call IwAnimInit after IwGraphicsInit has been called, and at shutdown time we need to call IwAnimTerminate.

Loading and accessing a 3D animation

The GROUP file format comes to our rescue once again in order to get animation data loaded into memory. The export process will have created a GROUP file for us already that will include the GEO, MTL, SKEL, and SKIN files, so we just need to add entries for the ANIM files that we want to use.

With everything referenced in the GROUP file, we just need to load it into memory using the resource manager, and then access the resources in the same way as we do for any other resource.

The following code snippet illustrates how we might load a GROUP file and then access the resources needed for rendering an animated 3D model:

CIwResGroup* lpFlagGroup = IwGetResManager()->
  LoadGroup("Flag/Flag.group");
CIwModel* lpFlag = static_cast<CIwModel*>(lpFlagGroup->
  GetResNamed("Flag", "CIwModel"));
CIwAnimSkel* lpSkel = static_cast<CIwAnimSkel*>(lpFlagGroup->
  GetResNamed("FlagPoleBase", "CIwAnimSkel"));
CIwAnimSkin* lpSkin = static_cast<CIwAnimSkin*>(lpFlagGroup->
  GetResNamed("Flag", "CIwAnimSkin"));
CIwAnim* lpFlagWobble = static_cast<CIwAnim*>(lpFlagGroup->
  GetResNamed("FlagWobble", "CIwAnim"));

Right, now that we have the resources in memory, we need to do something with them.

Playing back a 3D animation

In order to play back an animation, we need to let Marmalade know which animation we want to play, how fast it should be played back, and whether we want it to be a one shot or looping animation. All this and more is provided by the CIwAnimPlayer class.

After creating a new instance of CIwAnimPlayer, we must provide it with a pointer to the skeleton instance for animation. This is done as follows:

CIwAnimPlayer* lpAnimPlayer = new CIwAnimPlayer;
lpAnimPlayer->SetSkel(lpSkel);

The player object is now ready to start animating, so we just need to pass it details about the animation we want to play. This can be done with just a single line of code:

lpAnimPlayer->PlayAnim(lpFlagWobble, 1.0f,
                       CIwAnimBlendSource::LOOPING_F, 0.0f);

The PlayAnim method first takes a pointer to the CIwAnim instance we wish to play. It then expects to see a playback speed, some control flags, and a blending interval.

The playback speed is specified so that a value of 1 yields the normal exported animation speed. Doubling this value will play the animation back at twice the speed, and so on.

The function's third parameter is a set of control flags that are primarily used to indicate whether the animation should loop when it reaches the last key frame. If looping is desired, the flag CIwAnimBlendSource::LOOPING_F should be used.

There are a number of other values defined by CIwAnimBlendSource, but most of these are intended for read-only status flags and the CIwAnimPlayer class provides other methods that should be used to determine the current status. Therefore, the only other flag that will be used in this method is CIwAnimBlendSource::RESET_IF_SAME_F, which will force the animation player to restart the specified animation if it is already the current animation. If an animation that is already being played is passed in to the PlayAnim method, the request will be ignored unless this flag is used.

The animation player is now initialized, so the final thing that must be done is instruct it to calculate the required animation frame. This is done by calling the Update method of the CIwAnimPlayer instance on every iteration of the main game loop, as shown in the following code:

lpAnimPlayer->Update(lTimeStep);

The lTimeStep parameter is a float value indicating the amount of time (in seconds) by which the current animation state should be advanced. When this call completes, a copy of the skeleton will have been created with all the bones positioned and rotated correctly in order to render the current frame of animation.

Rendering a 3D animation

With the animation player now merrily updating away, the final step is to render the animated model. This is possibly the easiest part of the entire process, as the following code demonstrates:

IwGxSetViewMatrix(&lViewMatrix);
IwGxSetModelMatrix(&lModelMatrix);

IwAnimSetSkelContext(lpAnim->GetSkel());
IwAnimSetSkinContext(lpSkin);

lpFlag->Render();

IwAnimSetSkelContext(NULL);
IwAnimSetSkinContext(NULL);

Hopefully most of this already looks familiar to you. The first step is to set the view and model matrices we want to use for rendering. We then need to provide some information about the frame of animation, namely the animated skeleton and the skin data.

The skeleton information is maintained by the CIwAnimPlayer instance and can be retrieved using the GetSkel method. The skin is just the CIwAnimSkin instance as loaded by the resource manager. We use the IwAnimSetSkelContext and IwAnimSetSkinContext functions to provide this data to the rendering engine.

To render the animated model to the screen, all we have to do is call the Render method on lpFlag, which is a pointer to a CIwModel instance, just as we would if we were rendering the model without any animation.

After rendering, we clear the skin and skeleton contexts so that future model rendering calls won't try and use incorrect data during rendering. This is a good habit to get into as determining why an unanimated model has suddenly started deforming wildly could be a tricky bug to track down.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.4.191