]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_shared.c
alias mdl/md2 models now have a mdlmd2data_triangleneighbors array
[xonotic/darkplaces.git] / model_shared.c
index 236a5d60649be6b46d65558f05a25082fd9cd12a..a2ac48e6afda20853e9bb1e81e51b97da5784726 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,223 +24,335 @@ 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;
 
-void Mod_LoadSpriteModel (model_t *mod, void *buffer);
-void Mod_LoadBrushModel (model_t *mod, void *buffer);
-void Mod_LoadAliasModel (model_t *mod, void *buffer);
-void Mod_LoadQ2AliasModel (model_t *mod, void *buffer);
-model_t *Mod_LoadModel (model_t *mod, qboolean crash);
+// LordHavoc: increased from 512 to 2048
+#define        MAX_MOD_KNOWN   2048
+static model_t mod_known[MAX_MOD_KNOWN];
 
-#define        MAX_MOD_KNOWN   512
-model_t        mod_known[MAX_MOD_KNOWN];
-int            mod_numknown;
+rtexture_t *r_notexture;
+rtexturepool_t *r_notexturepool;
 
-extern void Mod_BrushInit();
-extern void Mod_AliasInit();
-extern void Mod_SpriteInit();
+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_LoadModels();
+
+       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);
 }
 
-/*
-===============
-Mod_Init
+void Mod_RenderInit(void)
+{
+       R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap);
+}
 
-Caches the data if needed
-===============
-*/
-void *Mod_Extradata (model_t *mod)
+void Mod_FreeModel (model_t *mod)
 {
-       void    *r;
-       
-       r = Cache_Check (&mod->cache);
-       if (r)
-               return r;
-
-       Mod_LoadModel (mod, true);
-       
-       if (!mod->cache.data)
-               Host_Error ("Mod_Extradata: caching failed");
-       return mod->cache.data;
+       R_FreeTexturePool(&mod->texturepool);
+       Mem_FreePool(&mod->mempool);
+
+       // clear the struct to make it available
+       memset(mod, 0, sizeof(model_t));
 }
 
-/*
-===================
-Mod_ClearAll
-===================
-*/
-void Mod_ClearAll (void)
+void Mod_UnloadModel (model_t *mod)
 {
-       int             i;
-       model_t *mod;
-       
-       for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
-               if (mod->type != mod_alias)
-                       mod->needload = true;
+       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_FindName
+Mod_LoadModel
 
+Loads a model
 ==================
 */
-model_t *Mod_FindName (char *name)
+static model_t *Mod_LoadModel (model_t *mod, qboolean crash, qboolean checkdisk, qboolean isworldmodel)
 {
-       int             i;
-       model_t *mod;
-       
-       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;
-                       
-       if (i == mod_numknown)
+       int crc;
+       void *buf;
+
+       mod->used = true;
+
+       if (mod->name[0] == '*') // submodel
+               return mod;
+
+       crc = 0;
+       buf = NULL;
+       if (!mod->needload)
        {
-               if (mod_numknown == MAX_MOD_KNOWN)
-                       Host_Error ("mod_numknown == MAX_MOD_KNOWN");
-               strcpy (mod->name, name);
-               mod->needload = true;
-               mod_numknown++;
+               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;
+
+               if (mod->crc == crc && mod->isworldmodel == isworldmodel)
+               {
+                       if (buf)
+                               Mem_Free(buf);
+                       return mod; // already loaded
+               }
        }
 
+       Con_DPrintf("loading model %s\n", mod->name);
+
+       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;
 }
 
-/*
-==================
-Mod_TouchModel
-
-==================
-*/
-void Mod_TouchModel (char *name)
+void Mod_CheckLoaded (model_t *mod)
 {
-       model_t *mod;
-       
-       mod = Mod_FindName (name);
-       
-       if (!mod->needload)
+       if (mod)
        {
-               if (mod->type == mod_alias)
-                       Cache_Check (&mod->cache);
+               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;
+               }
        }
 }
 
+/*
+===================
+Mod_ClearAll
+===================
+*/
+void Mod_ClearAll (void)
+{
+}
+
+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 < MAX_MOD_KNOWN;i++, mod++)
+               if (mod->name[0])
+                       if (!mod->used)
+                               Mod_FreeModel(mod);
+}
+
+void Mod_LoadModels(void)
+{
+       int i;
+       model_t *mod;
+
+       for (i = 0, mod = mod_known;i < MAX_MOD_KNOWN;i++, mod++)
+               if (mod->name[0])
+                       if (mod->used)
+                               Mod_CheckLoaded(mod);
+}
+
 /*
 ==================
-Mod_LoadModel
+Mod_FindName
 
-Loads a model into the cache
 ==================
 */
