Working with 3D Models

This section won't discuss any standard model formats. Instead it will mention the programmatic challenges behind animating a 3D figure. There may be some libraries that will automatically handle these problems, but it may be useful to be aware of what goes into moving the joints and the problems that can come up in game design.

Working with Joints

Generally, a joint is expressed as a full rendering matrix, including a position, and the x-y-z orientation. A complex model will have many of these, forming a sort of skeletal structure. Using a mechanical object as an example, smaller independent 3D models can be rendered at these joint positions, forming a full 3D figure. For the example of a robotic arm, the upper arm and forearm would each be a model, the palm of the hand, and every digit. Each of these would have joints between them, to join them together into a meaningful whole. As every piece is rendered, a new transformation takes place based on the next joint to render the piece attached to it. The process is repeated, pushing matrices on to the stack as the model gets deeper, and popping matrices as you return.

To animate such a figure, you could set the joints yourself, or work with some pre-defined poses that, when played in sequence, will animate the figure. In my own designs, I wrote routines that would iterate smoothly between the joint positions of each pose, resulting in a smooth transition that appeared natural. If you set up the poses and the timing between them correctly, you can generate very realistic movement with only a few poses. However, every frame will require that the joint positions be calculated, which can use up processor power and reduce your frame rate.

I've seen model representations that have many poses encased inside the binary file, defined at a certain number of frames per second, such that there is no need to iterate between poses smoothly, just to draw them exactly as they are. It results in a lot more data being loaded into memory, but it speeds up rendering by reducing the calculations required to iterate between the poses. This is especially true for models with many vertices that stretch like skin as the joints change their orientation. The calculations needed to position the vertices smoothly would tax the cpu utilization horribly, especially if you are dealing with many models in your scene at the same time.

There are some cases, however, where joint positioning cannot be expressed as part of a pose, or predefined in a file. Consider a rotating turret that can fire artillery shells. It may swivel on its base horizontally, and the turret itself has a vertical pitch to it. In order to fire accurately at a target, the turret will need to spin its base and pitch its turret so that it faces the correct direction. This cannot be accomplished by predefined poses if you want to express the positioning of the turret accurately. To handle this, you would need to calculate the target joint orientations and tell the object to move its joints smoothly from their current state to their target state.

Some model animations just play out exactly as they are defined, centered on the position of the model in the play area. This is adequate for models that basically move around the environment without any real need for a great deal of accuracy. The figures that run around in first person shooters are a good example. For a simulation that might demand more realism, like in a giant robot style game, where the feet might need to rest accurately on sloped hills, the joints have to be specifically calculated for the situation.

Each of these possibilities requires a different approach towards the program logic, the extent to which the models are predefined, and what needs to be stored at the object level.

Predefined animation loops, no smoothing:
Which animation loop are we playing?
Which "frame" of the animation are we on?
Each frame is a representation of the 3D model that doesn't require any special calculations. Just render the frame as is.

Smooth iteration between poses, robotic object:
Which animation loop are we playing?
Which frame are we on?
Where are our joints now?
Where do we want the joints to be?
How quickly should they get there?
The object is predefined as a skeletal structure that will require recursive calls to render the whole object, a transformation taking place between each joint.

Smooth iteration between poses, skinned object:
Which animation loop are we playing?
Which frame are we on?
Where are our joints now?
Where do we want the joints to be?
How quickly should they get there?
The object is predefined as a skeletal structure with additional vertices for the skin that adjust themselves based on the position of the joints. Once the full set of vertices are calculated, the entire object can be rendered at once.

Object skeleton defined, but all joints calculated in game:
Where are our joints now?
Where do we want each joint to be?
How quickly should each joint get there? (Each joint could have different speed)
The object can be predefined either as a robotic skeleton or a skinned object. The primary difference is that there are no frames of pre-defined joint positions.

It is also possible to have combinations. An object could be running along, but on top of it, a turret can turn to fire at an enemy. To handle a situation like this, the pre-defined poses only affect some of the joints. The rest of the joints can be set programmatically to fit the situation. Each joint is then handled its own way to calculate it new position during rendering.

If you can make your storage structure as general as possible, you will be able to support multiple styles of 3D model animation, opening up your capabilities to animate objects. Creating the models themselves is a different matter entirely. It will be a good idea to write a routine to import a 3D model from a commercialized format into your memory structure, allowing you to make use of retail 3D modelling software to generate your models. Software that supports creating the 3D models, setting up skeletal structures, positioning the joints for multiple poses and texture skinning would be the best option. Unfortunately, such packages rarely come cheap, and it can take a lot of research, design considerations and work to write your own.

Another type of challenge working with jointed objects is enforcing the limits of reasonable joint simulation. You wouldn't want to bend a leg all the way forward at the knee, or bend the fingers at a right angle sideways off the hand. So if you want to, for example, program a hand to point at an object, the limits of the finger joint will force the rest of the arm to adjust itself so that the finger can point at the object. Such problems are best solved logically with functions specially written for every type of model you are working with. Coming up with a data-driven way to handle the problem, such that object-specific logic is not needed, would likely take too much work to allow for the possibilities you might need in your game.

Have multiple modes for joints will also make it easier to position certain parts of the model based on controls. For instance, a figure is running forward but looking up. Just the head looks upward, requiring that there be a joint in the model that is untouched by the frame animations. This joint is set as a result of the up/down direction that the figure is looking.

As a final note, if an object is not on the screen at all, don't perform any joint calculations on it, unless those joint calculations have a real affect on the game. For example, performing the calculations for a figure running behind you would not be necessary. However, calculating the positioning of a turret that is off the map would be important, because the turret's direction determines where the shell will land!

Model Types

The most common type of 3D model is a muti-faceted polygonal figure, possibly with textures decaled on its surface. Most of the typical objects rendered in a 3D game are of this type.

Another type of model is the sprite. It is usually rendered as always facing the screen, and is typically no more than a square with a texture on it, possibly animated in some way. It is commonly used for special effects like fire, smoke or magic energy. Render many of them together and you can end up with interesting and spectacular effects. These objects are also referred to as particles.

Another sprite-style model is an object whose flat end always faces the viewpoint, but has length to it and is rendered such that a perspective effect is visible. Such models are good for rendering laser blasts, smoke trails, and wood signs that might be encountered in an arcade-style adventure game.

Note that it is possibly to combine all these types together in a single model. A jet that has a glow to its back engine as well as a flame shooting out its back might combine all three of these elements.

It is important in your design considerations to provide a way for multiple types of renderables to exist inside a single model. It will offer greater flexibility in the variety of objects you can simulate.