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