it is now possible to have multiple map models loaded at once - removed
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 4 Apr 2009 14:53:35 +0000 (14:53 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 4 Apr 2009 14:53:35 +0000 (14:53 +0000)
the isworldmodel flag, and submodel searches now use the mapname as a
second search key so submodels from multiple maps can coexist in memory
this is a cleanup that I had wanted to do for a long time...
added special modeldecompile support for submodels, they should now save
to a name based on the current map as well as the submodel number

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8854 d7cf8633-e32d-0410-b094-e92efae38249

cl_main.c
cl_parse.c
clvm_cmds.c
host_cmd.c
model_brush.c
model_shared.c
model_shared.h
sv_main.c

index 675c356..7b65168 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -461,7 +461,7 @@ static void CL_ModelIndexList_f(void)
 
        while(cl.model_precache[i] && i != MAX_MODELS)
        { // Valid Model
-               if(cl.model_precache[i]->loaded || cl.model_precache[i]->isworldmodel)
+               if(cl.model_precache[i]->loaded || i == 1)
                        Con_Printf("%3i: %-30s %-8s %-10i\n", i, cl.model_precache[i]->name, cl.model_precache[i]->modeldatatypestring, cl.model_precache[i]->surfmesh.num_triangles);
                else
                        Con_Printf("%3i: %-30s %-30s\n", i, cl.model_precache[i]->name, "--no local model found--");
index a64e1ff..28c4dda 100644 (file)
@@ -560,24 +560,24 @@ static void QW_CL_RequestNextDownload(void)
                if (!sv.active)
                        Mod_ClearUsed();
                for (i = 1;i < MAX_MODELS && cl.model_name[i][0];i++)
-                       Mod_FindName(cl.model_name[i]);
+                       Mod_FindName(cl.model_name[i], cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL);
                // precache any models used by the client (this also marks them used)
-               cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, false);
-               cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, false);
-               cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, false);
-               cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, false);
+               cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, NULL);
+               cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, NULL);
+               cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, NULL);
+               cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, NULL);
                Mod_PurgeUnused();
 
                // now we try to load everything that is new
 
                // world model
-               cl.model_precache[1] = Mod_ForName(cl.model_name[1], false, false, true);
+               cl.model_precache[1] = Mod_ForName(cl.model_name[1], false, false, NULL);
                if (cl.model_precache[1]->Draw == NULL)
                        Con_Printf("Map %s could not be found or downloaded\n", cl.model_name[1]);
 
                // normal models
                for (i = 2;i < MAX_MODELS && cl.model_name[i][0];i++)
-                       if ((cl.model_precache[i] = Mod_ForName(cl.model_name[i], false, false, false))->Draw == NULL)
+                       if ((cl.model_precache[i] = Mod_ForName(cl.model_name[i], false, false, cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL))->Draw == NULL)
                                Con_Printf("Model %s could not be found or downloaded\n", cl.model_name[i]);
 
                // check memory integrity
@@ -1029,7 +1029,7 @@ void CL_BeginDownloads(qboolean aborteddownload)
                        if (cl.model_precache[cl.loadmodel_current] && cl.model_precache[cl.loadmodel_current]->Draw)
                                continue;
                        CL_KeepaliveMessage(true);
-                       cl.model_precache[cl.loadmodel_current] = Mod_ForName(cl.model_name[cl.loadmodel_current], false, false, cl.loadmodel_current == 1);
+                       cl.model_precache[cl.loadmodel_current] = Mod_ForName(cl.model_name[cl.loadmodel_current], false, false, cl.model_name[cl.loadmodel_current][0] == '*' ? cl.model_name[1] : NULL);
                        if (cl.model_precache[cl.loadmodel_current] && cl.model_precache[cl.loadmodel_current]->Draw && cl.loadmodel_current == 1)
                        {
                                // we now have the worldmodel so we can set up the game world
@@ -1102,7 +1102,7 @@ void CL_BeginDownloads(qboolean aborteddownload)
                        if (cl.model_precache[cl.downloadmodel_current] && cl.model_precache[cl.downloadmodel_current]->Draw)
                                continue;
                        CL_KeepaliveMessage(true);
-                       if (strcmp(cl.model_name[cl.downloadmodel_current], "null") && !FS_FileExists(cl.model_name[cl.downloadmodel_current]))
+                       if (cl.model_name[cl.downloadmodel_current][0] != '*' && strcmp(cl.model_name[cl.downloadmodel_current], "null") && !FS_FileExists(cl.model_name[cl.downloadmodel_current]))
                        {
                                if (cl.downloadmodel_current == 1)
                                        Con_Printf("Map %s not found\n", cl.model_name[cl.downloadmodel_current]);
@@ -1116,7 +1116,7 @@ void CL_BeginDownloads(qboolean aborteddownload)
                                        return;
                                }
                        }
