#include "common.h" #include "display.h" #include "frustum.h" #include // Define display/draw environments for double buffering static DISPENV disp[2]; static DRAWENV draw[2]; static int db; static u_long ot[2][OTLEN]; // Ordering tables, two arrays for double buffering. These are basically the buckets for bucket sorting of polygons. const MATRIX identity = { ONE, 0, 0, 0, ONE, 0, 0, 0, ONE, 0, 0, 0, }; // Transform to change the coordinate system from PS1 Default (Y down, Z forward) to Quake (Z up, Y forward) const MATRIX quake_swizzle = { ONE, 0, 0, 0, 0, -ONE, 0, ONE, 0, 0, 0, 0, }; MATRIX light_cols = { ONE, 0, 0, ONE, 0, 0, ONE, 0, 0 }; MATRIX light_dirs = { ONE, 0, 0, 0, 0, 0, 0, 0, 0 }; MATRIX vp_matrix; u_long *curOT; // We're using a virtual screen resolution of 320x240, which is then scaled according to display resolution, // video mode and widescreen options to maintain a consistent aspect ratio. VECTOR aspect_scale = { SCREENWIDTH * ONE / 320, ONE, ONE }; u_short polyCount; u_char enableTexturing = 1; u_char enableVsync = 0; void display_init() { // This not only resets the GPU but it also installs the library's // ISR subsystem to the kernel ResetGraph(0); // Initialize GTE InitGeom(); // Screen depth for FOV control. Determines the distance of the camera to the near plane. // Setting this to half the virtual screen width gives us an exact 90 degree horizontal FOV when playing in 4:3 mode. gte_SetGeomScreen(160); // Start the display in progressive mode int screenHeight; display_reset(GetVideoMode(), 0, 0, &screenHeight); // Clear double buffer counter db = 0; // Apply the GPU environments PutDispEnv(&disp[db]); PutDrawEnv(&draw[db]); // Load test font FntLoad(960, 0); // Open up a test font text stream FntOpen(8, 16, SCREENWIDTH, screenHeight, 0, 512); } void display_reset(int mode, u_char interlaced, u_char widescreen, int *outScreenHeight) { if (mode != GetVideoMode()) SetVideoMode(mode); int screenHeight, yOffset; if (mode == MODE_NTSC) { screenHeight = SCREENHEIGHT_NTSC; yOffset = (240 - SCREENHEIGHT_NTSC) >> 1; // Vertically center the image for NTSC displays // Scale Y coordinates to correct the aspect ratio for NTSC's typical visible screen area aspect_scale.vy = 216 * ONE / 240; } else // MODE_PAL { screenHeight = SCREENHEIGHT_PAL; yOffset = (296 - SCREENHEIGHT_PAL) >> 1; // Vertically center the image for PAL displays // Scale Y coordinates to correct the aspect ratio for PAL's non-square pixels aspect_scale.vy = 256 * ONE / 240; } if (interlaced) { screenHeight <<= 1; aspect_scale.vy <<= 1; enableVsync = 1; // Define display environments, interlaced images cover the same area in VRAM SetDefDispEnv(&disp[0], 0, 0, SCREENWIDTH, screenHeight); SetDefDispEnv(&disp[1], 0, 0, SCREENWIDTH, screenHeight); // Define drawing environments, interlaced images cover the same area in VRAM SetDefDrawEnv(&draw[0], 0, 0, SCREENWIDTH, screenHeight); SetDefDrawEnv(&draw[1], 0, 0, SCREENWIDTH, screenHeight); disp[0].isinter = disp[1].isinter = 1; } else { enableVsync = 0; // Define display environments, first on top and second on bottom SetDefDispEnv(&disp[0], 0, 0, SCREENWIDTH, screenHeight); SetDefDispEnv(&disp[1], 0, screenHeight, SCREENWIDTH, screenHeight); // Define drawing environments, first on bottom and second on top SetDefDrawEnv(&draw[0], 0, screenHeight, SCREENWIDTH, screenHeight); SetDefDrawEnv(&draw[1], 0, 0, SCREENWIDTH, screenHeight); disp[0].isinter = disp[1].isinter = 0; } // Scale X coordinates to set the aspect ratio for 4:3 or 16:9 widescreen aspect_scale.vx = SCREENWIDTH * ONE / (widescreen ? 426 : 320); disp[0].screen.y = disp[1].screen.y = yOffset; disp[0].screen.h = disp[1].screen.h = screenHeight; // Set and enable clear color setRGB0(&draw[0], 0, 0, 0); setRGB0(&draw[1], 0, 0, 0); draw[0].isbg = 1; draw[0].dtd = 1; draw[1].isbg = 1; draw[1].dtd = 1; gte_SetGeomOffset(SCREENWIDTH >> 1, screenHeight >> 1); if (outScreenHeight != NULL) *outScreenHeight = screenHeight; } void display_start() { curOT = ot[db]; ClearOTagR(curOT, OTLEN); mem_prim_reset(db); polyCount = 0; gte_SetBackColor(48, 48, 48); // Ambient light color gte_SetColorMatrix(&light_cols); // Light color (up to three different lights) gte_SetLightMatrix(&identity); MATRIX proj_matrix = quake_swizzle; // Swizzle coordinates so that everything is in Quake coordinate system (Z up) ScaleMatrixL(&proj_matrix, &aspect_scale); // Apply aspect ratio correction for the current resolution MATRIX view_matrix; SVECTOR trot = { -cam_rot.vx, -cam_rot.vy, -cam_rot.vz, 0 }; // Inverse camera rotation (in Quake coordinates) VECTOR tpos = { -cam_pos.vx, -cam_pos.vy, -cam_pos.vz }; // Inverse camera position (in Quake coordinates) RotMatrixQ(&trot, &view_matrix); // Set camera rotation part of the view matrix ApplyMatrixLV(&view_matrix, &tpos, &tpos); // Apply camera rotation to camera position TransMatrix(&view_matrix, &tpos); // Apply transformed position to the translation part of the view matrix // Compose view and projection matrices to obtain a combined view-projection matrix CompMatrixLV(&proj_matrix, &view_matrix, &vp_matrix); int screenheight = (GetVideoMode() == MODE_NTSC) ? SCREENHEIGHT_NTSC : SCREENHEIGHT_PAL; frustum_update(&view_matrix, (SCREENWIDTH << 12) / aspect_scale.vx, (screenheight << 12) / aspect_scale.vy); } void display_finish() { // Flip buffer index db = !db; // Wait for all drawing to complete DrawSync(0); // Wait for vertical sync to cap the logic to 60fps (or 50 in PAL mode) and prevent screen tearing if (enableVsync) VSync(0); // Switch pages PutDispEnv(&disp[db]); PutDrawEnv(&draw[db]); // Enable display output, ResetGraph() disables it by default SetDispMask(1); DrawOTag(curOT + OTLEN - 1); // This performs a DMA transfer to quickly send all the primitives off to the GPU }