]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_shared.c
fix compile error with .avelocity
[xonotic/darkplaces.git] / model_shared.c
index 553928d2a3f275a48b6c7386ca73c2fd72173905..9389654b14823a587bdfddc24b69650f8f200d55 100644 (file)
@@ -50,17 +50,25 @@ static q3shader_data_t* q3shader_data;
 
 static void mod_start(void)
 {
-       int i;
+       int i, count;
        int nummodels = Mem_ExpandableArray_IndexRange(&models);
        dp_model_t *mod;
 
-       // parse the Q3 shader files
-       Mod_LoadQ3Shaders();
-
+       SCR_PushLoadingScreen(false, "Loading models", 1.0);
+       count = 0;
+       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)
+                               ++count;
        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);
+                       {
+                               SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
+                               Mod_LoadModel(mod, true, false);
+                               SCR_PopLoadingScreen(false);
+                       }
+       SCR_PopLoadingScreen(false);
 }
 
 static void mod_shutdown(void)
@@ -73,7 +81,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 +160,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 +182,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;
 }
@@ -184,6 +192,101 @@ void R_Model_Null_Draw(entity_render_t *ent)
        return;
 }
 
+
+typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qboolean loop, void *pass);
+
+int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
+{
+       const char *bufptr;
+       int start, len;
+       float fps;
+       unsigned int i;
+       qboolean loop;
+
+       bufptr = buf;
+       i = 0;
+       for(;;)
+       {
+               // an anim scene!
+               if (!COM_ParseToken_Simple(&bufptr, true, false))
+                       break;
+               if (!strcmp(com_token, "\n"))
+                       continue; // empty line
+               start = atoi(com_token);
+               if (!COM_ParseToken_Simple(&bufptr, true, false))
+                       break;
+               if (!strcmp(com_token, "\n"))
+               {
+                       Con_Printf("framegroups file: missing number of frames\n");
+                       continue;
+               }
+               len = atoi(com_token);
+               if (!COM_ParseToken_Simple(&bufptr, true, false))
+                       break;
+               // we default to looping as it's usually wanted, so to NOT loop you append a 0
+               if (strcmp(com_token, "\n"))
+               {
+                       fps = atof(com_token);
+                       if (!COM_ParseToken_Simple(&bufptr, true, false))
+                               break;
+                       if (strcmp(com_token, "\n"))
+                               loop = atoi(com_token) != 0;
+                       else
+                               loop = true;
+               }
+               else
+               {
+                       fps = 20;
+                       loop = true;
+               }
+
+               if(cb)
+                       cb(i, start, len, fps, loop, pass);
+               ++i;
+       }
+
+       return i;
+}
+
+void Mod_FrameGroupify_ParseGroups_Count (unsigned int i, int start, int len, float fps, qboolean loop, void *pass)
+{
+       unsigned int *cnt = (unsigned int *) pass;
+       ++*cnt;
+}
+
+void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qboolean loop, void *pass)
+{
+       dp_model_t *mod = (dp_model_t *) pass;
+       animscene_t *anim = &mod->animscenes[i];
+       dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d", i);
+       anim->firstframe = bound(0, start, mod->num_poses - 1);
+       anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
+       anim->framerate = max(1, fps);
+       anim->loop = !!loop;
+       //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
+}
+
+void Mod_FrameGroupify(dp_model_t *mod, const char *buf)
+{
+       unsigned int cnt;
+
+       // 0. count
+       cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
+       if(!cnt)
+       {
+               Con_Printf("no scene found in framegroups file, aborting\n");
+               return;
+       }
+       mod->numframes = cnt;
+
+       // 1. reallocate
+       // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
+       mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
+
+       // 2. parse
+       Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
+}
+
 /*
 ==================
 Mod_LoadModel
@@ -191,7 +294,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 +308,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,9 +317,8 @@ 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->crc = (unsigned int)-1;
                mod->loaded = false;
 
                VectorClear(mod->normalmins);
@@ -246,12 +345,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)
        {
@@ -277,13 +370,14 @@ 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);
+       
+       SCR_PushLoadingScreen(true, mod->name, 1);
 
        // LordHavoc: unload the existing model in this slot (if there is one)
        if (mod->loaded || mod->mempool)
                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 +392,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();
        }
 
@@ -320,7 +408,8 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q
                num = LittleLong(*((int *)buf));
                // call the apropriate loader
                loadmodel = mod;
-                    if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend);
+                    if (!strcasecmp(FS_FileExtension(mod->name), "obj")) Mod_OBJ_Load(mod, buf, bufend);
+               else if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend);
                else if (!memcmp(buf, "IDP2", 4)) Mod_IDP2_Load(mod, buf, bufend);
                else if (!memcmp(buf, "IDP3", 4)) Mod_IDP3_Load(mod, buf, bufend);
                else if (!memcmp(buf, "IDSP", 4)) Mod_IDSP_Load(mod, buf, bufend);
@@ -334,16 +423,26 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q
                else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
                Mem_Free(buf);
 
-               Mod_BuildVBOs();
+               buf = FS_LoadFile (va("%s.framegroups", mod->name), tempmempool, false, &filesize);
+               if(buf)
+               {
+                       Mod_FrameGroupify(mod, (const char *)buf);
+                       Mem_Free(buf);
+               }
 
-               // no fatal errors occurred, so this model is ready to use.
-               mod->loaded = true;
+               Mod_BuildVBOs();
        }
        else if (crash)
        {
                // LordHavoc: Sys_Error was *ANNOYING*
                Con_Printf ("Mod_LoadModel: %s not found\n", mod->name);
        }
+
+       // no fatal errors occurred, so this model is ready to use.
+       mod->loaded = true;
+
+       SCR_PopLoadingScreen(false);
+
        return mod;
 }
 
@@ -372,35 +471,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();
 
@@ -412,7 +497,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;
@@ -422,6 +507,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;
@@ -434,12 +523,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;
 }
 
@@ -452,12 +541,23 @@ Reloads all models if they have changed
 */
 void Mod_Reload(void)
 {
-       int i;
+       int i, count;
        int nummodels = Mem_ExpandableArray_IndexRange(&models);
        dp_model_t *mod;
+
+       SCR_PushLoadingScreen(false, "Reloading models", 1.0);
+       count = 0;
+       for (i = 0;i < nummodels;i++)
+               if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
+                       ++count;
        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);
