diff --git a/data/behavior_data.c b/data/behavior_data.c index 946a3808..0d6d75be 100644 --- a/data/behavior_data.c +++ b/data/behavior_data.c @@ -762,21 +762,17 @@ const BehaviorScript bhvTower[] = { BEGIN(OBJ_LIST_SURFACE), OR_INT(oFlags, (OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE)), LOAD_COLLISION_DATA(wf_seg7_collision_tower), - SET_FLOAT(oCollisionDistance, 3000), SET_FLOAT(oDrawingDistance, 20000), - BEGIN_LOOP(), - CALL_NATIVE(load_object_collision_model), - END_LOOP(), + CALL_NATIVE(load_object_static_model), + BREAK(), }; const BehaviorScript bhvBulletBillCannon[] = { BEGIN(OBJ_LIST_SURFACE), OR_INT(oFlags, OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE), LOAD_COLLISION_DATA(wf_seg7_collision_bullet_bill_cannon), - SET_FLOAT(oCollisionDistance, 300), - BEGIN_LOOP(), - CALL_NATIVE(load_object_collision_model), - END_LOOP(), + CALL_NATIVE(load_object_static_model), + BREAK(), }; const BehaviorScript bhvWfBreakableWallRight[] = { @@ -892,9 +888,9 @@ const BehaviorScript bhvWarpPipe[] = { SET_FLOAT(oDrawingDistance, 16000), SET_INT(oIntangibleTimer, 0), SET_HITBOX(/*Radius*/ 70, /*Height*/ 50), + CALL_NATIVE(load_object_static_model), BEGIN_LOOP(), CALL_NATIVE(bhv_warp_loop), - CALL_NATIVE(load_object_collision_model), END_LOOP(), }; @@ -1683,9 +1679,9 @@ const BehaviorScript bhvWfSolidTowerPlatform[] = { BEGIN(OBJ_LIST_SURFACE), OR_INT(oFlags, (OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW | OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE)), LOAD_COLLISION_DATA(wf_seg7_collision_platform), + CALL_NATIVE(load_object_static_model), BEGIN_LOOP(), CALL_NATIVE(bhv_wf_solid_tower_platform_loop), - CALL_NATIVE(load_object_collision_model), END_LOOP(), }; @@ -2668,23 +2664,17 @@ const BehaviorScript bhvBowserSubDoor[] = { OR_INT(oFlags, (OBJ_FLAG_ACTIVE_FROM_AFAR | OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE)), LOAD_COLLISION_DATA(ddd_seg7_collision_bowser_sub_door), SET_FLOAT(oDrawingDistance, 20000), - SET_FLOAT(oCollisionDistance, 20000), - BEGIN_LOOP(), - CALL_NATIVE(bhv_bowsers_sub_loop), - CALL_NATIVE(load_object_collision_model), - END_LOOP(), + CALL_NATIVE(bhv_bowsers_sub_init), + BREAK(), }; const BehaviorScript bhvBowsersSub[] = { BEGIN(OBJ_LIST_SURFACE), OR_INT(oFlags, (OBJ_FLAG_ACTIVE_FROM_AFAR | OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE)), SET_FLOAT(oDrawingDistance, 20000), - SET_FLOAT(oCollisionDistance, 20000), LOAD_COLLISION_DATA(ddd_seg7_collision_submarine), - BEGIN_LOOP(), - CALL_NATIVE(bhv_bowsers_sub_loop), - CALL_NATIVE(load_object_collision_model), - END_LOOP(), + CALL_NATIVE(bhv_bowsers_sub_init), + BREAK(), }; const BehaviorScript bhvSushiShark[] = { @@ -3777,15 +3767,14 @@ const BehaviorScript bhvMessagePanel[] = { BEGIN(OBJ_LIST_SURFACE), OR_INT(oFlags, (OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE)), LOAD_COLLISION_DATA(wooden_signpost_seg3_collision_0302DD80), - SET_FLOAT(oCollisionDistance, 150), SET_INTERACT_TYPE(INTERACT_TEXT), SET_INT(oInteractionSubtype, INT_SUBTYPE_SIGN), DROP_TO_FLOOR(), SET_HITBOX(/*Radius*/ 150, /*Height*/ 80), SET_INT(oWoodenPostTotalMarioAngle, 0), + CALL_NATIVE(load_object_static_model), BEGIN_LOOP(), SET_INT(oIntangibleTimer, 0), - CALL_NATIVE(load_object_collision_model), SET_INT(oInteractStatus, INT_STATUS_NONE), END_LOOP(), }; diff --git a/include/config/config_world.h b/include/config/config_world.h index 173a75da..2840d33a 100644 --- a/include/config/config_world.h +++ b/include/config/config_world.h @@ -12,7 +12,6 @@ */ // #define WORLD_SCALE 1 - /** * 0: Regular bounds * Same as vanilla sm64, boundaries are (-8192 to 8191) @@ -29,10 +28,6 @@ * level boundaries are 4 times as big (-32768 to 32767) * Collision calculations remain as fast as vanilla, at the cost of using far more RAM (16 times vanilla). * 64x64 collision cells. - * - * If you see "SURFACE POOL FULL" or "SURFACE NODE POOL FULL" in game, you should increase - * SURFACE_POOL_SIZE or SURFACE_NODE_POOL_SIZE, respectively, or reduce the amount of - * collision surfaces in your level. */ // Set this to the extended bounds mode you want, then do "make clean". @@ -70,18 +65,6 @@ STATIC_ASSERT(((EXTENDED_BOUNDS_MODE >= 0) && (EXTENDED_BOUNDS_MODE <= 3)), "You // The amount of cells in each axis in an area. #define NUM_CELLS (2 * LEVEL_BOUNDARY_MAX / CELL_SIZE) -// The maximum amount of collision surfaces (static and dynamic combined) -#define SURFACE_POOL_SIZE (LEVEL_BOUNDARY_MAX / 2) // Vanilla: 2300 -// The maximum amount of SurfaceNodes (static and dynamic combined). -// Each cell has a SurfaceNode for every surface which intersects it, -// so each cell a Surface intersects with gets its own SurfaceNode, -// so larger surfaces means more SurfaceNodes. -// Multiply SURFACE_POOL_SIZE by the average amount of cells the surfaces intersect. -#define SURFACE_NODE_POOL_SIZE (SURFACE_POOL_SIZE * 4) // Vanilla: 7000 - -// Flags for error messages. -#define NOT_ENOUGH_ROOM_FOR_SURFACES (1 << 0) -#define NOT_ENOUGH_ROOM_FOR_NODES (1 << 1) // Use this to convert game units to cell coordinates. #define GET_CELL_COORD(p) ((((s32)(p) + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & (NUM_CELLS - 1)) diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c index 786c97f0..9da3b661 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c @@ -25,35 +25,36 @@ SpatialPartitionCell gStaticSurfacePartition[NUM_CELLS][NUM_CELLS]; SpatialPartitionCell gDynamicSurfacePartition[NUM_CELLS][NUM_CELLS]; /** - * Pools of data to contain either surface nodes or surfaces. + * Pools of data that can contain either surface nodes or surfaces. + * The static surface pool is resized to be exactly the amount of memory needed for the level geometry. + * The dynamic surface pool is set at a fixed length and cleared every frame. */ -struct SurfaceNode *sSurfaceNodePool; -struct Surface *sSurfacePool; +void *gCurrStaticSurfacePool; +void *gDynamicSurfacePool; /** - * The size of the surface node pool (SURFACE_NODE_POOL_SIZE). + * The end of the data currently allocated to the surface pools. */ -s32 sSurfaceNodePoolSize = SURFACE_NODE_POOL_SIZE; +void *gCurrStaticSurfacePoolEnd; +void *gDynamicSurfacePoolEnd; /** - * The size of the surface pool (SURFACE_POOL_SIZE). + * The amount of data currently allocated to static surfaces. */ -s32 sSurfacePoolSize = SURFACE_POOL_SIZE; - -u8 gSurfacePoolError = 0x0; +u32 gTotalStaticSurfaceData; /** * Allocate the part of the surface node pool to contain a surface node. */ -static struct SurfaceNode *alloc_surface_node(void) { - struct SurfaceNode *node = &sSurfaceNodePool[gSurfaceNodesAllocated++]; +static struct SurfaceNode *alloc_surface_node(u32 dynamic) { + struct SurfaceNode **poolEnd = (struct SurfaceNode **)(dynamic ? &gDynamicSurfacePoolEnd : &gCurrStaticSurfacePoolEnd); + + struct SurfaceNode *node = *poolEnd; + (*poolEnd)++; + gSurfaceNodesAllocated++; node->next = NULL; - if (gSurfaceNodesAllocated >= sSurfaceNodePoolSize) { - gSurfacePoolError |= NOT_ENOUGH_ROOM_FOR_NODES; - } - return node; } @@ -61,13 +62,12 @@ static struct SurfaceNode *alloc_surface_node(void) { * Allocate the part of the surface pool to contain a surface and * initialize the surface. */ -static struct Surface *alloc_surface(void) { - - struct Surface *surface = &sSurfacePool[gSurfacesAllocated++]; - - if (gSurfacesAllocated >= sSurfacePoolSize) { - gSurfacePoolError |= NOT_ENOUGH_ROOM_FOR_SURFACES; - } +static struct Surface *alloc_surface(u32 dynamic) { + struct Surface **poolEnd = (struct Surface **)(dynamic ? &gDynamicSurfacePoolEnd : &gCurrStaticSurfacePoolEnd); + + struct Surface *surface = *poolEnd; + (*poolEnd)++; + gSurfacesAllocated++; surface->type = SURFACE_DEFAULT; surface->force = 0; @@ -98,6 +98,7 @@ static void clear_spatial_partition(SpatialPartitionCell *cells) { * Clears the static (level) surface partitions for new use. */ static void clear_static_surfaces(void) { + gTotalStaticSurfaceData = 0; clear_spatial_partition(&gStaticSurfacePartition[0][0]); } @@ -128,7 +129,7 @@ static void add_surface_to_cell(s32 dynamic, s32 cellX, s32 cellZ, struct Surfac s32 surfacePriority = surface->upperY * sortDir; - struct SurfaceNode *newNode = alloc_surface_node(); + struct SurfaceNode *newNode = alloc_surface_node(dynamic); newNode->surface = surface; if (dynamic) { @@ -234,8 +235,9 @@ static void add_surface(struct Surface *surface, s32 dynamic) { * Initializes a Surface struct using the given vertex data * @param vertexData The raw data containing vertex positions * @param vertexIndices Helper which tells positions in vertexData to start reading vertices + * @param dynamic If the surface belongs to an object or not */ -static struct Surface *read_surface_data(TerrainData *vertexData, TerrainData **vertexIndices) { +static struct Surface *read_surface_data(TerrainData *vertexData, TerrainData **vertexIndices, u32 dynamic) { Vec3t v[3]; Vec3f n; Vec3t offset; @@ -251,7 +253,7 @@ static struct Surface *read_surface_data(TerrainData *vertexData, TerrainData ** vec3f_normalize(n); - struct Surface *surface = alloc_surface(); + struct Surface *surface = alloc_surface(dynamic); vec3s_copy(surface->vertex1, v[0]); vec3s_copy(surface->vertex2, v[1]); @@ -338,7 +340,7 @@ static void load_static_surfaces(TerrainData **data, TerrainData *vertexData, s3 room = *(*surfaceRooms)++; } - surface = read_surface_data(vertexData, data); + surface = read_surface_data(vertexData, data, FALSE); if (surface != NULL) { surface->room = room; surface->type = surfaceType; @@ -396,11 +398,11 @@ static void load_environmental_regions(TerrainData **data) { } /** - * Allocate some of the main pool for surfaces (2300 surf) and for surface nodes (7000 nodes). + * Allocate the dynamic surface pool for object collision. */ void alloc_surface_pools(void) { - sSurfaceNodePool = main_pool_alloc(sSurfaceNodePoolSize * sizeof(struct SurfaceNode), MEMORY_POOL_LEFT); - sSurfacePool = main_pool_alloc(sSurfacePoolSize * sizeof(struct Surface), MEMORY_POOL_LEFT); + gDynamicSurfacePool = main_pool_alloc(DYNAMIC_SURFACE_POOL_SIZE, MEMORY_POOL_LEFT); + gDynamicSurfacePoolEnd = gDynamicSurfacePool; gCCMEnteredSlide = FALSE; reset_red_coins_collected(); @@ -470,6 +472,7 @@ u32 get_area_terrain_size(TerrainData *data) { void load_area_terrain(s32 index, TerrainData *data, RoomData *surfaceRooms, s16 *macroObjects) { s32 terrainLoadType; TerrainData *vertexData = NULL; + u32 surfacePoolData; // Initialize the data for this. gEnvironmentRegions = NULL; @@ -478,6 +481,10 @@ void load_area_terrain(s32 index, TerrainData *data, RoomData *surfaceRooms, s16 clear_static_surfaces(); + // Initialise a new surface pool for this block of static surface data + gCurrStaticSurfacePool = main_pool_alloc(main_pool_available() - 0x10, MEMORY_POOL_LEFT); + gCurrStaticSurfacePoolEnd = gCurrStaticSurfacePool; + // A while loop iterating through each section of the level data. Sections of data // are prefixed by a terrain "type." This type is reused for surfaces as the surface // type. @@ -514,6 +521,10 @@ void load_area_terrain(s32 index, TerrainData *data, RoomData *surfaceRooms, s16 } } + surfacePoolData = (uintptr_t)gCurrStaticSurfacePoolEnd - (uintptr_t)gCurrStaticSurfacePool; + gTotalStaticSurfaceData += surfacePoolData; + main_pool_realloc(gCurrStaticSurfacePool, surfacePoolData); + gNumStaticSurfaceNodes = gSurfaceNodesAllocated; gNumStaticSurfaces = gSurfacesAllocated; } @@ -526,6 +537,8 @@ void clear_dynamic_surfaces(void) { gSurfacesAllocated = gNumStaticSurfaces; gSurfaceNodesAllocated = gNumStaticSurfaceNodes; + gDynamicSurfacePoolEnd = gDynamicSurfacePool; + clear_spatial_partition(&gDynamicSurfacePartition[0][0]); } } @@ -566,7 +579,7 @@ void transform_object_vertices(TerrainData **data, TerrainData *vertexData) { /** * Load in the surfaces for the o. This includes setting the flags, exertion, and room. */ -void load_object_surfaces(TerrainData **data, TerrainData *vertexData) { +void load_object_surfaces(TerrainData **data, TerrainData *vertexData, u32 dynamic) { s32 i; s32 surfaceType = *(*data)++; @@ -576,14 +589,14 @@ void load_object_surfaces(TerrainData **data, TerrainData *vertexData) { TerrainData hasForce = surface_has_force(surfaceType); #endif - s32 flags = surf_has_no_cam_collision(surfaceType) | SURFACE_FLAG_DYNAMIC; + s32 flags = surf_has_no_cam_collision(surfaceType) | (dynamic ? SURFACE_FLAG_DYNAMIC : 0); // The DDD warp is initially loaded at the origin and moved to the proper // position in paintings.c and doesn't update its room, so set it here. RoomData room = (o->behavior == segmented_to_virtual(bhvDddWarp)) ? 5 : 0; for (i = 0; i < numSurfaces; i++) { - struct Surface *surface = read_surface_data(vertexData, data); + struct Surface *surface = read_surface_data(vertexData, data, dynamic); if (surface != NULL) { surface->object = o; @@ -601,7 +614,7 @@ void load_object_surfaces(TerrainData **data, TerrainData *vertexData) { surface->flags |= flags; surface->room = room; - add_surface(surface, TRUE); + add_surface(surface, dynamic); } #ifdef ALL_SURFACES_HAVE_FORCE @@ -673,8 +686,38 @@ void load_object_collision_model(void) { // TERRAIN_LOAD_CONTINUE acts as an "end" to the terrain data. while (*collisionData != TERRAIN_LOAD_CONTINUE) { - load_object_surfaces(&collisionData, vertexData); + load_object_surfaces(&collisionData, vertexData, TRUE); } } COND_BIT((marioDist < o->oDrawingDistance), o->header.gfx.node.flags, GRAPH_RENDER_ACTIVE); } + +/** + * Transform an object's vertices and add them to the static surface pool. + */ +void load_object_static_model(void) { + TerrainData vertexData[600]; + TerrainData *collisionData = o->collisionData; + u32 surfacePoolData; + + // Initialise a new surface pool for this block of surface data + gCurrStaticSurfacePool = main_pool_alloc(main_pool_available() - 0x10, MEMORY_POOL_LEFT); + gCurrStaticSurfacePoolEnd = gCurrStaticSurfacePool; + gSurfaceNodesAllocated = gNumStaticSurfaceNodes; + gSurfacesAllocated = gNumStaticSurfaces; + + collisionData++; + transform_object_vertices(&collisionData, vertexData); + + // TERRAIN_LOAD_CONTINUE acts as an "end" to the terrain data. + while (*collisionData != TERRAIN_LOAD_CONTINUE) { + load_object_surfaces(&collisionData, vertexData, FALSE); + } + + surfacePoolData = (uintptr_t)gCurrStaticSurfacePoolEnd - (uintptr_t)gCurrStaticSurfacePool; + gTotalStaticSurfaceData += surfacePoolData; + main_pool_realloc(gCurrStaticSurfacePool, surfacePoolData); + + gNumStaticSurfaceNodes = gSurfaceNodesAllocated; + gNumStaticSurfaces = gSurfacesAllocated; +} diff --git a/src/engine/surface_load.h b/src/engine/surface_load.h index adbd56e6..fcfc3c7f 100644 --- a/src/engine/surface_load.h +++ b/src/engine/surface_load.h @@ -11,7 +11,10 @@ #define NORMAL_FLOOR_THRESHOLD 0.01f #define NORMAL_CEIL_THRESHOLD -NORMAL_FLOOR_THRESHOLD -extern u8 gSurfacePoolError; +/** + * The size of the dynamic surface pool, in bytes. + */ +#define DYNAMIC_SURFACE_POOL_SIZE 0x8000 struct SurfaceNode { struct SurfaceNode *next; @@ -30,10 +33,11 @@ typedef struct SurfaceNode SpatialPartitionCell[NUM_SPATIAL_PARTITIONS]; extern SpatialPartitionCell gStaticSurfacePartition[NUM_CELLS][NUM_CELLS]; extern SpatialPartitionCell gDynamicSurfacePartition[NUM_CELLS][NUM_CELLS]; -extern struct SurfaceNode *sSurfaceNodePool; -extern struct Surface *sSurfacePool; -extern s32 sSurfaceNodePoolSize; -extern s32 sSurfacePoolSize; +extern void *gCurrStaticSurfacePool; +extern void *gDynamicSurfacePool; +extern void *gCurrStaticSurfacePoolEnd; +extern void *gDynamicSurfacePoolEnd; +extern u32 gTotalStaticSurfaceData; void alloc_surface_pools(void); #ifdef NO_SEGMENTED_MEMORY @@ -42,5 +46,6 @@ u32 get_area_terrain_size(TerrainData *data); void load_area_terrain(s32 index, TerrainData *data, RoomData *surfaceRooms, MacroObject *macroObjects); void clear_dynamic_surfaces(void); void load_object_collision_model(void); +void load_object_static_model(void); #endif // SURFACE_LOAD_H diff --git a/src/game/behavior_actions.h b/src/game/behavior_actions.h index 02a9d446..0487d542 100644 --- a/src/game/behavior_actions.h +++ b/src/game/behavior_actions.h @@ -204,7 +204,7 @@ void bhv_bub_loop(void); void bhv_exclamation_box_loop(void); void bhv_rotating_exclamation_mark_loop(void); void bhv_sound_spawner_init(void); -void bhv_bowsers_sub_loop(void); +void bhv_bowsers_sub_init(void); void bhv_sushi_shark_loop(void); void bhv_jrb_sliding_box_loop(void); void bhv_ship_part_3_loop(void); diff --git a/src/game/behaviors/ddd_sub.inc.c b/src/game/behaviors/ddd_sub.inc.c index 2d59a223..42350138 100644 --- a/src/game/behaviors/ddd_sub.inc.c +++ b/src/game/behaviors/ddd_sub.inc.c @@ -1,7 +1,9 @@ // ddd_sub.inc.c -void bhv_bowsers_sub_loop(void) { +void bhv_bowsers_sub_init(void) { if (save_file_get_flags() & (SAVE_FLAG_HAVE_KEY_2 | SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR)) { obj_mark_for_deletion(o); + } else { + load_object_static_model(); } } diff --git a/src/game/hud.c b/src/game/hud.c index 292ccef8..fd4a39bd 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -599,9 +599,6 @@ void render_hud(void) { render_hud_timer(); } - if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_SURFACES) print_text(10, 40, "SURFACE POOL FULL"); - if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_NODES) print_text(10, 60, "SURFACE NODE POOL FULL"); - #ifdef VANILLA_STYLE_CUSTOM_DEBUG if (gCustomDebugMode) { render_debug_mode(); diff --git a/src/game/object_list_processor.c b/src/game/object_list_processor.c index 6d20b4b7..4ee6d7d4 100644 --- a/src/game/object_list_processor.c +++ b/src/game/object_list_processor.c @@ -552,6 +552,9 @@ void update_terrain_objects(void) { gObjectCounter += update_objects_in_list(&gObjectLists[OBJ_LIST_SURFACE]); profiler_update(PROFILER_TIME_DYNAMIC); + + // If the dynamic surface pool has overflowed, throw an error. + assert((uintptr_t)gDynamicSurfacePoolEnd <= (uintptr_t)gDynamicSurfacePool + DYNAMIC_SURFACE_POOL_SIZE, "Dynamic surface pool size exceeded"); } /** diff --git a/src/game/puppyprint.c b/src/game/puppyprint.c index d30d13e9..141d2ed8 100644 --- a/src/game/puppyprint.c +++ b/src/game/puppyprint.c @@ -126,8 +126,7 @@ void puppyprint_calculate_ram_usage(void) { // gEffectsMemoryPool is 0x4000, gObjectMemoryPool is 0x800. Epic C limitations mean I can't just sizeof their values :) ramsizeSegment[5] = (EFFECTS_MEMORY_POOL + OBJECT_MEMORY_POOL + EFFECTS_MEMORY_POOL + OBJECT_MEMORY_POOL); - ramsizeSegment[6] = ((SURFACE_NODE_POOL_SIZE * sizeof(struct SurfaceNode)) - + ( SURFACE_POOL_SIZE * sizeof(struct Surface ))); + ramsizeSegment[6] = gTotalStaticSurfaceData + DYNAMIC_SURFACE_POOL_SIZE; ramsizeSegment[7] = gAudioHeapSize; } @@ -454,8 +453,8 @@ extern s16 gVisualSurfaceCount; void puppyprint_render_collision(void) { char textBytes[200]; - sprintf(textBytes, "Pool Size: %X#Node Size: %X#Surfaces Allocated: %d#Nodes Allocated: %d#Current Cell: %d", (SURFACE_NODE_POOL_SIZE * sizeof(struct SurfaceNode)), (SURFACE_POOL_SIZE * sizeof(struct Surface)), - gSurfacesAllocated, gSurfaceNodesAllocated, gVisualSurfaceCount); + sprintf(textBytes, "Static Pool Size: 0x%X#Dynamic Pool Size: 0x%X#Dynamic Pool Used: 0x%X#Surfaces Allocated: %d#Nodes Allocated: %d", gTotalStaticSurfaceData, DYNAMIC_SURFACE_POOL_SIZE,(uintptr_t)gDynamicSurfacePoolEnd - (uintptr_t)gDynamicSurfacePool, + gSurfacesAllocated, gSurfaceNodesAllocated); print_small_text(304, 60, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, 1);