]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_shared.c
mempools now have sentinel checks, for completeness
[xonotic/darkplaces.git] / model_shared.c
index 6209ace388328f1c5d0ef7b8ea6b2d2158906a88..4ece6a32638274b704f3848d68996b66b5077fcd 100644 (file)
@@ -8,7 +8,7 @@ of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the GNU General Public License for more details.
 
@@ -24,46 +24,220 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 
-model_t        *loadmodel;
-char   loadname[32];   // for hunk tags
+model_t *loadmodel;
 
 // LordHavoc: increased from 512 to 2048
 #define        MAX_MOD_KNOWN   2048
-model_t        mod_known[MAX_MOD_KNOWN];
-int            mod_numknown;
+static model_t mod_known[MAX_MOD_KNOWN];
+
+rtexture_t *r_notexture;
+rtexturepool_t *r_notexturepool;
+
+texture_t r_surf_notexture;
+
+void Mod_SetupNoTexture(void)
+{
+       int x, y;
+       qbyte pix[16][16][4];
+
+       // this makes a light grey/dark grey checkerboard texture
+       for (y = 0;y < 16;y++)
+       {
+               for (x = 0;x < 16;x++)
+               {
+                       if ((y < 8) ^ (x < 8))
+                       {
+                               pix[y][x][0] = 128;
+                               pix[y][x][1] = 128;
+                               pix[y][x][2] = 128;
+                               pix[y][x][3] = 255;
+                       }
+                       else
+                       {
+                               pix[y][x][0] = 64;
+                               pix[y][x][1] = 64;
+                               pix[y][x][2] = 64;
+                               pix[y][x][3] = 255;
+                       }
+               }
+       }
+
+       r_notexturepool = R_AllocTexturePool();
+       r_notexture = R_LoadTexture(r_notexturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP);
+}
+
+extern void Mod_BrushStartup (void);
+extern void Mod_BrushShutdown (void);
+
+static void mod_start(void)
+{
+       int i;
+       for (i = 0;i < MAX_MOD_KNOWN;i++)
+               if (mod_known[i].name[0])
+                       Mod_UnloadModel(&mod_known[i]);
+
+       Mod_SetupNoTexture();
+       Mod_BrushStartup();
+}
+
+static void mod_shutdown(void)
+{
+       int i;
+       for (i = 0;i < MAX_MOD_KNOWN;i++)
+               if (mod_known[i].name[0])
+                       Mod_UnloadModel(&mod_known[i]);
+
+       R_FreeTexturePool(&r_notexturepool);
+       Mod_BrushShutdown();
+}
+
+static void mod_newmap(void)
+{
+}
 
 /*
 ===============
 Mod_Init
 ===============
 */
+static void Mod_Print (void);
+static void Mod_Flush (void);
 void Mod_Init (void)
 {
        Mod_BrushInit();
        Mod_AliasInit();
        Mod_SpriteInit();
+
+       Cmd_AddCommand ("modellist", Mod_Print);
+       Cmd_AddCommand ("modelflush", Mod_Flush);
+}
+
+void Mod_RenderInit(void)
+{
+       R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap);
+}
+
+void Mod_FreeModel (model_t *mod)
+{
+       R_FreeTexturePool(&mod->texturepool);
+       Mem_FreePool(&mod->mempool);
+
+       // clear the struct to make it available
+       memset(mod, 0, sizeof(model_t));
+}
+
+void Mod_UnloadModel (model_t *mod)
+{
+       char name[MAX_QPATH];
+       qboolean isworldmodel;
+       strcpy(name, mod->name);
+       isworldmodel = mod->isworldmodel;
+       Mod_FreeModel(mod);
+       strcpy(mod->name, name);
+       mod->isworldmodel = isworldmodel;
+       mod->needload = true;
 }
 
 /*
-===============
-Mod_Init
+==================
+Mod_LoadModel
 
-Caches the data if needed
-===============
+Loads a model
+==================
 */
