Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/envfx_bubbles.c
7858 views
1
#include <ultra64.h>
2
3
#include "sm64.h"
4
#include "game_init.h"
5
#include "memory.h"
6
#include "envfx_snow.h"
7
#include "envfx_bubbles.h"
8
#include "engine/surface_collision.h"
9
#include "engine/math_util.h"
10
#include "engine/behavior_script.h"
11
#include "audio/external.h"
12
#include "textures.h"
13
14
#include "rendering_graph_node.h"
15
16
/**
17
* This file implements environment effects that are not snow:
18
* Flowers (unused), lava bubbles and jet stream/whirlpool bubbles.
19
* Refer to 'envfx_snow.c' for more info about environment effects.
20
* Note that the term 'bubbles' is used as a collective name for
21
* effects in this file even though flowers aren't bubbles. For the
22
* sake of concise naming, flowers fall under bubbles.
23
*/
24
25
s16 gEnvFxBubbleConfig[10];
26
static Gfx *sGfxCursor; // points to end of display list for bubble particles
27
static s32 sBubbleParticleCount;
28
static s32 sBubbleParticleMaxCount;
29
30
UNUSED s32 D_80330690 = 0;
31
UNUSED s32 D_80330694 = 0;
32
33
/// Template for a bubble particle triangle
34
Vtx_t gBubbleTempVtx[3] = {
35
{ { 0, 0, 0 }, 0, { 1544, 964 }, { 0xFF, 0xFF, 0xFF, 0xFF } },
36
{ { 0, 0, 0 }, 0, { 522, -568 }, { 0xFF, 0xFF, 0xFF, 0xFF } },
37
{ { 0, 0, 0 }, 0, { -498, 964 }, { 0xFF, 0xFF, 0xFF, 0xFF } },
38
};
39
40
static Gfx sGfxSaved[60 / 5];
41
static Gfx *sBubbleInterpolatedDisplayListPos[60 / 5];
42
static Vec3s sPrevBubblePositions[60];
43
44
void patch_interpolated_bubble_particles(void) {
45
s32 i;
46
for (i = 0; i < 60 / 5; i++) {
47
if (sBubbleInterpolatedDisplayListPos[i] != NULL) {
48
*sBubbleInterpolatedDisplayListPos[i] = sGfxSaved[i];
49
sBubbleInterpolatedDisplayListPos[i] = NULL;
50
}
51
}
52
}
53
54
/**
55
* Check whether the particle with the given index is
56
* laterally within distance of point (x, z). Used to
57
* kill flower and bubble particles.
58
*/
59
s32 particle_is_laterally_close(s32 index, s32 x, s32 z, s32 distance) {
60
s32 xPos = (gEnvFxBuffer + index)->xPos;
61
s32 zPos = (gEnvFxBuffer + index)->zPos;
62
63
if (sqr(xPos - x) + sqr(zPos - z) > sqr(distance)) {
64
return 0;
65
}
66
67
return 1;
68
}
69
70
/**
71
* Generate a uniform random number in range [-2000, -1000[ or [1000, 2000[
72
* Used to position flower particles
73
*/
74
s32 random_flower_offset(void) {
75
s32 result = random_float() * 2000.0f - 1000.0f;
76
if (result < 0) {
77
result -= 1000;
78
} else {
79
result += 1000;
80
}
81
82
return result;
83
}
84
85
/**
86
* Update flower particles. Flowers are scattered randomly in front of the
87
* camera, and can land on any ground
88
*/
89
void envfx_update_flower(Vec3s centerPos) {
90
s32 i;
91
struct FloorGeometry *floorGeo; // unused
92
s32 timer = gGlobalTimer;
93
94
s16 centerX = centerPos[0];
95
UNUSED s16 centerY = centerPos[1];
96
s16 centerZ = centerPos[2];
97
98
for (i = 0; i < sBubbleParticleMaxCount; i++) {
99
(gEnvFxBuffer + i)->isAlive = particle_is_laterally_close(i, centerX, centerZ, 3000);
100
if ((gEnvFxBuffer + i)->isAlive == 0) {
101
(gEnvFxBuffer + i)->xPos = random_flower_offset() + centerX;
102
(gEnvFxBuffer + i)->zPos = random_flower_offset() + centerZ;
103
(gEnvFxBuffer + i)->yPos = find_floor_height_and_data((gEnvFxBuffer + i)->xPos, 10000.0f,
104
(gEnvFxBuffer + i)->zPos, &floorGeo);
105
(gEnvFxBuffer + i)->isAlive = 1;
106
(gEnvFxBuffer + i)->animFrame = random_float() * 5.0f;
107
} else if ((timer & 0x03) == 0) {
108
(gEnvFxBuffer + i)->animFrame += 1;
109
if ((gEnvFxBuffer + i)->animFrame > 5) {
110
(gEnvFxBuffer + i)->animFrame = 0;
111
}
112
}
113
}
114
}
115
116
/**
117
* Update the position of a lava bubble to be somewhere around centerPos
118
* Uses find_floor to find the height of lava, if no floor or a non-lava
119
* floor is found the bubble y is set to -10000, which is why you can see
120
* occasional lava bubbles far below the course in Lethal Lava Land.
121
* In the second Bowser fight arena, the visual lava is above the lava
122
* floor so lava-bubbles are not normally visible, only if you bring the
123
* camera below the lava plane.
124
*/
125
void envfx_set_lava_bubble_position(s32 index, Vec3s centerPos) {
126
struct Surface *surface;
127
s16 floorY;
128
s16 centerX, centerY, centerZ;
129
130
centerX = centerPos[0];
131
centerY = centerPos[1];
132
centerZ = centerPos[2];
133
134
(gEnvFxBuffer + index)->xPos = random_float() * 6000.0f - 3000.0f + centerX;
135
(gEnvFxBuffer + index)->zPos = random_float() * 6000.0f - 3000.0f + centerZ;
136
137
if ((gEnvFxBuffer + index)->xPos > 8000) {
138
(gEnvFxBuffer + index)->xPos = 16000 - (gEnvFxBuffer + index)->xPos;
139
}
140
if ((gEnvFxBuffer + index)->xPos < -8000) {
141
(gEnvFxBuffer + index)->xPos = -16000 - (gEnvFxBuffer + index)->xPos;
142
}
143
144
if ((gEnvFxBuffer + index)->zPos > 8000) {
145
(gEnvFxBuffer + index)->zPos = 16000 - (gEnvFxBuffer + index)->zPos;
146
}
147
if ((gEnvFxBuffer + index)->zPos < -8000) {
148
(gEnvFxBuffer + index)->zPos = -16000 - (gEnvFxBuffer + index)->zPos;
149
}
150
151
floorY =
152
find_floor((gEnvFxBuffer + index)->xPos, centerY + 500, (gEnvFxBuffer + index)->zPos, &surface);
153
if (surface == NULL) {
154
(gEnvFxBuffer + index)->yPos = FLOOR_LOWER_LIMIT_MISC;
155
return;
156
}
157
158
if (surface->type == SURFACE_BURNING) {
159
(gEnvFxBuffer + index)->yPos = floorY;
160
} else {
161
(gEnvFxBuffer + index)->yPos = FLOOR_LOWER_LIMIT_MISC;
162
}
163
}
164
165
/**
166
* Update lava bubble animation and give the bubble a new position if the
167
* animation is over.
168
*/
169
void envfx_update_lava(Vec3s centerPos) {
170
s32 i;
171
s32 timer = gGlobalTimer;
172
s8 chance;
173
UNUSED s16 centerX, centerY, centerZ;
174
175
centerX = centerPos[0];
176
centerY = centerPos[1];
177
centerZ = centerPos[2];
178
179
for (i = 0; i < sBubbleParticleMaxCount; i++) {
180
if ((gEnvFxBuffer + i)->isAlive == 0) {
181
envfx_set_lava_bubble_position(i, centerPos);
182
(gEnvFxBuffer + i)->isAlive = 1;
183
} else if ((timer & 0x01) == 0) {
184
(gEnvFxBuffer + i)->animFrame += 1;
185
if ((gEnvFxBuffer + i)->animFrame > 8) {
186
(gEnvFxBuffer + i)->isAlive = 0;
187
(gEnvFxBuffer + i)->animFrame = 0;
188
}
189
}
190
}
191
192
if ((chance = (s32)(random_float() * 16.0f)) == 8) {
193
play_sound(SOUND_GENERAL_QUIET_BUBBLE2, gGlobalSoundSource);
194
}
195
}
196
197
/**
198
* Rotate the input x, y and z around the rotation origin of the whirlpool
199
* according to the pitch and yaw of the whirlpool.
200
*/
201
void envfx_rotate_around_whirlpool(s32 *x, s32 *y, s32 *z) {
202
s32 vecX = *x - gEnvFxBubbleConfig[ENVFX_STATE_DEST_X];
203
s32 vecY = *y - gEnvFxBubbleConfig[ENVFX_STATE_DEST_Y];
204
s32 vecZ = *z - gEnvFxBubbleConfig[ENVFX_STATE_DEST_Z];
205
f32 cosPitch = coss(gEnvFxBubbleConfig[ENVFX_STATE_PITCH]);
206
f32 sinPitch = sins(gEnvFxBubbleConfig[ENVFX_STATE_PITCH]);
207
f32 cosMYaw = coss(-gEnvFxBubbleConfig[ENVFX_STATE_YAW]);
208
f32 sinMYaw = sins(-gEnvFxBubbleConfig[ENVFX_STATE_YAW]);
209
210
f32 rotatedX = vecX * cosMYaw - sinMYaw * cosPitch * vecY - sinPitch * sinMYaw * vecZ;
211
f32 rotatedY = vecX * sinMYaw + cosPitch * cosMYaw * vecY - sinPitch * cosMYaw * vecZ;
212
f32 rotatedZ = vecY * sinPitch + cosPitch * vecZ;
213
214
*x = gEnvFxBubbleConfig[ENVFX_STATE_DEST_X] + (s32) rotatedX;
215
*y = gEnvFxBubbleConfig[ENVFX_STATE_DEST_Y] + (s32) rotatedY;
216
*z = gEnvFxBubbleConfig[ENVFX_STATE_DEST_Z] + (s32) rotatedZ;
217
}
218
219
/**
220
* Check whether a whirlpool bubble is alive. A bubble respawns when it is too
221
* low or close to the center.
222
*/
223
s32 envfx_is_whirlpool_bubble_alive(s32 index) {
224
s32 UNUSED sp4;
225
226
if ((gEnvFxBuffer + index)->bubbleY < gEnvFxBubbleConfig[ENVFX_STATE_DEST_Y] - 100) {
227
return 0;
228
}
229
230
if ((gEnvFxBuffer + index)->angleAndDist[1] < 10) {
231
return 0;
232
}
233
234
return 1;
235
}
236
237
/**
238
* Update whirlpool particles. Whirlpool particles start high and far from
239
* the center and get sucked into the sink in a spiraling motion.
240
*/
241
void envfx_update_whirlpool(void) {
242
s32 i;
243
244
for (i = 0; i < sBubbleParticleMaxCount; i++) {
245
(gEnvFxBuffer + i)->isAlive = envfx_is_whirlpool_bubble_alive(i);
246
if ((gEnvFxBuffer + i)->isAlive == 0) {
247
(gEnvFxBuffer + i)->angleAndDist[1] = random_float() * 1000.0f;
248
(gEnvFxBuffer + i)->angleAndDist[0] = random_float() * 65536.0f;
249
(gEnvFxBuffer + i)->xPos =
250
gEnvFxBubbleConfig[ENVFX_STATE_SRC_X]
251
+ sins((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1];
252
(gEnvFxBuffer + i)->zPos =
253
gEnvFxBubbleConfig[ENVFX_STATE_SRC_Z]
254
+ coss((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1];
255
(gEnvFxBuffer + i)->bubbleY =
256
gEnvFxBubbleConfig[ENVFX_STATE_SRC_Y] + (random_float() * 100.0f - 50.0f);
257
(gEnvFxBuffer + i)->yPos = (i + gEnvFxBuffer)->bubbleY;
258
(gEnvFxBuffer + i)->unusedBubbleVar = 0;
259
(gEnvFxBuffer + i)->isAlive = 1;
260
(gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer;
261
262
envfx_rotate_around_whirlpool(&(gEnvFxBuffer + i)->xPos, &(gEnvFxBuffer + i)->yPos,
263
&(gEnvFxBuffer + i)->zPos);
264
} else {
265
(gEnvFxBuffer + i)->angleAndDist[1] -= 40;
266
(gEnvFxBuffer + i)->angleAndDist[0] +=
267
(s16)(3000 - (gEnvFxBuffer + i)->angleAndDist[1] * 2) + 0x400;
268
(gEnvFxBuffer + i)->xPos =
269
gEnvFxBubbleConfig[ENVFX_STATE_SRC_X]
270
+ sins((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1];
271
(gEnvFxBuffer + i)->zPos =
272
gEnvFxBubbleConfig[ENVFX_STATE_SRC_Z]
273
+ coss((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1];
274
(gEnvFxBuffer + i)->bubbleY -= 40 - ((s16)(gEnvFxBuffer + i)->angleAndDist[1] / 100);
275
(gEnvFxBuffer + i)->yPos = (i + gEnvFxBuffer)->bubbleY;
276
277
envfx_rotate_around_whirlpool(&(gEnvFxBuffer + i)->xPos, &(gEnvFxBuffer + i)->yPos,
278
&(gEnvFxBuffer + i)->zPos);
279
}
280
}
281
}
282
283
/**
284
* Check whether a jet stream bubble should respawn. Happens if it is laterally
285
* 1000 units away from the source or 1500 units above it.
286
*/
287
s32 envfx_is_jestream_bubble_alive(s32 index) {
288
UNUSED s32 unk;
289
290
if (!particle_is_laterally_close(index, gEnvFxBubbleConfig[ENVFX_STATE_SRC_X],
291
gEnvFxBubbleConfig[ENVFX_STATE_SRC_Z], 1000)
292
|| gEnvFxBubbleConfig[ENVFX_STATE_SRC_Y] + 1500 < (gEnvFxBuffer + index)->yPos) {
293
return 0;
294
}
295
296
return 1;
297
}
298
299
/**
300
* Update the positions of jet stream bubble particles.
301
* They move up and outwards.
302
*/
303
void envfx_update_jetstream(void) {
304
s32 i;
305
306
for (i = 0; i < sBubbleParticleMaxCount; i++) {
307
(gEnvFxBuffer + i)->isAlive = envfx_is_jestream_bubble_alive(i);
308
if ((gEnvFxBuffer + i)->isAlive == 0) {
309
(gEnvFxBuffer + i)->angleAndDist[1] = random_float() * 300.0f;
310
(gEnvFxBuffer + i)->angleAndDist[0] = random_u16();
311
(gEnvFxBuffer + i)->xPos =
312
gEnvFxBubbleConfig[ENVFX_STATE_SRC_X]
313
+ sins((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1];
314
(gEnvFxBuffer + i)->zPos =
315
gEnvFxBubbleConfig[ENVFX_STATE_SRC_Z]
316
+ coss((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1];
317
(gEnvFxBuffer + i)->yPos =
318
gEnvFxBubbleConfig[ENVFX_STATE_SRC_Y] + (random_float() * 400.0f - 200.0f);
319
(gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer;
320
} else {
321
(gEnvFxBuffer + i)->angleAndDist[1] += 10;
322
(gEnvFxBuffer + i)->xPos += sins((gEnvFxBuffer + i)->angleAndDist[0]) * 10.0f;
323
(gEnvFxBuffer + i)->zPos += coss((gEnvFxBuffer + i)->angleAndDist[0]) * 10.0f;
324
(gEnvFxBuffer + i)->yPos -= ((gEnvFxBuffer + i)->angleAndDist[1] / 30) - 50;
325
}
326
}
327
}
328
329
/**
330
* Initialize bubble (or flower) effect by allocating a buffer to store
331
* the state of each particle and setting the initial and max count.
332
* Analogous to init_snow_particles, but for bubbles.
333
*/
334
s32 envfx_init_bubble(s32 mode) {
335
s32 i;
336
337
switch (mode) {
338
case ENVFX_MODE_NONE:
339
return 0;
340
341
case ENVFX_FLOWERS:
342
sBubbleParticleCount = 30;
343
sBubbleParticleMaxCount = 30;
344
break;
345
346
case ENVFX_LAVA_BUBBLES:
347
sBubbleParticleCount = 15;
348
sBubbleParticleMaxCount = 15;
349
break;
350
351
case ENVFX_WHIRLPOOL_BUBBLES:
352
sBubbleParticleCount = 60;
353
break;
354
355
case ENVFX_JETSTREAM_BUBBLES:
356
sBubbleParticleCount = 60;
357
break;
358
}
359
360
gEnvFxBuffer = mem_pool_alloc(gEffectsMemoryPool, sBubbleParticleCount * sizeof(struct EnvFxParticle));
361
if (!gEnvFxBuffer) {
362
return 0;
363
}
364
365
bzero(gEnvFxBuffer, sBubbleParticleCount * sizeof(struct EnvFxParticle));
366
bzero(gEnvFxBubbleConfig, sizeof(gEnvFxBubbleConfig));
367
368
switch (mode) {
369
case ENVFX_LAVA_BUBBLES:
370
for (i = 0; i < sBubbleParticleCount; i++) {
371
(gEnvFxBuffer + i)->animFrame = random_float() * 7.0f;
372
}
373
break;
374
}
375
376
gEnvFxMode = mode;
377
return 1;
378
}
379
380
/**
381
* Update particles depending on mode.
382
* Also sets the given vertices to the correct shape for each mode,
383
* though they are not being rotated yet.
384
*/
385
void envfx_bubbles_update_switch(s32 mode, Vec3s camTo, Vec3s vertex1, Vec3s vertex2, Vec3s vertex3) {
386
switch (mode) {
387
case ENVFX_FLOWERS:
388
envfx_update_flower(camTo);
389
vertex1[0] = 50; vertex1[1] = 0; vertex1[2] = 0;
390
vertex2[0] = 0; vertex2[1] = 75; vertex2[2] = 0;
391
vertex3[0] = -50; vertex3[1] = 0; vertex3[2] = 0;
392
break;
393
394
case ENVFX_LAVA_BUBBLES:
395
envfx_update_lava(camTo);
396
vertex1[0] = 100; vertex1[1] = 0; vertex1[2] = 0;
397
vertex2[0] = 0; vertex2[1] = 150; vertex2[2] = 0;
398
vertex3[0] = -100; vertex3[1] = 0; vertex3[2] = 0;
399
break;
400
401
case ENVFX_WHIRLPOOL_BUBBLES:
402
envfx_update_whirlpool();
403
vertex1[0] = 40; vertex1[1] = 0; vertex1[2] = 0;
404
vertex2[0] = 0; vertex2[1] = 60; vertex2[2] = 0;
405
vertex3[0] = -40; vertex3[1] = 0; vertex3[2] = 0;
406
break;
407
408
case ENVFX_JETSTREAM_BUBBLES:
409
envfx_update_jetstream();
410
vertex1[0] = 40; vertex1[1] = 0; vertex1[2] = 0;
411
vertex2[0] = 0; vertex2[1] = 60; vertex2[2] = 0;
412
vertex3[0] = -40; vertex3[1] = 0; vertex3[2] = 0;
413
break;
414
}
415
}
416
417
/**
418
* Append 15 vertices to 'gfx', which is enough for 5 bubbles starting at
419
* 'index'. The 3 input vertices represent the rotated triangle around (0,0,0)
420
* that will be translated to bubble positions to draw the bubble image
421
*/
422
void append_bubble_vertex_buffer(Gfx *gfx, s32 index, Vec3s vertex1, Vec3s vertex2, Vec3s vertex3,
423
Vtx *template) {
424
s32 i = 0;
425
Vtx *vertBuf = alloc_display_list(15 * sizeof(Vtx));
426
427
if (vertBuf == NULL) {
428
return;
429
}
430
431
for (i = 0; i < 15; i += 3) {
432
vertBuf[i] = template[0];
433
(vertBuf + i)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex1[0];
434
(vertBuf + i)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex1[1];
435
(vertBuf + i)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex1[2];
436
437
vertBuf[i + 1] = template[1];
438
(vertBuf + i + 1)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex2[0];
439
(vertBuf + i + 1)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex2[1];
440
(vertBuf + i + 1)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex2[2];
441
442
vertBuf[i + 2] = template[2];
443
(vertBuf + i + 2)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex3[0];
444
(vertBuf + i + 2)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex3[1];
445
(vertBuf + i + 2)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex3[2];
446
}
447
448
gSPVertex(gfx, VIRTUAL_TO_PHYSICAL(vertBuf), 15, 0);
449
}
450
451
/**
452
* Appends to the enfvx display list a command setting the appropriate texture
453
* for a specific particle. The display list is not passed as parameter but uses
454
* the global sGfxCursor instead.
455
*/
456
void envfx_set_bubble_texture(s32 mode, s16 index) {
457
void **imageArr;
458
s16 frame = (gEnvFxBuffer + index)->animFrame;
459
460
switch (mode) {
461
case ENVFX_FLOWERS:
462
imageArr = segmented_to_virtual(&flower_bubbles_textures_ptr_0B002008);
463
frame = (gEnvFxBuffer + index)->animFrame;
464
break;
465
466
case ENVFX_LAVA_BUBBLES:
467
imageArr = segmented_to_virtual(&lava_bubble_ptr_0B006020);
468
frame = (gEnvFxBuffer + index)->animFrame;
469
break;
470
471
case ENVFX_WHIRLPOOL_BUBBLES:
472
case ENVFX_JETSTREAM_BUBBLES:
473
imageArr = segmented_to_virtual(&bubble_ptr_0B006848);
474
frame = 0;
475
break;
476
}
477
478
gDPSetTextureImage(sGfxCursor++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, *(imageArr + frame));
479
gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006D68);
480
}
481
482
/**
483
* Updates the bubble particle positions, then generates and returns a display
484
* list drawing them.
485
*/
486
Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFrom, Vec3s camTo) {
487
s32 i;
488
s16 radius, pitch, yaw;
489
490
Vec3s vertex1;
491
Vec3s vertex2;
492
Vec3s vertex3;
493
Vec3s interpolatedVertices[3];
494
495
static Vec3s prevVertex1;
496
static Vec3s prevVertex2;
497
static Vec3s prevVertex3;
498
static u32 prevTimestamp;
499
500
Gfx *gfxStart;
501
502
gfxStart = alloc_display_list(((sBubbleParticleMaxCount / 5) * 10 + sBubbleParticleMaxCount + 3)
503
* sizeof(Gfx));
504
if (gfxStart == NULL) {
505
return NULL;
506
}
507
508
sGfxCursor = gfxStart;
509
510
orbit_from_positions(camTo, camFrom, &radius, &pitch, &yaw);
511
envfx_bubbles_update_switch(mode, camTo, vertex1, vertex2, vertex3);
512
rotate_triangle_vertices(vertex1, vertex2, vertex3, pitch, yaw);
513
514
if (gGlobalTimer == prevTimestamp + 1) {
515
interpolate_vectors_s16(interpolatedVertices[0], prevVertex1, vertex1);
516
interpolate_vectors_s16(interpolatedVertices[1], prevVertex2, vertex2);
517
interpolate_vectors_s16(interpolatedVertices[2], prevVertex3, vertex3);
518
}
519
vec3s_copy(prevVertex1, vertex1);
520
vec3s_copy(prevVertex2, vertex2);
521
vec3s_copy(prevVertex3, vertex3);
522
prevTimestamp = gGlobalTimer;
523
524
gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006D38);
525
526
for (i = 0; i < sBubbleParticleMaxCount; i += 5) {
527
Vtx *interpolatedVertBuf = alloc_display_list(15 * sizeof(Vtx));
528
s32 j, k;
529
gDPPipeSync(sGfxCursor++);
530
envfx_set_bubble_texture(mode, i);
531
sBubbleInterpolatedDisplayListPos[i / 5] = sGfxCursor;
532
for (j = 0; j < 5; j++) {
533
for (k = 0; k < 3; k++) {
534
Vtx *v = &interpolatedVertBuf[j * 3 + k];
535
v->v = gBubbleTempVtx[k];
536
if (gGlobalTimer != gEnvFxBuffer[i + j].spawnTimestamp && mode != ENVFX_LAVA_BUBBLES) {
537
v->v.ob[0] = (sPrevBubblePositions[i + j][0] + gEnvFxBuffer[i + j].xPos) / 2.0f + interpolatedVertices[k][0];
538
v->v.ob[1] = (sPrevBubblePositions[i + j][1] + gEnvFxBuffer[i + j].yPos) / 2.0f + interpolatedVertices[k][1];
539
v->v.ob[2] = (sPrevBubblePositions[i + j][2] + gEnvFxBuffer[i + j].zPos) / 2.0f + interpolatedVertices[k][2];
540
} else {
541
v->v.ob[0] = gEnvFxBuffer[i + j].xPos + interpolatedVertices[k][0];
542
v->v.ob[1] = gEnvFxBuffer[i + j].yPos + interpolatedVertices[k][1];
543
v->v.ob[2] = gEnvFxBuffer[i + j].zPos + interpolatedVertices[k][2];
544
}
545
}
546
}
547
gSPVertex(sGfxCursor++, VIRTUAL_TO_PHYSICAL(interpolatedVertBuf), 15, 0);
548
append_bubble_vertex_buffer(&sGfxSaved[i / 5], i, vertex1, vertex2, vertex3, (Vtx *) gBubbleTempVtx);
549
gSP1Triangle(sGfxCursor++, 0, 1, 2, 0);
550
gSP1Triangle(sGfxCursor++, 3, 4, 5, 0);
551
gSP1Triangle(sGfxCursor++, 6, 7, 8, 0);
552
gSP1Triangle(sGfxCursor++, 9, 10, 11, 0);
553
gSP1Triangle(sGfxCursor++, 12, 13, 14, 0);
554
}
555
for (i = 0; i < sBubbleParticleMaxCount; i++) {
556
sPrevBubblePositions[i][0] = gEnvFxBuffer[i].xPos;
557
sPrevBubblePositions[i][1] = gEnvFxBuffer[i].yPos;
558
sPrevBubblePositions[i][2] = gEnvFxBuffer[i].zPos;
559
}
560
561
gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006AB0);
562
gSPEndDisplayList(sGfxCursor++);
563
564
return gfxStart;
565
}
566
567
/**
568
* Set the maximum particle count from the gEnvFxBubbleConfig variable,
569
* which is set by the whirlpool or jet stream behavior.
570
*/
571
void envfx_set_max_bubble_particles(s32 mode) {
572
switch (mode) {
573
case ENVFX_WHIRLPOOL_BUBBLES:
574
sBubbleParticleMaxCount = gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT];
575
break;
576
case ENVFX_JETSTREAM_BUBBLES:
577
sBubbleParticleMaxCount = gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT];
578
break;
579
}
580
}
581
582
/**
583
* Update bubble-like environment effects. Assumes the mode is larger than 10,
584
* lower modes are snow effects which are updated in a different function.
585
* Returns a display list drawing the particles.
586
*/
587
Gfx *envfx_update_bubbles(s32 mode, Vec3s marioPos, Vec3s camTo, Vec3s camFrom) {
588
Gfx *gfx;
589
590
if (gEnvFxMode == 0 && !envfx_init_bubble(mode)) {
591
return NULL;
592
}
593
594
envfx_set_max_bubble_particles(mode);
595
596
if (sBubbleParticleMaxCount == 0) {
597
return NULL;
598
}
599
600
switch (mode) {
601
case ENVFX_FLOWERS:
602
gfx = envfx_update_bubble_particles(ENVFX_FLOWERS, marioPos, camFrom, camTo);
603
break;
604
605
case ENVFX_LAVA_BUBBLES:
606
gfx = envfx_update_bubble_particles(ENVFX_LAVA_BUBBLES, marioPos, camFrom, camTo);
607
break;
608
609
case ENVFX_WHIRLPOOL_BUBBLES:
610
gfx = envfx_update_bubble_particles(ENVFX_WHIRLPOOL_BUBBLES, marioPos, camFrom, camTo);
611
break;
612
613
case ENVFX_JETSTREAM_BUBBLES:
614
gfx = envfx_update_bubble_particles(ENVFX_JETSTREAM_BUBBLES, marioPos, camFrom, camTo);
615
break;
616
617
default:
618
return NULL;
619
}
620
621
return gfx;
622
}
623
624