-model_t *Mod_LoadModel (model_t *mod, qboolean crash)
+model_t *Mod_FindName (const char *name)
 {
-       void    *d;
-       unsigned *buf;
+       int i;
+       model_t *mod, *freemod;
 
-       if (!mod->needload)
+       if (!name[0])
+               Host_Error ("Mod_ForName: NULL name");
+
+// search the currently loaded models
+       freemod = NULL;
+       for (i = 0, mod = mod_known;i < MAX_MOD_KNOWN;i++, mod++)
        {
-               if (mod->type == mod_alias)
+               if (mod->name[0])
                {
-                       d = Cache_Check (&mod->cache);
-                       if (d)
+                       if (!strcmp (mod->name, name))
+                       {
+                               mod->used = true;
                                return mod;
+                       }
                }
-               else
-                       return mod;             // not cached at all
+               else if (freemod == NULL)
+                       freemod = mod;
        }
 
-       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);
-       
-       loadmodel = mod;
-
-// call the apropriate loader
-       mod->needload = false;
-       
-       switch (LittleLong(*(unsigned *)buf))
+       if (freemod)
        {
-       case IDPOLYHEADER:
-               Mod_LoadAliasModel (mod, buf);
-               break;
-
-       case MD2IDALIASHEADER: // LordHavoc: added Quake2 model support
-               Mod_LoadQ2AliasModel (mod, buf);
-               break;
-               
-       case IDSPRITEHEADER:
-               Mod_LoadSpriteModel (mod, buf);
-               break;
-       
-       default:
-               Mod_LoadBrushModel (mod, buf);
-               break;
+               mod = freemod;
+               strcpy (mod->name, name);
+               mod->needload = true;
+               mod->used = true;
+               return mod;
        }
-       qfree(buf);
 
-       return mod;
+       Host_Error ("Mod_FindName: ran out of models\n");
+       return NULL;
 }
 
 /*
 ==================
-Mod_ForName
+Mod_TouchModel
 
-Loads in a model for the given name
 ==================
 */
-model_t *Mod_ForName (char *name, qboolean crash)
+void Mod_TouchModel (const char *name)
 {
        model_t *mod;
-       
+
        mod = Mod_FindName (name);
-       
-       return Mod_LoadModel (mod, crash);
+       mod->used = true;
 }
 
-byte   *mod_base;
-
 /*
-=================
-RadiusFromBounds
-=================
+==================
+Mod_ForName
+
+Loads in a model for the given name
+==================
 */
-float RadiusFromBounds (vec3_t mins, vec3_t maxs)
+model_t *Mod_ForName (const char *name, qboolean crash, qboolean checkdisk, qboolean isworldmodel)
 {
-       int             i;
-       vec3_t  corner;
+       return Mod_LoadModel (Mod_FindName (name), crash, checkdisk, isworldmodel);
+}
 
-       for (i=0 ; i<3 ; i++)
-               corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
+qbyte *mod_base;
 
-       return Length (corner);
-}
 
 //=============================================================================
 
@@ -249,16 +361,50 @@ 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 ("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]);
+       Mod_LoadModels();
+}
+
+int Mod_FindTriangleWithEdge(int *elements, int numtriangles, int start, int end)
+{
+       int i;
+       for (i = 0;i < numtriangles;i++, elements += 3)
        {
-               Con_Printf ("%8p : %s\n",mod->cache.data, mod->name);
+               if (elements[0] == start && elements[1] == end)
+                       return i;
+               if (elements[1] == start && elements[2] == end)
+                       return i;
+               if (elements[2] == start && elements[0] == end)
+                       return i;
        }
+       return -1;
 }
 
-
+void Mod_BuildTriangleNeighbors(int *neighbors, int *elements, int numtriangles)
+{
+       int i, *e, *n;
+       for (i = 0, e = elements, n = neighbors;i < numtriangles;i++, e += 3, n += 3)
+       {
+               n[0] = Mod_FindTriangleWithEdge(elements, numtriangles, e[1], e[0]);
+               n[1] = Mod_FindTriangleWithEdge(elements, numtriangles, e[2], e[1]);
+               n[2] = Mod_FindTriangleWithEdge(elements, numtriangles, e[0], e[2]);
+       }
+}