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