PS2 Linux Programming
Sprite Movement Using Matrices
Introduction
In the previous tutorial
“Sprite Movement with Angles” the movement and rotation of a sprite generated
using a textured triangle strip was introduced. In this tutorial the position
and orientation of the sprite will be manipulated with matrix transformations.
Using such transformations, full control of the sprite in three dimensions can
be obtained using an orthogonal projection system. The material presented in
this tutorial serves as a good introduction to the principles used in 3D
graphics and games programming.
The structure used to hold
the sprite information is shown below and is similar to the one used in the
previous tutorial
typedef struct
{
FVector Pos; //
position on screen (center)
FVector TR, BR, TL, BL; //
untransformed coordinates
FMatrix World; //
world positional matrix
int w, h; //
width and height
int u, v; //
offset from texture top left
unsigned char r, g, b, a; //
colour and alpha
int FCountMax; //
Counter max value
int FCount; //
Frame Counter
int NFrames; //
number of animation frames
float Rot; //
Rotation angle in radians
} PS2Sprite_t;
The main changes are as
follows. The position of the sprite is now held in floating point using a
vector structure (FVector) defined as:
typedef struct
{
float v[4];
}FVector;
The untransformed
coordinates of the sprite, top left (TL), bottom left (BL), top right (TR) and
bottom right (BR) are also defined as FVectors, (these can now be stored in
three dimensions). This allows for full manipulation of these coordinates in
all three dimensions. The sprite now has a world transformation matrix
associated with it, FMatrix as defined below, which is used to position the
sprite in world coordinates.
typedef struct
{
float m[4][4];
}FMatrix;
The rotation equation
described in the previous tutorial is the perfect candidate for being turned
into a matrix as illustrated below. (x,y,z,1) are the original coordinates
which are transformed into (x’,y’,z’,w’).
The
main reason why a 4x4 matrix is used is that translation in 3 dimensions cannot
be accomplished with a 3x3 matrix so a homogeneous 4x4 matrix must be used
instead. The translation matrix is:
(x,y,z,1) are the original
coordinates and (x’,y’,z’,w’).are the translated coordinates.
One advantage of using
matrices is that they can be multiplied together (concatenated) to form one
final matrix that can be used to perform compound transformations. For example,
to translate, rotate, then translate again two homogeneous translation matrices
can be made as above, and a homogeneous rotation matrix and the three matrices
concatenated to form a final transformation matrix.
Investigating the source
code it can be seen that the standard maths functions defined in <math.h>
are not used. As stated previously, these functions on the PS2 are too __SLOW__
to be of any use when creating real games. In this tutorial, within vecmaths.cpp,
hand crafted assembly language routines are used for the required maths
operations.
In a later tutorial the
vector unit 0 (VU0) macro mode instructions will be investigated and it will be
seen that VU0 allows matrix calculations to be performed in a very efficient
and fast manner.
Much of the initialisation
that is performed in main.cpp has been described in previous tutorials and will
not be repeated here. Before the render loop is entered, the sprite structure
is initialised with appropriate information. Within the render loop, the sprite
can be rotated with the left and right buttons and the circle and cross buttons
are used to move the sprite backwards and forwards. Notice that the movement
and rotation buttons are all pressure sensitive, so the harder the buttons are
pressed the greater the associated action.
The GS packets are
constructed using the BuildSprite2D() function which is contained in the file
packet.cpp. BuildSprite2D() takes two parameters, the first is a pointer to the
structure describing the sprite to be drawn and the second is a pointer to the
start of the area of memory where the primitive data is to be built. One the
primitive data is built, it is sent to the GS using the DMAC operating in
normal mode. This could be extended to accommodate stitching if necessary
The prototype for
BuildSprite2D() is:
int
BuildSprite2D(PS2Sprite_t * pSprite, char *& pMem)
Notice that the actual
memory pointer, pMem, (rather than a copy of the pointer) is passed to this
function so that it can be incremented to reflect the amount of data written
into the packet memory ( this is the function of the *& operator).
The world transformation
matrix is built firstly by producing a rotation matrix from the angle of
rotation of the sprite then by filling in the translational components. This
matrix is then applied to the untransformed coordinated of the sprite to obtain
their world or screen positions. As before, the XYZ data for the registers are
constructed from the transformed coordinates of the quad.
At the end of the
function, the memory pointer, pMem in set to point to the next free location
after the written data.
On running the program the
sprite is drawn on screen as an animated character which walks around the
screen under control of the user. Full information on controlling the character
is contained within the accompanying readme file.
This tutorial has
introduced the concepts associated with movement and rotation under control of
user input using transformation matrices. This tutorial provided some
prerequisite information fundamental to understanding 3 dimensional graphics
and games programming.
Postscript.
In the file packet.cpp
there are two functions RotMatrixX() and RotMatrixY() commented out. Observe
the effect of using one of these instead of RotMatrixZ().
Dr Henry S Fortuna
University of Abertay
Dundee