-                       cl.model_precache[cl.downloadmodel_current] = Mod_ForName(cl.model_name[cl.downloadmodel_current], false, false, cl.downloadmodel_current == 1);
+                       cl.model_precache[cl.downloadmodel_current] = Mod_ForName(cl.model_name[cl.downloadmodel_current], false, false, cl.model_name[cl.downloadmodel_current][0] == '*' ? cl.model_name[1] : NULL);
                        if (cl.downloadmodel_current == 1)
                        {
                                // we now have the worldmodel so we can set up the game world
@@ -1445,6 +1445,10 @@ static void CL_SignonReply (void)
                break;
 
        case 4:
+               // after the level has been loaded, we shouldn't need the shaders, and
+               // if they are needed again they will be automatically loaded...
+               Mod_FreeQ3Shaders();
+
                Con_ClearNotify();
                if (COM_CheckParm("-profilegameonly"))
                        Sys_AllowProfiling(true);
@@ -1471,6 +1475,8 @@ void CL_ParseServerInfo (void)
        {
                SCR_BeginLoadingPlaque();
                S_StopAllSounds();
+               // free q3 shaders so that any newly downloaded shaders will be active
+               Mod_FreeQ3Shaders();
        }
 
        // check memory integrity
@@ -1631,12 +1637,12 @@ void CL_ParseServerInfo (void)
                if (!sv.active)
                        Mod_ClearUsed();
                for (i = 1;i < nummodels;i++)
-                       Mod_FindName(cl.model_name[i]);
+                       Mod_FindName(cl.model_name[i], cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL);
                // precache any models used by the client (this also marks them used)
-               cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, false);
-               cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, false);
-               cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, false);
-               cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, false);
+               cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, NULL);
+               cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, NULL);
+               cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, NULL);
+               cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, NULL);
                Mod_PurgeUnused();
 
                // do the same for sounds
@@ -2593,7 +2599,7 @@ void CL_ParseTempEntity(void)
 
        // LordHavoc: for compatibility with the Nehahra movie...
                case TE_LIGHTNING4NEH:
-                       CL_ParseBeam(Mod_ForName(MSG_ReadString(), true, false, false), false);
+                       CL_ParseBeam(Mod_ForName(MSG_ReadString(), true, false, NULL), false);
                        break;
 
                case TE_LAVASPLASH:
@@ -3659,7 +3665,7 @@ void CL_ParseServerMessage(void)
                                        {
                                                if (i >= 1 && i < MAX_MODELS)
                                                {
-                                                       dp_model_t *model = Mod_ForName(s, false, false, i == 1);
+                                                       dp_model_t *model = Mod_ForName(s, false, false, s[0] == '*' ? cl.model_name[1] : NULL);
                                                        if (!model)
                                                                Con_DPrintf("svc_precache: Mod_ForName(\"%s\") failed\n", s);
                                                        cl.model_precache[i] = model;
index 8b6e148..da4832e 100644 (file)
@@ -393,7 +393,7 @@ void VM_CL_precache_model (void)
                }
        }
        PRVM_G_FLOAT(OFS_RETURN) = 0;
-       m = Mod_ForName(name, false, false, false);
+       m = Mod_ForName(name, false, false, name[0] == '*' ? cl.model_name[1] : NULL);
        if(m && m->loaded)
        {
                for (i = 0;i < MAX_MODELS;i++)
index d77264f..6e3a2ab 100644 (file)
@@ -933,7 +933,7 @@ void Host_Loadgame_f (void)
                                        if (i >= 0 && i < MAX_MODELS)
                                        {
                                                strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
-                                               sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false);
+                                               sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
                                        }
                                        else
                                                Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
