#include #include #include #include #include #include "skybox_and_splitscreen.h" #include "code_800029B0.h" #include #include "memory.h" #include "../camera.h" #include #include "render_player.h" #include "code_80057C60.h" #include "menu_items.h" #include "actors.h" #include "render_courses.h" #include "math_util.h" #include "main.h" #include "menus.h" #include "port/Engine.h" #include "engine/Matrix.h" #include "engine/tracks/Track.h" #include "engine/editor/Editor.h" #include "port/Game.h" #include "math_util.h" #include "port/interpolation/FrameInterpolation.h" #include "engine/CoreMath.h" Vp D_802B8880[] = { { { { 640, 480, 511, 0 }, { 640, 480, 511, 0 } } }, }; Vtx D_802B8890[] = { { { { SCREEN_WIDTH, SCREEN_HEIGHT, -1 }, 0, { 0, 0 }, { 0xC8, 0xC8, 0xFF, 0xFF } } }, { { { SCREEN_WIDTH, 120, -1 }, 0, { 0, 0 }, { 0x1E, 0x1E, 0xFF, 0xFF } } }, { { { 0, 120, -1 }, 0, { 0, 0 }, { 0x1E, 0x1E, 0xFF, 0xFF } } }, { { { 0, SCREEN_HEIGHT, -1 }, 0, { 0, 0 }, { 0xC8, 0xC8, 0xFF, 0xFF } } }, { { { SCREEN_WIDTH, 120, -1 }, 0, { 0, 0 }, { 0x00, 0xDC, 0x00, 0xFF } } }, { { { SCREEN_WIDTH, 0, -1 }, 0, { 0, 0 }, { 0x78, 0xFF, 0x78, 0xFF } } }, { { { 0, 0, -1 }, 0, { 0, 0 }, { 0x78, 0xFF, 0x78, 0xFF } } }, { { { 0, 120, -1 }, 0, { 0, 0 }, { 0x00, 0xDC, 0x00, 0xFF } } }, }; Vtx D_802B8910[] = { { { { SCREEN_WIDTH, SCREEN_HEIGHT, -1 }, 0, { 0, 0 }, { 0xC8, 0xC8, 0xFF, 0xFF } } }, { { { SCREEN_WIDTH, 120, -1 }, 0, { 0, 0 }, { 0x1E, 0x1E, 0xFF, 0xFF } } }, { { { 0, 120, -1 }, 0, { 0, 0 }, { 0x1E, 0x1E, 0xFF, 0xFF } } }, { { { 0, SCREEN_HEIGHT, -1 }, 0, { 0, 0 }, { 0xC8, 0xC8, 0xFF, 0xFF } } }, { { { SCREEN_WIDTH, 120, -1 }, 0, { 0, 0 }, { 0x00, 0xDC, 0x00, 0xFF } } }, { { { SCREEN_WIDTH, 0, -1 }, 0, { 0, 0 }, { 0x78, 0xFF, 0x78, 0xFF } } }, { { { 0, 0, -1 }, 0, { 0, 0 }, { 0x78, 0xFF, 0x78, 0xFF } } }, { { { 0, 120, -1 }, 0, { 0, 0 }, { 0x00, 0xDC, 0x00, 0xFF } } }, }; Vtx D_802B8990[] = { { { { SCREEN_WIDTH, SCREEN_HEIGHT, -1 }, 0, { 0, 0 }, { 0xC8, 0xC8, 0xFF, 0xFF } } }, { { { SCREEN_WIDTH, 120, -1 }, 0, { 0, 0 }, { 0x1E, 0x1E, 0xFF, 0xFF } } }, { { { 0, 120, -1 }, 0, { 0, 0 }, { 0x1E, 0x1E, 0xFF, 0xFF } } }, { { { 0, SCREEN_HEIGHT, -1 }, 0, { 0, 0 }, { 0xC8, 0xC8, 0xFF, 0xFF } } }, { { { SCREEN_WIDTH, 120, -1 }, 0, { 0, 0 }, { 0x00, 0xDC, 0x00, 0xFF } } }, { { { SCREEN_WIDTH, 0, -1 }, 0, { 0, 0 }, { 0x78, 0xFF, 0x78, 0xFF } } }, { { { 0, 0, -1 }, 0, { 0, 0 }, { 0x78, 0xFF, 0x78, 0xFF } } }, { { { 0, 120, -1 }, 0, { 0, 0 }, { 0x00, 0xDC, 0x00, 0xFF } } }, }; Vtx D_802B8A10[] = { { { { SCREEN_WIDTH, SCREEN_HEIGHT, -1 }, 0, { 0, 0 }, { 0xC8, 0xC8, 0xFF, 0xFF } } }, { { { SCREEN_WIDTH, 120, -1 }, 0, { 0, 0 }, { 0x1E, 0x1E, 0xFF, 0xFF } } }, { { { 0, 120, -1 }, 0, { 0, 0 }, { 0x1E, 0x1E, 0xFF, 0xFF } } }, { { { 0, SCREEN_HEIGHT, -1 }, 0, { 0, 0 }, { 0xC8, 0xC8, 0xFF, 0xFF } } }, { { { SCREEN_WIDTH, 120, -1 }, 0, { 0, 0 }, { 0x00, 0xDC, 0x00, 0xFF } } }, { { { SCREEN_WIDTH, 0, -1 }, 0, { 0, 0 }, { 0x78, 0xFF, 0x78, 0xFF } } }, { { { 0, 0, -1 }, 0, { 0, 0 }, { 0x78, 0xFF, 0x78, 0xFF } } }, { { { 0, 120, -1 }, 0, { 0, 0 }, { 0x00, 0xDC, 0x00, 0xFF } } }, }; void race_set_viewport(ScreenContext* arg0) { s32 ulx; s32 uly; s32 lrx; s32 lry; s32 screenWidth = arg0->screenWidth * 2; s32 screenHeight = arg0->screenHeight * 2; s32 screenStartX = arg0->screenStartX * 4; s32 screenStartY = arg0->screenStartY * 4; arg0->viewport.vp.vscale[0] = screenWidth; arg0->viewport.vp.vscale[1] = screenHeight; arg0->viewport.vp.vscale[2] = 511; arg0->viewport.vp.vscale[3] = 0; arg0->viewport.vp.vtrans[0] = screenStartX; arg0->viewport.vp.vtrans[1] = screenStartY; arg0->viewport.vp.vtrans[2] = 511; arg0->viewport.vp.vtrans[3] = 0; gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&arg0->viewport)); screenWidth /= 4; screenHeight /= 4; screenStartX /= 4; screenStartY /= 4; lrx = screenStartX + screenWidth; if (lrx > SCREEN_WIDTH) { lrx = SCREEN_WIDTH; } lry = screenStartY + screenHeight; if (lry > SCREEN_HEIGHT) { lry = SCREEN_HEIGHT; } ulx = 0; uly = 0; gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, ulx, uly, lrx, lry); } UNUSED void func_802A38AC(void) { } void func_802A38B4(void) { init_rdp(); select_framebuffer(); gDPFullSync(gDisplayListHead++); gSPEndDisplayList(gDisplayListHead++); if (gQuitToMenuTransitionCounter != 0) { gQuitToMenuTransitionCounter--; return; } gGamestateNext = gGotoMode; gGamestate = 255; gIsInQuitToMenuTransition = 0; gQuitToMenuTransitionCounter = 0; gFadeModeSelection = 1; switch (gGotoMode) { case START_MENU_FROM_QUIT: if (gMenuSelection != LOGO_INTRO_MENU) { gMenuSelection = START_MENU; } break; case MAIN_MENU_FROM_QUIT: gMenuSelection = MAIN_MENU; break; case PLAYER_SELECT_MENU_FROM_QUIT: gMenuSelection = CHARACTER_SELECT_MENU; break; case COURSE_SELECT_MENU_FROM_QUIT: gMenuSelection = COURSE_SELECT_MENU; break; } } void func_802A39E0(ScreenContext* arg0) { s32 ulx = arg0->screenStartX - (arg0->screenWidth / 2); s32 uly = arg0->screenStartY - (arg0->screenHeight / 2); s32 lrx = arg0->screenStartX + (arg0->screenWidth / 2); s32 lry = arg0->screenStartY + (arg0->screenHeight / 2); if (ulx < 0) { ulx = 0; } if (uly < 0) { uly = 0; } if (lrx > SCREEN_WIDTH) { lrx = SCREEN_WIDTH; } if (lry > SCREEN_HEIGHT) { lry = SCREEN_HEIGHT; } if (ulx >= lrx) { lrx = ulx + 2; } if (uly >= lry) { lry = uly + 2; } gDPPipeSync(gDisplayListHead++); gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); gDPSetDepthImage(gDisplayListHead++, gPhysicalZBuffer); gDPSetColorImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, gPhysicalZBuffer); gDPSetFillColor(gDisplayListHead++, 0xFFFCFFFC); gDPPipeSync(gDisplayListHead++); gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, ulx, uly, lrx, lry); gDPFillRectangle(gDisplayListHead++, ulx, uly, lrx - 1, lry - 1); gDPPipeSync(gDisplayListHead++); gDPSetColorImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, VIRTUAL_TO_PHYSICAL(gPhysicalFramebuffers[sRenderingFramebuffer])); // 0x1FFFFFFF gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); gDPSetDepthSource(gDisplayListHead++, G_ZS_PIXEL); } /** * Initialize the z-buffer for the current frame. */ void init_z_buffer(void) { gDPPipeSync(gDisplayListHead++); gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); gDPSetDepthImage(gDisplayListHead++, gPhysicalZBuffer); gDPSetColorImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, gPhysicalZBuffer); gDPSetFillColor(gDisplayListHead++, 0xFFFCFFFC); gDPPipeSync(gDisplayListHead++); gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); gDPFillRectangle(gDisplayListHead++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); gDPPipeSync(gDisplayListHead++); gDPSetColorImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, VIRTUAL_TO_PHYSICAL(gPhysicalFramebuffers[sRenderingFramebuffer])); gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); gDPSetDepthSource(gDisplayListHead++, G_ZS_PIXEL); } /** * Sets the initial RDP (Reality Display Processor) rendering settings. **/ void init_rdp(void) { gDPPipeSync(gDisplayListHead++); gDPPipelineMode(gDisplayListHead++, G_PM_1PRIMITIVE); gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); gDPSetTextureLOD(gDisplayListHead++, G_TL_TILE); gDPSetTextureLUT(gDisplayListHead++, G_TT_NONE); gDPSetTextureDetail(gDisplayListHead++, G_TD_CLAMP); gDPSetTexturePersp(gDisplayListHead++, G_TP_PERSP); gDPSetTextureFilter(gDisplayListHead++, G_TF_BILERP); gDPSetTextureConvert(gDisplayListHead++, G_TC_FILT); gDPSetCombineKey(gDisplayListHead++, G_CK_NONE); gDPSetAlphaCompare(gDisplayListHead++, G_AC_NONE); gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); gDPSetBlendMask(gDisplayListHead++, 0xFF); gDPSetColorDither(gDisplayListHead++, G_CD_DISABLE); gDPPipeSync(gDisplayListHead++); gSPClipRatio(gDisplayListHead++, FRUSTRATIO_1); } void set_viewport(void) { gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(D_802B8880)); gSPClearGeometryMode(gDisplayListHead++, G_CLEAR_ALL_MODES); gSPSetGeometryMode(gDisplayListHead++, G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH); } /** * Tells the RDP which of the three framebuffers it shall draw to. */ void select_framebuffer(void) { gDPSetColorImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, VIRTUAL_TO_PHYSICAL(gPhysicalFramebuffers[sRenderingFramebuffer])); gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(D_800DC5D0, D_800DC5D4, D_800DC5D8, 1) << 0x10 | GPACK_RGBA5551(D_800DC5D0, D_800DC5D4, D_800DC5D8, 1)); gDPPipeSync(gDisplayListHead++); gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); gDPFillWideRectangle(gDisplayListHead++, OTRGetRectDimensionFromLeftEdge(0), 0, OTRGetGameRenderWidth(), SCREEN_HEIGHT); gDPPipeSync(gDisplayListHead++); gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); } void func_802A4300(void) { if (gActiveScreenMode == SCREEN_MODE_1P) { return; } if (D_800DC5B0 != 0) { return; } gDPPipeSync(gDisplayListHead++); gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); gDPSetColorImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, VIRTUAL_TO_PHYSICAL(gPhysicalFramebuffers[sRenderingFramebuffer])); gDPSetFillColor(gDisplayListHead++, 0x00010001); gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(D_802B8880)); gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); gDPPipeSync(gDisplayListHead++); switch (gActiveScreenMode) { case SCREEN_MODE_2P_SPLITSCREEN_VERTICAL: gDPFillRectangle(gDisplayListHead++, 157, 0, 159, 239); break; case SCREEN_MODE_2P_SPLITSCREEN_HORIZONTAL: // Integer Rect getter: the float getter's negative result through // _SHIFTL is float->unsigned UB, which saturates to 0 on ARM64 and // clips the divider's left extension (see draw_box_fill_wide). gDPFillWideRectangle(gDisplayListHead++, OTRGetRectDimensionFromLeftEdge(0), 119, OTRGetGameRenderWidth(), 121); break; case SCREEN_MODE_3P_4P_SPLITSCREEN: gDPFillRectangle(gDisplayListHead++, 157, 0, 159, 239); gDPFillWideRectangle(gDisplayListHead++, OTRGetRectDimensionFromLeftEdge(0), 119, OTRGetGameRenderWidth(), 121); break; } gDPPipeSync(gDisplayListHead++); gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); } UNUSED Gfx D_802B8A90[] = { gsDPPipeSync(), gsDPSetRenderMode(G_RM_OPA_SURF, G_RM_OPA_SURF2), gsDPSetCycleType(G_CYC_FILL), gsDPSetFillColor(0x00000000), gsDPFillRectangle(0, 0, 319, 239), gsDPPipeSync(), gsDPSetCycleType(G_CYC_1CYCLE), gsSPEndDisplayList(), }; void func_802A450C(Vtx* skybox) { s32 i; if (bFog) { if (gFogColour.r < 0) { gFogColour.r = 0; } if (gFogColour.g < 0) { gFogColour.g = 0; } if (gFogColour.b < 0) { gFogColour.b = 0; } if (gFogColour.r > 255) { gFogColour.r = 255; } if (gFogColour.g > 255) { gFogColour.g = 255; } if (gFogColour.b > 255) { gFogColour.b = 255; } for (i = 0; i < 8; i++) { skybox[i].v.cn[0] = (s16) gFogColour.r; skybox[i].v.cn[1] = (s16) gFogColour.g; skybox[i].v.cn[2] = (s16) gFogColour.b; } return; } SkyboxColours* prop = (SkyboxColours*) &CM_GetProps()->Skybox; skybox[0].v.cn[0] = prop->TopRight.r; skybox[0].v.cn[1] = prop->TopRight.g; skybox[0].v.cn[2] = prop->TopRight.b; skybox[1].v.cn[0] = prop->BottomRight.r; skybox[1].v.cn[1] = prop->BottomRight.g; skybox[1].v.cn[2] = prop->BottomRight.b; skybox[2].v.cn[0] = prop->BottomLeft.r; skybox[2].v.cn[1] = prop->BottomLeft.g; skybox[2].v.cn[2] = prop->BottomLeft.b; skybox[3].v.cn[0] = prop->TopLeft.r; skybox[3].v.cn[1] = prop->TopLeft.g; skybox[3].v.cn[2] = prop->TopLeft.b; // Floor skybox[4].v.cn[0] = prop->FloorTopRight.r; skybox[4].v.cn[1] = prop->FloorTopRight.g; skybox[4].v.cn[2] = prop->FloorTopRight.b; skybox[5].v.cn[0] = prop->FloorBottomRight.r; skybox[5].v.cn[1] = prop->FloorBottomRight.g; skybox[5].v.cn[2] = prop->FloorBottomRight.b; skybox[6].v.cn[0] = prop->FloorBottomLeft.r; skybox[6].v.cn[1] = prop->FloorBottomLeft.g; skybox[6].v.cn[2] = prop->FloorBottomLeft.b; skybox[7].v.cn[0] = prop->FloorTopLeft.r; skybox[7].v.cn[1] = prop->FloorTopLeft.g; skybox[7].v.cn[2] = prop->FloorTopLeft.b; } Mtx gIdentityMatrix2 = { toFixedPointMatrix(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0), }; void func_802A487C(Vtx* arg0) { init_rdp(); if (!IsRainbowRoad()) { gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); gSPClearGeometryMode(gDisplayListHead++, G_ZBUFFER | G_LIGHTING); guOrtho(&gGfxPool->mtxScreen, 0.0f, SCREEN_WIDTH, 0.0f, SCREEN_HEIGHT, 0.0f, 5.0f, 1.0f); gSPPerspNormalize(gDisplayListHead++, 0xFFFF); gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxScreen), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); gSPMatrix(gDisplayListHead++, &gIdentityMatrix2, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPVertex(gDisplayListHead++, &arg0[4], 4, 0); gSP2Triangles(gDisplayListHead++, 0, 3, 1, 0, 1, 3, 2, 0); } } void func_802A4A0C(Vtx* vtx, ScreenContext* screen) { Camera* camera = screen->camera; s16 temp_t5; f32 temp_f0; UNUSED s32 pad[2]; UNUSED u16 pad2; u16 sp128; Mat4 matrix1 = { 0 }; Mat4 matrix2 = { 0 }; Mat4 matrix3 = { 0 }; Vec3f sp5C; f32 sp58; func_802A450C(vtx); // Widescreen skybox // Note that this is the correct fit for each screen due to how the viewport works vtx[0].v.ob[0] = OTRGetRectDimensionFromRightEdge(SCREEN_WIDTH); vtx[1].v.ob[0] = OTRGetRectDimensionFromRightEdge(SCREEN_WIDTH); vtx[2].v.ob[0] = OTRGetRectDimensionFromLeftEdge(0); vtx[3].v.ob[0] = OTRGetRectDimensionFromLeftEdge(0); vtx[4].v.ob[0] = OTRGetRectDimensionFromRightEdge(SCREEN_WIDTH); vtx[5].v.ob[0] = OTRGetRectDimensionFromRightEdge(SCREEN_WIDTH); vtx[6].v.ob[0] = OTRGetRectDimensionFromLeftEdge(0); vtx[7].v.ob[0] = OTRGetRectDimensionFromLeftEdge(0); sp5C[0] = 0.0f; sp5C[1] = 0.0f; sp5C[2] = 30000.0f; func_802B5564(matrix1, &sp128, camera->unk_B4, gScreenAspect, CM_GetProps()->NearPersp, CM_GetProps()->FarPersp, 1.0f); func_802B5794(matrix2, camera->pos, camera->lookAt); mtxf_multiplication(matrix3, matrix1, matrix2); sp58 = ((matrix3[0][3] * sp5C[0]) + (matrix3[1][3] * sp5C[1]) + (matrix3[2][3] * sp5C[2])) + matrix3[3][3]; mtxf_translate_vec3f_mat4(sp5C, matrix3); temp_f0 = (1.0 / sp58); sp5C[0] *= temp_f0; sp5C[1] *= temp_f0; sp5C[0] *= 160.0f; sp5C[1] *= 120.0f; temp_t5 = 120 - (s32) sp5C[1]; screen->cameraHeight = temp_t5; vtx[1].v.ob[1] = temp_t5; vtx[2].v.ob[1] = temp_t5; vtx[4].v.ob[1] = temp_t5; vtx[7].v.ob[1] = temp_t5; init_rdp(); gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); gSPClearGeometryMode(gDisplayListHead++, G_ZBUFFER | G_LIGHTING); guOrtho(&gGfxPool->mtxScreen, 0.0f, SCREEN_WIDTH, 0.0f, SCREEN_HEIGHT, 0.0f, 5.0f, 1.0f); gSPPerspNormalize(gDisplayListHead++, 0xFFFF); gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxScreen), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); gSPMatrix(gDisplayListHead++, &gIdentityMatrix2, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPVertex(gDisplayListHead++, &vtx[0], 4, 0); gSP2Triangles(gDisplayListHead++, 0, 3, 1, 0, 1, 3, 2, 0); if (IsRainbowRoad()) { gSPVertex(gDisplayListHead++, &vtx[4], 4, 0); gSP2Triangles(gDisplayListHead++, 0, 3, 1, 0, 1, 3, 2, 0); } } void func_802A4D18(void) { // This may not be originally accurate // Because the near/far persp values are a track member variable now. // Regardless, this would likely only effected the menus. if (gGamestate != RACING) { D_8015014C = 6800.0f; D_80150150 = 3.0f; } switch (gScreenModeSelection) { /* switch 1; irregular */ case SCREEN_MODE_1P: /* switch 1 */ gScreenAspect = 1.33333334f; return; case SCREEN_MODE_2P_SPLITSCREEN_VERTICAL: /* switch 1 */ gScreenAspect = 0.66666667f; return; case SCREEN_MODE_2P_SPLITSCREEN_HORIZONTAL: /* switch 1 */ gScreenAspect = 2.66666667f; return; case SCREEN_MODE_3P_4P_SPLITSCREEN: /* switch 1 */ gScreenAspect = 1.33333334f; return; } } void func_802A4EF4(void) { switch (gActiveScreenMode) { case SCREEN_MODE_1P: func_8001F394(gPlayerOne); break; case SCREEN_MODE_2P_SPLITSCREEN_VERTICAL: func_8001F394(gPlayerOne); func_8001F394(gPlayerTwo); break; case SCREEN_MODE_2P_SPLITSCREEN_HORIZONTAL: func_8001F394(gPlayerOne); func_8001F394(gPlayerTwo); break; case SCREEN_MODE_3P_4P_SPLITSCREEN: func_8001F394(gPlayerOne); func_8001F394(gPlayerTwo); func_8001F394(gPlayerThree); func_8001F394(gPlayerFour); break; } } /** * Refactored function, a little wonky due to preserving * the original logic. * * The swapping of logic may be a required for proper draw order. * Or, perhaps they slapped functions in differing orders for each screen on a wim. */ void race_begin_viewport(ScreenContext* screen, s32 mode) { init_rdp(); // -------------------------------------------------- // Depth clear BEFORE viewport (horizontal + 3P/4P) // -------------------------------------------------- switch(gActiveScreenMode) { case SCREEN_MODE_2P_SPLITSCREEN_HORIZONTAL: case SCREEN_MODE_3P_4P_SPLITSCREEN: func_802A39E0(screen); // Clear z-buffer only for this screen break; } race_set_viewport(screen); // differ on p3 gSPClearGeometryMode(gDisplayListHead++, G_CLEAR_ALL_MODES); gSPSetGeometryMode(gDisplayListHead++, G_SHADE | G_SHADING_SMOOTH | G_CLIPPING); switch(gActiveScreenMode) { case SCREEN_MODE_1P: init_z_buffer(); // Clear the whole z-buffer select_framebuffer(); break; case SCREEN_MODE_2P_SPLITSCREEN_VERTICAL: func_802A39E0(screen); // Clear z-buffer only for this screen break; } } /** * Creates the blank screen for player four in * three player mode */ void race_blank_viewport(ScreenContext* screen) { init_rdp(); gSPClearGeometryMode(gDisplayListHead++, G_CLEAR_ALL_MODES); gSPSetGeometryMode(gDisplayListHead++, G_SHADE | G_SHADING_SMOOTH | G_CLIPPING); gDPPipeSync(gDisplayListHead++); func_802A39E0(screen); gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); gDPSetColorImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, VIRTUAL_TO_PHYSICAL(gPhysicalFramebuffers[sRenderingFramebuffer])); gDPSetFillColor(gDisplayListHead++, 0x00010001); gDPPipeSync(gDisplayListHead++); gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 160, 120, SCREEN_WIDTH, SCREEN_HEIGHT); gDPFillRectangle(gDisplayListHead++, 160, 120, OTRGetDimensionFromRightEdge(320), SCREEN_HEIGHT - 1); gDPPipeSync(gDisplayListHead++); gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); race_set_viewport(screen); } void race_begin_viewport_4p(ScreenContext* screen) { init_rdp(); gSPClearGeometryMode(gDisplayListHead++, G_CLEAR_ALL_MODES); gSPSetGeometryMode(gDisplayListHead++, G_SHADE | G_SHADING_SMOOTH | G_CLIPPING); race_set_viewport(screen); func_802A39E0(screen); } void render_screens(ScreenContext* screen, s32 mode, s32 someId, s32 playerId) { Mat4 matrix; Camera* camera = screen->camera; s32 screenId = screen - gScreenContexts; if (NULL == camera) { printf("[skybox_and_splitscreen.c] Skipping rendering for screen %ld. This viewport has no camera\n", (long) (screen - gScreenContexts)); return; } switch(mode) { case RENDER_SCREEN_MODE_3P_4P_PLAYER_FOUR: // Blank screen if (gPlayerCountSelection1 == 3) { race_blank_viewport(screen); func_80093A5C(RENDER_SCREEN_MODE_3P_4P_PLAYER_FOUR); if (D_800DC5B8 != 0) { render_hud(RENDER_SCREEN_MODE_3P_4P_PLAYER_FOUR); } gNumScreens += 1; return; } else { // Player four race_begin_viewport_4p(screen); if ((CVarGetInteger("gDrawSky", true) == true)) { CM_RaceDrawSky(screen, someId); func_80093A30(someId); // Fill box for thunderbolt? } } break; default: race_begin_viewport(screen, mode); if ((CVarGetInteger("gDrawSky", true) == true)) { CM_RaceDrawSky(screen, someId); func_80093A30(someId); // Fill box for thunderbolt? } break; } if (gActiveScreenMode == SCREEN_MODE_2P_SPLITSCREEN_HORIZONTAL) { gSPSetGeometryMode(gDisplayListHead++, G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH); } init_rdp(); race_set_viewport(screen); gSPSetGeometryMode(gDisplayListHead++, G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH); gDPSetRenderMode(gDisplayListHead++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2); // Setup camera perspective and lookAt CM_SetViewProjection(camera); // Create a matrix for the track and game objects FrameInterpolation_RecordOpenChild("track", TAG_TRACK(((playerId << 4) | camera->cameraId))); Mat4 trackMatrix; mtxf_identity(trackMatrix); if (gIsMirrorMode != 0) { trackMatrix[0][0] = -1.0f; // trackMatrix[1][1] = -1.0f; // trackMatrix[2][2] = -1.0f; } render_set_position(trackMatrix, 0); // Draw track geography if (CVarGetInteger("gDrawTrackGeometry", true) == true) { render_track(screen); } FrameInterpolation_RecordCloseChild(); // Draw dynamic game objects if (CVarGetInteger("gDrawCActors", true) == true) { render_course_actors(screen); } if (CVarGetInteger("gDrawCPPActors", true) == true) { CM_DrawActors(camera); } if (CVarGetInteger("gDrawStaticMeshActors", true) == true) { CM_DrawStaticMeshActors(); } if (CVarGetInteger("gDrawObjects", true) == true) { render_object(screen); } if (CVarGetInteger("gDrawPlayers", true) == true) { if (CM_IsTourEnabled() == false) { render_players(camera, screenId); } } if (CVarGetInteger("gDrawTransparentTrack", true) == true) { // Track geography draw_transparent_geography(screen, playerId); } if (CVarGetInteger("gDrawParticles", true) == true) { switch (playerId) { // Render player particles or some effect case 0: func_80021B0C(); break; case 1: func_80021C78(); break; case 2: func_80021D40(); break; case 3: func_80021DA8(); break; }; } if (CVarGetInteger("gDrawItemBoxes", true) == true) { render_item_boxes(screen); } render_player_snow_effect(camera); func_80058BF4(); // Setup texture modes if (D_800DC5B8 != 0) { func_80058C20(mode); // Setup hud matrix } func_80093A5C(mode); // Perhaps pause render? if (CVarGetInteger("gDrawHUD", true) == true) { if (D_800DC5B8 != 0) { render_hud(mode); } } // Do not increment in single player mode if (mode != RENDER_SCREEN_MODE_1P_PLAYER_ONE) { gNumScreens += 1; } } // Makes the screen small at the start of a race void set_screen(void) { ScreenContext* wrapper = &gScreenContexts[0]; Player* player = &gPlayers[0]; Camera* camera = &cameras[0]; struct Controller* controller = &gControllers[0]; // struct? size = 0x10. unk++ doesn't work cause s32 too small. s32* unk = &D_8015F790[0]; s32 i; for (i = 0; i < 4; i++) { wrapper->controllers = controller; // wrapper->camera = &cameras[i]; // Done in spawn_players now wrapper->player = player; wrapper->unkC = unk; // Tick is not enabled in the editor, so the screen needs to begin at the proper size. if (((Editor_IsEnabled()) && (Editor_IsPaused()) && (i == PLAYER_ONE)) || CM_IsTourEnabled() == true) { wrapper->screenWidth = SCREEN_WIDTH; wrapper->screenHeight = SCREEN_HEIGHT; } else { // Normal race start, screen is small wrapper->screenWidth = 4; wrapper->screenHeight = 4; } wrapper->pathCounter = 1; switch (gActiveScreenMode) { case SCREEN_MODE_1P: if (i == 0) { wrapper->screenStartX = 160; } wrapper->screenStartY = 120; break; case SCREEN_MODE_2P_SPLITSCREEN_VERTICAL: if (i == 0) { wrapper->screenStartX = 80; wrapper->screenStartY = 120; } else if (i == 1) { wrapper->screenStartX = 240; wrapper->screenStartY = 120; } break; case SCREEN_MODE_2P_SPLITSCREEN_HORIZONTAL: if (i == 0) { wrapper->screenStartX = 160; wrapper->screenStartY = 60; } else if (i == 1) { wrapper->screenStartX = 160; wrapper->screenStartY = 180; } break; case SCREEN_MODE_3P_4P_SPLITSCREEN: if (i == 0) { wrapper->screenStartX = 80; wrapper->screenStartY = 60; } else if (i == 1) { wrapper->screenStartX = 240; wrapper->screenStartY = 60; } else if (i == 2) { wrapper->screenStartX = 80; wrapper->screenStartY = 180; } else { wrapper->screenStartX = 240; wrapper->screenStartY = 180; } break; } player++; camera++; wrapper++; unk += 0x10; } } void set_editor_screen(void) { ScreenContext* wrapper = &gScreenContexts[0]; wrapper->controllers = gControllerOne; wrapper->camera = gFreecamCamera; wrapper->player = gPlayerOne; wrapper->unkC = &D_8015F790[0]; wrapper->screenWidth = SCREEN_WIDTH; wrapper->screenHeight = SCREEN_HEIGHT; wrapper->pathCounter = 1; } void copy_framebuffer(s32 arg0, s32 arg1, s32 width, s32 height, u16* source, u16* target) { s32 var_v1; s32 var_a1; s32 targetIndex; s32 sourceIndex; targetIndex = 0; for (var_v1 = 0; var_v1 < height; var_v1++) { sourceIndex = ((arg1 + var_v1) * SCREEN_WIDTH) + arg0; for (var_a1 = 0; var_a1 < width; var_a1++, targetIndex++, sourceIndex++) { target[targetIndex] = source[sourceIndex]; } } } void func_802A7728(void) { s16 temp_v0; if (gActiveScreenMode == SCREEN_MODE_3P_4P_SPLITSCREEN) { D_800DC5DC = 0; } else { D_800DC5DC = 128; } D_800DC5E0 = 0; temp_v0 = (s16) sRenderedFramebuffer - 1; if (temp_v0 < 0) { temp_v0 = 2; } else if (temp_v0 > 2) { temp_v0 = 0; } if (NULL == gPhysicalFramebuffers[temp_v0]) { return; } copy_framebuffer(D_800DC5DC, D_800DC5E0, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture68272C)); copy_framebuffer(D_800DC5DC + 64, D_800DC5E0, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture682928)); copy_framebuffer(D_800DC5DC, D_800DC5E0 + 32, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture682B24)); copy_framebuffer(D_800DC5DC + 64, D_800DC5E0 + 32, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture682D20)); copy_framebuffer(D_800DC5DC, D_800DC5E0 + 64, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture682F1C)); copy_framebuffer(D_800DC5DC + 64, D_800DC5E0 + 64, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture683118)); } void func_802A7940(void) { s16 temp_v0; if (gActiveScreenMode == SCREEN_MODE_3P_4P_SPLITSCREEN) { D_800DC5DC = 0; } else { D_800DC5DC = 128; } D_800DC5E0 = 0; temp_v0 = (s16) sRenderedFramebuffer - 1; if (temp_v0 < 0) { temp_v0 = 2; } else if (temp_v0 > 2) { temp_v0 = 0; } if (gPhysicalFramebuffers[temp_v0] == NULL) { return; } copy_framebuffer(D_800DC5DC, D_800DC5E0, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture68272C)); copy_framebuffer(D_800DC5DC + 64, D_800DC5E0, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture682928)); copy_framebuffer(D_800DC5DC, D_800DC5E0 + 32, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture682B24)); copy_framebuffer(D_800DC5DC + 64, D_800DC5E0 + 32, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture682D20)); copy_framebuffer(D_800DC5DC, D_800DC5E0 + 64, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture682F1C)); copy_framebuffer(D_800DC5DC + 64, D_800DC5E0 + 64, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]), (u16*) LOAD_ASSET_RAW(gTexture683118)); }