diff --git a/src/Headers/game.h b/src/Headers/game.h index b166add..10a1140 100644 --- a/src/Headers/game.h +++ b/src/Headers/game.h @@ -171,6 +171,7 @@ extern int gDebugMode; extern int gFullscreenModeAppliedOnBoot; extern int gMaxItemsAllocatedInAPass; extern int gNitroParticleGroup; +extern int gNumObjNodes; extern int gWindowHeight; extern int gWindowWidth; extern int gCurrentSaveSlot; diff --git a/src/Headers/structs.h b/src/Headers/structs.h index 7434dd9..99747f2 100644 --- a/src/Headers/structs.h +++ b/src/Headers/structs.h @@ -236,14 +236,14 @@ struct ObjNode struct ObjNode *ShadowNode; // ptr to node's shadow (if any) - u_short Slot; // sort value + uint16_t Slot; // sort value Byte Genre; // obj genre (skeleton, display_group, custom, event) Byte Type; // obj type (If Genre=display_group: model# in group. If Genre is skel: skel#.) Byte Group; // obj group (If Genre=display_group: index into gObjectGroupList.) void (*MoveCall)(struct ObjNode *); // pointer to object's move routine void (*SplineMoveCall)(struct ObjNode *); // pointer to object's spline move routine void (*CustomDrawFunction)(struct ObjNode *);// pointer to object's custom draw function - u_long StatusBits; // various status bits + uint32_t StatusBits; // various status bits TQ3Point3D Coord; // coord of object TQ3Point3D OldCoord; // coord @ previous frame @@ -256,8 +256,8 @@ struct ObjNode TQ3Vector3D Scale; // scale of object TQ3Point2D TargetOff; // target offsets - u_long CType; // collision type bits - u_long CBits; // collision attribute bits + uint32_t CType; // collision type bits + uint32_t CBits; // collision attribute bits Byte NumCollisionBoxes; CollisionBoxType *CollisionBoxes;// Ptr to array of collision rectangles CollisionBoxType *OldCollisionBoxes; diff --git a/src/System/Objects.c b/src/System/Objects.c index d607976..ec4130d 100644 --- a/src/System/Objects.c +++ b/src/System/Objects.c @@ -17,6 +17,7 @@ /****************************/ static void FlushObjectDeleteQueue(int queueID); +static void DisposeObjNodeMemory(ObjNode* node); /****************************/ @@ -24,25 +25,30 @@ static void FlushObjectDeleteQueue(int queueID); /****************************/ #define OBJ_DEL_Q_SIZE 100 +#define OBJ_BUDGET 500 /**********************/ /* VARIABLES */ /**********************/ +static Boolean gPooledObjNodesInUse[OBJ_BUDGET]; +static ObjNode gObjNodePool[OBJ_BUDGET]; +static ObjNode gObjNodeTemplate; + // OBJECT LIST ObjNode *gFirstNodePtr = nil; - + ObjNode *gCurrentNode,*gMostRecentlyAddedNode,*gNextNode; - - +int gNumObjNodes = 0; + NewObjectDefinitionType gNewObjectDefinition; TQ3Point3D gCoord; TQ3Vector3D gDelta; -// Source port change: We now have a double-buffered deletion queue, so objects -// that get queued for deletion during frame N will be deleted at the end of frame N+1. +// Double-buffered deletion queue: objects that get queued for deletion during +// frame N will be deleted at the end of frame N+1. // This gives live objects a full frame to clean up references to dead objects. static int gNumObjsInDeleteQueue[2]; static ObjNode* gObjectDeleteQueue[2][OBJ_DEL_Q_SIZE]; @@ -62,15 +68,36 @@ float gAutoFadeStartDist; void InitObjectManager(void) { + /* INIT LINKED LIST */ - /* INIT LINKED LIST */ - - gCurrentNode = nil; - - /* CLEAR ENTIRE OBJECT LIST */ - gFirstNodePtr = nil; // no node yet + gNumObjNodes = 0; + + /* INIT OBJECT POOL */ + + memset(gObjNodePool, 0, sizeof(gObjNodePool)); + memset(gPooledObjNodesInUse, 0, sizeof(gPooledObjNodesInUse)); + + /* MAKE OBJECT TEMPLATE */ + + gObjNodeTemplate = (ObjNode) + { + .CType = 0, // must init ctype to something (INVALID_NODE_FLAG might be set from last delete) + .Scale = {1,1,1}, + .BoundingSphere = {.origin={0,0,0}, .radius=40, .isEmpty=kQ3False}, + .EffectChannel = -1, // no effect channel yet + .ParticleGroup = -1, // no particle group + .SplineObjectIndex = -1, // no index yet + .StatusBits = STATUS_BIT_DETACHED, // not attached to linked list yet + }; + + Render_SetDefaultModifiers(&gObjNodeTemplate.RenderModifiers); + + /* INIT NEW OBJ DEF */ + + memset(&gNewObjectDefinition, 0, sizeof(NewObjectDefinitionType)); + gNewObjectDefinition.scale = 1; } @@ -83,18 +110,40 @@ void InitObjectManager(void) ObjNode *MakeNewObject(NewObjectDefinitionType *newObjDef) { -ObjNode *newNodePtr; + ObjNode *newNodePtr = NULL; + + /* TRY TO GET AN OBJECT FROM THE POOL */ + + for (int i = 0; i < OBJ_BUDGET; i++) + { + if (!gPooledObjNodesInUse[i]) + { + gPooledObjNodesInUse[i] = true; // lock that one + newNodePtr = &gObjNodePool[i]; // point to pooled node + break; + } + } + + /* POOL FULL, ALLOCATE NEW NODE ON HEAP */ + + if (newNodePtr == NULL) + { + newNodePtr = (ObjNode*) AllocPtr(sizeof(ObjNode)); + } + + /* MAKE SURE WE GOT ONE */ + + GAME_ASSERT(newNodePtr); - /* MAKE SURE SCALE != 0 */ + /* MAKE SURE SCALE != 0 */ float scale = newObjDef->scale; if (scale == 0.0f) scale = 0.0001f; - /* INITIALIZE NEW NODE */ - - newNodePtr = (ObjNode*) NewPtrClear(sizeof(ObjNode)); // source port change: use NewPtrClear so all fields start at 0 - GAME_ASSERT(newNodePtr); + /* INITIALIZE NEW NODE */ + + *newNodePtr = gObjNodeTemplate; newNodePtr->Slot = newObjDef->slot; newNodePtr->Type = newObjDef->type; @@ -116,29 +165,21 @@ ObjNode *newNodePtr; newNodePtr->Rot.y = newObjDef->rot; newNodePtr->Rot.z = 0; - newNodePtr->BoundingSphere.radius = 40; // set default bounding sphere at offset 0,0,0 - - newNodePtr->EffectChannel = -1; // no streaming sound effect - newNodePtr->ParticleGroup = -1; // no particle group - newNodePtr->SplineObjectIndex = -1; // no index yet - - Render_SetDefaultModifiers(&newNodePtr->RenderModifiers); - newNodePtr->RenderModifiers.statusBits = 0; - newNodePtr->RenderModifiers.diffuseColor = (TQ3ColorRGBA) { 1,1,1,1 }; // default diffuse color is opaque white - if (newObjDef->flags & STATUS_BIT_ONSPLINE) newNodePtr->SplineMoveCall = newObjDef->moveCall; // save spline move routine else newNodePtr->MoveCall = newObjDef->moveCall; // save move routine /* INSERT NODE INTO LINKED LIST */ - + newNodePtr->StatusBits |= STATUS_BIT_DETACHED; // its not attached to linked list yet AttachObject(newNodePtr); /* CLEANUP */ - + gMostRecentlyAddedNode = newNodePtr; // remember this + gNumObjNodes++; + return(newNodePtr); } @@ -590,7 +631,7 @@ void DeleteObject(ObjNode *theNode) { // We're out of room in the delete queue. Just delete the node immediately, // but that might cause the game to become unstable (unless we're here from DeleteAllNodes). - DisposePtr((Ptr) theNode); + DisposeObjNodeMemory(theNode); } else { @@ -705,6 +746,31 @@ short slot; } + +/***************** DISPOSE OBJECT MEMORY ****************/ + +static void DisposeObjNodeMemory(ObjNode* node) +{ + GAME_ASSERT(node != NULL); + + ptrdiff_t poolIndex = node - gObjNodePool; + + if (poolIndex >= 0 && poolIndex < OBJ_BUDGET) + { + // node is pooled, put back into pool + GAME_ASSERT_MESSAGE(gPooledObjNodesInUse[poolIndex], "Pooled node freed twice"); + gPooledObjNodesInUse[poolIndex] = false; + } + else + { + // node was allocated on heap + DisposePtr((Ptr) node); + } + + gNumObjNodes--; +} + + /***************** FLUSH OBJECT DELETE QUEUE ****************/ static void FlushObjectDeleteQueue(int qid) @@ -712,7 +778,12 @@ static void FlushObjectDeleteQueue(int qid) int num = gNumObjsInDeleteQueue[qid]; for (int i = 0; i < num; i++) - DisposePtr((Ptr) gObjectDeleteQueue[qid][i]); + { + if (gObjectDeleteQueue[qid][i]) + { + DisposeObjNodeMemory(gObjectDeleteQueue[qid][i]); + } + } gNumObjsInDeleteQueue[qid] = 0; } diff --git a/src/System/SDLMaintenance.c b/src/System/SDLMaintenance.c index 0cef4fc..1fdc8d7 100644 --- a/src/System/SDLMaintenance.c +++ b/src/System/SDLMaintenance.c @@ -38,7 +38,7 @@ static void UpdateDebugStats(void) snprintf( gDebugTextBuffer, sizeof(gDebugTextBuffer), - "fps: %d\ntris: %d\nmeshes: %d+%d\ntiles: %ld/%ld%s\n\nx: %d\nz: %d\ny: %f %s%s\n%s\n%s\n\n\n\n\n\n\n\n\n\n\n" + "fps: %d\ntris: %d\nmeshes: %d+%d\ntiles: %ld/%ld%s\nnodes: %d\n\nx: %d\nz: %d\ny: %.3f %s%s\n%s\n%s\n\n\n\n\n\n\n\n\n\n" "Bugdom %s\nOpenGL %s, %s @ %dx%d", (int)roundf(fps), gRenderStats.triangles, @@ -47,6 +47,7 @@ static void UpdateDebugStats(void) gSupertileBudget - gNumFreeSupertiles, gSupertileBudget, gSuperTileMemoryListExists ? "" : " (no terrain)", + gNumObjNodes, (int)(gPlayerObj? gPlayerObj->Coord.x: 0), (int)(gPlayerObj? gPlayerObj->Coord.z: 0), gPlayerObj? gPlayerObj->Coord.y: 0,