@@ -2084,7 +2084,7 @@ void Host_Viewmodel_f (void)
        if (!e)
                return;
 
-       m = Mod_ForName (Cmd_Argv(1), false, true, false);
+       m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
        if (!m || !m->loaded || !m->Draw)
        {
                Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
index 723f4da..f652f22 100644 (file)
@@ -27,7 +27,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 
 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128", "how large water polygons should be (smaller values produce more polygons which give better warping effects)"};
-cvar_t halflifebsp = {0, "halflifebsp", "0", "indicates the current map is hlbsp format (useful to know because of different bounding box sizes)"};
 cvar_t r_novis = {0, "r_novis", "0", "draws whole level, see also sv_cullentities_pvs 0"};
 cvar_t r_picmipworld = {CVAR_SAVE, "r_picmipworld", "1", "whether gl_picmip shall apply to world textures too"};
 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0", "pretends there was no texture lump found in the q1bsp/hlbsp loading (useful for debugging this rare case)"};
@@ -54,7 +53,6 @@ static texture_t mod_q1bsp_texture_water;
 void Mod_BrushInit(void)
 {
 //     Cvar_RegisterVariable(&r_subdivide_size);
-       Cvar_RegisterVariable(&halflifebsp);
        Cvar_RegisterVariable(&r_novis);
        Cvar_RegisterVariable(&r_picmipworld);
        Cvar_RegisterVariable(&r_nosurftextures);
@@ -1487,17 +1485,14 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                        // LordHavoc: HL sky textures are entirely different than quake
                        if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
                        {
-                               if (loadmodel->isworldmodel)
+                               data = loadimagepixelsbgra(tx->name, false, false);
+                               if (data && image_width == 256 && image_height == 128)
                                {
-                                       data = loadimagepixelsbgra(tx->name, false, false);
-                                       if (data && image_width == 256 && image_height == 128)
-                                       {
-                                               R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
-                                               Mem_Free(data);
-                                       }
-                                       else if (mtdata != NULL)
-                                               R_Q1BSP_LoadSplitSky(mtdata, mtwidth, mtheight, 1);
+                                       R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
+                                       Mem_Free(data);
                                }
+                               else if (mtdata != NULL)
+                                       R_Q1BSP_LoadSplitSky(mtdata, mtwidth, mtheight, 1);
                        }
                        else
                        {
@@ -3447,9 +3442,13 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
        mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
        mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
-
-       if (loadmodel->isworldmodel)
-               Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
+       mod->Draw = R_Q1BSP_Draw;
+       mod->DrawDepth = R_Q1BSP_DrawDepth;
+       mod->DrawDebug = R_Q1BSP_DrawDebug;
+       mod->GetLightInfo = R_Q1BSP_GetLightInfo;
+       mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+       mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+       mod->DrawLight = R_Q1BSP_DrawLight;
 
 // load into heap
 
@@ -3555,21 +3554,26 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
                if (i > 0)
                {
                        char name[10];
-                       // LordHavoc: only register submodels if it is the world
-                       // (prevents external bsp models from replacing world submodels with
-                       //  their own)
-                       if (!loadmodel->isworldmodel)
-                               continue;
                        // duplicate the basic information
                        dpsnprintf(name, sizeof(name), "*%i", i);
-                       mod = Mod_FindName(name);
+                       mod = Mod_FindName(name, loadmodel->name);
                        // copy the base model to this one
                        *mod = *loadmodel;
                        // rename the clone back to its proper name
                        strlcpy(mod->name, name, sizeof(mod->name));
+                       mod->brush.parentmodel = loadmodel;
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;
                        mod->mempool = NULL;
+                       mod->brush.TraceLineOfSight = NULL;
+                       mod->brush.GetPVS = NULL;
+                       mod->brush.FatPVS = NULL;
+                       mod->brush.BoxTouchingPVS = NULL;
+                       mod->brush.BoxTouchingLeafPVS = NULL;
+                       mod->brush.BoxTouchingVisibleLeafs = NULL;
+                       mod->brush.FindBoxClusters = NULL;
+                       mod->brush.LightPoint = NULL;
+                       mod->brush.AmbientSoundLevelsForPoint = NULL;
                }
 
                mod->brush.submodel = i;
@@ -3593,29 +3597,6 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
                mod->sortedmodelsurfaces = (int *)datapointer;datapointer += mod->nummodelsurfaces * sizeof(int);
                Mod_MakeSortedSurfaces(mod);
 
-               // this gets altered below if sky or water is used
-               mod->DrawSky = NULL;
-               mod->DrawAddWaterPlanes = NULL;
-               mod->Draw = R_Q1BSP_Draw;
-               mod->DrawDepth = R_Q1BSP_DrawDepth;
-               mod->DrawDebug = R_Q1BSP_DrawDebug;
-               mod->GetLightInfo = R_Q1BSP_GetLightInfo;
-               mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
-               mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
-               mod->DrawLight = R_Q1BSP_DrawLight;
-               if (i != 0)
-               {
-                       mod->brush.TraceLineOfSight = NULL;
-                       mod->brush.GetPVS = NULL;
-                       mod->brush.FatPVS = NULL;
-                       mod->brush.BoxTouchingPVS = NULL;
-                       mod->brush.BoxTouchingLeafPVS = NULL;
-                       mod->brush.BoxTouchingVisibleLeafs = NULL;
-                       mod->brush.FindBoxClusters = NULL;
-                       mod->brush.LightPoint = NULL;
-                       mod->brush.AmbientSoundLevelsForPoint = NULL;
-               }
-
                // copy the submodel bounds, then enlarge the yaw and rotated bounds according to radius
                // (previously this code measured the radius of the vertices of surfaces in the submodel, but that broke submodels that contain only CLIP brushes, which do not produce surfaces)
                VectorCopy(bm->mins, mod->normalmins);
@@ -3636,18 +3617,25 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
                mod->radius = modelradius;
                mod->radius2 = modelradius * modelradius;
 
+               // this gets altered below if sky or water is used
+               mod->DrawSky = NULL;
+               mod->DrawAddWaterPlanes = NULL;
+
                // scan surfaces for sky and water and flag the submodel as possessing these features or not
                // build lightstyle lists for quick marking of dirty lightmaps when lightstyles flicker
                if (mod->nummodelsurfaces)
                {
                        for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
-                       {
-                               // we only need to have a drawsky function if it is used(usually only on world model)
                                if (surface->texture->basematerialflags & MATERIALFLAG_SKY)
-                                       mod->DrawSky = R_Q1BSP_DrawSky;
+                                       break;
+                       if (j < mod->nummodelsurfaces)
+                               mod->DrawSky = R_Q1BSP_DrawSky;
+
+                       for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
                                if (surface->texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
-                                       mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
-                       }
+                                       break;
+                       if (j < mod->nummodelsurfaces)
+                               mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
 
                        // build lightstyle update chains
                        // (used to rapidly mark lightmapupdateflags on many surfaces
@@ -4097,9 +4085,6 @@ void static Mod_Q2BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        i = LittleLong(header->version);
        if (i != Q2BSPVERSION)
                Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
-       mod->brush.ishlbsp = false;
-       if (loadmodel->isworldmodel)
-               Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
 
        mod_base = (unsigned char *)header;
 
@@ -5827,9 +5812,6 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        i = LittleLong(header->version);
        if (i != Q3BSPVERSION && i != Q3BSPVERSION_IG && i != Q3BSPVERSION_LIVE)
                Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
-       mod->brush.ishlbsp = false;
-       if (loadmodel->isworldmodel)
-               Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
 
        mod->soundfromcenter = true;
        mod->TraceBox = Mod_Q3BSP_TraceBox;
@@ -5845,6 +5827,8 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
        mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
        mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
+       mod->brush.AmbientSoundLevelsForPoint = NULL;
+       mod->brush.RoundUpToHullSize = NULL;
        mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
        mod->Draw = R_Q1BSP_Draw;
        mod->DrawDepth = R_Q1BSP_DrawDepth;
@@ -5853,7 +5837,6 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        mod->DrawLight = R_Q1BSP_DrawLight;
-       mod->DrawAddWaterPlanes = NULL;
 
        mod_base = (unsigned char *)header;
 
@@ -5952,16 +5935,14 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
                if (i > 0)
                {
                        char name[10];
-                       // LordHavoc: only register submodels if it is the world
-                       // (prevents external bsp models from replacing world submodels with
-                       //  their own)
-                       if (!loadmodel->isworldmodel)
-                               continue;
                        // duplicate the basic information
                        dpsnprintf(name, sizeof(name), "*%i", i);
-                       mod = Mod_FindName(name);
+                       mod = Mod_FindName(name, loadmodel->name);
+                       // copy the base model to this one
                        *mod = *loadmodel;
+                       // rename the clone back to its proper name
                        strlcpy(mod->name, name, sizeof(mod->name));
+                       mod->brush.parentmodel = loadmodel;
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;
                        mod->mempool = NULL;
@@ -5973,7 +5954,7 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
                        mod->brush.BoxTouchingVisibleLeafs = NULL;
                        mod->brush.FindBoxClusters = NULL;
                        mod->brush.LightPoint = NULL;
-                       mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
+                       mod->brush.AmbientSoundLevelsForPoint = NULL;
                }
                mod->brush.submodel = i;
 
@@ -6025,21 +6006,21 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
                mod->radius = modelradius;
                mod->radius2 = modelradius * modelradius;
 
+               // this gets altered below if sky or water is used
+               mod->DrawSky = NULL;
+               mod->DrawAddWaterPlanes = NULL;
+
                for (j = 0;j < mod->nummodelsurfaces;j++)
                        if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & MATERIALFLAG_SKY)
                                break;
                if (j < mod->nummodelsurfaces)
                        mod->DrawSky = R_Q1BSP_DrawSky;
-               else
-                       mod->DrawSky = NULL;
 
                for (j = 0;j < mod->nummodelsurfaces;j++)
                        if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
                                break;
                if (j < mod->nummodelsurfaces)
                        mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
-               else
-                       mod->DrawAddWaterPlanes = NULL;
        }
 }
 
index 664f0da..c8cbc43 100644 (file)
@@ -54,13 +54,10 @@ static void mod_start(void)
        int nummodels = Mem_ExpandableArray_IndexRange(&models);
        dp_model_t *mod;
 
-       // parse the Q3 shader files
-       Mod_LoadQ3Shaders();
-
        for (i = 0;i < nummodels;i++)
                if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
                        if (mod->used)
-                               Mod_LoadModel(mod, true, false, mod->isworldmodel);
+                               Mod_LoadModel(mod, true, false);
 }
 
 static void mod_shutdown(void)
@@ -73,7 +70,7 @@ static void mod_shutdown(void)
                if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
                        Mod_UnloadModel(mod);
 
-       Mem_FreePool (&q3shaders_mem);
+       Mod_FreeQ3Shaders();
 }
 
 static void mod_newmap(void)