-void *Mod_Extradata (model_t *mod)
+static model_t *Mod_LoadModel (model_t *mod, qboolean crash, qboolean checkdisk, qboolean isworldmodel)
 {
-       void    *r;
+       int crc;
+       void *buf;
+
+       mod->used = true;
+
+       if (mod->name[0] == '*') // submodel
+               return mod;
+
+       crc = 0;
+       buf = NULL;
+       if (!mod->needload)
+       {
+               if (checkdisk)
+               {
+                       buf = COM_LoadFile (mod->name, false);
+                       if (!buf)
+                       {
+                               if (crash)
+                                       Host_Error ("Mod_LoadModel: %s not found", mod->name); // LordHavoc: Sys_Error was *ANNOYING*
+                               return NULL;
+                       }
+
+                       crc = CRC_Block(buf, com_filesize);
+               }
+               else
+                       crc = mod->crc;
 
-       r = Cache_Check (&mod->cache);
-       if (r)
-               return r;
+               if (mod->crc == crc && mod->isworldmodel == isworldmodel)
+               {
+                       if (buf)
+                               Mem_Free(buf);
+                       return mod; // already loaded
+               }
+       }
 
-       Mod_LoadModel (mod, true);
+       Con_DPrintf("loading model %s\n", mod->name);
 
-       if (!mod->cache.data)
-               Host_Error ("Mod_Extradata: caching failed");
-       return mod->cache.data;
+       if (!buf)
+       {
+               buf = COM_LoadFile (mod->name, false);
+               if (!buf)
+               {
+                       if (crash)
+                               Host_Error ("Mod_LoadModel: %s not found", mod->name);
+                       return NULL;
+               }
+               crc = CRC_Block(buf, com_filesize);
+       }
+
+       // allocate a new model
+       loadmodel = mod;
+
+       // LordHavoc: unload the existing model in this slot (if there is one)
+       Mod_UnloadModel(mod);
+       mod->isworldmodel = isworldmodel;
+       mod->needload = false;
+       mod->used = true;
+       mod->crc = crc;
+
+       // all models use memory, so allocate a memory pool
+       mod->mempool = Mem_AllocPool(mod->name);
+       // all models load textures, so allocate a texture pool
+       if (cls.state != ca_dedicated)
+               mod->texturepool = R_AllocTexturePool();
+
+       // call the apropriate loader
+            if (!memcmp(buf, "IDPO"    , 4)) Mod_LoadAliasModel  (mod, buf);
+       else if (!memcmp(buf, "IDP2"    , 4)) Mod_LoadQ2AliasModel(mod, buf);
+       else if (!memcmp(buf, "ZYMOTIC" , 7)) Mod_LoadZymoticModel(mod, buf);
+       else if (!memcmp(buf, "IDSP"    , 4)) Mod_LoadSpriteModel (mod, buf);
+       else                                  Mod_LoadBrushModel  (mod, buf);
+
+       Mem_Free(buf);
+
+       return mod;
+}
+
+void Mod_CheckLoaded (model_t *mod)
+{
+       if (mod)
+       {
+               if (mod->needload)
+                       Mod_LoadModel(mod, true, true, mod->isworldmodel);
+               else
+               {
+                       if (mod->type == mod_invalid)
+                               Host_Error("Mod_CheckLoaded: invalid model\n");
+                       mod->used = true;
+                       return;
+               }
+       }
 }
 
 /*
@@ -73,12 +247,27 @@ Mod_ClearAll
 */
 void Mod_ClearAll (void)
 {
-       int             i;
-       model_t *mod;
+}
+
+void Mod_ClearUsed(void)
+{
+       int i;
+       model_t *mod;
+
+       for (i = 0, mod = mod_known;i < MAX_MOD_KNOWN;i++, mod++)
+               if (mod->name[0])
+                       mod->used = false;
+}
+
+void Mod_PurgeUnused(void)
+{
+       int i;
+       model_t *mod;
 
-       for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
-               if (!mod->cachesize)
-                       mod->needload = true;
+       for (i = 0, mod = mod_known;i < MAX_MOD_KNOWN;i++, mod++)
+               if (mod->name[0])
+                       if (!mod->used)
+                               Mod_FreeModel(mod);
 }
 
 /*
@@ -89,29 +278,39 @@ Mod_FindName
 */
 model_t *Mod_FindName (char *name)
 {
-       int             i;
-       model_t *mod;
+       int i;
+       model_t *mod, *freemod;
 
        if (!name[0])
                Host_Error ("Mod_ForName: NULL name");
 
-//
 // search the currently loaded models
-//
-       for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
-               if (!strcmp (mod->name, name) )
-                       break;
+       freemod = NULL;
+       for (i = 0, mod = mod_known;i < MAX_MOD_KNOWN;i++, mod++)
+       {
+               if (mod->name[0])
+               {
+                       if (!strcmp (mod->name, name))
+                       {
+                               mod->used = true;
+                               return mod;
+                       }
+               }
+               else if (freemod == NULL)
+                       freemod = mod;
+       }
 
-       if (i == mod_numknown)
+       if (freemod)
        {
-               if (mod_numknown == MAX_MOD_KNOWN)
-                       Host_Error ("mod_numknown == MAX_MOD_KNOWN");
+               mod = freemod;
                strcpy (mod->name, name);
                mod->needload = true;
-               mod_numknown++;
+               mod->used = true;
+               return mod;
        }
 
-       return mod;
+       Host_Error ("Mod_FindName: ran out of models\n");
+       return NULL;
 }
 
 /*
@@ -125,85 +324,7 @@ void Mod_TouchModel (char *name)
        model_t *mod;
 
        mod = Mod_FindName (name);
-
-       if (!mod->needload)
-               if (mod->cachesize)
-                       Cache_Check (&mod->cache);
-}
-
-/*
-==================
-Mod_LoadModel
-
-Loads a model into the cache
-==================
-*/
-model_t *Mod_LoadModel (model_t *mod, qboolean crash)
-{
-       void    *d;
-       unsigned *buf;
-
-       if (!mod->needload)
-       {
-               if (mod->cachesize)
-               {
-                       d = Cache_Check (&mod->cache);
-                       if (d)
-                               return mod;
-               }
-               else
-                       return mod;             // not cached at all
-       }
-
-       Con_DPrintf("loading model %s\n", mod->name);
-
-// load the file
-       buf = (unsigned *)COM_LoadMallocFile (mod->name, false);
-       if (!buf)
-       {
-               if (crash)
-                       Host_Error ("Mod_NumForName: %s not found", mod->name); // LordHavoc: Sys_Error was *ANNOYING*
-               return NULL;
-       }
-
-// allocate a new model
-//     COM_FileBase (mod->name, loadname);
-       strcpy(loadname, mod->name);
-
-       loadmodel = mod;
-
-// call the apropriate loader
-       mod->needload = false;
-
-       // LordHavoc: clear some important stuff in the model_t structure
-       mod->flags = 0;
-       mod->flags2 = 0;
-
-       switch (LittleLong(*(unsigned *)buf))
-       {
-       case IDPOLYHEADER:
-               Mod_LoadAliasModel (mod, buf);
-               break;
-
-       case MD2IDALIASHEADER: // LordHavoc: added Quake2 model support
-               Mod_LoadQ2AliasModel (mod, buf);
-               break;
-
-       case (('O'<<24)+('M'<<16)+('Y'<<8)+'Z'):
-               Mod_LoadZymoticModel(mod, buf);
-               break;
-
-       case IDSPRITEHEADER:
-               Mod_LoadSpriteModel (mod, buf);
-               break;
-       
-       default:
-               Mod_LoadBrushModel (mod, buf);
-               break;
-       }
-       qfree(buf);
-
-       return mod;
+       mod->used = true;
 }
 
 /*
@@ -213,32 +334,13 @@ Mod_ForName
 Loads in a model for the given name
 ==================
 */
