intro_geo.c (17802B)
1 #include <PR/ultratypes.h> 2 3 #include "game/memory.h" 4 #include "game/segment2.h" 5 #include "game/segment7.h" 6 #include "intro_geo.h" 7 #include "sm64.h" 8 #include "textures.h" 9 #include "types.h" 10 #include "buffers/framebuffers.h" 11 #include "game/game_init.h" 12 #include "audio/external.h" 13 14 // frame counts for the zoom in, hold, and zoom out of title model 15 #define INTRO_STEPS_ZOOM_IN 20 16 #define INTRO_STEPS_HOLD_1 75 17 #define INTRO_STEPS_ZOOM_OUT 91 18 19 // background types 20 #define INTRO_BACKGROUND_SUPER_MARIO 0 21 #define INTRO_BACKGROUND_GAME_OVER 1 22 23 struct GraphNodeMore { 24 /*0x00*/ struct GraphNode node; 25 /*0x14*/ void *todo; 26 /*0x18*/ u32 unk18; 27 }; 28 29 // intro geo bss 30 #if defined(VERSION_SH) || defined(VERSION_CN) 31 static u16 *sFramebuffers[3]; 32 #endif 33 static s32 sGameOverFrameCounter; 34 static s32 sGameOverTableIndex; 35 static s16 sIntroFrameCounter; 36 static s32 sTmCopyrightAlpha; 37 38 /** 39 * Geo callback to render the "Super Mario 64" logo on the title screen 40 */ 41 Gfx *geo_intro_super_mario_64_logo(s32 state, struct GraphNode *node, UNUSED void *context) { 42 struct GraphNode *graphNode = node; 43 Gfx *dl = NULL; 44 Gfx *dlIter = NULL; 45 Mtx *scaleMat; 46 f32 *scaleTable1 = segmented_to_virtual(intro_seg7_table_0700C790); 47 f32 *scaleTable2 = segmented_to_virtual(intro_seg7_table_0700C880); 48 f32 scaleX; 49 f32 scaleY; 50 f32 scaleZ; 51 52 if (state != 1) { 53 sIntroFrameCounter = 0; 54 } else if (state == 1) { 55 graphNode->flags = (graphNode->flags & 0xFF) | (LAYER_OPAQUE << 8); 56 scaleMat = alloc_display_list(sizeof(*scaleMat)); 57 dl = alloc_display_list(4 * sizeof(*dl)); 58 dlIter = dl; 59 60 // determine scale based on the frame counter 61 if (sIntroFrameCounter >= 0 && sIntroFrameCounter < INTRO_STEPS_ZOOM_IN) { 62 // zooming in 63 scaleX = scaleTable1[sIntroFrameCounter * 3]; 64 scaleY = scaleTable1[sIntroFrameCounter * 3 + 1]; 65 scaleZ = scaleTable1[sIntroFrameCounter * 3 + 2]; 66 } else if (sIntroFrameCounter >= INTRO_STEPS_ZOOM_IN && sIntroFrameCounter < INTRO_STEPS_HOLD_1) { 67 // holding 68 scaleX = 1.0f; 69 scaleY = 1.0f; 70 scaleZ = 1.0f; 71 } else if (sIntroFrameCounter >= INTRO_STEPS_HOLD_1 && sIntroFrameCounter < INTRO_STEPS_ZOOM_OUT) { 72 // zooming out 73 scaleX = scaleTable2[(sIntroFrameCounter - INTRO_STEPS_HOLD_1) * 3]; 74 scaleY = scaleTable2[(sIntroFrameCounter - INTRO_STEPS_HOLD_1) * 3 + 1]; 75 scaleZ = scaleTable2[(sIntroFrameCounter - INTRO_STEPS_HOLD_1) * 3 + 2]; 76 } else { 77 // disappeared 78 scaleX = 0.0f; 79 scaleY = 0.0f; 80 scaleZ = 0.0f; 81 } 82 guScale(scaleMat, scaleX, scaleY, scaleZ); 83 84 gSPMatrix(dlIter++, scaleMat, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); 85 gSPDisplayList(dlIter++, &intro_seg7_dl_logo); // draw model 86 gSPPopMatrix(dlIter++, G_MTX_MODELVIEW); 87 gSPEndDisplayList(dlIter); 88 89 sIntroFrameCounter++; 90 } 91 return dl; 92 } 93 94 /** 95 * Geo callback to render TM and Copyright on the title screen 96 */ 97 Gfx *geo_intro_tm_copyright(s32 state, struct GraphNode *node, UNUSED void *context) { 98 struct GraphNode *graphNode = node; 99 Gfx *dl = NULL; 100 Gfx *dlIter = NULL; 101 102 if (state != 1) { // reset 103 sTmCopyrightAlpha = 0; 104 } else if (state == 1) { // draw 105 dl = alloc_display_list(5 * sizeof(*dl)); 106 dlIter = dl; 107 gSPDisplayList(dlIter++, dl_proj_mtx_fullscreen); 108 gDPSetEnvColor(dlIter++, 255, 255, 255, sTmCopyrightAlpha); 109 switch (sTmCopyrightAlpha) { 110 case 255: // opaque 111 graphNode->flags = (graphNode->flags & 0xFF) | (LAYER_OPAQUE << 8); 112 gDPSetRenderMode(dlIter++, G_RM_AA_OPA_SURF, G_RM_AA_OPA_SURF2); 113 break; 114 default: // blend 115 graphNode->flags = (graphNode->flags & 0xFF) | (LAYER_TRANSPARENT << 8); 116 gDPSetRenderMode(dlIter++, G_RM_AA_XLU_SURF, G_RM_AA_XLU_SURF2); 117 break; 118 } 119 gSPDisplayList(dlIter++, &intro_seg7_dl_copyright); // draw model 120 gSPEndDisplayList(dlIter); 121 122 // Once the "Super Mario 64" logo has just about zoomed fully, fade in the "TM" and copyright text 123 if (sIntroFrameCounter >= 19) { 124 sTmCopyrightAlpha += 26; 125 if (sTmCopyrightAlpha > 255) { 126 sTmCopyrightAlpha = 255; 127 } 128 } 129 } 130 return dl; 131 } 132 133 /** 134 * Generates a display list for a single background tile 135 * 136 * @param index which tile to render (value from 0 to 11) 137 * @param backgroundTable array describing which image to use for each tile (0 denotes a "Super Mario 64" image, and 1 denotes a "Game Over" image) 138 */ 139 static Gfx *intro_backdrop_one_image(s32 index, s8 *backgroundTable) { 140 // intro screen background display lists for each of four 80x20 textures 141 static const Gfx *introBackgroundDlRows[] = { title_screen_bg_dl_0A000130, title_screen_bg_dl_0A000148, 142 title_screen_bg_dl_0A000160, title_screen_bg_dl_0A000178 }; 143 144 // intro screen background texture X offsets 145 static float xCoords[] = { 146 0, 80, 160, 240, 147 0, 80, 160, 240, 148 0, 80, 160, 240, 149 }; 150 151 // intro screen background texture Y offsets 152 static float yCoords[] = { 153 160, 160, 160, 160, 154 80, 80, 80, 80, 155 0, 0, 0, 0, 156 }; 157 158 // table that points to either the "Super Mario 64" or "Game Over" tables 159 static const u8 *const *textureTables[] = { mario_title_texture_table, game_over_texture_table }; 160 161 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 162 Gfx *displayList = alloc_display_list(36 * sizeof(*displayList)); 163 Gfx *displayListIter = displayList; 164 const u8 *const *vIntroBgTable = segmented_to_virtual(textureTables[backgroundTable[index]]); 165 s32 i; 166 167 guTranslate(mtx, xCoords[index], yCoords[index], 0.0f); 168 gSPMatrix(displayListIter++, mtx, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_PUSH); 169 gSPDisplayList(displayListIter++, &title_screen_bg_dl_0A000118); 170 for (i = 0; i < 4; i++) { 171 gDPLoadTextureBlock(displayListIter++, vIntroBgTable[i], G_IM_FMT_RGBA, G_IM_SIZ_16b, 80, 20, 0, 172 G_TX_CLAMP, G_TX_CLAMP, 7, 6, G_TX_NOLOD, G_TX_NOLOD) 173 gSPDisplayList(displayListIter++, introBackgroundDlRows[i]); 174 } 175 gSPPopMatrix(displayListIter++, G_MTX_MODELVIEW); 176 gSPEndDisplayList(displayListIter); 177 return displayList; 178 } 179 180 static s8 introBackgroundIndexTable[] = { 181 INTRO_BACKGROUND_SUPER_MARIO, INTRO_BACKGROUND_SUPER_MARIO, INTRO_BACKGROUND_SUPER_MARIO, 182 INTRO_BACKGROUND_SUPER_MARIO, INTRO_BACKGROUND_SUPER_MARIO, INTRO_BACKGROUND_SUPER_MARIO, 183 INTRO_BACKGROUND_SUPER_MARIO, INTRO_BACKGROUND_SUPER_MARIO, INTRO_BACKGROUND_SUPER_MARIO, 184 INTRO_BACKGROUND_SUPER_MARIO, INTRO_BACKGROUND_SUPER_MARIO, INTRO_BACKGROUND_SUPER_MARIO, 185 }; 186 187 // only one table of indexes listed 188 static s8 *introBackgroundTables[] = { introBackgroundIndexTable }; 189 190 /** 191 * Geo callback to render the intro background tiles 192 */ 193 Gfx *geo_intro_regular_backdrop(s32 state, struct GraphNode *node, UNUSED void *context) { 194 struct GraphNodeMore *graphNode = (struct GraphNodeMore *) node; 195 s32 index = graphNode->unk18 & 0xff; // TODO: word at offset 0x18 of struct GraphNode (always ends up being 0) 196 s8 *backgroundTable = introBackgroundTables[index]; 197 Gfx *dl = NULL; 198 Gfx *dlIter = NULL; 199 s32 i; 200 201 if (state == 1) { // draw 202 dl = alloc_display_list(16 * sizeof(*dl)); 203 dlIter = dl; 204 graphNode->node.flags = (graphNode->node.flags & 0xFF) | (LAYER_OPAQUE << 8); 205 gSPDisplayList(dlIter++, &dl_proj_mtx_fullscreen); 206 gSPDisplayList(dlIter++, &title_screen_bg_dl_0A000100); 207 for (i = 0; i < 12; i++) { 208 gSPDisplayList(dlIter++, intro_backdrop_one_image(i, backgroundTable)); 209 } 210 gSPDisplayList(dlIter++, &title_screen_bg_dl_0A000190); 211 gSPEndDisplayList(dlIter); 212 } 213 return dl; 214 } 215 216 static s8 gameOverBackgroundTable[] = { 217 INTRO_BACKGROUND_GAME_OVER, INTRO_BACKGROUND_GAME_OVER, INTRO_BACKGROUND_GAME_OVER, 218 INTRO_BACKGROUND_GAME_OVER, INTRO_BACKGROUND_GAME_OVER, INTRO_BACKGROUND_GAME_OVER, 219 INTRO_BACKGROUND_GAME_OVER, INTRO_BACKGROUND_GAME_OVER, INTRO_BACKGROUND_GAME_OVER, 220 INTRO_BACKGROUND_GAME_OVER, INTRO_BACKGROUND_GAME_OVER, INTRO_BACKGROUND_GAME_OVER, 221 }; 222 223 /** 224 * Geo callback to render the Game Over background tiles 225 */ 226 Gfx *geo_intro_gameover_backdrop(s32 state, struct GraphNode *node, UNUSED void *context) { 227 struct GraphNode *graphNode = node; 228 Gfx *dl = NULL; 229 Gfx *dlIter = NULL; 230 s32 j; 231 s32 i; 232 233 if (state != 1) { // reset 234 sGameOverFrameCounter = 0; 235 sGameOverTableIndex = -2; 236 for (i = 0; i < ARRAY_COUNT(gameOverBackgroundTable); i++) { 237 gameOverBackgroundTable[i] = INTRO_BACKGROUND_GAME_OVER; 238 } 239 } else { // draw 240 dl = alloc_display_list(16 * sizeof(*dl)); 241 dlIter = dl; 242 if (sGameOverTableIndex == -2) { 243 if (sGameOverFrameCounter == 180) { 244 sGameOverTableIndex++; 245 sGameOverFrameCounter = 0; 246 } 247 } else { 248 // transition tile from "Game Over" to "Super Mario 64" 249 if (sGameOverTableIndex != 11 && !(sGameOverFrameCounter & 0x1)) { 250 // order of tiles that are flipped from "Game Over" to "Super Mario 64" 251 static s8 flipOrder[] = { 0, 1, 2, 3, 7, 11, 10, 9, 8, 4, 5, 6 }; 252 253 sGameOverTableIndex++; 254 gameOverBackgroundTable[flipOrder[sGameOverTableIndex]] = 255 INTRO_BACKGROUND_SUPER_MARIO; 256 } 257 } 258 if (sGameOverTableIndex != 11) { 259 sGameOverFrameCounter++; 260 } 261 graphNode->flags = (graphNode->flags & 0xFF) | (LAYER_OPAQUE << 8); 262 263 // draw all the tiles 264 gSPDisplayList(dlIter++, &dl_proj_mtx_fullscreen); 265 gSPDisplayList(dlIter++, &title_screen_bg_dl_0A000100); 266 for (j = 0; j < ARRAY_COUNT(gameOverBackgroundTable); j++) { 267 gSPDisplayList(dlIter++, intro_backdrop_one_image(j, gameOverBackgroundTable)); 268 } 269 gSPDisplayList(dlIter++, &title_screen_bg_dl_0A000190); 270 gSPEndDisplayList(dlIter); 271 } 272 return dl; 273 } 274 275 #if defined(VERSION_SH) || defined(VERSION_CN) 276 277 extern Gfx title_screen_bg_dl_0A0065E8[]; 278 extern Gfx title_screen_bg_dl_0A006618[]; 279 extern Gfx title_screen_bg_dl_0A007548[]; 280 281 // Data 282 s8 sFaceVisible[] = { 283 1, 1, 1, 1, 1, 1, 1, 1, 284 1, 1, 1, 1, 1, 1, 1, 1, 285 1, 1, 0, 0, 0, 0, 1, 1, 286 1, 1, 0, 0, 0, 0, 1, 1, 287 1, 1, 1, 1, 1, 1, 1, 1, 288 1, 1, 1, 1, 1, 1, 1, 1, 289 }; 290 291 s8 sFaceToggleOrder[] = { 292 0, 1, 2, 3, 4, 5, 6, 7, 293 15, 23, 31, 39, 47, 46, 45, 44, 294 43, 42, 41, 40, 32, 24, 16, 8, 295 9, 10, 11, 12, 13, 14, 22, 30, 296 38, 37, 36, 35, 34, 33, 25, 17, 297 }; 298 299 s8 sFaceCounter = 0; 300 301 void intro_gen_face_texrect(Gfx **dlIter) { 302 s32 x; 303 s32 y; 304 305 for (y = 0; y < 6; y++) { 306 for (x = 0; x < 8; x++) { 307 if (sFaceVisible[y*8 + x] != 0) { 308 gSPTextureRectangle((*dlIter)++, (x * 40) << 2, (y * 40) << 2, (x * 40 + 39) << 2, (y * 40 + 39) << 2, 0, 309 0, 0, 4 << 10, 1 << 10); 310 } 311 } 312 } 313 } 314 315 Gfx *intro_draw_face(u16 *image, s32 imageW, s32 imageH) { 316 Gfx *dl; 317 Gfx *dlIter; 318 319 dl = alloc_display_list(110 * sizeof(Gfx)); 320 321 if (dl == NULL) { 322 return dl; 323 } else { 324 dlIter = dl; 325 } 326 327 gSPDisplayList(dlIter++, title_screen_bg_dl_0A0065E8); 328 329 gDPLoadTextureBlock(dlIter++, VIRTUAL_TO_PHYSICAL(image), G_IM_FMT_RGBA, G_IM_SIZ_16b, imageW, imageH, 0, G_TX_CLAMP | G_TX_NOMIRROR, G_TX_CLAMP | G_TX_NOMIRROR, 6, 6, G_TX_NOLOD, G_TX_NOLOD); 330 331 intro_gen_face_texrect(&dlIter); 332 333 gSPDisplayList(dlIter++, title_screen_bg_dl_0A006618); 334 335 gSPEndDisplayList(dlIter++); 336 337 return dl; 338 } 339 340 #ifdef VERSION_CN 341 342 // TODO: See whether this matches other game versions too, clearly there was no actual 343 // substantive change in this function 344 u16 *intro_sample_framebuffer(s32 imageW, s32 imageH, s32 sampleW, s32 sampleH) { 345 u16 *fb; 346 u16 *image; 347 s32 xIndex; 348 s32 yIndex; 349 f32 size; 350 f32 r, g, b; 351 s32 iy, ix, sy, sx; 352 s32 xOffset, yOffset; 353 u16 fbr, fbg, fbb; 354 f32 f1, f2, f3; 355 356 fb = sFramebuffers[sRenderingFramebuffer]; 357 image = alloc_display_list(imageW * imageH * sizeof(u16)); 358 359 if (image == NULL) { 360 return image; 361 } 362 363 for (iy = 0; iy < imageH; iy++) { 364 yOffset = 80; 365 366 for (ix = 0; ix < imageW; ix++) { 367 xOffset = 120; 368 369 r = 0; 370 g = 0; 371 b = 0; 372 373 for (sy = 0; sy < sampleH; sy++) { 374 for (sx = 0; sx < sampleW; sx++) { 375 yIndex = (sampleH * iy + yOffset) + sy; 376 xIndex = (sampleW * ix + xOffset) + sx; 377 378 fbr = fb[320 * yIndex + xIndex]; 379 fbg = fb[320 * yIndex + xIndex]; 380 fbb = fb[320 * yIndex + xIndex]; 381 382 f1 = (fbr >> 0xB) & 0x1F; 383 f2 = (fbg >> 0x6) & 0x1F; 384 f3 = (fbb >> 0x1) & 0x1F; 385 386 r += f1; 387 g += f2; 388 b += f3; 389 } 390 } 391 392 size = sampleW * sampleH; 393 394 fbr = ((u16) (r / size + 0.5) << 0xB) & 0xF800; 395 fbg = ((u16) (g / size + 0.5) << 0x6) & 0x7C0; 396 fbb = ((u16) (b / size + 0.5) << 0x1) & 0x3E; 397 398 image[imageH * iy + ix] = fbr + fbg + fbb + 1; 399 } 400 } 401 402 return image; 403 } 404 405 #else 406 407 u16 *intro_sample_framebuffer(s32 imageW, s32 imageH, s32 sampleW, s32 sampleH) { 408 u16 *fb; 409 u16 *image; 410 s32 pixel; 411 f32 size; 412 f32 r, g, b; 413 s32 iy, ix, sy, sx; 414 415 s32 xOffset = 120; 416 s32 yOffset = 80; 417 418 fb = sFramebuffers[sRenderingFramebuffer]; 419 image = alloc_display_list(imageW * imageH * sizeof(u16)); 420 421 if (image == NULL) { 422 return image; 423 } 424 425 for (iy = 0; iy < imageH; iy++) { 426 for (ix = 0; ix < imageW; ix++) { 427 r = 0; 428 g = 0; 429 b = 0; 430 431 for (sy = 0; sy < sampleH; sy++) { 432 for (sx = 0; sx < sampleW; sx++) { 433 u16 fbr, fbg, fbb; 434 f32 f1, f2, f3; 435 pixel = 320 * (sampleH * iy + sy + yOffset) + (sampleW * ix + xOffset) + sx; 436 437 fbr = fb[pixel]; 438 fbg = fb[pixel]; 439 fbb = fb[pixel]; 440 441 f1 = ((fbr >> 0xB) & 0x1F); 442 f2 = ((fbg >> 0x6) & 0x1F); 443 f3 = ((fbb >> 0x1) & 0x1F); 444 445 r += f1; 446 g += f2; 447 b += f3; 448 } 449 } 450 451 size = sampleW * sampleH; 452 image[imageH * iy + ix] = ((((u16) (r / size + 0.5) << 0xB) & 0xF800) & 0xffff) + 453 ((((u16) (g / size + 0.5) << 0x6) & 0x7C0) & 0xffff) + 454 ((((u16) (b / size + 0.5) << 0x1) & 0x3E) & 0xffff) + 1; 455 } 456 } 457 458 return image; 459 } 460 461 #endif 462 463 Gfx *geo_intro_face_easter_egg(s32 state, struct GraphNode *node, UNUSED void *context) { 464 struct GraphNodeGenerated *genNode; 465 u16 *image; 466 Gfx *dl = NULL; 467 s32 i; 468 469 genNode = (struct GraphNodeGenerated *) node; 470 471 if (state != 1) { 472 sFramebuffers[0] = gFramebuffer0; 473 sFramebuffers[1] = gFramebuffer1; 474 sFramebuffers[2] = gFramebuffer2; 475 476 for (i = 0; i < 48; i++) { 477 sFaceVisible[i] = 0; 478 } 479 } else if (state == 1) { 480 if (sFaceCounter == 0) { 481 if (gPlayer1Controller->buttonPressed & Z_TRIG) { 482 play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource); 483 sFaceVisible[0] ^= 1; 484 sFaceCounter++; 485 } 486 } else { 487 sFaceVisible[sFaceToggleOrder[sFaceCounter++]] ^= 1; 488 if (sFaceCounter >= 40) { 489 sFaceCounter = 0; 490 } 491 } 492 493 // Draw while the first or last face is visible. 494 if (sFaceVisible[0] == 1 || sFaceVisible[17] == 1) { 495 image = intro_sample_framebuffer(40, 40, 2, 2); 496 if (image != NULL) { 497 genNode->fnNode.node.flags = (genNode->fnNode.node.flags & 0xFF) | (LAYER_OPAQUE << 8); 498 dl = intro_draw_face(image, 40, 40); 499 } 500 } 501 } 502 503 return dl; 504 } 505 506 Gfx *geo_intro_rumble_pak_graphic(s32 state, struct GraphNode *node, UNUSED void *context) { 507 struct GraphNodeGenerated *genNode = (struct GraphNodeGenerated *)node; 508 Gfx *dlIter; 509 Gfx *dl; 510 s32 introContext; 511 s8 backgroundTileSix; 512 #ifdef AVOID_UB 513 dl = NULL; 514 backgroundTileSix = 0; 515 #endif 516 517 if (state != 1) { 518 dl = NULL; 519 } else if (state == 1) { 520 genNode->fnNode.node.flags = (genNode->fnNode.node.flags & 0xFF) | (LAYER_OPAQUE << 8); 521 introContext = genNode->parameter & 0xFF; 522 if (introContext == 0) { 523 backgroundTileSix = introBackgroundIndexTable[6]; 524 } else if (introContext == 1) { 525 backgroundTileSix = gameOverBackgroundTable[6]; 526 } 527 if (backgroundTileSix == INTRO_BACKGROUND_SUPER_MARIO) { 528 dl = alloc_display_list(3 * sizeof(*dl)); 529 if (dl != NULL) { 530 dlIter = dl; 531 gSPDisplayList(dlIter++, &title_screen_bg_dl_0A007548); 532 gSPEndDisplayList(dlIter); 533 } 534 } else { 535 dl = NULL; 536 } 537 } 538 return dl; 539 } 540 541 #endif