@@ -152,14 +149,14 @@ void Mod_RenderInit(void)
 void Mod_UnloadModel (dp_model_t *mod)
 {
        char name[MAX_QPATH];
-       qboolean isworldmodel;
        qboolean used;
+       dp_model_t *parentmodel;
 
        if (developer_loading.integer)
                Con_Printf("unloading model %s\n", mod->name);
 
        strlcpy(name, mod->name, sizeof(name));
-       isworldmodel = mod->isworldmodel;
+       parentmodel = mod->brush.parentmodel;
        used = mod->used;
        if (mod->surfmesh.ebo3i)
                R_Mesh_DestroyBufferObject(mod->surfmesh.ebo3i);
@@ -174,7 +171,7 @@ void Mod_UnloadModel (dp_model_t *mod)
        memset(mod, 0, sizeof(dp_model_t));
        // restore the fields we want to preserve
        strlcpy(mod->name, name, sizeof(mod->name));
-       mod->isworldmodel = isworldmodel;
+       mod->brush.parentmodel = parentmodel;
        mod->used = used;
        mod->loaded = false;
 }
@@ -191,7 +188,7 @@ Mod_LoadModel
 Loads a model
 ==================
 */
-dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, qboolean isworldmodel)
+dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
 {
        int num;
        unsigned int crc;
@@ -205,9 +202,6 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q
        
        if (!strcmp(mod->name, "null"))
        {
-               if (mod->isworldmodel != isworldmodel)
-                       mod->loaded = false;
-
                if(mod->loaded)
                        return mod;
 
@@ -217,7 +211,6 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q
                if (developer_loading.integer)
                        Con_Printf("loading model %s\n", mod->name);
 
-               mod->isworldmodel = isworldmodel;
                mod->used = true;
                mod->crc = -1;
                mod->loaded = false;
@@ -246,12 +239,6 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q
 
        // even if the model is loaded it still may need reloading...
 
-       // if the model is a worldmodel and is being referred to as a
-       // non-worldmodel here, then it needs reloading to get rid of the
-       // submodels
-       if (mod->isworldmodel != isworldmodel)
-               mod->loaded = false;
-
        // if it is not loaded or checkdisk is true we need to calculate the crc
        if (!mod->loaded || checkdisk)
        {
@@ -283,7 +270,6 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q
                Mod_UnloadModel(mod);
 
        // load the model
-       mod->isworldmodel = isworldmodel;
        mod->used = true;
        mod->crc = crc;
        // errors can prevent the corresponding mod->loaded = true;
@@ -298,15 +284,9 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q
        VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
        VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
 
-       // if we're loading a worldmodel, then this is a level change
-       if (mod->isworldmodel)
+       if (!q3shaders_mem)
        {
-               // clear out any stale submodels or worldmodels lying around
-               // if we did this clear before now, an error might abort loading and
-               // leave things in a bad state
-               Mod_RemoveStaleWorldModels(mod);
-               // reload q3 shaders, to make sure they are ready to go for this level
-               // (including any models loaded for this level)
+               // load q3 shaders for the first time, or after a level change
                Mod_LoadQ3Shaders();
        }
 
@@ -373,35 +353,21 @@ void Mod_PurgeUnused(void)
        }
 }
 
-// only used during loading!
-void Mod_RemoveStaleWorldModels(dp_model_t *skip)
-{
-       int i;
-       int nummodels = Mem_ExpandableArray_IndexRange(&models);
-       dp_model_t *mod;
-       for (i = 0;i < nummodels;i++)
-       {
-               if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->isworldmodel && mod->loaded && skip != mod)
-               {
-                       Mod_UnloadModel(mod);
-                       mod->isworldmodel = false;
-                       mod->used = false;
-               }
-       }
-}
-
 /*
 ==================
 Mod_FindName
 
 ==================
 */
