Adjust the static surface pool's size automatically per level and add static object collision (#444)

This commit is contained in:
arthurtilly
2022-07-06 14:27:23 +12:00
committed by GitHub
parent c55153242d
commit b864e0d6da
9 changed files with 108 additions and 87 deletions

View File

@@ -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(),
};

View File

@@ -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))

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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();

View File

@@ -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");
}
/**

View File

@@ -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);