// *********************************************************************************** // Programs written by R.Swan - rs108@mdx.ac.uk - www.netyaroze-europe.com/~middex2 // Taken from the tutorial by P.Passmore - p.passmore@mdx.ac.uk // Tutorial file can be found at the above web site // Step eleven - add a split screen. This requires twice the OTs and related stuff // - new split screen routine is in RenderWorld() and InitSplitScreen() // *********************************************************************************** // *********************************************************************************** // Preprocessor functions // *********************************************************************************** // **** include libraries and files #include // load standard libraries #include // load playstation library #include "pad.h" // load joypad reading // **** set up defines #define true (1) #define false (0) #define OTABLE_LENGTH (12) // define length of OTable #define MAX_PACKETS (124000) // define size of GPU scratchpad #define IS_PAL_USED (true) // true = PAL mode, false = NTSC mode #define PAL_WIDTH (320) // size of PAL screen #define PAL_HEIGHT (240) #define NTSC_WIDTH (320) // size of NTSC screen #define NTSC_HEIGHT (256) #define GROUND_MAXX (15) // define ground x size #define GROUND_MAXZ (15) // define ground z size #define GROUND_MAX_OBJECTS (225) // total max number of ground objects #define GROUND_SIZE (1200) // X and Z dimensions of one road object #define CAR_MEM_ADDR (0x80090000) // location of car .TMD data #define ROAD_MEM_ADDR (0x80098000) // location of track .TMD data #define ROAD_TEX_MEM_ADDR (0x800B0000) // location of texture .TIM data // **** create global variables, structures, headers and arrays (mostly system stuff) int Running = true; // define program to be running u_long VerticalSync = 0; // set up speed counter for processor GsOT OTable_Header[4]; // Header files holding OTable info GsOT_TAG OTable_Array[4][1<Object_Handler.coord2, &tLocalWorld, &tLocalScreen); // get the local world and screen // matrices. Needed for lightsourcing GsSetLightMatrix(&tLocalWorld); // Set local world matrix for lighting GsSetLsMatrix(&tLocalScreen); // Set local screen matrix for perspective GsSortObject4( &tObject->Object_Handler, tOTable, 4, (u_long*)getScratchAddr(0)); // Put object in OTable ready for drawing }; // **** draw the entire track on screen void DrawTrack(Track_Header0 *tTrack, GsOT *tOTable) { MATRIX tLocalWorld, tLocalScreen; // create two MATRIX structures int tCounter; for (tCounter = 0; tCounter < tTrack->numberObjects; tCounter++) { // go through every cell in data array GsGetLws(tTrack->Object_Handler[tCounter].coord2, &tLocalWorld, &tLocalScreen); // get local world and screen // matrices. Needed for lightsourcing GsSetLightMatrix(&tLocalWorld); // Set local world matrix for lighting GsSetLsMatrix(&tLocalScreen); // Set local screen matrix for perspective GsSortObject4( &tTrack->Object_Handler[tCounter], tOTable, 2, (u_long*)getScratchAddr(0)); // Put object in OTable ready for drawing // their value of 2 is lower than the // car's value of 4, therefore car is // drawn after ground which is correct }; }; // *********************************************************************************** // User defined initialisation functions // *********************************************************************************** // **** Load in and setup a .TIM picture file (in this case to be used for a texture) void LoadTexture(long tMemAddress) { RECT tRect; // rectangular area to be used for TIM GsIMAGE tTim; // TIM image information tMemAddress += 4; // advance memory pointer to data GsGetTimInfo((u_long *) tMemAddress, &tTim); // fill tTim with info from TIM tRect.x = tTim.px; // tell GPU where graphics data is tRect.y = tTim.py; tRect.w = tTim.pw; tRect.h = tTim.ph; LoadImage(&tRect, tTim.pixel); // load video memory with TIM data if((tTim.pmode>>3) & 0x01) // do this if TIM uses a CLUT { tRect.x = tTim.cx; // tell GPU where colour table is tRect.y = tTim.cy; tRect.w = tTim.cw; tRect.h = tTim.ch; LoadImage(&tRect, tTim.clut); // load video memory with CLUT data } DrawSync(0); // force LoadImages to complete } // **** Setup up screen void InitialiseGraphics() { if (IS_PAL_USED) // set up PAL screen { SetVideoMode(MODE_PAL); // define screen as PAL GsInitGraph(PAL_WIDTH, PAL_HEIGHT, GsINTER|GsOFSGPU, 1, 0); // define screen resolution, whether // interlaced, dithered & colour depth GsDefDispBuff(0, 0, 0, PAL_HEIGHT); // define top left corners of // both screen buffers in memory } else // OR set up NTSC screen { SetVideoMode(MODE_NTSC); // define screen as NTSC GsInitGraph(NTSC_WIDTH, NTSC_HEIGHT, GsINTER|GsOFSGPU, 1, 0); GsDefDispBuff(0, 0, 0, NTSC_HEIGHT); // define top left corners of // both screen buffers in memory }; GsInit3D(); // initialise 3d graphics routine so // 3d and font functions can work OTable_Header[0].length = OTABLE_LENGTH; // notify OTable headers of OTable OTable_Header[1].length = OTABLE_LENGTH; // array lengths OTable_Header[2].length = OTABLE_LENGTH; OTable_Header[3].length = OTABLE_LENGTH; OTable_Header[0].org = OTable_Array[0]; // notify OTable headers of OTable OTable_Header[1].org = OTable_Array[1]; // array memory locations OTable_Header[2].org = OTable_Array[2]; OTable_Header[3].org = OTable_Array[3]; GsClearOt(0, 0, &OTable_Header[0]); // clear out OTable sorting info GsClearOt(0, 0, &OTable_Header[1]); // ready for use (not strictly necessary GsClearOt(0, 0, &OTable_Header[2]); // as they are cleared before use in GsClearOt(0, 0, &OTable_Header[3]); // RenderWorld() }; // **** Define the split screen positions void InitSplitScreen(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { SplitScreenInfo[0].clip.x = x1; // set split 1 on buffer 0 SplitScreenInfo[0].clip.y = y1; SplitScreenInfo[0].clip.w = w1; SplitScreenInfo[0].clip.h = h1; SplitScreenInfo[0].ofs[0] = x1 + (w1 / 2); // set origin into middle of window SplitScreenInfo[0].ofs[1] = y1 + (h1 / 2); SplitScreenInfo[1].clip.x = x1; // set split 1 on buffer 1 SplitScreenInfo[1].clip.y = y1 + 240; SplitScreenInfo[1].clip.w = w1; SplitScreenInfo[1].clip.h = h1; SplitScreenInfo[1].ofs[0] = x1 + (w1 / 2); // set origin into middle of window SplitScreenInfo[1].ofs[1] = y1 + (h1 / 2) + 240; SplitScreenInfo[2].clip.x = x2; // set split 2 on buffer 0 SplitScreenInfo[2].clip.y = y2; SplitScreenInfo[2].clip.w = w2; SplitScreenInfo[2].clip.h = h2; SplitScreenInfo[2].ofs[0] = x2 + (w2 / 2); // set origin into middle of window SplitScreenInfo[2].ofs[1] = y2 + (h2 / 2); SplitScreenInfo[3].clip.x = x2; // set split 2 on buffer 1 SplitScreenInfo[3].clip.y = y2 + 240; SplitScreenInfo[3].clip.w = w2; SplitScreenInfo[3].clip.h = h2; SplitScreenInfo[3].ofs[0] = x2 + (w2 / 2); // set origin into middle of window SplitScreenInfo[3].ofs[1] = y2 + (h2 / 2) + 240; }; // **** Initialise a new object - ie, set up its rotation, position and model void InitModel(Object_Header0 *tObject, int tX, int tY, int tZ, u_long *tMemAddress) { tObject->rotation.vx = 0; // define orientation as zero tObject->rotation.vy = 0; tObject->rotation.vz = 0; tObject->speed = 0; // define velocity of car as zero AddModelToHeader(tObject, tX, tY, tZ, tMemAddress); // set up rest of object }; // **** Add infomation to object header to specify a 3d model to be associated with it void AddModelToHeader(Object_Header0 *tObject, int tX, int tY, int tZ, u_long *tMemAddress) { tMemAddress++; // move pointer past model Id GsMapModelingData(tMemAddress); // specify that .TMD data exists // once past the model Id GsInitCoordinate2(WORLD, &tObject->Object_Coord); // set objects coord system to that // of the world (ie, 0, 0, 0) tMemAddress += 2; // move pointer to actual 3D data GsLinkObject4((u_long)tMemAddress, &tObject->Object_Handler, 0); // associate the model with it's handler tObject->Object_Handler.coord2 = &tObject->Object_Coord; // associate coords of model to // it's handler tObject->Object_Coord.coord.t[0] = tX; // set model's initial X translation tObject->Object_Coord.coord.t[1] = tY; // Y tObject->Object_Coord.coord.t[2] = tZ; // Z tObject->Object_Coord.flg = 0; // Set to zero so object is to be updated }; // Initialise the Track void InitTrack() { long tXpos, tZpos; // temp variables char tTrackStatus; // temp variable TrackData.numberObjects = 0; // define no objects in world to start LoadTexture(ROAD_TEX_MEM_ADDR); for (tXpos = 0; tXposnumberObjects; // determine which object is being handled tTrack->Object_Pointer[tNumber] = (u_long *) tMemAddress; // tell model where road TIM is tTrack->Object_Pointer[tNumber]++; // advance pointer past initial info GsMapModelingData(tTrack->Object_Pointer[tNumber]); // specify that TIM data exists GsInitCoordinate2(WORLD, &tTrack->Object_Coord[tNumber]); // signify object origin = WORLD origin tTrack->Object_Pointer[tNumber] += 2; // advance pointer again! (standard) GsLinkObject4((u_long)tTrack->Object_Pointer[tNumber], &tTrack->Object_Handler[tNumber], 0); // associate the model with it's handler tTrack->Object_Handler[tNumber].attribute = GsDIV1; // signify polygon subdivision occurs tTrack->Object_Handler[tNumber].coord2 = &tTrack->Object_Coord[tNumber]; // associate coords of model to handler tTrack->Object_Coord[tNumber].coord.t[0] = tX; // setup initial position of model tTrack->Object_Coord[tNumber].coord.t[1] = tY; tTrack->Object_Coord[tNumber].coord.t[2] = tZ; tTrack->Object_Coord[tNumber].flg = 0; // signify it has to be recalculated tTrack->numberObjects++; // increase counter for next object }; // **** Define all lights that will be used in object rendering void InitAllLights() { InitLight(&LightSource, 0, 1, 0, 0, 127, 127, 255); // define light GsSetAmbient(2047,2047,2047); // set ambient light in the scene }; // **** Explicitly define a light source (up to three maximum in scene) void InitLight(GsF_LIGHT *tLight, int tNum, int tXv, int tYv, int tZv, int tR, int tG, int tB) { tLight->vx = tXv; tLight->vy = tYv; tLight->vz = tZv; // define vector for light from source tLight->r = tR; tLight->g = tG; tLight->b = tB; // define RGB values for the light GsSetFlatLight(tNum, tLight); // assign light to an ID number (0-2) // and turning light on }; // **** Define all information for the viewpoint void InitView(GsRVIEW2 *tView, int tProjDist, int tRZ, int tFromX, int tFromY, int tFromZ, int tToX, int tToY, int tToZ, GsCOORDINATE2 *tReference) { GsSetProjection(tProjDist); // distance from view projection screen tView->vpx = tFromX; tView->vpy = tFromY; tView->vpz = tFromZ; // set up position to look from tView->vrx = tToX; tView->vry = tToY; tView->vrz = tToZ; // set up position to look to tView->rz=-tRZ; // define rotation about view line if (tReference != false) tView->super = tReference; // set origin of model else tView->super = WORLD; }; // *********************************************************************************** // User defined miscellaneous functions // *********************************************************************************** // **** Let Yaroze respond to joypad input void ProcessUserInput() { u_long PAD = PadRead(); // read status of joypad 1 if (PAD& PADselect) Running = false; // select will quit if (PAD& PADstart) { // return car to center, and stop motion CarModel.speed = 0; // set car speed to zero CarModel.rotation.vx = 0; // straighten car and reset position CarModel.rotation.vy = 0; CarModel.rotation.vz = 0; RotateModel(&CarModel, 0, 0, 0); MoveModelTo(&CarModel, 0, 0, 0);}; if (PAD& PADup) { // move car away from viewer if (CarModel.speed<100) CarModel.speed ++;}; if (PAD& PADdown) { // move car towards viewer if (CarModel.speed>-100) CarModel.speed --;}; if (PAD& PADleft) { // rotate car anti-clockwise RotateModel(&CarModel, 0, -16, 0);}; if (PAD& PADright) { // rotate car clockwise RotateModel(&CarModel, 0, 16, 0);}; if (PAD& PADsquare) { // split screen horizontally InitSplitScreen(0, 0, 320, 120, 0, 120, 320, 120); SplitScreenMode=false;}; if (PAD& PADtriangle) { // split screen vertically InitSplitScreen(0, 0, 160, 240, 160, 0, 160, 240); SplitScreenMode=false;}; if (PAD& PADcircle) { // split screen vertically InitSplitScreen(0, 0, 320, 240, 0, 0, 320, 240); SplitScreenMode=false;}; if (PAD& PADcross) { // move split screen SplitScreenMode=true;}; }; // **** Move an object to a coordinate void MoveModelTo(Object_Header0 *tObject, int tXc, int tYc, int tZc) { tObject->Object_Coord.coord.t[0] = tXc; // set the object's x coord tObject->Object_Coord.coord.t[1] = tYc; // set the object's y coord tObject->Object_Coord.coord.t[2] = tZc; // set the object's z coord tObject->Object_Coord.flg = 0; // signify object has changed }; // **** Move an object relative to where it is at the moment void MoveModelBy(Object_Header0 *tObject, int tXv, int tYv, int tZv) { tObject->Object_Coord.coord.t[0] += tXv; // add to the object's x coord tObject->Object_Coord.coord.t[1] += tYv; // add to the object's y coord tObject->Object_Coord.coord.t[2] += tZv; // add to the object's z coord tObject->Object_Coord.flg = 0; // signify object has changed }; // **** Translate an object according to the direction it is facing void MoveModelForward(Object_Header0 *tObject, int tSpeed) { MATRIX tMatrix; // temporary rotation matrix SVECTOR tOneUnitForward, tDirectionResult; // two vectors for rotation if(tSpeed != 0) // as long as speed is not zero, { // (would cause divide by zero error) tOneUnitForward.vx = 0; // to move forward = no x translation tOneUnitForward.vy = 0; // to move forward = no y translation tOneUnitForward.vz = ONE; // to move forward = 4096 z translation RotMatrix(&tObject->rotation, &tMatrix); // turn vector into rotation matrix ApplyMatrixSV(&tMatrix, &tOneUnitForward, &tDirectionResult); // multiply our direction vector with // the rotation matrix and give the // result as another direction vector tObject->Object_Coord.coord.t[0] += tDirectionResult.vx * tSpeed / 4096; tObject->Object_Coord.coord.t[1] += tDirectionResult.vy * tSpeed / 4096; tObject->Object_Coord.coord.t[2] += tDirectionResult.vz * tSpeed / 4096; tObject->Object_Coord.flg = 0; // signify object has changed }; }; // **** Rotate an object about it's own center void RotateModel(Object_Header0 *tObject, int tXr, int tYr, int tZr) { MATRIX tMatrix; // temporary rotation matrix ResetRotation(tObject->Object_Coord.coord.m); // reset rotation part of objects // rotation and translation matrix tObject->rotation.vx = (tObject->rotation.vx + tXr) % ONE; // set up rotation about the axes tObject->rotation.vy = (tObject->rotation.vy + tYr) % ONE; tObject->rotation.vz = (tObject->rotation.vz + tZr) % ONE; RotMatrix(&tObject->rotation, &tMatrix); // turn vector into rotation matrix MulMatrix0(&tObject->Object_Coord.coord, &tMatrix, &tObject->Object_Coord.coord); // multiply object and rotation matrices tObject->Object_Coord.flg = 0; // signify object has changed }; // **** Reset a three by three rotation/scaling matrix void ResetRotation(short tArray[3][3]) { tArray[0][0]=tArray[1][1]=tArray[2][2]=ONE; // turn scalars to one tArray[0][1]=tArray[0][2]=tArray[1][0]=tArray[1][2]=tArray[2][0]=tArray[2][1]=0; // turn other cells into zero };