-dp_model_t *Mod_FindName(const char *name)
+dp_model_t *Mod_FindName(const char *name, const char *parentname)
 {
        int i;
        int nummodels;
        dp_model_t *mod;
 
+       if (!parentname)
+               parentname = "";
+
        // if we're not dedicatd, the renderer calls will crash without video
        Host_StartVideo();
 
@@ -413,7 +379,7 @@ dp_model_t *Mod_FindName(const char *name)
        // search the currently loaded models
        for (i = 0;i < nummodels;i++)
        {
-               if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !strcmp(mod->name, name))
+               if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !strcmp(mod->name, name) && ((!mod->brush.parentmodel && !parentname[0]) || (mod->brush.parentmodel && parentname[0] && !strcmp(mod->brush.parentmodel->name, parentname))))
                {
                        mod->used = true;
                        return mod;
@@ -423,6 +389,10 @@ dp_model_t *Mod_FindName(const char *name)
        // no match found, create a new one
        mod = (dp_model_t *) Mem_ExpandableArray_AllocRecord(&models);
        strlcpy(mod->name, name, sizeof(mod->name));
+       if (parentname[0])
+               mod->brush.parentmodel = Mod_FindName(parentname, NULL);
+       else
+               mod->brush.parentmodel = NULL;
        mod->loaded = false;
        mod->used = true;
        return mod;
@@ -435,12 +405,12 @@ Mod_ForName
 Loads in a model for the given name
 ==================
 */
-dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, qboolean isworldmodel)
+dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, const char *parentname)
 {
        dp_model_t *model;
-       model = Mod_FindName(name);
-       if (model->name[0] != '*' && (!model->loaded || checkdisk))
-               Mod_LoadModel(model, crash, checkdisk, isworldmodel);
+       model = Mod_FindName(name, parentname);
+       if (!model->loaded || checkdisk)
+               Mod_LoadModel(model, crash, checkdisk);
        return model;
 }
 
