Q Solutions

Introduction

In the second part of development when the decision was made to take the rough draft and turn the code into a complete game, I decided to make the animation system a separate component that could be used in other projects. While this makes it more complicated that it needed to be for this game, it is still only 150 lines of Tcl code for the whole system.

The interface between the game and the inimation system is via a display list (in this case called Efffects. This list is built by the animation system (somewhat inefficiently) and called by the Togl redraw proc to display the effects.

Traditional 2D animation systems rely on sprites. All we have to do is move rectangular bitmaps around the screne to create effects. With 3D we have to create objects and apply geometry transformations to those objects to create the effect we desire.

Components

Broadly speaking there are four parts to the animation system.

  1. Allocation.
  2. Animation Loop.
  3. Display list rebuilding.
  4. The effect procedures themselves.

Active Display List.

When Initialised, the animation system allocates a number of display lists from the Scene manager which are placed in a free pool. A request to allocate a new animation will allocate a free list from the pool When the animation effect finishes the allocated display list is returned to the pool.

Whenever a animation is created or it ends, the display list ( in this case Effects) is rebuilt. Each active animation has it's display list added to a vector. This vector is passed to the openGL glCallLists call. This means with one openGL call we can render hundreds or even thousands of Animation effects.

Animation Loop

To reduce display load, the scene is only redrawn once per animation cycle (currently 50mS).
Each animation cycle we rebuild the lists, but it is up to the Scene manager to decide when to redraw the display. The Animation loop makes a request for the scene to be redrawn at the end of each cycle. The Scene queues the redraw for when it best suit it.
In this way the animations can be stopped, slowed, or delayed but they will always remain in time sync with the game even if the display does not get redrawn.

Every cycle each animation procedure that has been registed is called. The procedure is passed parameters that include the allocated display list, various time information , and any other arguments that were present at the time of allocation.

The animation proc should rebuild the display list with the openGL drawing commands that are used to render the animation for when the Scene is next redrawn. Some animations may only require that the display list is generated once and then the animation proc idles for the rest of it's lifetime.

Both time and count are passed to the animation proc so that both time and step based animations can be performed.

Rotation effects

Since rotation and scaling are common functions for a number of animations, we create a static animation proc that never ends to provide general rotation matrices.
We preallocate a number of display lists with well known names, including:

    SlowRotateY, SlowRotateXYZ, FastRotateZ, Pulsator.
Rather than each animation computing it's own time based rotation transformation, it can call one of these well known display lists prior to rendering it's effect.
We also at this time producde an AntiRotate matrix that undoes the eye view rotations so that we can make items such as the Adjcent Bomb Numbers always face the viewer (Billboarding).
Note that these display lists are not placed in the animation calllist vector so they do not get displayed. They are designed to be called from other display lists to give rotational effects.

Allocation

To start an animation effect we call the animation controller with the following information:

  1. Life time (in ms)
    How long the effect should stay running (or forever).
  2. name of the effect
    This is just the name of a tcl procedure minus the prefix of sfx/
  3. Arguments for the procedure
    Any number of arguments can be passed to the procedure as long as it is expecting them.
Example
	$Mine(Anim) alloc 3000 ShieldUp 500 $themine [~ POINTFIELD]

This command starts the Shieldup effect for a period of 3 seconds. The procedure takes the following arguments:
  1. 500
    In this case this is the time for the shield to grow full size
  2. $themine
    This is the x,y,z position in the cube to place the shield
  3. [~ POINTFIELD]
    The name of the display list that contains the shield drawing commands. In this case it is a unit sphere made up of approximately 1,000 points.

SFX

Each effect is produced by a Tcl procedure. We prefix the name of the procedure with the text sfx/ as a convention which helps in locating code but for no other purpose. It does limit the potential for name collision within the game. Each sfx procedure is called with the following arguments:

  1. dlist
    The name of the display list that it can draw into.
  2. times
    A list of timing information that includes to number of animation steps so far, the total time the effect has been in existance, and the lifetime of the effect.
    The above information allows the effect to synchronise with real time.
  3. args
    Any extra arguments specified when the effect was created. This allows the same effect procedure to be used with different data.
Example
proc sfx/ShieldUp {name dlist times delay mine shield} {
	foreach {x y z} [split $mine ,] {break}
	foreach {loop time maxtime} $times {break}

	set ratio [expr {double($time) / $delay}]
	if {$ratio > 1.5} {return}

	glNewList $dlist GL_COMPILE
	glPushMatrix
	glCallList [~ MoveToZero]
	glTranslatef $x $y -$z
	glScalef $ratio $ratio $ratio
	glCallList $shield

	glPopMatrix
	glEndList
}

Explanation
The above extracts the x,y,z position to draw the shield, and the time information.
It then computes how long into the delay period we are as a ratio of 0 to 1.5

This is used to determine the size of the shield.
We then recreate the display list placing the commands to position the shield, scale the shield (from 0 to 1.5 units) and then place a call to the supplied display list (which we assume will actually draw the shield effect).

After delay ms has elapsed the ratio gets bigger than 1.5 and the procedure exits without changing the display list. Thus the display list remains static for the period of time between delay and maxtime.

After maxtime the animation controller will zero the display list and place it back in the free pool, the display list will be removed from the glCallLists vector, and then the main display list Effects will be rebuilt specifing that there is one less display list in the vector.
The code to rebuild the main list is simply


	glNewList [~ Effects] GL_COMPILE
	  glCallLists $number GL_INT $vector
	glEndList

In the program we start this animation when we select the DeFuse bomb function. We pass the animation procedure the arguments:

    delay=500 shield=PONTFIELD

Inside the POINTFIELD display list is:


	glNewList  [Scene name POINTFIELD] GL_COMPILE
	glDisable GL_LIGHTING
	glPushMatrix
	glPointSize 1
	glColor3f 1 1 1
	glCallList [~ AntiSlowRotateXYZ]
	makeWS 0.5 100 POINT
	glEnable GL_LIGHTING
	glPopMatrix
	glEndList


We can see that as well as making a sphere of 100x100 points we also call another display list called AntiSlowRotateXYZ. You may have guessed that this is one of the well known rotation display lists that are automatically maintained.
The effect of this is that the wire sphere will rotate even though the animation effect is no longer recreating the display list.
This works because the calls to nested display lists are not inlined in the openGL servers. So if we change a list that is called by another list , the new list will be used even though we have not rebuilt the list that calls the nested list.

The above also demonstrates that the animation effects can be isolated from the actual drawing commands so that the effect can be used in diverse and unrelated situations.
For example, if in the above example we replace the shield argument with 'A Tree' display list, we could get a growing tree placed at the position specified without having to create a new special effect for it. Maybe of more use would be a display list that contained text. In any case , it goes to show that the system is very flexible.

To Infinity and Beyond

While Tcl will struggle to do intensive partical system animations there is enough potential that with considerate coding we can get acceptable speed in pure Scripting for one of the most demanding PC applications, Gaming!
 
 
 


Copyright © September 2005