]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_shared.c
Add an entity parameter for CSQC_InputEvent
[xonotic/darkplaces.git] / model_shared.c
index 05c6fe57326ddf3615712181b9c5c0371f8a3334..19bbbc0a3735e5c04d75a01b459fd1a47ab0a1e2 100644 (file)
@@ -27,17 +27,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "r_shadow.h"
 #include "polygon.h"
 
-cvar_t r_mipskins = {CVAR_SAVE, "r_mipskins", "0", "mipmaps model skins so they render faster in the distance and do not display noise artifacts, can cause discoloration of skins if they contain undesirable border colors"};
-cvar_t r_mipnormalmaps = {CVAR_SAVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
-cvar_t mod_generatelightmaps_unitspersample = {CVAR_SAVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
-cvar_t mod_generatelightmaps_borderpixels = {CVAR_SAVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
-cvar_t mod_generatelightmaps_texturesize = {CVAR_SAVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
-cvar_t mod_generatelightmaps_lightmapsamples = {CVAR_SAVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
-cvar_t mod_generatelightmaps_vertexsamples = {CVAR_SAVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
-cvar_t mod_generatelightmaps_gridsamples = {CVAR_SAVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
-cvar_t mod_generatelightmaps_lightmapradius = {CVAR_SAVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
-cvar_t mod_generatelightmaps_vertexradius = {CVAR_SAVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
-cvar_t mod_generatelightmaps_gridradius = {CVAR_SAVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
+cvar_t r_mipskins = {CVAR_CLIENT | CVAR_SAVE, "r_mipskins", "0", "mipmaps model skins so they render faster in the distance and do not display noise artifacts, can cause discoloration of skins if they contain undesirable border colors"};
+cvar_t r_mipnormalmaps = {CVAR_CLIENT | CVAR_SAVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
+cvar_t mod_generatelightmaps_unitspersample = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
+cvar_t mod_generatelightmaps_borderpixels = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
+cvar_t mod_generatelightmaps_texturesize = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
+cvar_t mod_generatelightmaps_lightmapsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
+cvar_t mod_generatelightmaps_vertexsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
+cvar_t mod_generatelightmaps_gridsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
+cvar_t mod_generatelightmaps_lightmapradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
+cvar_t mod_generatelightmaps_vertexradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
+cvar_t mod_generatelightmaps_gridradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
 
 dp_model_t *loadmodel;
 
@@ -65,7 +65,7 @@ static void mod_start(void)
        int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
        dp_model_t *mod;
 
-       SCR_PushLoadingScreen(false, "Loading models", 1.0);
+       SCR_PushLoadingScreen("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] != '*')
@@ -75,7 +75,7 @@ static void mod_start(void)
                if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
                        if (mod->used)
                        {
-                               SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
+                               SCR_PushLoadingScreen(mod->name, 1.0 / count);
                                Mod_LoadModel(mod, true, false);
                                SCR_PopLoadingScreen(false);
                        }
@@ -148,10 +148,10 @@ static void mod_newmap(void)
 Mod_Init
 ===============
 */
-static void Mod_Print(void);
-static void Mod_Precache (void);
-static void Mod_Decompile_f(void);
-static void Mod_GenerateLightmaps_f(void);
+static void Mod_Print_f(cmd_state_t *cmd);
+static void Mod_Precache_f(cmd_state_t *cmd);
+static void Mod_Decompile_f(cmd_state_t *cmd);
+static void Mod_GenerateLightmaps_f(cmd_state_t *cmd);
 void Mod_Init (void)
 {
        mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
@@ -174,10 +174,10 @@ void Mod_Init (void)
        Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
        Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
 
-       Cmd_AddCommand ("modellist", Mod_Print, "prints a list of loaded models");
-       Cmd_AddCommand ("modelprecache", Mod_Precache, "load a model");
-       Cmd_AddCommand ("modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
-       Cmd_AddCommand ("mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
+       Cmd_AddCommand(&cmd_client, "modellist", Mod_Print_f, "prints a list of loaded models");
+       Cmd_AddCommand(&cmd_client, "modelprecache", Mod_Precache_f, "load a model");
+       Cmd_AddCommand(&cmd_client, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
+       Cmd_AddCommand(&cmd_client, "mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
 }
 
 void Mod_RenderInit(void)
@@ -199,15 +199,23 @@ void Mod_UnloadModel (dp_model_t *mod)
        used = mod->used;
        if (mod->mempool)
        {
-               if (mod->surfmesh.data_element3i_indexbuffer)
+               if (mod->surfmesh.data_element3i_indexbuffer && !mod->surfmesh.data_element3i_indexbuffer->isdynamic)
                        R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
                mod->surfmesh.data_element3i_indexbuffer = NULL;
-               if (mod->surfmesh.data_element3s_indexbuffer)
+               if (mod->surfmesh.data_element3s_indexbuffer && !mod->surfmesh.data_element3s_indexbuffer->isdynamic)
                        R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
                mod->surfmesh.data_element3s_indexbuffer = NULL;
-               if (mod->surfmesh.vbo_vertexbuffer)
-                       R_Mesh_DestroyMeshBuffer(mod->surfmesh.vbo_vertexbuffer);
-               mod->surfmesh.vbo_vertexbuffer = NULL;
+               if (mod->surfmesh.data_vertex3f_vertexbuffer && !mod->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
+                       R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_vertex3f_vertexbuffer);
+               mod->surfmesh.data_vertex3f_vertexbuffer = NULL;
+               mod->surfmesh.data_svector3f_vertexbuffer = NULL;
+               mod->surfmesh.data_tvector3f_vertexbuffer = NULL;
+               mod->surfmesh.data_normal3f_vertexbuffer = NULL;
+               mod->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
+               mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
+               mod->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
+               mod->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
+               mod->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
        }
        // free textures/memory attached to the model
        R_FreeTexturePool(&mod->texturepool);
@@ -450,9 +458,9 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
        if (developer_loading.integer)
                Con_Printf("loading model %s\n", mod->name);
        
-       SCR_PushLoadingScreen(true, mod->name, 1);
+       SCR_PushLoadingScreen(mod->name, 1);
 
-       // LordHavoc: unload the existing model in this slot (if there is one)
+       // LadyHavoc: unload the existing model in this slot (if there is one)
        if (mod->loaded || mod->mempool)
                Mod_UnloadModel(mod);
 
@@ -519,7 +527,7 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
        }
        else if (crash)
        {
-               // LordHavoc: Sys_Error was *ANNOYING*
+               // LadyHavoc: Sys_Error was *ANNOYING*
                Con_Printf ("Mod_LoadModel: %s not found\n", mod->name);
        }
 
@@ -630,7 +638,7 @@ void Mod_Reload(void)
        int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
        dp_model_t *mod;
 
-       SCR_PushLoadingScreen(false, "Reloading models", 1.0);
+       SCR_PushLoadingScreen("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)
@@ -638,7 +646,7 @@ void Mod_Reload(void)
        for (i = 0;i < nummodels;i++)
                if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
                {
-                       SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
+                       SCR_PushLoadingScreen(mod->name, 1.0 / count);
                        Mod_LoadModel(mod, true, true);
                        SCR_PopLoadingScreen(false);
                }
@@ -655,7 +663,7 @@ unsigned char *mod_base;
 Mod_Print
 ================
 */
-static void Mod_Print(void)
+static void Mod_Print_f(cmd_state_t *cmd)
 {
        int i;
        int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
@@ -679,10 +687,10 @@ static void Mod_Print(void)
 Mod_Precache
 ================
 */
-static void Mod_Precache(void)
+static void Mod_Precache_f(cmd_state_t *cmd)
 {
-       if (Cmd_Argc() == 2)
-               Mod_ForName(Cmd_Argv(1), false, true, Cmd_Argv(1)[0] == '*' ? cl.model_name[1] : NULL);
+       if (Cmd_Argc(cmd) == 2)
+               Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
        else
                Con_Print("usage: modelprecache <filename>\n");
 }
@@ -746,7 +754,7 @@ qboolean Mod_ValidateElements(int *element3i, unsigned short *element3s, int num
                Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, element3i, element3s, filename, fileline);
                Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
                Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
-               Con_Printf(", %i elements mismatch between element3i and element3s (example: element3s[%i] is %i and element3i[i] is %i)", invalidmismatchcount, invalidmismatchexample, element3i ? element3i[invalidmismatchexample] : 0, invalidmismatchexample, element3s ? element3s[invalidmismatchexample] : 0);
+               Con_Printf(", %i elements mismatch between element3i and element3s (example: element3s[%i] is %i and element3i[%i] is %i)", invalidmismatchcount, invalidmismatchexample, element3s ? element3s[invalidmismatchexample] : 0, invalidmismatchexample, element3i ? element3i[invalidmismatchexample] : 0);
                Con_Print(".  Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
 
                // edit the elements to make them safer, as the driver will crash otherwise
@@ -1105,9 +1113,9 @@ void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_
        VectorCopy(mesh->vertex3f, nmaxs);
        for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
        {
-               if (nmins[0] > v[0]) nmins[0] = v[0];if (nmaxs[0] < v[0]) nmaxs[0] = v[0];
-               if (nmins[1] > v[1]) nmins[1] = v[1];if (nmaxs[1] < v[1]) nmaxs[1] = v[1];
-               if (nmins[2] > v[2]) nmins[2] = v[2];if (nmaxs[2] < v[2]) nmaxs[2] = v[2];
+               if (nmins[0] > v[0]) { nmins[0] = v[0]; } if (nmaxs[0] < v[0]) { nmaxs[0] = v[0]; }
+               if (nmins[1] > v[1]) { nmins[1] = v[1]; } if (nmaxs[1] < v[1]) { nmaxs[1] = v[1]; }
+               if (nmins[2] > v[2]) { nmins[2] = v[2]; } if (nmaxs[2] < v[2]) { nmaxs[2] = v[2]; }
        }
        // calculate center and radius
        ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
@@ -1423,14 +1431,6 @@ static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
        memcpy (&entry->shader, shader, sizeof (q3shaderinfo_t));
 }
 
-extern cvar_t mod_noshader_default_offsetmapping;
-extern cvar_t mod_q3shader_default_offsetmapping;
-extern cvar_t mod_q3shader_default_offsetmapping_scale;
-extern cvar_t mod_q3shader_default_offsetmapping_bias;
-extern cvar_t mod_q3shader_default_polygonoffset;
-extern cvar_t mod_q3shader_default_polygonfactor;
-extern cvar_t mod_q3shader_force_addalpha;
-extern cvar_t mod_q3shader_force_terrain_alphaflag;
 void Mod_LoadQ3Shaders(void)
 {
        int j;
@@ -1962,7 +1962,7 @@ void Mod_LoadQ3Shaders(void)
                                // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
                                else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
                                {
-                                       if (Cvar_VariableValue(parameter[1]) == 0.0f)
+                                       if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == 0.0f)
                                                shader.dpshaderkill = dpshaderkill;
                                }
                                // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
@@ -1973,37 +1973,37 @@ void Mod_LoadQ3Shaders(void)
                                                op = parameter[2];
                                        if(!op)
                                        {
-                                               if (Cvar_VariableValue(parameter[1]) != 0.0f)
+                                               if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != 0.0f)
                                                        shader.dpshaderkill = dpshaderkill;
                                        }
                                        else if (numparameters >= 4 && !strcmp(op, "=="))
                                        {
-                                               if (Cvar_VariableValue(parameter[1]) == atof(parameter[3]))
+                                               if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == atof(parameter[3]))
                                                        shader.dpshaderkill = dpshaderkill;
                                        }
                                        else if (numparameters >= 4 && !strcmp(op, "!="))
                                        {
-                                               if (Cvar_VariableValue(parameter[1]) != atof(parameter[3]))
+                                               if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != atof(parameter[3]))
                                                        shader.dpshaderkill = dpshaderkill;
                                        }
                                        else if (numparameters >= 4 && !strcmp(op, ">"))
                                        {
-                                               if (Cvar_VariableValue(parameter[1]) > atof(parameter[3]))
+                                               if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) > atof(parameter[3]))
                                                        shader.dpshaderkill = dpshaderkill;
                                        }
                                        else if (numparameters >= 4 && !strcmp(op, "<"))
                                        {
-                                               if (Cvar_VariableValue(parameter[1]) < atof(parameter[3]))
+                                               if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) < atof(parameter[3]))
                                                        shader.dpshaderkill = dpshaderkill;
                                        }
                                        else if (numparameters >= 4 && !strcmp(op, ">="))
                                        {
-                                               if (Cvar_VariableValue(parameter[1]) >= atof(parameter[3]))
+                                               if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) >= atof(parameter[3]))
                                                        shader.dpshaderkill = dpshaderkill;
                                        }
                                        else if (numparameters >= 4 && !strcmp(op, "<="))
                                        {
-                                               if (Cvar_VariableValue(parameter[1]) <= atof(parameter[3]))
+                                               if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) <= atof(parameter[3]))
                                                        shader.dpshaderkill = dpshaderkill;
                                        }
                                        else
@@ -2404,7 +2404,7 @@ nothing                GL_ZERO GL_ONE
                                firstpostlayer = rgbgenvertexlayer + 1;
                                // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
                                if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
-                                       texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR;
+                                       texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
                        }
                        else if (rgbgendiffuselayer >= 0)
                        {
@@ -2465,6 +2465,7 @@ nothing                GL_ZERO GL_ONE
                texture->specularscalemod = shader->specularscalemod;
                texture->specularpowermod = shader->specularpowermod;
                texture->rtlightambient = shader->rtlightambient;
+               texture->refractive_index = mod_q3shader_default_refractive_index.value;
                if (shader->dpreflectcube[0])
                        texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
 
@@ -2554,6 +2555,7 @@ nothing                GL_ZERO GL_ONE
        {
                if (developer_extra.integer)
                        Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
+               texture->basematerialflags = defaultmaterialflags;
                texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
        }
        else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
@@ -2650,7 +2652,7 @@ void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *
 
 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
 {
-       int i, j;
+       long unsigned int i, j;
        for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
        {
                if (texture->shaderpasses[i])
@@ -2885,6 +2887,9 @@ void Mod_MakeSortedSurfaces(dp_model_t *mod)
 
 void Mod_BuildVBOs(void)
 {
+       if(cls.state == ca_dedicated)
+               return;
+
        if (!loadmodel->surfmesh.num_vertices)
                return;
 
@@ -2901,32 +2906,6 @@ void Mod_BuildVBOs(void)
                }
        }
 
-       // build r_vertexmesh_t array
-       // (compressed interleaved array for D3D)
-       if (!loadmodel->surfmesh.data_vertexmesh && vid.useinterleavedarrays)
-       {
-               int vertexindex;
-               int numvertices = loadmodel->surfmesh.num_vertices;
-               r_vertexmesh_t *vertexmesh;
-               loadmodel->surfmesh.data_vertexmesh = vertexmesh = (r_vertexmesh_t*)Mem_Alloc(loadmodel->mempool, numvertices * sizeof(r_vertexmesh_t));
-               for (vertexindex = 0;vertexindex < numvertices;vertexindex++, vertexmesh++)
-               {
-                       VectorCopy(loadmodel->surfmesh.data_vertex3f + 3*vertexindex, vertexmesh->vertex3f);
-                       VectorScale(loadmodel->surfmesh.data_svector3f + 3*vertexindex, 1.0f, vertexmesh->svector3f);
-                       VectorScale(loadmodel->surfmesh.data_tvector3f + 3*vertexindex, 1.0f, vertexmesh->tvector3f);
-                       VectorScale(loadmodel->surfmesh.data_normal3f + 3*vertexindex, 1.0f, vertexmesh->normal3f);
-                       if (loadmodel->surfmesh.data_lightmapcolor4f)
-                               Vector4Copy(loadmodel->surfmesh.data_lightmapcolor4f + 4*vertexindex, vertexmesh->color4f);
-                       Vector2Copy(loadmodel->surfmesh.data_texcoordtexture2f + 2*vertexindex, vertexmesh->texcoordtexture2f);
-                       if (loadmodel->surfmesh.data_texcoordlightmap2f)
-                               Vector2Scale(loadmodel->surfmesh.data_texcoordlightmap2f + 2*vertexindex, 1.0f, vertexmesh->texcoordlightmap2f);
-                       if (loadmodel->surfmesh.data_skeletalindex4ub)
-                               Vector4Copy(loadmodel->surfmesh.data_skeletalindex4ub + 4*vertexindex, vertexmesh->skeletalindex4ub);
-                       if (loadmodel->surfmesh.data_skeletalweight4ub)
-                               Vector4Copy(loadmodel->surfmesh.data_skeletalweight4ub + 4*vertexindex, vertexmesh->skeletalweight4ub);
-               }
-       }
-
        // upload short indices as a buffer
        if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
                loadmodel->surfmesh.data_element3s_indexbuffer = R_Mesh_CreateMeshBuffer(loadmodel->surfmesh.data_element3s, loadmodel->surfmesh.num_triangles * sizeof(short[3]), loadmodel->name, true, false, false, true);
@@ -2936,38 +2915,40 @@ void Mod_BuildVBOs(void)
                loadmodel->surfmesh.data_element3i_indexbuffer = R_Mesh_CreateMeshBuffer(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles * sizeof(int[3]), loadmodel->name, true, false, false, false);
 
        // only build a vbo if one has not already been created (this is important for brush models which load specially)
-       // vertex buffer is several arrays and we put them in the same buffer
-       //
-       // is this wise?  the texcoordtexture2f array is used with dynamic
-       // vertex/svector/tvector/normal when rendering animated models, on the
-       // other hand animated models don't use a lot of vertices anyway...
-       if (!loadmodel->surfmesh.vbo_vertexbuffer && !vid.useinterleavedarrays)
+       // we put several vertex data streams in the same buffer
+       if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
        {
                int size;
                unsigned char *mem;
                size = 0;
-               loadmodel->surfmesh.vbooffset_vertexmesh         = size;if (loadmodel->surfmesh.data_vertexmesh        ) size += loadmodel->surfmesh.num_vertices * sizeof(r_vertexmesh_t);
-               loadmodel->surfmesh.vbooffset_vertex3f           = size;if (loadmodel->surfmesh.data_vertex3f          ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
-               loadmodel->surfmesh.vbooffset_svector3f          = size;if (loadmodel->surfmesh.data_svector3f         ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
-               loadmodel->surfmesh.vbooffset_tvector3f          = size;if (loadmodel->surfmesh.data_tvector3f         ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
-               loadmodel->surfmesh.vbooffset_normal3f           = size;if (loadmodel->surfmesh.data_normal3f          ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
-               loadmodel->surfmesh.vbooffset_texcoordtexture2f  = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
-               loadmodel->surfmesh.vbooffset_texcoordlightmap2f = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
-               loadmodel->surfmesh.vbooffset_lightmapcolor4f    = size;if (loadmodel->surfmesh.data_lightmapcolor4f   ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
-               loadmodel->surfmesh.vbooffset_skeletalindex4ub   = size;if (loadmodel->surfmesh.data_skeletalindex4ub  ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
-               loadmodel->surfmesh.vbooffset_skeletalweight4ub  = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
+               loadmodel->surfmesh.data_vertex3f_bufferoffset           = size;if (loadmodel->surfmesh.data_vertex3f          ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+               loadmodel->surfmesh.data_svector3f_bufferoffset          = size;if (loadmodel->surfmesh.data_svector3f         ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+               loadmodel->surfmesh.data_tvector3f_bufferoffset          = size;if (loadmodel->surfmesh.data_tvector3f         ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+               loadmodel->surfmesh.data_normal3f_bufferoffset           = size;if (loadmodel->surfmesh.data_normal3f          ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+               loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset  = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
+               loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
+               loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset    = size;if (loadmodel->surfmesh.data_lightmapcolor4f   ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
+               loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset   = size;if (loadmodel->surfmesh.data_skeletalindex4ub  ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
+               loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset  = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
                mem = (unsigned char *)Mem_Alloc(tempmempool, size);
-               if (loadmodel->surfmesh.data_vertexmesh        ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertexmesh        , loadmodel->surfmesh.data_vertexmesh        , loadmodel->surfmesh.num_vertices * sizeof(r_vertexmesh_t));
-               if (loadmodel->surfmesh.data_vertex3f          ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertex3f          , loadmodel->surfmesh.data_vertex3f          , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
-               if (loadmodel->surfmesh.data_svector3f         ) memcpy(mem + loadmodel->surfmesh.vbooffset_svector3f         , loadmodel->surfmesh.data_svector3f         , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
-               if (loadmodel->surfmesh.data_tvector3f         ) memcpy(mem + loadmodel->surfmesh.vbooffset_tvector3f         , loadmodel->surfmesh.data_tvector3f         , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
-               if (loadmodel->surfmesh.data_normal3f          ) memcpy(mem + loadmodel->surfmesh.vbooffset_normal3f          , loadmodel->surfmesh.data_normal3f          , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
-               if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordtexture2f , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
-               if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordlightmap2f, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
-               if (loadmodel->surfmesh.data_lightmapcolor4f   ) memcpy(mem + loadmodel->surfmesh.vbooffset_lightmapcolor4f   , loadmodel->surfmesh.data_lightmapcolor4f   , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
-               if (loadmodel->surfmesh.data_skeletalindex4ub  ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalindex4ub  , loadmodel->surfmesh.data_skeletalindex4ub  , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
-               if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalweight4ub , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
-               loadmodel->surfmesh.vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
+               if (loadmodel->surfmesh.data_vertex3f          ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset          , loadmodel->surfmesh.data_vertex3f          , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+               if (loadmodel->surfmesh.data_svector3f         ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset         , loadmodel->surfmesh.data_svector3f         , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+               if (loadmodel->surfmesh.data_tvector3f         ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset         , loadmodel->surfmesh.data_tvector3f         , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+               if (loadmodel->surfmesh.data_normal3f          ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset          , loadmodel->surfmesh.data_normal3f          , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+               if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
+               if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
+               if (loadmodel->surfmesh.data_lightmapcolor4f   ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset   , loadmodel->surfmesh.data_lightmapcolor4f   , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
+               if (loadmodel->surfmesh.data_skeletalindex4ub  ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset  , loadmodel->surfmesh.data_skeletalindex4ub  , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
+               if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
+               loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
+               loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
+               loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
+               loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
+               loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
+               loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
+               loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
+               loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
+               loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
                Mem_Free(mem);
        }
 }
@@ -3258,7 +3239,7 @@ Mod_Decompile_f
 decompiles a model to editable files
 ================
 */
-static void Mod_Decompile_f(void)
+static void Mod_Decompile_f(cmd_state_t *cmd)
 {
        int i, j, k, l, first, count;
        dp_model_t *mod;
@@ -3276,13 +3257,13 @@ static void Mod_Decompile_f(void)
        int framegroupstextsize = 0;
        char vabuf[1024];
 
-       if (Cmd_Argc() != 2)
+       if (Cmd_Argc(cmd) != 2)
        {
                Con_Print("usage: modeldecompile <filename>\n");
                return;
        }
 
-       strlcpy(inname, Cmd_Argv(1), sizeof(inname));
+       strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
        FS_StripExtension(inname, basename, sizeof(basename));
 
        mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
@@ -3941,15 +3922,23 @@ static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
        if (model->surfmesh.num_vertices > 65536)
                model->surfmesh.data_element3s = NULL;
 
-       if (model->surfmesh.data_element3i_indexbuffer)
+       if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
                R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
        model->surfmesh.data_element3i_indexbuffer = NULL;
-       if (model->surfmesh.data_element3s_indexbuffer)
+       if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
                R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
        model->surfmesh.data_element3s_indexbuffer = NULL;
-       if (model->surfmesh.vbo_vertexbuffer)
-               R_Mesh_DestroyMeshBuffer(model->surfmesh.vbo_vertexbuffer);
-       model->surfmesh.vbo_vertexbuffer = 0;
+       if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
+               R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
+       model->surfmesh.data_vertex3f_vertexbuffer = NULL;
+       model->surfmesh.data_svector3f_vertexbuffer = NULL;
+       model->surfmesh.data_tvector3f_vertexbuffer = NULL;
+       model->surfmesh.data_normal3f_vertexbuffer = NULL;
+       model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
+       model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
+       model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
+       model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
+       model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
 
        // convert all triangles to unique vertex data
        outvertexindex = 0;
@@ -4359,9 +4348,9 @@ static void Mod_GenerateLightmaps(dp_model_t *model)
        loadmodel = oldloadmodel;
 }
 
-static void Mod_GenerateLightmaps_f(void)
+static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
 {
-       if (Cmd_Argc() != 1)
+       if (Cmd_Argc(cmd) != 1)
        {
                Con_Printf("usage: mod_generatelightmaps\n");
                return;
@@ -4410,9 +4399,10 @@ texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdra
 {
        int i;
        texture_t *t;
-       for (i = 0; i < mod->num_textures; i++)
-               if (!strcmp(mod->data_textures[i].name, name))
-                       return mod->data_textures + i;
+       int drawflag = defaultdrawflags & DRAWFLAG_MASK;
+       for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
+               if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
+                       return t;
        if (mod->max_textures <= mod->num_textures)
        {
                texture_t *oldtextures = mod->data_textures;
@@ -4424,6 +4414,9 @@ texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdra
        }
        t = &mod->data_textures[mod->num_textures++];
        Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, false, true, defaulttexflags, defaultmaterialflags);
+       t->mesh_drawflag = drawflag;
+       t->mesh_defaulttexflags = defaulttexflags;
+       t->mesh_defaultmaterialflags = defaultmaterialflags;
        switch (defaultdrawflags & DRAWFLAG_MASK)
        {
        case DRAWFLAG_ADDITIVE:
@@ -4610,7 +4603,7 @@ static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
        }
 }
 
-void Mod_Mesh_ComputeBounds(dp_model_t *mod)
+static void Mod_Mesh_ComputeBounds(dp_model_t *mod)
 {
        int i;
        vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
@@ -4666,9 +4659,51 @@ void Mod_Mesh_ComputeBounds(dp_model_t *mod)
        }
 }
 
+void Mod_Mesh_Validate(dp_model_t *mod)
+{
+       int i;
+       qboolean warned = false;
+       for (i = 0; i < mod->num_surfaces; i++)
+       {
+               msurface_t *surf = mod->data_surfaces + i;
+               int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
+               int first = surf->num_firstvertex;
+               int end = surf->num_firstvertex + surf->num_vertices;
+               int j;
+               for (j = 0;j < surf->num_triangles * 3;j++)
+               {
+                       if (e[j] < first || e[j] >= end)
+                       {
+                               if (!warned)
+                                       Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
+                               warned = true;
+                               e[j] = first;
+                       }
+               }
+       }
+}
+
+static void Mod_Mesh_UploadDynamicBuffers(dp_model_t *mod)
+{
+       mod->surfmesh.data_element3s_indexbuffer = mod->surfmesh.data_element3s ? R_BufferData_Store(mod->surfmesh.num_triangles * sizeof(short[3]), mod->surfmesh.data_element3s, R_BUFFERDATA_INDEX16, &mod->surfmesh.data_element3s_bufferoffset) : NULL;
+       mod->surfmesh.data_element3i_indexbuffer = mod->surfmesh.data_element3i ? R_BufferData_Store(mod->surfmesh.num_triangles * sizeof(int[3]), mod->surfmesh.data_element3i, R_BUFFERDATA_INDEX32, &mod->surfmesh.data_element3i_bufferoffset) : NULL;
+       mod->surfmesh.data_vertex3f_vertexbuffer = mod->surfmesh.data_vertex3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_vertex3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_vertex3f_bufferoffset) : NULL;
+       mod->surfmesh.data_svector3f_vertexbuffer = mod->surfmesh.data_svector3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_svector3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_svector3f_bufferoffset) : NULL;
+       mod->surfmesh.data_tvector3f_vertexbuffer = mod->surfmesh.data_tvector3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_tvector3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_tvector3f_bufferoffset) : NULL;
+       mod->surfmesh.data_normal3f_vertexbuffer = mod->surfmesh.data_normal3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_normal3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_normal3f_bufferoffset) : NULL;
+       mod->surfmesh.data_texcoordtexture2f_vertexbuffer = mod->surfmesh.data_texcoordtexture2f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[2]), mod->surfmesh.data_texcoordtexture2f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_texcoordtexture2f_bufferoffset) : NULL;
+       mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = mod->surfmesh.data_texcoordlightmap2f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[2]), mod->surfmesh.data_texcoordlightmap2f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_texcoordlightmap2f_bufferoffset) : NULL;
+       mod->surfmesh.data_lightmapcolor4f_vertexbuffer = mod->surfmesh.data_lightmapcolor4f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[4]), mod->surfmesh.data_lightmapcolor4f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_lightmapcolor4f_bufferoffset) : NULL;
+       mod->surfmesh.data_skeletalindex4ub_vertexbuffer = mod->surfmesh.data_skeletalindex4ub ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(unsigned char[4]), mod->surfmesh.data_skeletalindex4ub, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_skeletalindex4ub_bufferoffset) : NULL;
+       mod->surfmesh.data_skeletalweight4ub_vertexbuffer = mod->surfmesh.data_skeletalweight4ub ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(unsigned char[4]), mod->surfmesh.data_skeletalweight4ub, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_skeletalweight4ub_bufferoffset) : NULL;
+}
+
 void Mod_Mesh_Finalize(dp_model_t *mod)
 {
+       if (gl_paranoid.integer)
+               Mod_Mesh_Validate(mod);
        Mod_Mesh_ComputeBounds(mod);
        Mod_Mesh_MakeSortedSurfaces(mod);
        Mod_BuildTextureVectorsFromNormals(0, mod->surfmesh.num_vertices, mod->surfmesh.num_triangles, mod->surfmesh.data_vertex3f, mod->surfmesh.data_texcoordtexture2f, mod->surfmesh.data_normal3f, mod->surfmesh.data_element3i, mod->surfmesh.data_svector3f, mod->surfmesh.data_tvector3f, true);
+       Mod_Mesh_UploadDynamicBuffers(mod);
 }