@@ -458,7 +428,7 @@ void Mod_Reload(void)
        dp_model_t *mod;
        for (i = 0;i < nummodels;i++)
                if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
-                       Mod_LoadModel(mod, true, true, mod->isworldmodel);
+                       Mod_LoadModel(mod, true, true);
 }
 
 unsigned char *mod_base;
@@ -479,8 +449,15 @@ static void Mod_Print(void)
 
        Con_Print("Loaded models:\n");
        for (i = 0;i < nummodels;i++)
-               if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
-                       Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
+       {
+               if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
+               {
+                       if (mod->brush.numsubmodels)
+                               Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
+                       else
+                               Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
+               }
+       }
 }
 
 /*
@@ -491,7 +468,7 @@ Mod_Precache
 static void Mod_Precache(void)
 {
        if (Cmd_Argc() == 2)
-               Mod_ForName(Cmd_Argv(1), false, true, cl.worldmodel && !strcasecmp(Cmd_Argv(1), cl.worldmodel->name));
+               Mod_ForName(Cmd_Argv(1), false, true, Cmd_Argv(1)[0] == '*' ? cl.model_name[1] : NULL);
        else
                Con_Print("usage: modelprecache <filename>\n");
 }
@@ -1219,18 +1196,9 @@ q3wavefunc_t Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
        return Q3WAVEFUNC_NONE;
 }
 
-static void Q3Shaders_Clear()
+void Mod_FreeQ3Shaders(void)
 {
-       /* Just clear out everything... */
-       Mem_FreePool (&q3shaders_mem);
-       /* ...and alloc the structs again. */
-       q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
-       q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
-               sizeof (q3shader_data_t));
-       Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
-               q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
-       Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
-               q3shaders_mem, sizeof (char**), 256);
+       Mem_FreePool(&q3shaders_mem);
 }
 
 static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
