PS2 Linux Programming

 

Sprite Movement with Angles

 

Introduction

 

This tutorial will show how to draw and manipulate 2D sprites using the triangle strip primitive. Full freedom of movement of the sprite in 2 dimensions will be introduced.

 

 

The Triangle Strip Primitive

 

The GS supports a Triangle Strip primitive, which is a series of continuous triangles sharing sides. The first triangle is drawn with 3 pieces of vertex information (1, 2, and 3 in the diagram below), and the succeeding ones are drawn whenever one further piece of vertex information is added (vertex 4 draws the second triangle in the diagram below). Full information on the format of a GS triangle strip is described on page 36 of the GS Users Manual.

 

A quad drawn with a triangle strip, (which will be textured with a bitmap image), is illustrated in the diagram below.

 


 

 

 


Four vertices are required in order to define the quad using a triangle strip primitive. Each vertex is independently defined and has position, depth, texture coordinates etc. Suitable transformations can be used to translate, scale, and rotate the sprite.

 

 

Sprite Structure

 

The structure used to hold the sprite information is shown below and is similar to the one used in the previous tutorial. The main additions are that the position on screen is now held as floats rather than ints, there are variables to store half the width and half the height of the sprite as floats, and there is a float to hold the angle of rotation of the sprite in radians. The function of these variable will become clear.

 

 

typedef struct

{

        float x, y;                           // position on screen (center)

        int z;                                 // z depth (big = near)

        float fHalfW, fHalfH;            // width/2 and height/2

        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;

 

 

2D Transformations

 

Sprite coordinates are best defined in “local space” with the origin being defined as the point at which the sprite is to be rotated about. This makes rotating, mirroring etc. much easier. As the name suggests, local space coordinates define the positions of the vertices of the quad relative to an origin which is within the same coordinate system as the quad itself. In this example, the local origin will be defined at the centre of the quad, i.e. the point where the two diagonals of the quad meet.

 

The simplest 2D transform is translation. This is achieved by adding a translation amount for each axis on to the coordinates of all of the vertices of the sprite. For example, to translate a sprite 4 units right and 10 units down, 4 is added to the x component and 10 to the y component of all 4 vertices that make up the sprite.

 

Rotation is a little harder. When rotation is applied, the vertices are rotated about the origin. The method to be used will depend on how the vertex information is stored for the sprite. If the absolute (or world) position of the vertices are stored, a translation can be used to position the point of rotation of the sprite at the origin, perform rotation, then translate the sprite back to where it was originally positioned. If there is access to the untransformed sprite coordinated, as in the example being created here, then rotation can simply be applied to these local coordinates. The equation for rotating a point about the origin is as follows (note that this is rotation about the Z axis in 3D space):

 

 

X’ = X * cos(A) – Y * sin(A)

Y’ = X * sin(A) + Y * cos(A)

 

 

A is the angle of rotation, X’ and Y’ are the rotated coordinates and X, Y are the original coordinates. Thus, in order to rotate the quad, the above equation is applied to all four coordinated to obtain the new, or rotated coordinates.

 

Moving the sprite in the direction it is pointing is now relatively straightforward. The angle of rotation, A, is known so to move the sprite, the following amounts are added to the positions of all the vertices of the quad:

 

 

dx = D * sin(A)

dy = D * cos(A)

 

The application of the above movement equation can be seen in operation in the MoveSprite() function within main.cpp.

 

Investigating the source code it can be seen that the standard maths functions of sinf and cosf which are defined in <math.h> are used. These functions on the PS2 are __SLOW__, even although they are using floating point as opposed to doubles. They are not just a little slow, they are slow enough to be useless when creating real games. For the purposes of this tutorial these standard maths functions are acceptable since they are not being used very often, but in future tutorials hand crafted assembly language routines will be used which are considerably faster.

 

 

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 structures are initialised with appropriate information. Within the render loop the sprite animation sequence can be changed with the triangle and square buttons, 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).

 

From the position of the centre of the sprite the coordinates of the untransformed vertices are first determined. These coordinates are then rotated by the angle that is contained within the sprite structure to determine the rotated coordinates of the vertices.

 

The prim field of the GIFtag is set up to draw a triangle strip primitive and texture mapping is enabled. Notice that the REGS field of the GIFTag is configured with the line of code:

 

pGIFTag->s.REGS = (uint64(0x5) << 32) | (uint64(0x35353531));

 

Each hex digit refers to a GIF register address, 1 being RGBAQ, 3 being UV and 5 being XYZ2. After the GIFTag, 9 registers are configured, one colour, four vertex positions and four texture coordinates.

 

Notice that the XYZ data for the registers are constructed from the rotated coordinates of the quad. Also, in order to preserve accuracy, the floating point coordinate values are multiplied by 16.0f (also in floating point) before being passed into the function AddVertexFP() which adds the vertex information to the XYZ2 packed register data.

 

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, two sprites are drawn on screen, one is static, the other is 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 2d movement and rotation under control of user input. In the next tutorial the techniques will be extended to investigate the use of matrices to transform the vertices.

 

 

Dr Henry S Fortuna

University of Abertay Dundee

h.s.fortuna@abertay.ac.uk