-model_t *Mod_ForName (char *name, qboolean crash)
+model_t *Mod_ForName (char *name, qboolean crash, qboolean checkdisk, qboolean isworldmodel)
 {
-       model_t *mod;
-       
-       mod = Mod_FindName (name);
-       
-       return Mod_LoadModel (mod, crash);
+       return Mod_LoadModel (Mod_FindName (name), crash, checkdisk, isworldmodel);
 }
 
-byte   *mod_base;
+qbyte *mod_base;
 
-/*
-=================
-RadiusFromBounds
-=================
-*/
-float RadiusFromBounds (vec3_t mins, vec3_t maxs)
-{
-       int             i;
-       vec3_t  corner;
-
-       for (i=0 ; i<3 ; i++)
-               corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
-
-       return Length (corner);
-}
 
 //=============================================================================
 
@@ -247,16 +349,23 @@ float RadiusFromBounds (vec3_t mins, vec3_t maxs)
 Mod_Print
 ================
 */
-void Mod_Print (void)
+static void Mod_Print (void)
 {
        int             i;
        model_t *mod;
 
-       Con_Printf ("Cached models:\n");
-       for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)
-       {
-               Con_Printf ("%4iK : %8p : %s\n", (mod->cachesize + 1023) / 1024, mod->cache.data, mod->name);
-       }
+       Con_Printf ("Loaded models:\n");
+       for (i = 0, mod = mod_known;i < MAX_MOD_KNOWN;i++, mod++)
+               if (mod->name[0])
+                       Con_Printf ("%4iK %s\n", mod->mempool ? (mod->mempool->totalsize + 1023) / 1024 : 0, mod->name);
 }
 
+static void Mod_Flush (void)
+{
+       int             i;
 
+       Con_Printf ("Unloading models\n");
+       for (i = 0;i < MAX_MOD_KNOWN;i++)
+               if (mod_known[i].name[0])
+                       Mod_UnloadModel(&mod_known[i]);
+}