+               {
+                       SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
+                       Mod_LoadModel(mod, true, true);
+                       SCR_PopLoadingScreen(false);
+               }
+       SCR_PopLoadingScreen(false);
 }
 
 unsigned char *mod_base;
@@ -478,8 +578,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);
+               }
+       }
 }
 
 /*
@@ -490,7 +597,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");
 }
@@ -858,6 +965,8 @@ shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtria
        newmesh->maxtriangles = maxtriangles;
        newmesh->numverts = 0;
        newmesh->numtriangles = 0;
+       memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
+       memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
 
        newmesh->vertex3f = (float *)data;data += maxverts * sizeof(float[3]);
        if (light)
@@ -888,6 +997,8 @@ shadowmesh_t *Mod_ShadowMesh_ReAlloc(mempool_t *mempool, shadowmesh_t *oldmesh,
        newmesh = Mod_ShadowMesh_Alloc(mempool, oldmesh->numverts, oldmesh->numtriangles, oldmesh->map_diffuse, oldmesh->map_specular, oldmesh->map_normal, light, neighbors, false);
        newmesh->numverts = oldmesh->numverts;
        newmesh->numtriangles = oldmesh->numtriangles;
+       memcpy(newmesh->sideoffsets, oldmesh->sideoffsets, sizeof(oldmesh->sideoffsets));
+       memcpy(newmesh->sidetotals, oldmesh->sidetotals, sizeof(oldmesh->sidetotals));
 
        memcpy(newmesh->vertex3f, oldmesh->vertex3f, oldmesh->numverts * sizeof(float[3]));
        if (newmesh->svector3f && oldmesh->svector3f)
@@ -1143,6 +1254,34 @@ void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
        }
 }
 
+void Mod_CreateCollisionMesh(dp_model_t *mod)
+{
+       int k;
+       int numcollisionmeshtriangles;
+       const msurface_t *surface;
+       mempool_t *mempool = mod->mempool;
+       if (!mempool && mod->brush.parentmodel)
+               mempool = mod->brush.parentmodel->mempool;
+       // make a single combined collision mesh for physics engine use
+       numcollisionmeshtriangles = 0;
+       for (k = 0;k < mod->nummodelsurfaces;k++)
+       {
+               surface = mod->data_surfaces + mod->firstmodelsurface + k;
+               if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
+                       continue;
+               numcollisionmeshtriangles += surface->num_triangles;
+       }
+       mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles, NULL, NULL, NULL, false, false, true);
+       for (k = 0;k < mod->nummodelsurfaces;k++)
+       {
+               surface = mod->data_surfaces + mod->firstmodelsurface + k;
+               if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
+                       continue;
+               Mod_ShadowMesh_AddMesh(mempool, mod->brush.collisionmesh, NULL, NULL, NULL, mod->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
+       }
+       mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mempool, mod->brush.collisionmesh, false, true, false);
+}
+
 void Mod_GetTerrainVertex3fTexCoord2fFromBGRA(const unsigned char *imagepixels, int imagewidth, int imageheight, int ix, int iy, float *vertex3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
 {
        float v[3], tc[3];
@@ -1218,18 +1357,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)
@@ -1241,7 +1371,14 @@ static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
        {
                if (strcasecmp (entry->shader.name, shader->name) == 0)
                {
-                       Con_Printf("Shader '%s' already defined\n", shader->name);
+                       unsigned char *start, *end, *start2;
+                       start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
+                       end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
+                       start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
+                       if(memcmp(start, start2, end - start))
+                               Con_Printf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
+                       else
+                               Con_DPrintf("Shader '%s' already defined\n", shader->name);
                        return;
                }
                lastEntry = entry;
@@ -1279,7 +1416,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)
@@ -1299,6 +1444,8 @@ void Mod_LoadQ3Shaders(void)
                        shader.reflectfactor = 1;
                        Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
                        shader.r_water_wateralpha = 1;
+                       shader.specularscalemod = 1;
+                       shader.specularpowermod = 1;
 
                        strlcpy(shader.name, com_token, sizeof(shader.name));
                        if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
@@ -1345,8 +1492,8 @@ void Mod_LoadQ3Shaders(void)
                                                        if (!COM_ParseToken_QuakeC(&text, true))
                                                                break;
                                                }
-                                               for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
-                                                       parameter[j][0] = 0;
+                                               //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
+                                               //      parameter[j][0] = 0;
                                                if (developer.integer >= 100)
                                                {
                                                        Con_Printf("%s %i: ", shader.name, shader.numlayers - 1);
@@ -1564,8 +1711,8 @@ void Mod_LoadQ3Shaders(void)
                                        if (!COM_ParseToken_QuakeC(&text, true))
                                                break;
                                }
-                               for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
-                                       parameter[j][0] = 0;
+                               //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
+                               //      parameter[j][0] = 0;
                                if (fileindex == 0 && !strcasecmp(com_token, "}"))
                                        break;
                                if (developer.integer >= 100)
@@ -1697,6 +1844,14 @@ void Mod_LoadQ3Shaders(void)
                                        Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
                                        shader.r_water_wateralpha = atof(parameter[11]);
                                }
+                               else if (!strcasecmp(parameter[0], "dp_glossintensitymod") && numparameters >= 2)
+                               {
+                                       shader.specularscalemod = atof(parameter[1]);
+                               }
+                               else if (!strcasecmp(parameter[0], "dp_glossexponentmod") && numparameters >= 2)
+                               {
+                                       shader.specularpowermod = atof(parameter[1]);
+                               }
                                else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
                                {
                                        int i, deformindex;
@@ -1769,12 +1924,17 @@ void Mod_LoadQ3Shaders(void)
                }
                Mem_Free(f);
        }
+       FS_FreeSearch(search);
 }
 
 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)
@@ -1787,24 +1947,30 @@ q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
 qboolean Mod_LoadTextureFromQ3Shader(texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags)
 {
        int j;
+       int texflagsmask;
        qboolean success = true;
        q3shaderinfo_t *shader;
        if (!name)
                name = "";
        strlcpy(texture->name, name, sizeof(texture->name));
        shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
+
+       texflagsmask = ~0;
+       if(!(defaulttexflags & TEXF_PICMIP))
+               texflagsmask &= ~TEXF_PICMIP;
+       if(!(defaulttexflags & TEXF_COMPRESS))
+               texflagsmask &= ~TEXF_COMPRESS;
+       texture->specularscalemod = 1; // unless later loaded from the shader
+       texture->specularpowermod = 1; // unless later loaded from the shader
+
        if (shader)
        {
                if (developer_loading.integer)
                        Con_Printf("%s: loaded shader for %s\n", loadmodel->name, name);
                texture->surfaceparms = shader->surfaceparms;
-               texture->textureflags = shader->textureflags;
 
                // allow disabling of picmip or compression by defaulttexflags
-               if(!(defaulttexflags & TEXF_PICMIP))
-                       texture->textureflags &= ~TEXF_PICMIP;
-               if(!(defaulttexflags & TEXF_COMPRESS))
-                       texture->textureflags &= ~TEXF_COMPRESS;
+               texture->textureflags = shader->textureflags & texflagsmask;
 
                if (shader->surfaceparms & Q3SURFACEPARM_SKY)
                {
@@ -1890,7 +2056,7 @@ nothing                GL_ZERO GL_ONE
                                {
                                        texture->skinframes[j] = NULL;
                                }
-                               else if (!(texture->skinframes[j] = R_SkinFrame_LoadExternal(primarylayer->texturename[j], primarylayer->texflags, false)))
+                               else if (!(texture->skinframes[j] = R_SkinFrame_LoadExternal(primarylayer->texturename[j], primarylayer->texflags & texflagsmask, false)))
                                {
                                        Con_Printf("^1%s:^7 could not load texture ^3\"%s\"^7 (frame %i) for shader ^2\"%s\"\n", loadmodel->name, primarylayer->texturename[j], j, texture->name);
                                        texture->skinframes[j] = R_SkinFrame_LoadMissing();
@@ -1900,6 +2066,9 @@ nothing                GL_ZERO GL_ONE
                if (shader->backgroundlayer >= 0)
                {
                        q3shaderinfo_layer_t* backgroundlayer = shader->layers + shader->backgroundlayer;
+                       // copy over one secondarylayer parameter
+                       memcpy(texture->backgroundtcmods, backgroundlayer->tcmods, sizeof(texture->backgroundtcmods));
+                       // load the textures
                        texture->backgroundnumskinframes = backgroundlayer->numframes;
                        texture->backgroundskinframerate = backgroundlayer->framerate;
                        for (j = 0;j < backgroundlayer->numframes;j++)
@@ -1908,7 +2077,7 @@ nothing                GL_ZERO GL_ONE
                                {
                                        texture->skinframes[j] = NULL;
                                }
-                               else if (!(texture->backgroundskinframes[j] = R_SkinFrame_LoadExternal(backgroundlayer->texturename[j], backgroundlayer->texflags, false)))
+                               else if (!(texture->backgroundskinframes[j] = R_SkinFrame_LoadExternal(backgroundlayer->texturename[j], backgroundlayer->texflags & texflagsmask, false)))
                                {
                                        Con_Printf("^1%s:^7 could not load texture ^3\"%s\"^7 (background frame %i) for shader ^2\"%s\"\n", loadmodel->name, backgroundlayer->texturename[j], j, texture->name);
                                        texture->backgroundskinframes[j] = R_SkinFrame_LoadMissing();
@@ -1927,6 +2096,8 @@ nothing                GL_ZERO GL_ONE
                texture->reflectfactor = shader->reflectfactor;
                Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
                texture->r_water_wateralpha = shader->r_water_wateralpha;
+               texture->specularscalemod = shader->specularscalemod;
+               texture->specularpowermod = shader->specularpowermod;
        }
        else if (!strcmp(texture->name, "noshader") || !texture->name[0])
        {
@@ -2168,8 +2339,54 @@ void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firs
                *lastvertexpointer = lastvertex;
 }
 
+void Mod_MakeSortedSurfaces(dp_model_t *mod)
+{
+       // make an optimal set of texture-sorted batches to draw...
+       int j, t;
+       int *firstsurfacefortexture;
+       int *numsurfacesfortexture;
+       if (!mod->sortedmodelsurfaces)
+               mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
+       firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
+       numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
+       memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
+       for (j = 0;j < mod->nummodelsurfaces;j++)
+       {
+               const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
+               int t = (int)(surface->texture - mod->data_textures);
+               numsurfacesfortexture[t]++;
+       }
+       j = 0;
+       for (t = 0;t < mod->num_textures;t++)
+       {
+               firstsurfacefortexture[t] = j;
+               j += numsurfacesfortexture[t];
+       }
+       for (j = 0;j < mod->nummodelsurfaces;j++)
+       {
+               const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
+               int t = (int)(surface->texture - mod->data_textures);
+               mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
+       }
+       Mem_Free(firstsurfacefortexture);
+       Mem_Free(numsurfacesfortexture);
+}
+
 static void Mod_BuildVBOs(void)
 {
+       if (developer.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
+       {
+               int i;
+               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+               {
+                       if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
+                       {
+                               Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
+                               loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+                       }
+               }
+       }
+
        if (!gl_support_arb_vertex_buffer_object)
                return;
 
@@ -2177,12 +2394,7 @@ static void Mod_BuildVBOs(void)
        if (loadmodel->surfmesh.num_triangles)
        {
                if (loadmodel->surfmesh.data_element3s)
-               {
-                       int i;
-                       for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-                               loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
                        loadmodel->surfmesh.ebo3s = R_Mesh_CreateStaticBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB, loadmodel->surfmesh.data_element3s, loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]), loadmodel->name);
-               }
                else
                        loadmodel->surfmesh.ebo3i = R_Mesh_CreateStaticBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles * sizeof(unsigned int[3]), loadmodel->name);
        }
@@ -2227,10 +2439,10 @@ static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const cha
        size_t l;
        size_t outbufferpos = 0;
        size_t outbuffermax = 0x100000;
-       char *outbuffer = Z_Malloc(outbuffermax), *oldbuffer;
+       char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
        const msurface_t *surface;
        const int maxtextures = 256;
-       char *texturenames = Z_Malloc(maxtextures * MAX_QPATH);
+       char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
 
        // construct the mtllib file
        l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
@@ -2254,7 +2466,7 @@ static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const cha
                {
                        outbuffermax *= 2;
                        oldbuffer = outbuffer;
-                       outbuffer = Z_Malloc(outbuffermax);
+                       outbuffer = (char *) Z_Malloc(outbuffermax);
                        memcpy(outbuffer, oldbuffer, outbufferpos);
                        Z_Free(oldbuffer);
                }
@@ -2277,7 +2489,7 @@ static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const cha
                {
                        outbuffermax *= 2;
                        oldbuffer = outbuffer;
-                       outbuffer = Z_Malloc(outbuffermax);
+                       outbuffer = (char *) Z_Malloc(outbuffermax);
                        memcpy(outbuffer, oldbuffer, outbufferpos);
                        Z_Free(oldbuffer);
                }
@@ -2296,7 +2508,7 @@ static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const cha
                        {
                                outbuffermax *= 2;
                                oldbuffer = outbuffer;
-                               outbuffer = Z_Malloc(outbuffermax);
+                               outbuffer = (char *) Z_Malloc(outbuffermax);
                                memcpy(outbuffer, oldbuffer, outbufferpos);
                                Z_Free(oldbuffer);
                        }
@@ -2328,23 +2540,34 @@ static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int first
        int transformindex;
        int poseindex;
        int cornerindex;
+       float modelscale;
        const int *e;
        const float *pose;
        size_t l;
        size_t outbufferpos = 0;
        size_t outbuffermax = 0x100000;
-       char *outbuffer = Z_Malloc(outbuffermax), *oldbuffer;
+       char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
        const msurface_t *surface;
        l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
        if (l > 0)
                outbufferpos += l;
+       modelscale = 1;
+       if(model->num_poses >= 0)
+               modelscale = sqrt(model->data_poses[0] * model->data_poses[0] + model->data_poses[1] * model->data_poses[1] + model->data_poses[2] * model->data_poses[2]);
+       if(fabs(modelscale - 1) > 1e-4)
+       {
+               if(firstpose == 0) // only print the when writing the reference pose
+                       Con_Printf("The model has an old-style model scale of %f\n", modelscale);
+       }
+       else
+               modelscale = 1;
        for (transformindex = 0;transformindex < model->num_bones;transformindex++)
        {
                if (outbufferpos >= outbuffermax >> 1)
                {
                        outbuffermax *= 2;
                        oldbuffer = outbuffer;
-                       outbuffer = Z_Malloc(outbuffermax);
+                       outbuffer = (char *) Z_Malloc(outbuffermax);
                        memcpy(outbuffer, oldbuffer, outbufferpos);
                        Z_Free(oldbuffer);
                }
@@ -2371,7 +2594,7 @@ static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int first
                        {
                                outbuffermax *= 2;
                                oldbuffer = outbuffer;
-                               outbuffer = Z_Malloc(outbuffermax);
+                               outbuffer = (char *) Z_Malloc(outbuffermax);
                                memcpy(outbuffer, oldbuffer, outbufferpos);
                                Z_Free(oldbuffer);
                        }
@@ -2425,7 +2648,7 @@ static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int first
                        test[2][3] = pose[11];
 }
 #endif
-                       l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i %f %f %f %f %f %f\n", transformindex, pose[3], pose[7], pose[11], DEG2RAD(angles[ROLL]), DEG2RAD(angles[PITCH]), DEG2RAD(angles[YAW]));
+                       l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i %f %f %f %f %f %f\n", transformindex, pose[3] * modelscale, pose[7] * modelscale, pose[11] * modelscale, DEG2RAD(angles[ROLL]), DEG2RAD(angles[PITCH]), DEG2RAD(angles[YAW]));
                        if (l > 0)
                                outbufferpos += l;
                }
@@ -2447,7 +2670,7 @@ static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int first
                                {
                                        outbuffermax *= 2;
                                        oldbuffer = outbuffer;
-                                       outbuffer = Z_Malloc(outbuffermax);
+                                       outbuffer = (char *) Z_Malloc(outbuffermax);
                                        memcpy(outbuffer, oldbuffer, outbufferpos);
                                        Z_Free(oldbuffer);
                                }
@@ -2513,7 +2736,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");
@@ -2534,7 +2764,7 @@ static void Mod_Decompile_f(void)
        }
 
        // export SMD if possible (only for skeletal models)
-       if (mod->surfmesh.num_triangles && mod->num_poses)
+       if (mod->surfmesh.num_triangles && mod->num_bones)
        {
                dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
                Mod_Decompile_SMD(mod, outname, 0, 1, true);
@@ -2560,7 +2790,7 @@ static void Mod_Decompile_f(void)
                                        if ((animname[l] < '0' || animname[l] > '9') && animname[l] != '_')
                                                k = l + 1;
                                animname[k] = 0;
-                               count = (mod->num_poses / mod->num_bones) - first;
+                               count = mod->num_poses - first;
                                for (j = i + 1;j < mod->numframes;j++)
                                {
                                        strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));