@@ -1280,7 +1248,15 @@ void Mod_LoadQ3Shaders(void)
        int numparameters;
        char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
 
-       Q3Shaders_Clear();
+       Mod_FreeQ3Shaders();
+
+       q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
+       q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
+               sizeof (q3shader_data_t));
+       Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
+               q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
+       Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
+               q3shaders_mem, sizeof (char**), 256);
 
        search = FS_Search("scripts/*.shader", true, false);
        if (!search)
@@ -1774,8 +1750,12 @@ void Mod_LoadQ3Shaders(void)
 
 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
 {
-       unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
-       q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
+       unsigned short hash;
+       q3shader_hash_entry_t* entry;
+       if (!q3shaders_mem)
+               Mod_LoadQ3Shaders();
+       hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
+       entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
        while (entry != NULL)
        {
                if (strcasecmp (entry->shader.name, name) == 0)
@@ -2558,7 +2538,14 @@ static void Mod_Decompile_f(void)
        strlcpy(inname, Cmd_Argv(1), sizeof(inname));
        FS_StripExtension(inname, basename, sizeof(basename));
 
-       mod = Mod_ForName(inname, false, true, cl.worldmodel && !strcasecmp(inname, cl.worldmodel->name));
+       mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
+       if (mod->brush.submodel)
+       {
+               // if we're decompiling a submodel, be sure to give it a proper name based on its parent
+               FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
+               dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
+               outname[0] = 0;
+       }
        if (!mod)
        {
                Con_Print("No such model\n");
index 3476a4a..6935b86 100644 (file)
@@ -604,14 +604,15 @@ typedef struct model_brush_s
        // string of entity definitions (.map format)
        char *entities;
 
-       // if non-zero this is a submodel
+       // if not NULL this is a submodel
+       struct model_s *parentmodel;
        // (this is the number of the submodel, an index into submodels)
        int submodel;
 
        // number of submodels in this map (just used by server to know how many
        // submodels to load)
        int numsubmodels;
-       // pointers to each of the submodels if .isworldmodel is true
+       // pointers to each of the submodels
        struct model_s **submodels;
 
        int num_planes;
@@ -797,8 +798,6 @@ typedef struct model_s
        qboolean                loaded;
        // set if the model is used in current map, models which are not, are purged
        qboolean                used;
-       // true if this is the world model (I.E. defines what sky to use, and may contain submodels)
-       qboolean                isworldmodel;
        // CRC of the file this model was loaded from, to reload if changed
        unsigned int    crc;
        // mod_brush, mod_alias, mod_sprite
@@ -910,9 +909,9 @@ extern cvar_t r_fullbrights;
 
 void Mod_Init (void);
 void Mod_Reload (void);
-dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, qboolean isworldmodel);
-dp_model_t *Mod_FindName (const char *name);
-dp_model_t *Mod_ForName (const char *name, qboolean crash, qboolean checkdisk, qboolean isworldmodel);
+dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk);
+dp_model_t *Mod_FindName (const char *name, const char *parentname);
+dp_model_t *Mod_ForName (const char *name, qboolean crash, qboolean checkdisk, const char *parentname);
 void Mod_UnloadModel (dp_model_t *mod);
 
 void Mod_ClearUsed(void);
@@ -941,6 +940,7 @@ shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh,
 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *firstmesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius);
 void Mod_ShadowMesh_Free(shadowmesh_t *mesh);
 
