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.

 

 

Sprite Structure

 

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;

 

 

Matrices

 

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.

 

 

The main Program

 

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 BuildSprite2D function

 

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.

 

 

Conclusion

 

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

h.s.fortuna@abertay.ac.uk