// RCDEMO - Radio Controlled Car demo v0.1 // // see README.TXT // // James Shaughnessy // http://www.netyaroze-europe.com/~shaughnj #include #include "pad.h" #include "sincos.h" // Sin/Cos LUTs (4096) #include "addr.h" // Combine mem addresses for .dat (cheers Darco!) // DEBUG flag #define DEBUG #define MOVE_STEP (16) // used in debugging #define SUBDIVISION_LEVEL (1) // 0-5 == None to 32x32 #define NUM_PLAYERS (1) #define CAMERA_HEIGHT (1024) #define CAR_SIZE (144) // Diagonal cross (halved) #define FW_ANGLE (384) // Front-to-Wheel angle (4096=360) #define WHEEL_RADIUS (32) #define MAX_STEER (384) #define MAX_SPEED (320) #define SCREENW (512) #define SCREENH (256) #define OT_LENGTH (12) // World OT #define PRI_OT_LENGTH (12) // Priority OT #define NUM_PACKETS (204800) #define STATIONARY (0) // Totally fixed camera #define FOLLOW_CAR (1) // Camera pans following car #define BEHIND_CAR (2) // 3rd person behind view #define IN_CAR (3) // 1st person view // Typedef struct definitions typedef struct { GsDOBJ2 gsdobj2; GsCOORDINATE2 gscoordinate2; } OBJECT; typedef struct { long x, y, z; long speed; long oldspeed; long steering; long rotation; long wheelrotation; short size; OBJECT Car; // Player's 3D TMD model OBJECT Wheel[4]; // The 4 Wheel TMDs (no!) } PLAYER; // Global Variables // Two Ordering Tables, one for each buffer GsOT WorldOT[2]; // Two Priority Ordering Tables, one for each buffer GsOT PriOT[2]; // Two Ordering Table Tags, one for each buffer GsOT_TAG OTTag[2][1< 0) Player[0].speed--; } if (padStatus & PADsquare) // Reverse { if (Player[0].speed > (-MAX_SPEED/2)) Player[0].speed-=2; } else if (!(padStatus & PADR1)) // Decelerate (if not R1) { if (Player[0].speed < 0) Player[0].speed++; } if (padStatus & PADleft) // Steer left { if (Player[0].steering > -MAX_STEER) Player[0].steering-=16; } else if (padStatus & PADright) // Steer right { if (Player[0].steering < MAX_STEER) Player[0].steering+=16; } else if (!(padStatus & PADL1)) // L1 holds the steering { if (Player[0].steering > 0) Player[0].steering-=16; else if (Player[0].steering < 0) Player[0].steering+=16; } if (padStatus & PADtriangle) { if (++cameraMode > IN_CAR) cameraMode = STATIONARY; } //Set car rotation relative to speed Player[0].rotation += (Player[0].speed * Player[0].steering)>>10; // Keep within legal range // important!! if (Player[0].rotation < 0) Player[0].rotation += 4096; else if (Player[0].rotation >= 4096) Player[0].rotation -= 4096; // Move car forward by relative amount (.rotation must be in legal range) Player[0].x -= (Player[0].speed * SIN[Player[0].rotation])>>14; Player[0].z -= (Player[0].speed * COS[Player[0].rotation])>>14; // PAD + L2/R2 moves the camera if (padStatus & PADR2) { if (padStatus & PADleft) MoveCamera(&view, -MOVE_STEP, 0, 0); else if (padStatus & PADright) MoveCamera(&view, MOVE_STEP, 0, 0); if (padStatus & PADup) MoveCamera(&view, 0, 0, MOVE_STEP); else if (padStatus & PADdown) MoveCamera(&view, 0, 0, -MOVE_STEP); } else if (padStatus & PADL2) { if (padStatus & PADup) MoveCamera(&view, 0, -MOVE_STEP, 0); else if (padStatus & PADdown) MoveCamera(&view, 0, MOVE_STEP, 0); } // Position Player Cars PositionObject(&Player[0].Car, Player[0].x, Player[0].y, Player[0].z); // Set Car Rotation and pitch (acceleration dip) RotateObject(&Player[0].Car, (Player[0].oldspeed-Player[0].speed)<<4, Player[0].rotation, 0); // Set position of wheels SetCarWheels(&Player[0]); // Set Camera Mode switch (cameraMode) { case STATIONARY : view.super = WORLD; break; case FOLLOW_CAR : PointCamera(&view, Player[0].x, Player[0].y, Player[0].z); view.super = WORLD; break; case BEHIND_CAR : PointCamera(&view, Player[0].x, Player[0].y, Player[0].z); PositionCamera(&view, Player[0].x + (SIN[Player[0].rotation]>>3), Player[0].y-128, Player[0].z + (COS[Player[0].rotation]>>3)); view.super = WORLD; break; case IN_CAR : PointCamera(&view, -Player[0].steering, -128, -1024); PositionCamera(&view, 0, -128, 512); view.super = &Player[0].Car.gscoordinate2; break; default : break; } // Activate view (must be called once per frame) GsSetRefView2(&view); } // Draw the world and perform the vertical blank wait void DrawWorld() { activeBuff = GsGetActiveBuff(); GsSetWorkBase((PACKET *)GpuPacketArea[activeBuff]); // Clear the Ordering Tables GsClearOt(0, 0, &WorldOT[activeBuff]); GsClearOt(0, 0, &PriOT[activeBuff]); // Draw Car (and wheels) to PriOT DrawObject(&Player[0].Car, &PriOT[activeBuff]); DrawObject(&Player[0].Wheel[0], &PriOT[activeBuff]); DrawObject(&Player[0].Wheel[1], &PriOT[activeBuff]); DrawObject(&Player[0].Wheel[2], &PriOT[activeBuff]); DrawObject(&Player[0].Wheel[3], &PriOT[activeBuff]); // Draw World to WorldOT DrawObject(&Ground, &WorldOT[activeBuff]); #ifdef DEBUG // Display H-Sync counter FntPrint("HSyncs: %d\n\n", hsyncs); FntPrint("Camera (%d,%d,%d)\n", view.vpx, view.vpy, view.vpz); FntPrint("cameraMode = %d\n", cameraMode); FntPrint("\nPlayer1.rotation = %d\n", Player[0].rotation); #endif FntFlush(-1); // Wait for end of drawing, a vertical blank then swap buffers DrawSync(0); hsyncs = VSync(0); GsSwapDispBuff(); // Register Clear in OT GsSortClear(0, 0, 0, &WorldOT[activeBuff]); // Draw the Ordering Tables GsDrawOt(&WorldOT[activeBuff]); GsDrawOt(&PriOT[activeBuff]); } // Sets the wheels on a player's car void SetCarWheels(PLAYER *player) { // temp angles long a1, a2; if ( (a1 = player->rotation - FW_ANGLE) < 0) a1 += 4096; if ( (a2 = player->rotation + FW_ANGLE) >= 4096) a2 -= 4096; // Set wheel rotation relative to speed Player[0].wheelrotation += Player[0].speed<<2; if (Player[0].wheelrotation < 0) Player[0].wheelrotation += 4096; else if (Player[0].wheelrotation >= 4096) Player[0].wheelrotation -= 4096; // Set Wheel positions PositionObject(&player->Wheel[0], player->x - ((player->size * SIN[a1])>>12), player->y, player->z - ((player->size * COS[a1])>>12)); PositionObject(&player->Wheel[1], player->x - ((player->size * SIN[a2])>>12), player->y, player->z - ((player->size * COS[a2])>>12)); PositionObject(&player->Wheel[2], player->x + ((player->size * SIN[a1])>>12), player->y, player->z + ((player->size * COS[a1])>>12)); PositionObject(&player->Wheel[3], player->x + ((player->size * SIN[a2])>>12), player->y, player->z + ((player->size * COS[a2])>>12)); // Set Wheel direction/spin (wheel order 0fl, 1fr, 2br, 3bl) RotateObject(&player->Wheel[0], -player->wheelrotation, player->rotation + 2048 + player->steering, 0); RotateObject(&player->Wheel[1], player->wheelrotation, player->rotation + player->steering, 0); RotateObject(&player->Wheel[2], player->wheelrotation, player->rotation, 0); RotateObject(&player->Wheel[3], -player->wheelrotation, player->rotation + 2048, 0); } // Inits player variables and model data void InitPlayer(PLAYER *player) { player->x = 0; player->y = -WHEEL_RADIUS; player->z = 0; player->speed = 0; player->oldspeed = 0; player->rotation = 0; player->steering = 0; player->wheelrotation = 0; player->size = CAR_SIZE; Init3DObject(&player->Car, RCCAR_TMD_ADDR); Init3DObject(&player->Wheel[0], WHEEL_TMD_ADDR); Init3DObject(&player->Wheel[1], WHEEL_TMD_ADDR); Init3DObject(&player->Wheel[2], WHEEL_TMD_ADDR); Init3DObject(&player->Wheel[3], WHEEL_TMD_ADDR); } // Sets the position of a 3D object void PositionObject(OBJECT *object, int x, int y, int z) { // Translate t array by component values object->gscoordinate2.coord.t[0] = x; object->gscoordinate2.coord.t[1] = y; object->gscoordinate2.coord.t[2] = z; // Signal change flag object->gscoordinate2.flg = 0; } // 3D object rotation functions // Sets objects rotation direction, in X, Y then Z // RotMatrixX/Y/Z are used to control order void RotateObject(OBJECT *object, int x, int y, int z) { // Reset object's rotation matrix to identity ResetMatrix(object->gscoordinate2.coord.m); // Perform Rotation in each axis RotMatrixX(x, &object->gscoordinate2.coord); RotMatrixY(y, &object->gscoordinate2.coord); RotMatrixZ(z, &object->gscoordinate2.coord); // Signal change flag object->gscoordinate2.flg = 0; } // Resets an object rotation matrix to identity void ResetMatrix(short m[3][3]) { m[0][0] = m[1][1] = m[2][2] = ONE; // Diagonal m[1][0] = m[2][0] = 0; m[0][1] = m[2][1] = 0; m[0][2] = m[1][2] = 0; } // Initialises a 3D object with a TMD model void Init3DObject(OBJECT *object, u_long memaddr) { // Map TMD to memory address GsMapModelingData((u_long *)(memaddr+4)); // Init object coordinate system GsInitCoordinate2(WORLD, &object->gscoordinate2); // Link TMD to object handler GsLinkObject4((u_long)(memaddr+12), &object->gsdobj2, 0); // Assign coords object->gsdobj2.coord2 = &object->gscoordinate2; // Set initial location to world origin object->gscoordinate2.coord.t[0] = 0; object->gscoordinate2.coord.t[1] = 0; object->gscoordinate2.coord.t[2] = 0; // Signal change flag object->gscoordinate2.flg = 0; } // Sorts a 3D object into an OT void DrawObject(OBJECT *object, GsOT *pOT) { // Temporary local screen and local world matrices MATRIX lscreen, lworld; // Get local world and screen coordinates used in light calculations GsGetLws(object->gsdobj2.coord2, &lworld, &lscreen); // Set light source matrix GsSetLightMatrix(&lworld); // Set local screen matrix for GTE (geometry transformation engine) GsSetLsMatrix(&lscreen); // Sort object in OT GsSortObject4(&object->gsdobj2, pOT, 14-OT_LENGTH, (u_long *)getScratchAddr(0)); } // Camera Functions // Point camer at coordinates (x,y,z) void PointCamera(GsRVIEW2 *pview, int x, int y, int z) { // Set view position pview->vrx = x; pview->vry = y; pview->vrz = z; } // Sets camera position to (x,y,z) void PositionCamera(GsRVIEW2 *pview, int x, int y, int z) { // Set view position pview->vpx = x; pview->vpy = y; pview->vpz = z; } // Moves camera position by (x,y,z) void MoveCamera(GsRVIEW2 *pview, int x, int y, int z) { // Set view position pview->vpx += x; pview->vpy += y; pview->vpz += z; } // Initialises view parameters void InitView(GsRVIEW2 *pview, int projdist) { // Set projection distance GsSetProjection(projdist); // Set view position, view reference position, and z axis rotation pview->vpx = CAMERA_HEIGHT; pview->vpy = -CAMERA_HEIGHT/2; pview->vpz = -CAMERA_HEIGHT; pview->vrx = 0; pview->vry = 0; pview->vrz = 0; pview->rz = 0; // Set origin of coord system (WORLD or local) pview->super = WORLD; // Activate view GsSetRefView2(pview); } // Lighting Functions // Initialises lighting system void InitAllLights() { InitParallelLight(&ParallelLight[0], 0, 1, 1, 1, 0x00, 0x50, 0x50); InitParallelLight(&ParallelLight[1], 1, 1, 2, -2, 0x50, 0x00, 0x50); InitParallelLight(&ParallelLight[2], 2, -1, 1, 1, 0x50, 0x50, 0x00); GsSetAmbient(1024, 1024, 1024); // range 0-4095 GsSetLightMode(GsLMODE_NORMAL); // _NORMAL _FOG or _LOFF } // Initialises a parallel light source void InitParallelLight(GsF_LIGHT *lt, int id, int x, int y, int z, int r, int g, int b) { // Set light direction and colour lt->vx = x; lt->vy = y; lt->vz = z; lt->r = r; lt->g = g; lt->b = b; // Activate parallel light GsSetFlatLight(id, lt); } // Initialises Video, 3D Graphics and the Frame Buffer void InitGraphics() { int i; SetVideoMode(MODE_PAL); GsInitGraph(SCREENW, SCREENH, GsOFSGPU|GsNONINTER, 1, 0); // Dither ON GsDISPENV.screen.y = 20; GsDISPENV.screen.h = SCREENH; GsDefDispBuff(0, 0, 0, 256); // Init 3D graphics GsInit3D(); // Init Ordering Tables for (i = 0; i < 2; i++) { WorldOT[i].length = OT_LENGTH; WorldOT[i].org = OTTag[i]; GsClearOt(0, 0, &WorldOT[i]); PriOT[i].length = PRI_OT_LENGTH; PriOT[i].org = PriOTTag[i]; GsClearOt(0, 0, &PriOT[i]); } // Loads the debug font into Frame Buffer FntLoad(960, 256); // Font printing location FntOpen(-144, -112, 288, 224, 0, 512); } // Loads a TIM into the frame buffer void LoadTIM(u_long memaddr) { RECT rect; GsIMAGE im; // Get TIM info GsGetTimInfo((u_long *)memaddr+1, &im); // Set rect and load into frame buffer setRECT(&rect, im.px, im.py, im.pw, im.ph); LoadImage(&rect, im.pixel); // Test if TIM has a clut (4 or 8 bit) and so load clut too if ((im.pmode>>3) & 1) { setRECT(&rect, im.cx, im.cy, im.cw, im.ch); LoadImage(&rect, im.clut); } DrawSync(0); } // PAD Routines // low-level pad buffers: never need to touch volatile u_char *bb0, *bb1; // call once only in program initialisation void PadInit (void) { GetPadBuf(&bb0, &bb1); } // call once per VSync(0) // puts controller pad status into unsigned long integer // please refer to the manuals if you want explanation // of the internals of this function u_long PadRead(void) { return(~(*(bb0+3) | *(bb0+2) << 8 | *(bb1+3) << 16 | *(bb1+2) << 24)); }