+void Mod_FreeQ3Shaders(void);
 void Mod_LoadQ3Shaders(void);
 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name);
 qboolean Mod_LoadTextureFromQ3Shader(texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags);
index db27151..b7fbd4d 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -157,6 +157,7 @@ cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be use
 cvar_t sv_autodemo_perclient = {CVAR_SAVE, "sv_autodemo_perclient", "0", "set to 1 to enable autorecorded per-client demos (they'll start to record at the beginning of a match); set it to 2 to also record client->server packets (for debugging)"};
 cvar_t sv_autodemo_perclient_nameformat = {CVAR_SAVE, "sv_autodemo_perclient_nameformat", "sv_autodemos/%Y-%m-%d_%H-%M", "The format of the sv_autodemo_perclient filename, followed by the map name, the client number and the IP address + port number, separated by underscores (the date is encoded using strftime escapes)" };
 
+cvar_t halflifebsp = {0, "halflifebsp", "0", "indicates the current map is hlbsp format (useful to know because of different bounding box sizes)"};
 
 server_t sv;
 server_static_t svs;
@@ -438,6 +439,8 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_autodemo_perclient);
        Cvar_RegisterVariable (&sv_autodemo_perclient_nameformat);
 
+       Cvar_RegisterVariable (&halflifebsp);
+
        // any special defaults for gamemodes go here
        if (gamemode == GAME_HIPNOTIC)
        {
@@ -2399,7 +2402,7 @@ int SV_ModelIndex(const char *s, int precachemode)
                                if (precachemode == 1)
                                        Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
                                strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i]));
-                               sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false);
+                               sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, s[0] == '*' ? sv.modelname : NULL);
                                if (sv.state != ss_loading)
                                {
                                        MSG_WriteByte(&sv.reliable_datagram, svc_precache);
@@ -2701,7 +2704,7 @@ void SV_SaveSpawnparms (void)
 
 /*
 ================
-SV_/pawnServer
+SV_SpawnServer
 
 This is called at the start of each level
 ================
@@ -2743,7 +2746,10 @@ void SV_SpawnServer (const char *server)
                SV_VM_End();
        }
 
-       worldmodel = Mod_ForName(modelname, false, true, true);
+       // free q3 shaders so that any newly downloaded shaders will be active
+       Mod_FreeQ3Shaders();
+
+       worldmodel = Mod_ForName(modelname, false, true, NULL);
        if (!worldmodel || !worldmodel->TraceBox)
        {
                Con_Printf("Couldn't load map %s\n", modelname);
@@ -2800,6 +2806,8 @@ void SV_SpawnServer (const char *server)
        // level's data which is no longer valiud
        cls.signon = 0;
 
+       Cvar_SetValue("halflifebsp", worldmodel->brush.ishlbsp);
+
        if(*sv_random_seed.string)
        {
                srand(sv_random_seed.integer);
@@ -2867,7 +2875,7 @@ void SV_SpawnServer (const char *server)
        for (i = 1;i < sv.worldmodel->brush.numsubmodels;i++)
        {
                dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
-               sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, false);
+               sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, sv.modelname);
        }
 
 //