Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/engine/surface_load.c
7857 views
1
#include <PR/ultratypes.h>
2
3
#include "prevent_bss_reordering.h"
4
5
#include "sm64.h"
6
#include "game/ingame_menu.h"
7
#include "graph_node.h"
8
#include "behavior_script.h"
9
#include "behavior_data.h"
10
#include "game/memory.h"
11
#include "game/object_helpers.h"
12
#include "game/macro_special_objects.h"
13
#include "surface_collision.h"
14
#include "game/mario.h"
15
#include "game/object_list_processor.h"
16
#include "surface_load.h"
17
#include "game/game_init.h"
18
#include "math_util.h"
19
20
#include "game/settings.h"
21
22
s32 unused8038BE90;
23
24
/**
25
* Partitions for course and object surfaces. The arrays represent
26
* the 16x16 cells that each level is split into.
27
*/
28
SpatialPartitionCell gStaticSurfacePartition[NUM_CELLS][NUM_CELLS];
29
SpatialPartitionCell gDynamicSurfacePartition[NUM_CELLS][NUM_CELLS];
30
31
/**
32
* Pools of data to contain either surface nodes or surfaces.
33
*/
34
#ifdef USE_SYSTEM_MALLOC
35
static struct AllocOnlyPool *sStaticSurfaceNodePool;
36
static struct AllocOnlyPool *sStaticSurfacePool;
37
static struct AllocOnlyPool *sDynamicSurfaceNodePool;
38
static struct AllocOnlyPool *sDynamicSurfacePool;
39
static u8 sStaticSurfaceLoadComplete;
40
#else
41
struct SurfaceNode *sSurfaceNodePool;
42
struct Surface *sSurfacePool;
43
44
/**
45
* The size of the surface pool (2300).
46
*/
47
s16 sSurfacePoolSize;
48
#endif
49
50
51
u8 unused8038EEA8[0x30];
52
53
/**
54
* Allocate the part of the surface node pool to contain a surface node.
55
*/
56
static struct SurfaceNode *alloc_surface_node(void) {
57
#ifdef USE_SYSTEM_MALLOC
58
struct AllocOnlyPool *pool = !sStaticSurfaceLoadComplete ?
59
sStaticSurfaceNodePool : sDynamicSurfaceNodePool;
60
struct SurfaceNode *node = alloc_only_pool_alloc(pool, sizeof(struct SurfaceNode));
61
#else
62
struct SurfaceNode *node = &sSurfaceNodePool[gSurfaceNodesAllocated];
63
#endif
64
gSurfaceNodesAllocated++;
65
66
node->next = NULL;
67
68
#ifndef USE_SYSTEM_MALLOC
69
//! A bounds check! If there's more surface nodes than 7000 allowed,
70
// we, um...
71
// Perhaps originally just debug feedback?
72
if (gSurfaceNodesAllocated >= 7000) {
73
}
74
#endif
75
76
return node;
77
}
78
79
/**
80
* Allocate the part of the surface pool to contain a surface and
81
* initialize the surface.
82
*/
83
static struct Surface *alloc_surface(void) {
84
#ifdef USE_SYSTEM_MALLOC
85
struct AllocOnlyPool *pool = !sStaticSurfaceLoadComplete ?
86
sStaticSurfacePool : sDynamicSurfacePool;
87
struct Surface *surface = alloc_only_pool_alloc(pool, sizeof(struct Surface));
88
#else
89
struct Surface *surface = &sSurfacePool[gSurfacesAllocated];
90
#endif
91
gSurfacesAllocated++;
92
93
#ifndef USE_SYSTEM_MALLOC
94
//! A bounds check! If there's more surfaces than the 2300 allowed,
95
// we, um...
96
// Perhaps originally just debug feedback?
97
if (gSurfacesAllocated >= sSurfacePoolSize) {
98
}
99
#endif
100
101
surface->type = 0;
102
surface->force = 0;
103
surface->flags = 0;
104
surface->room = 0;
105
surface->object = NULL;
106
107
return surface;
108
}
109
110
/**
111
* Iterates through the entire partition, clearing the surfaces.
112
*/
113
static void clear_spatial_partition(SpatialPartitionCell *cells) {
114
register s32 i = NUM_CELLS * NUM_CELLS;
115
116
while (i--) {
117
(*cells)[SPATIAL_PARTITION_FLOORS].next = NULL;
118
(*cells)[SPATIAL_PARTITION_CEILS].next = NULL;
119
(*cells)[SPATIAL_PARTITION_WALLS].next = NULL;
120
121
cells++;
122
}
123
}
124
125
/**
126
* Clears the static (level) surface partitions for new use.
127
*/
128
static void clear_static_surfaces(void) {
129
clear_spatial_partition(&gStaticSurfacePartition[0][0]);
130
}
131
132
/**
133
* Add a surface to the correct cell list of surfaces.
134
* @param dynamic Determines whether the surface is static or dynamic
135
* @param cellX The X position of the cell in which the surface resides
136
* @param cellZ The Z position of the cell in which the surface resides
137
* @param surface The surface to add
138
*/
139
static void add_surface_to_cell(s16 dynamic, s16 cellX, s16 cellZ, struct Surface *surface) {
140
struct SurfaceNode *newNode = alloc_surface_node();
141
struct SurfaceNode *list;
142
s16 surfacePriority;
143
s16 priority;
144
s16 sortDir;
145
s16 listIndex;
146
147
if (surface->normal.y > 0.01) {
148
listIndex = SPATIAL_PARTITION_FLOORS;
149
sortDir = 1; // highest to lowest, then insertion order
150
} else if (surface->normal.y < -0.01) {
151
listIndex = SPATIAL_PARTITION_CEILS;
152
sortDir = -1; // lowest to highest, then insertion order
153
} else {
154
listIndex = SPATIAL_PARTITION_WALLS;
155
sortDir = 0; // insertion order
156
157
if (surface->normal.x < -0.707 || surface->normal.x > 0.707) {
158
surface->flags |= SURFACE_FLAG_X_PROJECTION;
159
}
160
}
161
162
//! (Surface Cucking) Surfaces are sorted by the height of their first
163
// vertex. Since vertices aren't ordered by height, this causes many
164
// lower triangles to be sorted higher. This worsens surface cucking since
165
// many functions only use the first triangle in surface order that fits,
166
// missing higher surfaces.
167
// upperY would be a better sort method.
168
surfacePriority = surface->vertex1[1] * sortDir;
169
170
newNode->surface = surface;
171
172
if (dynamic) {
173
list = &gDynamicSurfacePartition[cellZ][cellX][listIndex];
174
} else {
175
list = &gStaticSurfacePartition[cellZ][cellX][listIndex];
176
}
177
178
// Loop until we find the appropriate place for the surface in the list.
179
while (list->next != NULL) {
180
priority = list->next->surface->vertex1[1] * sortDir;
181
182
if (surfacePriority > priority) {
183
break;
184
}
185
186
list = list->next;
187
}
188
189
newNode->next = list->next;
190
list->next = newNode;
191
}
192
193
/**
194
* Returns the lowest of three values.
195
*/
196
static s16 min_3(s16 a0, s16 a1, s16 a2) {
197
if (a1 < a0) {
198
a0 = a1;
199
}
200
201
if (a2 < a0) {
202
a0 = a2;
203
}
204
205
return a0;
206
}
207
208
/**
209
* Returns the highest of three values.
210
*/
211
static s16 max_3(s16 a0, s16 a1, s16 a2) {
212
if (a1 > a0) {
213
a0 = a1;
214
}
215
216
if (a2 > a0) {
217
a0 = a2;
218
}
219
220
return a0;
221
}
222
223
/**
224
* Every level is split into 16 * 16 cells of surfaces (to limit computing
225
* time). This function determines the lower cell for a given x/z position.
226
* @param coord The coordinate to test
227
*/
228
static s16 lower_cell_index(s16 coord) {
229
s16 index;
230
231
// Move from range [-0x2000, 0x2000) to [0, 0x4000)
232
coord += LEVEL_BOUNDARY_MAX;
233
if (coord < 0) {
234
coord = 0;
235
}
236
237
// [0, 16)
238
index = coord / CELL_SIZE;
239
240
// Include extra cell if close to boundary
241
//! Some wall checks are larger than the buffer, meaning wall checks can
242
// miss walls that are near a cell border.
243
if (coord % CELL_SIZE < 50) {
244
index -= 1;
245
}
246
247
if (index < 0) {
248
index = 0;
249
}
250
251
// Potentially > 15, but since the upper index is <= 15, not exploitable
252
return index;
253
}
254
255
/**
256
* Every level is split into 16 * 16 cells of surfaces (to limit computing
257
* time). This function determines the upper cell for a given x/z position.
258
* @param coord The coordinate to test
259
*/
260
static s16 upper_cell_index(s16 coord) {
261
s16 index;
262
263
// Move from range [-0x2000, 0x2000) to [0, 0x4000)
264
coord += LEVEL_BOUNDARY_MAX;
265
if (coord < 0) {
266
coord = 0;
267
}
268
269
// [0, 16)
270
index = coord / CELL_SIZE;
271
272
// Include extra cell if close to boundary
273
//! Some wall checks are larger than the buffer, meaning wall checks can
274
// miss walls that are near a cell border.
275
if (coord % CELL_SIZE > CELL_SIZE - 50) {
276
index += 1;
277
}
278
279
if (index > NUM_CELLS_INDEX) {
280
index = NUM_CELLS_INDEX;
281
}
282
283
// Potentially < 0, but since lower index is >= 0, not exploitable
284
return index;
285
}
286
287
/**
288
* Every level is split into 16x16 cells, this takes a surface, finds
289
* the appropriate cells (with a buffer), and adds the surface to those
290
* cells.
291
* @param surface The surface to check
292
* @param dynamic Boolean determining whether the surface is static or dynamic
293
*/
294
static void add_surface(struct Surface *surface, s32 dynamic) {
295
// minY/maxY maybe? s32 instead of s16, though.
296
UNUSED s32 unused1, unused2;
297
s16 minX, minZ, maxX, maxZ;
298
299
s16 minCellX, minCellZ, maxCellX, maxCellZ;
300
301
s16 cellZ, cellX;
302
// cellY maybe? s32 instead of s16, though.
303
UNUSED s32 unused3 = 0;
304
305
minX = min_3(surface->vertex1[0], surface->vertex2[0], surface->vertex3[0]);
306
minZ = min_3(surface->vertex1[2], surface->vertex2[2], surface->vertex3[2]);
307
maxX = max_3(surface->vertex1[0], surface->vertex2[0], surface->vertex3[0]);
308
maxZ = max_3(surface->vertex1[2], surface->vertex2[2], surface->vertex3[2]);
309
310
minCellX = lower_cell_index(minX);
311
maxCellX = upper_cell_index(maxX);
312
minCellZ = lower_cell_index(minZ);
313
maxCellZ = upper_cell_index(maxZ);
314
315
for (cellZ = minCellZ; cellZ <= maxCellZ; cellZ++) {
316
for (cellX = minCellX; cellX <= maxCellX; cellX++) {
317
add_surface_to_cell(dynamic, cellX, cellZ, surface);
318
}
319
}
320
}
321
322
UNUSED static void stub_surface_load_1(void) {
323
}
324
325
/**
326
* Initializes a Surface struct using the given vertex data
327
* @param vertexData The raw data containing vertex positions
328
* @param vertexIndices Helper which tells positions in vertexData to start reading vertices
329
*/
330
static struct Surface *read_surface_data(s16 *vertexData, s16 **vertexIndices) {
331
struct Surface *surface;
332
register s32 x1, y1, z1;
333
register s32 x2, y2, z2;
334
register s32 x3, y3, z3;
335
s32 maxY, minY;
336
f32 nx, ny, nz;
337
f32 mag;
338
s16 offset1, offset2, offset3;
339
340
offset1 = 3 * (*vertexIndices)[0];
341
offset2 = 3 * (*vertexIndices)[1];
342
offset3 = 3 * (*vertexIndices)[2];
343
344
x1 = *(vertexData + offset1 + 0);
345
y1 = *(vertexData + offset1 + 1);
346
z1 = *(vertexData + offset1 + 2);
347
348
x2 = *(vertexData + offset2 + 0);
349
y2 = *(vertexData + offset2 + 1);
350
z2 = *(vertexData + offset2 + 2);
351
352
x3 = *(vertexData + offset3 + 0);
353
y3 = *(vertexData + offset3 + 1);
354
z3 = *(vertexData + offset3 + 2);
355
356
// (v2 - v1) x (v3 - v2)
357
nx = (y2 - y1) * (z3 - z2) - (z2 - z1) * (y3 - y2);
358
ny = (z2 - z1) * (x3 - x2) - (x2 - x1) * (z3 - z2);
359
nz = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2);
360
mag = sqrtf(nx * nx + ny * ny + nz * nz);
361
362
// Could have used min_3 and max_3 for this...
363
minY = y1;
364
if (y2 < minY) {
365
minY = y2;
366
}
367
if (y3 < minY) {
368
minY = y3;
369
}
370
371
maxY = y1;
372
if (y2 > maxY) {
373
maxY = y2;
374
}
375
if (y3 > maxY) {
376
maxY = y3;
377
}
378
379
// Checking to make sure no DIV/0
380
if (mag < 0.0001) {
381
return NULL;
382
}
383
mag = (f32)(1.0 / mag);
384
nx *= mag;
385
ny *= mag;
386
nz *= mag;
387
388
surface = alloc_surface();
389
390
vec3s_copy(surface->prevVertex1, surface->vertex1);
391
vec3s_copy(surface->prevVertex2, surface->vertex2);
392
vec3s_copy(surface->prevVertex3, surface->vertex3);
393
surface->modifiedTimestamp = gGlobalTimer;
394
395
surface->vertex1[0] = x1;
396
surface->vertex2[0] = x2;
397
surface->vertex3[0] = x3;
398
399
surface->vertex1[1] = y1;
400
surface->vertex2[1] = y2;
401
surface->vertex3[1] = y3;
402
403
surface->vertex1[2] = z1;
404
surface->vertex2[2] = z2;
405
surface->vertex3[2] = z3;
406
407
surface->normal.x = nx;
408
surface->normal.y = ny;
409
surface->normal.z = nz;
410
411
surface->originOffset = -(nx * x1 + ny * y1 + nz * z1);
412
413
surface->lowerY = minY - 5;
414
surface->upperY = maxY + 5;
415
416
return surface;
417
}
418
419
/**
420
* Returns whether a surface has exertion/moves Mario
421
* based on the surface type.
422
*/
423
static s32 surface_has_force(s16 surfaceType) {
424
s32 hasForce = FALSE;
425
426
switch (surfaceType) {
427
case SURFACE_0004: // Unused
428
case SURFACE_FLOWING_WATER:
429
case SURFACE_DEEP_MOVING_QUICKSAND:
430
case SURFACE_SHALLOW_MOVING_QUICKSAND:
431
case SURFACE_MOVING_QUICKSAND:
432
case SURFACE_HORIZONTAL_WIND:
433
case SURFACE_INSTANT_MOVING_QUICKSAND:
434
hasForce = TRUE;
435
break;
436
437
default:
438
break;
439
}
440
return hasForce;
441
}
442
443
/**
444
* Returns whether a surface should have the
445
* SURFACE_FLAG_NO_CAM_COLLISION flag.
446
*/
447
static s32 surf_has_no_cam_collision(s16 surfaceType) {
448
s32 flags = 0;
449
450
switch (surfaceType) {
451
case SURFACE_NO_CAM_COLLISION:
452
case SURFACE_NO_CAM_COLLISION_77: // Unused
453
case SURFACE_NO_CAM_COL_VERY_SLIPPERY:
454
case SURFACE_SWITCH:
455
flags = SURFACE_FLAG_NO_CAM_COLLISION;
456
break;
457
458
default:
459
break;
460
}
461
462
return flags;
463
}
464
465
/**
466
* Load in the surfaces for a given surface type. This includes setting the flags,
467
* exertion, and room.
468
*/
469
static void load_static_surfaces(s16 **data, s16 *vertexData, s16 surfaceType, s8 **surfaceRooms) {
470
s32 i;
471
s32 numSurfaces;
472
struct Surface *surface;
473
s8 room = 0;
474
s16 hasForce = surface_has_force(surfaceType);
475
s16 flags = surf_has_no_cam_collision(surfaceType);
476
477
numSurfaces = *(*data);
478
*data += 1;
479
480
for (i = 0; i < numSurfaces; i++) {
481
if (*surfaceRooms != NULL) {
482
room = *(*surfaceRooms);
483
*surfaceRooms += 1;
484
}
485
486
surface = read_surface_data(vertexData, data);
487
if (surface != NULL) {
488
surface->room = room;
489
surface->type = surfaceType;
490
surface->flags = (s8) flags;
491
492
if (hasForce) {
493
surface->force = *(*data + 3);
494
} else {
495
surface->force = 0;
496
}
497
498
add_surface(surface, FALSE);
499
}
500
501
*data += 3;
502
if (hasForce) {
503
*data += 1;
504
}
505
}
506
}
507
508
/**
509
* Read the data for vertices for reference by triangles.
510
*/
511
static s16 *read_vertex_data(s16 **data) {
512
s32 numVertices;
513
UNUSED s16 unused1[3];
514
UNUSED s16 unused2[3];
515
s16 *vertexData;
516
517
numVertices = *(*data);
518
(*data)++;
519
520
vertexData = *data;
521
*data += 3 * numVertices;
522
523
return vertexData;
524
}
525
526
/**
527
* Loads in special environmental regions, such as water, poison gas, and JRB fog.
528
*/
529
static void load_environmental_regions(s16 **data) {
530
s32 numRegions;
531
s32 i;
532
533
gEnvironmentRegions = *data;
534
numRegions = *(*data)++;
535
536
if (numRegions > 20) {
537
}
538
539
for (i = 0; i < numRegions; i++) {
540
UNUSED s16 val, loX, loZ, hiX, hiZ;
541
s16 height;
542
543
val = *(*data)++;
544
545
loX = *(*data)++;
546
hiX = *(*data)++;
547
loZ = *(*data)++;
548
hiZ = *(*data)++;
549
550
height = *(*data)++;
551
552
gEnvironmentLevels[i] = height;
553
}
554
}
555
556
/**
557
* Allocate some of the main pool for surfaces (2300 surf) and for surface nodes (7000 nodes).
558
*/
559
void alloc_surface_pools(void) {
560
#ifdef USE_SYSTEM_MALLOC
561
sStaticSurfaceNodePool = alloc_only_pool_init();
562
sStaticSurfacePool = alloc_only_pool_init();
563
sDynamicSurfaceNodePool = alloc_only_pool_init();
564
sDynamicSurfacePool = alloc_only_pool_init();
565
#else
566
sSurfacePoolSize = 2300;
567
sSurfaceNodePool = main_pool_alloc(7000 * sizeof(struct SurfaceNode), MEMORY_POOL_LEFT);
568
sSurfacePool = main_pool_alloc(sSurfacePoolSize * sizeof(struct Surface), MEMORY_POOL_LEFT);
569
#endif
570
571
gCCMEnteredSlide = 0;
572
reset_red_coins_collected();
573
}
574
575
#ifdef NO_SEGMENTED_MEMORY
576
/**
577
* Get the size of the terrain data, to get the correct size when copying later.
578
*/
579
u32 get_area_terrain_size(s16 *data) {
580
s16 *startPos = data;
581
s32 end = FALSE;
582
s16 terrainLoadType;
583
s32 numVertices;
584
s32 numRegions;
585
s32 numSurfaces;
586
s16 hasForce;
587
588
while (!end) {
589
terrainLoadType = *data++;
590
591
switch (terrainLoadType) {
592
case TERRAIN_LOAD_VERTICES:
593
numVertices = *data++;
594
data += 3 * numVertices;
595
break;
596
597
case TERRAIN_LOAD_OBJECTS:
598
data += get_special_objects_size(data);
599
break;
600
601
case TERRAIN_LOAD_ENVIRONMENT:
602
numRegions = *data++;
603
data += 6 * numRegions;
604
break;
605
606
case TERRAIN_LOAD_CONTINUE:
607
continue;
608
609
case TERRAIN_LOAD_END:
610
end = TRUE;
611
break;
612
613
default:
614
numSurfaces = *data++;
615
hasForce = surface_has_force(terrainLoadType);
616
data += (3 + hasForce) * numSurfaces;
617
break;
618
}
619
}
620
621
return data - startPos;
622
}
623
#endif
624
625
626
/**
627
* Process the level file, loading in vertices, surfaces, some objects, and environmental
628
* boxes (water, gas, JRB fog).
629
*/
630
void load_area_terrain(s16 index, s16 *data, s8 *surfaceRooms, s16 *macroObjects) {
631
s16 terrainLoadType;
632
s16 *vertexData;
633
UNUSED s32 unused;
634
635
// Initialize the data for this.
636
gEnvironmentRegions = NULL;
637
unused8038BE90 = 0;
638
gSurfaceNodesAllocated = 0;
639
gSurfacesAllocated = 0;
640
#ifdef USE_SYSTEM_MALLOC
641
alloc_only_pool_clear(sStaticSurfaceNodePool);
642
alloc_only_pool_clear(sStaticSurfacePool);
643
alloc_only_pool_clear(sDynamicSurfaceNodePool);
644
alloc_only_pool_clear(sDynamicSurfacePool);
645
sStaticSurfaceLoadComplete = FALSE;
646
647
// Originally they forgot to clear this matrix,
648
// results in segfaults if this is not done.
649
clear_dynamic_surfaces();
650
#endif
651
652
clear_static_surfaces();
653
654
// A while loop iterating through each section of the level data. Sections of data
655
// are prefixed by a terrain "type." This type is reused for surfaces as the surface
656
// type.
657
while (TRUE) {
658
terrainLoadType = *data;
659
data++;
660
661
if (TERRAIN_LOAD_IS_SURFACE_TYPE_LOW(terrainLoadType)) {
662
load_static_surfaces(&data, vertexData, terrainLoadType, &surfaceRooms);
663
} else if (terrainLoadType == TERRAIN_LOAD_VERTICES) {
664
vertexData = read_vertex_data(&data);
665
} else if (terrainLoadType == TERRAIN_LOAD_OBJECTS) {
666
spawn_special_objects(index, &data);
667
} else if (terrainLoadType == TERRAIN_LOAD_ENVIRONMENT) {
668
load_environmental_regions(&data);
669
} else if (terrainLoadType == TERRAIN_LOAD_CONTINUE) {
670
continue;
671
} else if (terrainLoadType == TERRAIN_LOAD_END) {
672
break;
673
} else if (TERRAIN_LOAD_IS_SURFACE_TYPE_HIGH(terrainLoadType)) {
674
load_static_surfaces(&data, vertexData, terrainLoadType, &surfaceRooms);
675
continue;
676
}
677
}
678
679
if (macroObjects != NULL && *macroObjects != -1) {
680
// If the first macro object presetID is within the range [0, 29].
681
// Generally an early spawning method, every object is in BBH (the first level).
682
if (0 <= *macroObjects && *macroObjects < 30) {
683
spawn_macro_objects_hardcoded(index, macroObjects);
684
}
685
// A more general version that can spawn more objects.
686
else {
687
spawn_macro_objects(index, macroObjects);
688
}
689
}
690
691
gNumStaticSurfaceNodes = gSurfaceNodesAllocated;
692
gNumStaticSurfaces = gSurfacesAllocated;
693
694
#ifdef USE_SYSTEM_MALLOC
695
sStaticSurfaceLoadComplete = TRUE;
696
#endif
697
}
698
699
/**
700
* If not in time stop, clear the surface partitions.
701
*/
702
void clear_dynamic_surfaces(void) {
703
if (!(gTimeStopState & TIME_STOP_ACTIVE)) {
704
#ifdef USE_SYSTEM_MALLOC
705
if (gSurfacesAllocated > gNumStaticSurfaces) {
706
alloc_only_pool_clear(sDynamicSurfacePool);
707
}
708
if (gSurfaceNodesAllocated > gNumStaticSurfaceNodes) {
709
alloc_only_pool_clear(sDynamicSurfaceNodePool);
710
}
711
#endif
712
713
gSurfacesAllocated = gNumStaticSurfaces;
714
gSurfaceNodesAllocated = gNumStaticSurfaceNodes;
715
716
clear_spatial_partition(&gDynamicSurfacePartition[0][0]);
717
}
718
}
719
720
UNUSED static void unused_80383604(void) {
721
}
722
723
/**
724
* Applies an object's transformation to the object's vertices.
725
*/
726
void transform_object_vertices(s16 **data, s16 *vertexData) {
727
register s16 *vertices;
728
register f32 vx, vy, vz;
729
register s32 numVertices;
730
731
Mat4 *objectTransform;
732
Mat4 m;
733
734
objectTransform = &gCurrentObject->transform;
735
736
numVertices = *(*data);
737
(*data)++;
738
739
vertices = *data;
740
741
if (gCurrentObject->header.gfx.throwMatrix == NULL) {
742
gCurrentObject->header.gfx.throwMatrix = objectTransform;
743
obj_build_transform_from_pos_and_angle(gCurrentObject, O_POS_INDEX, O_FACE_ANGLE_INDEX);
744
}
745
746
obj_apply_scale_to_matrix(gCurrentObject, m, *objectTransform);
747
748
// Go through all vertices, rotating and translating them to transform the object.
749
while (numVertices--) {
750
vx = *(vertices++);
751
vy = *(vertices++);
752
vz = *(vertices++);
753
754
//! No bounds check on vertex data
755
*vertexData++ = (s16)(vx * m[0][0] + vy * m[1][0] + vz * m[2][0] + m[3][0]);
756
*vertexData++ = (s16)(vx * m[0][1] + vy * m[1][1] + vz * m[2][1] + m[3][1]);
757
*vertexData++ = (s16)(vx * m[0][2] + vy * m[1][2] + vz * m[2][2] + m[3][2]);
758
}
759
760
*data = vertices;
761
}
762
763
/**
764
* Load in the surfaces for the gCurrentObject. This includes setting the flags, exertion, and room.
765
*/
766
void load_object_surfaces(s16 **data, s16 *vertexData) {
767
s32 surfaceType;
768
s32 i;
769
s32 numSurfaces;
770
s16 hasForce;
771
s16 flags;
772
s16 room;
773
774
surfaceType = *(*data);
775
(*data)++;
776
777
numSurfaces = *(*data);
778
(*data)++;
779
780
hasForce = surface_has_force(surfaceType);
781
782
flags = surf_has_no_cam_collision(surfaceType);
783
flags |= SURFACE_FLAG_DYNAMIC;
784
785
// The DDD warp is initially loaded at the origin and moved to the proper
786
// position in paintings.c and doesn't update its room, so set it here.
787
if (gCurrentObject->behavior == segmented_to_virtual(bhvDddWarp)) {
788
room = 5;
789
} else {
790
room = 0;
791
}
792
793
for (i = 0; i < numSurfaces; i++) {
794
struct Surface *surface = read_surface_data(vertexData, data);
795
796
if (surface != NULL) {
797
surface->object = gCurrentObject;
798
surface->type = surfaceType;
799
800
if (hasForce) {
801
surface->force = *(*data + 3);
802
} else {
803
surface->force = 0;
804
}
805
806
surface->flags |= flags;
807
surface->room = (s8) room;
808
add_surface(surface, TRUE);
809
}
810
811
if (hasForce) {
812
*data += 4;
813
} else {
814
*data += 3;
815
}
816
}
817
}
818
819
/**
820
* Transform an object's vertices, reload them, and render the object.
821
*/
822
void load_object_collision_model(void) {
823
UNUSED s32 unused;
824
s16 vertexData[600];
825
826
s16 *collisionData = gCurrentObject->collisionData;
827
f32 marioDist = gCurrentObject->oDistanceToMario;
828
f32 tangibleDist = gCurrentObject->oCollisionDistance;
829
830
// On an object's first frame, the distance is set to 19000.0f.
831
// If the distance hasn't been updated, update it now.
832
if (gCurrentObject->oDistanceToMario == 19000.0f) {
833
marioDist = dist_between_objects(gCurrentObject, gMarioObject);
834
}
835
836
// If the object collision is supposed to be loaded more than the
837
// drawing distance of 4000, extend the drawing range.
838
if (gCurrentObject->oCollisionDistance > 4000.0f) {
839
gCurrentObject->oDrawingDistance = gCurrentObject->oCollisionDistance;
840
}
841
842
// Update if no Time Stop, in range, and in the current room.
843
if (!(gTimeStopState & TIME_STOP_ACTIVE) && marioDist < tangibleDist
844
&& !(gCurrentObject->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM)) {
845
collisionData++;
846
transform_object_vertices(&collisionData, vertexData);
847
848
// TERRAIN_LOAD_CONTINUE acts as an "end" to the terrain data.
849
while (*collisionData != TERRAIN_LOAD_CONTINUE) {
850
load_object_surfaces(&collisionData, vertexData);
851
}
852
}
853
854
if (marioDist < gCurrentObject->oDrawingDistance * configDrawDistanceMultiplier || configDrawDistanceMultiplier <= 0.0f) {
855
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
856
} else {
857
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
858
}
859
}
860
861