]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
implemented occlusion query support on corona rendering, this enables
[xonotic/darkplaces.git] / gl_rmain.c
index 658858daa3687618a1f8ca42833e5ed3e458024b..9d83c1f1efc1c9575f3e7a775b68da84248278d4 100644 (file)
@@ -33,7 +33,7 @@ rtexturepool_t *r_main_texturepool;
 //
 r_refdef_t r_refdef;
 
-cvar_t r_depthfirst = {CVAR_SAVE, "r_depthfirst", "1", "renders a depth-only version of the scene before normal rendering begins to eliminate overdraw, values: 0 = off, 1 = world depth, 2 = world and model depth"};
+cvar_t r_depthfirst = {CVAR_SAVE, "r_depthfirst", "0", "renders a depth-only version of the scene before normal rendering begins to eliminate overdraw, values: 0 = off, 1 = world depth, 2 = world and model depth"};
 cvar_t r_useinfinitefarclip = {CVAR_SAVE, "r_useinfinitefarclip", "1", "enables use of a special kind of projection matrix that has an extremely large farclip"};
 cvar_t r_nearclip = {0, "r_nearclip", "1", "distance from camera of nearclip plane" };
 cvar_t r_showbboxes = {0, "r_showbboxes", "0", "shows bounding boxes of server entities, value controls opacity scaling (1 = 10%,  10 = 100%)"};
@@ -58,7 +58,7 @@ cvar_t r_fullbright = {0, "r_fullbright","0", "makes map very bright and renders
 cvar_t r_wateralpha = {CVAR_SAVE, "r_wateralpha","1", "opacity of water polygons"};
 cvar_t r_dynamic = {CVAR_SAVE, "r_dynamic","1", "enables dynamic lights (rocket glow and such)"};
 cvar_t r_fullbrights = {CVAR_SAVE, "r_fullbrights", "1", "enables glowing pixels in quake textures (changes need r_restart to take effect)"};
-cvar_t r_shadows = {CVAR_SAVE, "r_shadows", "0", "casts fake stencil shadows from models onto the world (rtlights are unaffected by this)"};
+cvar_t r_shadows = {CVAR_SAVE, "r_shadows", "0", "casts fake stencil shadows from models onto the world (rtlights are unaffected by this); when set to 2, always cast the shadows DOWN, otherwise use the model lighting"};
 cvar_t r_shadows_throwdistance = {CVAR_SAVE, "r_shadows_throwdistance", "500", "how far to cast shadows from models"};
 cvar_t r_q1bsp_skymasking = {0, "r_q1bsp_skymasking", "1", "allows sky polygons in quake1 maps to obscure other geometry"};
 cvar_t r_polygonoffset_submodel_factor = {0, "r_polygonoffset_submodel_factor", "0", "biases depth values of world submodels such as doors, to prevent z-fighting artifacts in Quake maps"};
@@ -168,6 +168,10 @@ rtexture_t *r_texture_gammaramps;
 unsigned int r_texture_gammaramps_serial;
 //rtexture_t *r_texture_fogintensity;
 
+unsigned int r_queries[R_MAX_OCCLUSION_QUERIES];
+unsigned int r_numqueries;
+unsigned int r_maxqueries;
+
 char r_qwskincache[MAX_SCOREBOARD][MAX_QPATH];
 skinframe_t *r_qwskincache_skinframe[MAX_SCOREBOARD];
 
@@ -242,7 +246,7 @@ float FogPoint_World(const vec3_t p)
 
 float FogPoint_Model(const vec3_t p)
 {
-       return FogForDistance(VectorDistance((p), rsurface.modelorg));
+       return FogForDistance(VectorDistance((p), rsurface.modelorg) * Matrix4x4_ScaleFromMatrix(&rsurface.matrix));
 }
 
 static void R_BuildBlankTextures(void)
@@ -478,12 +482,12 @@ static const char *builtinshaderstring =
 "uniform vec3 Gamma;\n"
 "#endif\n"
 "//uncomment these if you want to use them:\n"
-"// uniform vec4 UserVec1;\n"
+"uniform vec4 UserVec1;\n"
 "// uniform vec4 UserVec2;\n"
 "// uniform vec4 UserVec3;\n"
 "// uniform vec4 UserVec4;\n"
 "// uniform float ClientTime;\n"
-"// uniform vec2 PixelSize;\n"
+"uniform vec2 PixelSize;\n"
 "void main(void)\n"
 "{\n"
 "      gl_FragColor = texture2D(Texture_First, gl_TexCoord[0].xy);\n"
@@ -495,7 +499,14 @@ static const char *builtinshaderstring =
 "#endif\n"
 "\n"
 "#ifdef USEPOSTPROCESSING\n"
-"// add your own postprocessing here or make your own ifdef for it\n"
+"// do r_glsl_dumpshader, edit glsl/default.glsl, and replace this by your own postprocessing if you want\n"
+"// this code does a blur with the radius specified in the first component of r_glsl_postprocess_uservec1 and blends it using the second component\n"
+"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2(-0.987688, -0.156434)) * UserVec1.y;\n"
+"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2(-0.156434, -0.891007)) * UserVec1.y;\n"
+"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2( 0.891007, -0.453990)) * UserVec1.y;\n"
+"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2( 0.707107,  0.707107)) * UserVec1.y;\n"
+"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2(-0.453990,  0.891007)) * UserVec1.y;\n"
+"      gl_FragColor /= (1 + 5 * UserVec1.y);\n"
 "#endif\n"
 "\n"
 "#ifdef USEGAMMARAMPS\n"
@@ -847,7 +858,8 @@ static const char *builtinshaderstring =
 "      myhalf terrainblend = clamp(myhalf(gl_Color.a) * color.a * 2.0 - 0.5, myhalf(0.0), myhalf(1.0));\n"
 "      //myhalf terrainblend = min(myhalf(gl_Color.a) * color.a * 2.0, myhalf(1.0));\n"
 "      //myhalf terrainblend = myhalf(gl_Color.a) * color.a > 0.5;\n"
-"      color = mix(myhalf4(texture2D(Texture_SecondaryColor, TexCoord)), color, terrainblend);\n"
+"      color.rgb = mix(myhalf3(texture2D(Texture_SecondaryColor, TexCoord)), color.rgb, terrainblend);\n"
+"      color.a = 1.0;\n"
 "      //color = mix(myhalf4(1, 0, 0, 1), color, terrainblend);\n"
 "#endif\n"
 "\n"
@@ -859,7 +871,7 @@ static const char *builtinshaderstring =
 "      myhalf3 glosscolor = mix(myhalf3(texture2D(Texture_SecondaryGloss, TexCoord)), myhalf3(texture2D(Texture_Gloss, TexCoord)), terrainblend);\n"
 "#  endif\n"
 "# else\n"
-"      myhalf3 surfacenormal = normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5));\n"
+"      myhalf3 surfacenormal = normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5, 0.5, 0.5));\n"
 "#  ifdef USESPECULAR\n"
 "      myhalf3 glosscolor = myhalf3(texture2D(Texture_Gloss, TexCoord));\n"
 "#  endif\n"
@@ -880,10 +892,16 @@ static const char *builtinshaderstring =
 "      myhalf3 diffusenormal = myhalf3(normalize(LightVector));\n"
 "# endif\n"
 "# ifdef USESPECULAR\n"
+"#  ifndef USEEXACTSPECULARMATH\n"
 "      myhalf3 specularnormal = normalize(diffusenormal + myhalf3(normalize(EyeVector)));\n"
 "\n"
+"#  endif\n"
 "      // calculate directional shading\n"
+"#  ifdef USEEXACTSPECULARMATH\n"
+"      color.rgb = myhalf(texture2D(Texture_Attenuation, vec2(length(CubeVector), 0.0))) * (color.rgb * (AmbientScale + DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0))) + (SpecularScale * pow(myhalf(max(float(dot(reflect(diffusenormal, surfacenormal), normalize(EyeVector)))*-1.0, 0.0)), SpecularPower)) * glosscolor);\n"
+"#  else\n"
 "      color.rgb = myhalf(texture2D(Texture_Attenuation, vec2(length(CubeVector), 0.0))) * (color.rgb * (AmbientScale + DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0))) + (SpecularScale * pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower)) * glosscolor);\n"
+"#  endif\n"
 "# else\n"
 "#  ifdef USEDIFFUSE\n"
 "      // calculate directional shading\n"
@@ -908,13 +926,17 @@ static const char *builtinshaderstring =
 "      // directional model lighting\n"
 "# ifdef USEDIFFUSE\n"
 "      // get the light normal\n"
-"      myhalf3 diffusenormal = myhalf3(LightVector);\n"
+"      myhalf3 diffusenormal = myhalf3(normalize(LightVector));\n"
 "# endif\n"
 "# ifdef USESPECULAR\n"
 "      // calculate directional shading\n"
 "      color.rgb *= AmbientColor + DiffuseColor * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0));\n"
+"#  ifdef USEEXACTSPECULARMATH\n"
+"      color.rgb += myhalf3(texture2D(Texture_Gloss, TexCoord)) * SpecularColor * pow(myhalf(max(float(dot(reflect(diffusenormal, surfacenormal), normalize(EyeVector)))*-1.0, 0.0)), SpecularPower);\n"
+"#  else\n"
 "      myhalf3 specularnormal = normalize(diffusenormal + myhalf3(normalize(EyeVector)));\n"
 "      color.rgb += myhalf3(texture2D(Texture_Gloss, TexCoord)) * SpecularColor * pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower);\n"
+"#  endif\n"
 "# else\n"
 "#  ifdef USEDIFFUSE\n"
 "\n"
@@ -933,13 +955,29 @@ static const char *builtinshaderstring =
 "      // deluxemap lightmapping using light vectors in modelspace (evil q3map2)\n"
 "\n"
 "      // get the light normal\n"
-"      myhalf3 diffusenormal_modelspace = myhalf3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - myhalf3(0.5);\n"
-"      myhalf3 diffusenormal = normalize(myhalf3(dot(diffusenormal_modelspace, myhalf3(VectorS)), dot(diffusenormal_modelspace, myhalf3(VectorT)), dot(diffusenormal_modelspace, myhalf3(VectorR))));\n"
-"      // calculate directional shading\n"
-"      myhalf3 tempcolor = color.rgb * (DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0)));\n"
+"      myhalf3 diffusenormal_modelspace = myhalf3(texture2D(Texture_Deluxemap, TexCoordLightmap)) * 2.0 + myhalf3(-1.0, -1.0, -1.0);\n"
+"      myhalf3 diffusenormal;\n"
+"      diffusenormal.x = dot(diffusenormal_modelspace, myhalf3(VectorS));\n"
+"      diffusenormal.y = dot(diffusenormal_modelspace, myhalf3(VectorT));\n"
+"      diffusenormal.z = dot(diffusenormal_modelspace, myhalf3(VectorR));\n"
+"      // calculate directional shading (and undoing the existing angle attenuation on the lightmap by the division)\n"
+"      // note that q3map2 is too stupid to calculate proper surface normals when q3map_nonplanar\n"
+"      // is used (the lightmap and deluxemap coords correspond to virtually random coordinates\n"
+"      // on that luxel, and NOT to its center, because recursive triangle subdivision is used\n"
+"      // to map the luxels to coordinates on the draw surfaces), which also causes\n"
+"      // deluxemaps to be wrong because light contributions from the wrong side of the surface\n"
+"      // are added up. To prevent divisions by zero or strong exaggerations, a max()\n"
+"      // nudge is done here at expense of some additional fps. This is ONLY needed for\n"
+"      // deluxemaps, tangentspace deluxemap avoid this problem by design.\n"
+"      myhalf3 tempcolor = color.rgb * (DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal) / max(0.25, diffusenormal.z)), 0.0)));\n"
+"              // 0.25 supports up to 75.5 degrees normal/deluxe angle\n"
 "# ifdef USESPECULAR\n"
+"#  ifdef USEEXACTSPECULARMATH\n"
+"      tempcolor += myhalf3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(myhalf(max(float(dot(reflect(diffusenormal, surfacenormal), normalize(EyeVector)))*-1.0, 0.0)), SpecularPower);\n"
+"#  else\n"
 "      myhalf3 specularnormal = myhalf3(normalize(diffusenormal + myhalf3(normalize(EyeVector))));\n"
 "      tempcolor += myhalf3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower);\n"
+"#  endif\n"
 "# endif\n"
 "\n"
 "      // apply lightmap color\n"
@@ -953,12 +991,16 @@ static const char *builtinshaderstring =
 "      // deluxemap lightmapping using light vectors in tangentspace (hmap2 -light)\n"
 "\n"
 "      // get the light normal\n"
-"      myhalf3 diffusenormal = normalize(myhalf3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - myhalf3(0.5));\n"
-"      // calculate directional shading\n"
-"      myhalf3 tempcolor = color.rgb * (DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0)));\n"
+"      myhalf3 diffusenormal = myhalf3(texture2D(Texture_Deluxemap, TexCoordLightmap)) * 2.0 + myhalf3(-1.0, -1.0, -1.0);\n"
+"      // calculate directional shading (and undoing the existing angle attenuation on the lightmap by the division)\n"
+"      myhalf3 tempcolor = color.rgb * (DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal) / diffusenormal.z), 0.0)));\n"
 "# ifdef USESPECULAR\n"
+"#  ifdef USEEXACTSPECULARMATH\n"
+"      tempcolor += myhalf3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(myhalf(max(float(dot(reflect(diffusenormal, surfacenormal), normalize(EyeVector)))*-1.0, 0.0)), SpecularPower);\n"
+"#  else\n"
 "      myhalf3 specularnormal = myhalf3(normalize(diffusenormal + myhalf3(normalize(EyeVector))));\n"
 "      tempcolor += myhalf3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower);\n"
+"#  endif\n"
 "# endif\n"
 "\n"
 "      // apply lightmap color\n"
@@ -1057,13 +1099,14 @@ typedef enum shaderpermutation_e
        SHADERPERMUTATION_CUBEFILTER = 1<<5, // (lightsource) use cubemap light filter
        SHADERPERMUTATION_GLOW = 1<<6, // (lightmap) blend in an additive glow texture
        SHADERPERMUTATION_SPECULAR = 1<<7, // (lightsource or deluxemapping) render specular effects
-       SHADERPERMUTATION_REFLECTION = 1<<8, // normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
-       SHADERPERMUTATION_OFFSETMAPPING = 1<<9, // adjust texcoords to roughly simulate a displacement mapped surface
-       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<10, // adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
-       SHADERPERMUTATION_GAMMARAMPS = 1<<11, // gamma (postprocessing only)
-       SHADERPERMUTATION_POSTPROCESSING = 1<<12, // user defined postprocessing
-       SHADERPERMUTATION_LIMIT = 1<<13, // size of permutations array
-       SHADERPERMUTATION_COUNT = 13 // size of shaderpermutationinfo array
+       SHADERPERMUTATION_EXACTSPECULARMATH = 1<<8, // (lightsource or deluxemapping) use exact reflection map for specular effects, as opposed to the usual OpenGL approximation
+       SHADERPERMUTATION_REFLECTION = 1<<9, // normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
+       SHADERPERMUTATION_OFFSETMAPPING = 1<<10, // adjust texcoords to roughly simulate a displacement mapped surface
+       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<11, // adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
+       SHADERPERMUTATION_GAMMARAMPS = 1<<12, // gamma (postprocessing only)
+       SHADERPERMUTATION_POSTPROCESSING = 1<<13, // user defined postprocessing
+       SHADERPERMUTATION_LIMIT = 1<<14, // size of permutations array
+       SHADERPERMUTATION_COUNT = 14 // size of shaderpermutationinfo array
 }
 shaderpermutation_t;
 
@@ -1078,6 +1121,7 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
        {"#define USECUBEFILTER\n", " cubefilter"},
        {"#define USEGLOW\n", " glow"},
        {"#define USESPECULAR\n", " specular"},
+       {"#define USEEXACTSPECULARMATH\n", " exactspecularmath"},
        {"#define USEREFLECTION\n", " reflection"},
        {"#define USEOFFSETMAPPING\n", " offsetmapping"},
        {"#define USEOFFSETMAPPING_RELIEFMAPPING\n", " reliefmapping"},
@@ -1203,13 +1247,13 @@ static char *R_GLSL_GetText(const char *filename, qboolean printfromdisknotice)
        }
        else if (!strcmp(filename, "glsl/default.glsl"))
        {
-               shaderstring = Mem_Alloc(r_main_mempool, strlen(builtinshaderstring) + 1);
+               shaderstring = (char *) Mem_Alloc(r_main_mempool, strlen(builtinshaderstring) + 1);
                memcpy(shaderstring, builtinshaderstring, strlen(builtinshaderstring) + 1);
        }
        return shaderstring;
 }
 
-static void R_GLSL_CompilePermutation(shadermode_t mode, shaderpermutation_t permutation)
+static void R_GLSL_CompilePermutation(unsigned int mode, unsigned int permutation)
 {
        int i;
        shadermodeinfo_t *modeinfo = shadermodeinfo + mode;
@@ -1380,8 +1424,8 @@ static void R_GLSL_CompilePermutation(shadermode_t mode, shaderpermutation_t per
 
 void R_GLSL_Restart_f(void)
 {
-       shadermode_t mode;
-       shaderpermutation_t permutation;
+       unsigned int mode;
+       unsigned int permutation;
        for (mode = 0;mode < SHADERMODE_COUNT;mode++)
                for (permutation = 0;permutation < SHADERPERMUTATION_LIMIT;permutation++)
                        if (r_glsl_permutations[mode][permutation].program)
@@ -1393,7 +1437,7 @@ void R_GLSL_DumpShader_f(void)
 {
        int i;
 
-       qfile_t *file = FS_Open("glsl/default.glsl", "w", false, false);
+       qfile_t *file = FS_OpenRealFile("glsl/default.glsl", "w", false);
        if(!file)
        {
                Con_Printf("failed to write to glsl/default.glsl\n");
@@ -1413,7 +1457,7 @@ void R_GLSL_DumpShader_f(void)
        Con_Printf("glsl/default.glsl written\n");
 }
 
-void R_SetupShader_SetPermutation(shadermode_t mode, unsigned int permutation)
+void R_SetupShader_SetPermutation(unsigned int mode, unsigned int permutation)
 {
        r_glsl_permutation_t *perm = &r_glsl_permutations[mode][permutation];
        if (r_glsl_permutation != perm)
@@ -1473,7 +1517,7 @@ void R_SetupGenericTwoTextureShader(int texturemode)
        if (gl_support_fragment_shader)
        {
                if (r_glsl.integer && r_glsl_usegeneric.integer)
-                       R_SetupShader_SetPermutation(SHADERMODE_GENERIC, SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | (texturemode == GL_MODULATE ? SHADERPERMUTATION_COLORMAPPING : (texturemode == GL_ADD ? SHADERPERMUTATION_GLOW : (texturemode == GL_DECAL ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0))));
+                       R_SetupShader_SetPermutation(SHADERMODE_GENERIC, SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | (r_shadow_glossexact.integer ? SHADERPERMUTATION_EXACTSPECULARMATH : 0) | (texturemode == GL_MODULATE ? SHADERPERMUTATION_COLORMAPPING : (texturemode == GL_ADD ? SHADERPERMUTATION_GLOW : (texturemode == GL_DECAL ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0))));
                else if (r_glsl_permutation)
                {
                        r_glsl_permutation = NULL;
@@ -1512,7 +1556,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
        // minimum features necessary to avoid wasting rendering time in the
        // fragment shader on features that are not being used
        unsigned int permutation = 0;
-       shadermode_t mode = 0;
+       unsigned int mode = 0;
        // TODO: implement geometry-shader based shadow volumes someday
        if (r_glsl_offsetmapping.integer)
        {
@@ -1652,6 +1696,9 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
                        permutation |= SHADERPERMUTATION_REFLECTION;
        }
+       if(permutation & SHADERPERMUTATION_SPECULAR)
+               if(r_shadow_glossexact.integer)
+                       permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
        R_SetupShader_SetPermutation(mode, permutation);
        if (mode == SHADERMODE_LIGHTSOURCE)
        {
@@ -1691,7 +1738,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                        if (r_glsl_permutation->loc_SpecularScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, r_refdef.lightmapintensity * specularscale);
                }
                if (r_glsl_permutation->loc_TintColor >= 0) qglUniform4fARB(r_glsl_permutation->loc_TintColor, rsurface.texture->lightmapcolor[0], rsurface.texture->lightmapcolor[1], rsurface.texture->lightmapcolor[2], rsurface.texture->lightmapcolor[3]);
-               if (r_glsl_permutation->loc_GlowScale     >= 0) qglUniform1fARB(r_glsl_permutation->loc_GlowScale, r_hdr_glowintensity.value);
+               if (r_glsl_permutation->loc_GlowScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_GlowScale, r_hdr_glowintensity.value);
                // additive passes are only darkened by fog, not tinted
                if (r_glsl_permutation->loc_FogColor >= 0)
                {
@@ -1736,21 +1783,29 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                else
                        qglUniform3fARB(r_glsl_permutation->loc_Color_Shirt, 0, 0, 0);
        }
-       if (r_glsl_permutation->loc_FogRangeRecip >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogRangeRecip, r_refdef.fograngerecip);
-       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower);
+       if (r_glsl_permutation->loc_FogRangeRecip >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogRangeRecip, r_refdef.fograngerecip * Matrix4x4_ScaleFromMatrix(&rsurface.matrix));
+       if(permutation & SHADERPERMUTATION_EXACTSPECULARMATH)
+       {
+               if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * 0.25);
+       }
+       else
+       {
+               if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower);
+       }
        if (r_glsl_permutation->loc_OffsetMapping_Scale >= 0) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value);
        CHECKGLERROR
 }
 
 #define SKINFRAME_HASH 1024
 
-struct
+typedef struct
 {
        int loadsequence; // incremented each level change
        memexpandablearray_t array;
        skinframe_t *hash[SKINFRAME_HASH];
 }
-r_skinframe;
+r_skinframe_t;
+r_skinframe_t r_skinframe;
 
 void R_SkinFrame_PrepareForPurge(void)
 {
@@ -1862,7 +1917,42 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
        return item;
 }
 
-skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboolean complain)
+#define R_SKINFRAME_LOAD_AVERAGE_COLORS(cnt, getpixel) \
+       { \
+               unsigned long long avgcolor[5], wsum; \
+               int pix, comp, w; \
+               avgcolor[0] = 0; \
+               avgcolor[1] = 0; \
+               avgcolor[2] = 0; \
+               avgcolor[3] = 0; \
+               avgcolor[4] = 0; \
+               wsum = 0; \
+               for(pix = 0; pix < cnt; ++pix) \
+               { \
+                       w = 0; \
+                       for(comp = 0; comp < 3; ++comp) \
+                               w += getpixel; \
+                       if(w) /* ignore perfectly black pixels because that is better for model skins */ \
+                       { \
+                               ++wsum; \
+                               /* comp = 3; -- not needed, comp is always 3 when we get here */ \
+                               w = getpixel; \
+                               for(comp = 0; comp < 3; ++comp) \
+                                       avgcolor[comp] += getpixel * w; \
+                               avgcolor[3] += w; \
+                       } \
+                       /* comp = 3; -- not needed, comp is always 3 when we get here */ \
+                       avgcolor[4] += getpixel; \
+               } \
+               if(avgcolor[3] == 0) /* no pixels seen? even worse */ \
+                       avgcolor[3] = 1; \
+               skinframe->avgcolor[0] = avgcolor[2] / (255.0 * avgcolor[3]); \
+               skinframe->avgcolor[1] = avgcolor[1] / (255.0 * avgcolor[3]); \
+               skinframe->avgcolor[2] = avgcolor[0] / (255.0 * avgcolor[3]); \
+               skinframe->avgcolor[3] = avgcolor[4] / (255.0 * cnt); \
+       }
+
+skinframe_t *R_SkinFrame_LoadExternal_CheckAlpha(const char *name, int textureflags, qboolean complain, qboolean *has_alpha)
 {
        // FIXME: it should be possible to disable loading various layers using
        // cvars, to prevent wasted loading time and memory usage if the user does
@@ -1879,6 +1969,8 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        int basepixels_height;
        skinframe_t *skinframe;
 
+       *has_alpha = false;
+
        if (cls.state == ca_dedicated)
                return NULL;
 
@@ -1921,6 +2013,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                if (j < basepixels_width * basepixels_height * 4)
                {
                        // has transparent pixels
+                       *has_alpha = true;
                        pixels = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4);
                        for (j = 0;j < image_width * image_height * 4;j += 4)
                        {
@@ -1934,12 +2027,15 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                }
        }
 
+       R_SKINFRAME_LOAD_AVERAGE_COLORS(basepixels_width * basepixels_height, basepixels[4 * pix + comp]);
+       //Con_Printf("Texture %s has average colors %f %f %f alpha %f\n", name, skinframe->avgcolor[0], skinframe->avgcolor[1], skinframe->avgcolor[2], skinframe->avgcolor[3]);
+
        // _norm is the name used by tenebrae and has been adopted as standard
        if (loadnormalmap)
        {
                if ((pixels = loadimagepixelsbgra(va("%s_norm", skinframe->basename), false, false)) != NULL)
                {
-                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
                        Mem_Free(pixels);
                        pixels = NULL;
                }
@@ -1947,7 +2043,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                {
                        pixels = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4);
                        Image_HeightmapToNormalmap_BGRA(bumppixels, pixels, image_width, image_height, false, r_shadow_bumpscale_bumpmap.value);
-                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
                        Mem_Free(pixels);
                        Mem_Free(bumppixels);
                }
@@ -1955,7 +2051,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                {
                        pixels = (unsigned char *)Mem_Alloc(tempmempool, basepixels_width * basepixels_height * 4);
                        Image_HeightmapToNormalmap_BGRA(basepixels, pixels, basepixels_width, basepixels_height, false, r_shadow_bumpscale_basetexture.value);
-                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), basepixels_width, basepixels_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), basepixels_width, basepixels_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
                        Mem_Free(pixels);
                }
        }
@@ -1973,6 +2069,12 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        return skinframe;
 }
 
+skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboolean complain)
+{
+       qboolean has_alpha;
+       return R_SkinFrame_LoadExternal_CheckAlpha(name, textureflags, complain, &has_alpha);
+}
+
 static rtexture_t *R_SkinFrame_TextureForSkinLayer(const unsigned char *in, int width, int height, const char *name, const unsigned int *palette, int textureflags, qboolean force)
 {
        int i;
@@ -2044,6 +2146,9 @@ skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, co
                }
        }
 
+       R_SKINFRAME_LOAD_AVERAGE_COLORS(width * height, skindata[4 * pix + comp]);
+       //Con_Printf("Texture %s has average colors %f %f %f alpha %f\n", name, skinframe->avgcolor[0], skinframe->avgcolor[1], skinframe->avgcolor[2], skinframe->avgcolor[3]);
+
        return skinframe;
 }
 
@@ -2051,6 +2156,7 @@ skinframe_t *R_SkinFrame_LoadInternalQuake(const char *name, int textureflags, i
 {
        int i;
        unsigned char *temp1, *temp2;
+       unsigned int *palette;
        skinframe_t *skinframe;
 
        if (cls.state == ca_dedicated)
@@ -2061,6 +2167,8 @@ skinframe_t *R_SkinFrame_LoadInternalQuake(const char *name, int textureflags, i
        if (skinframe && skinframe->base)
                return skinframe;
 
+       palette = (loadglowtexture ? palette_bgra_nofullbrights : ((skinframe->textureflags & TEXF_ALPHA) ? palette_bgra_transparent : palette_bgra_complete));
+
        skinframe->stain = NULL;
        skinframe->merged = NULL;
        skinframe->base = r_texture_notexture;
@@ -2089,7 +2197,7 @@ skinframe_t *R_SkinFrame_LoadInternalQuake(const char *name, int textureflags, i
                Mem_Free(temp1);
        }
        // use either a custom palette, or the quake palette
-       skinframe->base = skinframe->merged = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_merged", skinframe->basename), (loadglowtexture ? palette_bgra_nofullbrights : ((skinframe->textureflags & TEXF_ALPHA) ? palette_bgra_transparent : palette_bgra_complete)), skinframe->textureflags, true); // all
+       skinframe->base = skinframe->merged = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_merged", skinframe->basename), palette, skinframe->textureflags, true); // all
        if (loadglowtexture)
                skinframe->glow = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_glow", skinframe->basename), palette_bgra_onlyfullbrights, skinframe->textureflags, false); // glow
        if (loadpantsandshirt)
@@ -2108,6 +2216,9 @@ skinframe_t *R_SkinFrame_LoadInternalQuake(const char *name, int textureflags, i
                        skinframe->fog = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_fog", skinframe->basename), palette_bgra_alpha, skinframe->textureflags, true); // fog mask
        }
 
+       R_SKINFRAME_LOAD_AVERAGE_COLORS(width * height, ((unsigned char *)palette)[skindata[pix]*4 + comp]);
+       //Con_Printf("Texture %s has average colors %f %f %f alpha %f\n", name, skinframe->avgcolor[0], skinframe->avgcolor[1], skinframe->avgcolor[2], skinframe->avgcolor[3]);
+
        return skinframe;
 }
 
@@ -2129,11 +2240,20 @@ skinframe_t *R_SkinFrame_LoadMissing(void)
        skinframe->glow = NULL;
        skinframe->fog = NULL;
 
+       skinframe->avgcolor[0] = rand() / RAND_MAX;
+       skinframe->avgcolor[1] = rand() / RAND_MAX;
+       skinframe->avgcolor[2] = rand() / RAND_MAX;
+       skinframe->avgcolor[3] = 1;
+
        return skinframe;
 }
 
 void gl_main_start(void)
 {
+       r_numqueries = 0;
+       r_maxqueries = 0;
+       memset(r_queries, 0, sizeof(r_queries));
+
        memset(r_qwskincache, 0, sizeof(r_qwskincache));
        memset(r_qwskincache_skinframe, 0, sizeof(r_qwskincache_skinframe));
 
@@ -2163,6 +2283,13 @@ void gl_main_start(void)
 
 void gl_main_shutdown(void)
 {
+       if (r_maxqueries)
+               qglDeleteQueriesARB(r_maxqueries, r_queries);
+
+       r_numqueries = 0;
+       r_maxqueries = 0;
+       memset(r_queries, 0, sizeof(r_queries));
+
        memset(r_qwskincache, 0, sizeof(r_qwskincache));
        memset(r_qwskincache_skinframe, 0, sizeof(r_qwskincache_skinframe));
 
@@ -2348,6 +2475,22 @@ GL_Init
 extern char *ENGINE_EXTENSIONS;
 void GL_Init (void)
 {
+       gl_renderer = (const char *)qglGetString(GL_RENDERER);
+       gl_vendor = (const char *)qglGetString(GL_VENDOR);
+       gl_version = (const char *)qglGetString(GL_VERSION);
+       gl_extensions = (const char *)qglGetString(GL_EXTENSIONS);
+
+       if (!gl_extensions)
+               gl_extensions = "";
+       if (!gl_platformextensions)
+               gl_platformextensions = "";
+
+       Con_Printf("GL_VENDOR: %s\n", gl_vendor);
+       Con_Printf("GL_RENDERER: %s\n", gl_renderer);
+       Con_Printf("GL_VERSION: %s\n", gl_version);
+       Con_DPrintf("GL_EXTENSIONS: %s\n", gl_extensions);
+       Con_DPrintf("%s_EXTENSIONS: %s\n", gl_platform, gl_platformextensions);
+
        VID_CheckExtensions();
 
        // LordHavoc: report supported extensions
@@ -2470,11 +2613,14 @@ static void R_View_UpdateEntityVisible (void)
        if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs)
        {
                // worldmodel can check visibility
+               memset(r_refdef.viewcache.entityvisible, 0, r_refdef.scene.numentities);
                for (i = 0;i < r_refdef.scene.numentities;i++)
                {
                        ent = r_refdef.scene.entities[i];
-                       r_refdef.viewcache.entityvisible[i] = !(ent->flags & renderimask) && ((ent->model && ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE)) || !R_CullBox(ent->mins, ent->maxs)) && ((ent->effects & EF_NODEPTHTEST) || (ent->flags & RENDER_VIEWMODEL) || r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, ent->mins, ent->maxs));
-
+                       if (!(ent->flags & renderimask))
+                       if (!R_CullBox(ent->mins, ent->maxs) || (ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE)))
+                       if ((ent->effects & EF_NODEPTHTEST) || (ent->flags & RENDER_VIEWMODEL) || r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, ent->mins, ent->maxs))
+                               r_refdef.viewcache.entityvisible[i] = true;
                }
                if(r_cullentities_trace.integer && r_refdef.scene.worldmodel->brush.TraceLineOfSight)
                {
@@ -2858,7 +3004,7 @@ static void R_Water_StartFrame(void)
 
        // calculate desired texture sizes
        // can't use water if the card does not support the texture size
-       if (!r_water.integer || !r_glsl.integer || !gl_support_fragment_shader || waterwidth > gl_max_texture_size || waterheight > gl_max_texture_size)
+       if (!r_water.integer || !r_glsl.integer || !gl_support_fragment_shader || waterwidth > gl_max_texture_size || waterheight > gl_max_texture_size || r_showsurfaces.integer)
                texturewidth = textureheight = waterwidth = waterheight = 0;
        else if (gl_support_arb_texture_non_power_of_two)
        {
@@ -3179,7 +3325,7 @@ void R_Bloom_CopyBloomTexture(float colorscale)
        R_SetupGenericShader(true);
        R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
        R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
-       R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+       R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
        r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
        // we now have a bloom image in the framebuffer
@@ -3224,7 +3370,7 @@ void R_Bloom_MakeTexture(void)
                GL_Color(r, r, r, 1);
                R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
                R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
-               R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
                // copy the vertically blurred bloom view to a texture
@@ -3268,7 +3414,7 @@ void R_Bloom_MakeTexture(void)
                        //r = (dir ? 1.0f : brighten)/(range*2+1);
                        r = (dir ? 1.0f : brighten)/(range*2+1)*(1 - x*x/(float)(range*range));
                        GL_Color(r, r, r, 1);
-                       R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+                       R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
                        r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
                        GL_BlendFunc(GL_ONE, GL_ONE);
                }
@@ -3288,7 +3434,7 @@ void R_Bloom_MakeTexture(void)
                R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
                R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
                GL_Color(1, 1, 1, 1);
-               R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
                GL_BlendFunc(GL_ONE, GL_ONE);
@@ -3296,7 +3442,7 @@ void R_Bloom_MakeTexture(void)
                R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
                R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
                GL_Color(r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, 1);
-               R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
                qglBlendEquationEXT(GL_FUNC_ADD_EXT);
 
@@ -3404,6 +3550,9 @@ static void R_BlendView(void)
                if (r_glsl_permutation->loc_UserVec1 >= 0)
                {
                        float a=0, b=0, c=0, d=0;
+#if _MSC_VER >= 1400
+#define sscanf sscanf_s
+#endif
                        sscanf(r_glsl_postprocess_uservec1.string, "%f %f %f %f", &a, &b, &c, &d);
                        qglUniform4fARB(r_glsl_permutation->loc_UserVec1, a, b, c, d);
                }
@@ -3425,7 +3574,7 @@ static void R_BlendView(void)
                        sscanf(r_glsl_postprocess_uservec4.string, "%f %f %f %f", &a, &b, &c, &d);
                        qglUniform4fARB(r_glsl_permutation->loc_UserVec4, a, b, c, d);
                }
-               R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height;
                return;
        }
@@ -3445,7 +3594,7 @@ static void R_BlendView(void)
                GL_BlendFunc(GL_ONE, GL_ONE);
                R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
                R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
-               R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height;
        }
        else if (r_bloomstate.texture_bloom)
@@ -3474,14 +3623,14 @@ static void R_BlendView(void)
                else
                {
                        R_SetupGenericShader(true);
-                       R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+                       R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
                        r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height;
                        // now blend on the bloom texture
                        GL_BlendFunc(GL_ONE, GL_ONE);
                        R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
                        R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
                }
-               R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height;
        }
        if (r_refdef.viewblend[3] >= (1.0f / 256.0f))
@@ -3493,7 +3642,7 @@ static void R_BlendView(void)
                R_SetupGenericShader(false);
                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
-               R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
        }
 }
 
@@ -3958,7 +4107,7 @@ void R_RenderScene(qboolean addwaterplanes)
        R_ResetViewRendering2D();
 }
 
-static const int bboxelements[36] =
+static const unsigned short bboxelements[36] =
 {
        5, 1, 3, 5, 3, 7,
        6, 2, 0, 6, 0, 4,
@@ -4003,7 +4152,7 @@ void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, floa
        R_Mesh_ColorPointer(color4f, 0, 0);
        R_Mesh_ResetTextureState();
        R_SetupGenericShader(false);
-       R_Mesh_Draw(0, 8, 12, bboxelements, 0, 0);
+       R_Mesh_Draw(0, 8, 0, 12, NULL, bboxelements, 0, 0);
 }
 
 static void R_DrawEntityBBoxes_Callback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
@@ -4011,11 +4160,16 @@ static void R_DrawEntityBBoxes_Callback(const entity_render_t *ent, const rtligh
        int i;
        float color[4];
        prvm_edict_t *edict;
+       prvm_prog_t *prog_save = prog;
+
        // this function draws bounding boxes of server entities
        if (!sv.active)
                return;
+
        GL_CullFace(GL_NONE);
        R_SetupGenericShader(false);
+
+       prog = 0;
        SV_VM_Begin();
        for (i = 0;i < numsurfaces;i++)
        {
@@ -4036,6 +4190,7 @@ static void R_DrawEntityBBoxes_Callback(const entity_render_t *ent, const rtligh
                R_DrawBBoxMesh(edict->priv.server->areamins, edict->priv.server->areamaxs, color[0], color[1], color[2], color[3]);
        }
        SV_VM_End();
+       prog = prog_save;
 }
 
 static void R_DrawEntityBBoxes(void)
@@ -4043,22 +4198,32 @@ static void R_DrawEntityBBoxes(void)
        int i;
        prvm_edict_t *edict;
        vec3_t center;
+       prvm_prog_t *prog_save = prog;
+
        // this function draws bounding boxes of server entities
        if (!sv.active)
                return;
+
+       prog = 0;
        SV_VM_Begin();
        for (i = 0;i < prog->num_edicts;i++)
        {
                edict = PRVM_EDICT_NUM(i);
                if (edict->priv.server->free)
                        continue;
+               // exclude the following for now, as they don't live in world coordinate space and can't be solid:
+               if(PRVM_EDICTFIELDVALUE(edict, prog->fieldoffsets.tag_entity)->edict != 0)
+                       continue;
+               if(PRVM_EDICTFIELDVALUE(edict, prog->fieldoffsets.viewmodelforclient)->edict != 0)
+                       continue;
                VectorLerp(edict->priv.server->areamins, 0.5f, edict->priv.server->areamaxs, center);
                R_MeshQueue_AddTransparent(center, R_DrawEntityBBoxes_Callback, (entity_render_t *)NULL, i, (rtlight_t *)NULL);
        }
        SV_VM_End();
+       prog = prog_save;
 }
 
-int nomodelelements[24] =
+unsigned short nomodelelements[24] =
 {
        5, 2, 0,
        5, 1, 2,
@@ -4146,7 +4311,7 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
        else
                R_Mesh_ColorPointer(nomodelcolor4f, 0, 0);
        R_Mesh_ResetTextureState();
-       R_Mesh_Draw(0, 6, 8, nomodelelements, 0, 0);
+       R_Mesh_Draw(0, 6, 0, 8, NULL, nomodelelements, 0, 0);
 }
 
 void R_DrawNoModel(entity_render_t *ent)
@@ -4238,7 +4403,7 @@ void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_
        R_Mesh_TexCoordPointer(0, 2, spritetexcoord2f, 0, 0);
        // FIXME: fixed function path can't properly handle r_refdef.view.colorscale > 1
        GL_Color(cr * fog * r_refdef.view.colorscale, cg * fog * r_refdef.view.colorscale, cb * fog * r_refdef.view.colorscale, ca);
-       R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+       R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
 
        if (blendfunc2 == GL_ONE_MINUS_SRC_ALPHA)
        {
@@ -4246,7 +4411,7 @@ void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_
                GL_BlendFunc(blendfunc1, GL_ONE);
                fog = 1 - fog;
                GL_Color(r_refdef.fogcolor[0] * fog, r_refdef.fogcolor[1] * fog, r_refdef.fogcolor[2] * fog, ca);
-               R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
        }
 }
 
@@ -4399,8 +4564,9 @@ static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
 
 void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
 {
+       int w, h, idx;
        int i;
-       model_t *model = ent->model;
+       dp_model_t *model = ent->model;
        float f;
        float tcmat[12];
        q3shaderinfo_layer_tcmod_t *tcmod;
@@ -4439,7 +4605,7 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
        }
 
        // update currentskinframe to be a qw skin or animation frame
-       if ((i = ent->entitynumber - 1) >= 0 && i < cl.maxclients)
+       if ((i = ent->entitynumber - 1) >= 0 && i < cl.maxclients && cls.protocol == PROTOCOL_QUAKEWORLD && cl.scores[i].qw_skin[0] && !strcmp(ent->model->name, "progs/player.mdl"))
        {
                if (strcmp(r_qwskincache[i], cl.scores[i].qw_skin))
                {
@@ -4527,6 +4693,14 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
                case Q3TCMOD_SCROLL:
                        Matrix4x4_CreateTranslate(&matrix, tcmod->parms[0] * r_refdef.scene.time, tcmod->parms[1] * r_refdef.scene.time, 0);
                        break;
+               case Q3TCMOD_PAGE: // poor man's animmap (to store animations into a single file, useful for HTTP downloaded textures)
+                       w = (int) tcmod->parms[0];
+                       h = (int) tcmod->parms[1];
+                       f = r_refdef.scene.time / (tcmod->parms[2] * w * h);
+                       f = f - floor(f);
+                       idx = (int) floor(f * w * h);
+                       Matrix4x4_CreateTranslate(&matrix, (idx % w) / tcmod->parms[0], (idx / w) / tcmod->parms[1], 0);
+                       break;
                case Q3TCMOD_STRETCH:
                        f = 1.0f / R_EvaluateQ3WaveFunc(tcmod->wavefunc, tcmod->waveparms);
                        Matrix4x4_CreateFromQuakeEntity(&matrix, 0.5f * (1 - f), 0.5 * (1 - f), 0, 0, 0, 0, f);
@@ -4717,7 +4891,7 @@ void R_Mesh_ResizeArrays(int newvertices)
 
 void RSurf_ActiveWorldEntity(void)
 {
-       model_t *model = r_refdef.scene.worldmodel;
+       dp_model_t *model = r_refdef.scene.worldmodel;
        if (rsurface.array_size < model->surfmesh.num_vertices)
                R_Mesh_ResizeArrays(model->surfmesh.num_vertices);
        rsurface.matrix = identitymatrix;
@@ -4761,7 +4935,9 @@ void RSurf_ActiveWorldEntity(void)
        rsurface.modeltexcoordlightmap2f_bufferobject = model->surfmesh.vbo;
        rsurface.modeltexcoordlightmap2f_bufferoffset = model->surfmesh.vbooffset_texcoordlightmap2f;
        rsurface.modelelement3i = model->surfmesh.data_element3i;
-       rsurface.modelelement3i_bufferobject = model->surfmesh.ebo;
+       rsurface.modelelement3s = model->surfmesh.data_element3s;
+       rsurface.modelelement3i_bufferobject = model->surfmesh.ebo3i;
+       rsurface.modelelement3s_bufferobject = model->surfmesh.ebo3s;
        rsurface.modellightmapoffsets = model->surfmesh.data_lightmapoffsets;
        rsurface.modelnum_vertices = model->surfmesh.num_vertices;
        rsurface.modelnum_triangles = model->surfmesh.num_triangles;
@@ -4784,7 +4960,7 @@ void RSurf_ActiveWorldEntity(void)
 
 void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, qboolean wanttangents)
 {
-       model_t *model = ent->model;
+       dp_model_t *model = ent->model;
        if (rsurface.array_size < model->surfmesh.num_vertices)
                R_Mesh_ResizeArrays(model->surfmesh.num_vertices);
        rsurface.matrix = ent->matrix;
@@ -4874,7 +5050,9 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
        rsurface.modeltexcoordlightmap2f_bufferobject = model->surfmesh.vbo;
        rsurface.modeltexcoordlightmap2f_bufferoffset = model->surfmesh.vbooffset_texcoordlightmap2f;
        rsurface.modelelement3i = model->surfmesh.data_element3i;
-       rsurface.modelelement3i_bufferobject = model->surfmesh.ebo;
+       rsurface.modelelement3s = model->surfmesh.data_element3s;
+       rsurface.modelelement3i_bufferobject = model->surfmesh.ebo3i;
+       rsurface.modelelement3s_bufferobject = model->surfmesh.ebo3s;
        rsurface.modellightmapoffsets = model->surfmesh.data_lightmapoffsets;
        rsurface.modelnum_vertices = model->surfmesh.num_vertices;
        rsurface.modelnum_triangles = model->surfmesh.num_triangles;
@@ -5328,7 +5506,7 @@ void RSurf_DrawBatch_Simple(int texturenumsurfaces, msurface_t **texturesurfacel
        if (texturenumsurfaces == 1)
        {
                GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
-               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
        }
        else if (r_batchmode.integer == 2)
        {
@@ -5341,7 +5519,7 @@ void RSurf_DrawBatch_Simple(int texturenumsurfaces, msurface_t **texturesurfacel
                        j = i + 1;
                        if (surface->num_triangles > MAXBATCHTRIANGLES)
                        {
-                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
                                continue;
                        }
                        memcpy(batchelements, rsurface.modelelement3i + 3 * surface->num_firsttriangle, surface->num_triangles * sizeof(int[3]));
@@ -5360,7 +5538,7 @@ void RSurf_DrawBatch_Simple(int texturenumsurfaces, msurface_t **texturesurfacel
                        }
                        surface2 = texturesurfacelist[j-1];
                        numvertices = endvertex - firstvertex;
-                       R_Mesh_Draw(firstvertex, numvertices, batchtriangles, batchelements, 0, 0);
+                       R_Mesh_Draw(firstvertex, numvertices, 0, batchtriangles, batchelements, NULL, 0, 0);
                }
        }
        else if (r_batchmode.integer == 1)
@@ -5375,7 +5553,7 @@ void RSurf_DrawBatch_Simple(int texturenumsurfaces, msurface_t **texturesurfacel
                        numvertices = surface2->num_firstvertex + surface2->num_vertices - surface->num_firstvertex;
                        numtriangles = surface2->num_firsttriangle + surface2->num_triangles - surface->num_firsttriangle;
                        GL_LockArrays(surface->num_firstvertex, numvertices);
-                       R_Mesh_Draw(surface->num_firstvertex, numvertices, numtriangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+                       R_Mesh_Draw(surface->num_firstvertex, numvertices, surface->num_firsttriangle, numtriangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
                }
        }
        else
@@ -5384,7 +5562,7 @@ void RSurf_DrawBatch_Simple(int texturenumsurfaces, msurface_t **texturesurfacel
                {
                        surface = texturesurfacelist[i];
                        GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
-                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
                }
        }
 }
@@ -5438,7 +5616,7 @@ static void RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(int
                                R_Mesh_TexBind(reflectiontexunit, R_GetTexture(r_texture_black));
                }
                GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
-               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
        }
 }
 
@@ -5459,7 +5637,7 @@ static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, msurfa
                if (deluxemaptexunit >= 0)
                        R_Mesh_TexBind(deluxemaptexunit, R_GetTexture(surface->deluxemaptexture));
                GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
-               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
        }
        else if (r_batchmode.integer == 2)
        {
@@ -5475,7 +5653,7 @@ static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, msurfa
                        j = i + 1;
                        if (surface->num_triangles > MAXBATCHTRIANGLES)
                        {
-                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
                                continue;
                        }
                        memcpy(batchelements, rsurface.modelelement3i + 3 * surface->num_firsttriangle, surface->num_triangles * sizeof(int[3]));
@@ -5494,7 +5672,7 @@ static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, msurfa
                        }
                        surface2 = texturesurfacelist[j-1];
                        numvertices = endvertex - firstvertex;
-                       R_Mesh_Draw(firstvertex, numvertices, batchtriangles, batchelements, 0, 0);
+                       R_Mesh_Draw(firstvertex, numvertices, 0, batchtriangles, batchelements, NULL, 0, 0);
                }
        }
        else if (r_batchmode.integer == 1)
@@ -5528,7 +5706,7 @@ static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, msurfa
                        numvertices = surface2->num_firstvertex + surface2->num_vertices - surface->num_firstvertex;
                        numtriangles = surface2->num_firsttriangle + surface2->num_triangles - surface->num_firsttriangle;
                        GL_LockArrays(surface->num_firstvertex, numvertices);
-                       R_Mesh_Draw(surface->num_firstvertex, numvertices, numtriangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+                       R_Mesh_Draw(surface->num_firstvertex, numvertices, surface->num_firsttriangle, numtriangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
                }
 #if 0
                Con_Printf("\n");
@@ -5543,7 +5721,7 @@ static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, msurfa
                        if (deluxemaptexunit >= 0)
                                R_Mesh_TexBind(deluxemaptexunit, R_GetTexture(surface->deluxemaptexture));
                        GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
-                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
                }
        }
 }
@@ -5561,7 +5739,7 @@ static void RSurf_DrawBatch_ShowSurfaces(int texturenumsurfaces, msurface_t **te
                        {
                                float f = ((j + surface->num_firsttriangle) & 31) * (1.0f / 31.0f) * r_refdef.view.colorscale;
                                GL_Color(f, f, f, 1);
-                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, 1, (rsurface.modelelement3i + 3 * (j + surface->num_firsttriangle)), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * (j + surface->num_firsttriangle)));
+                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle + j, 1, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
                        }
                }
        }
@@ -5573,9 +5751,30 @@ static void RSurf_DrawBatch_ShowSurfaces(int texturenumsurfaces, msurface_t **te
                        int k = (int)(((size_t)surface) / sizeof(msurface_t));
                        GL_Color((k & 15) * (1.0f / 16.0f) * r_refdef.view.colorscale, ((k >> 4) & 15) * (1.0f / 16.0f) * r_refdef.view.colorscale, ((k >> 8) & 15) * (1.0f / 16.0f) * r_refdef.view.colorscale, 1);
                        GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
-                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
+               }
+       }
+}
+
+static void RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(int texturenumsurfaces, msurface_t **texturesurfacelist)
+{
+       int texturesurfaceindex;
+       int i;
+       float *v, *c2;
+       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       {
+               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+               for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c2 += 4)
+               {
+                       c2[0] = 0.5;
+                       c2[1] = 0.5;
+                       c2[2] = 0.5;
+                       c2[3] = 1;
                }
        }
+       rsurface.lightmapcolor4f = rsurface.array_color4f;
+       rsurface.lightmapcolor4f_bufferobject = 0;
+       rsurface.lightmapcolor4f_bufferoffset = 0;
 }
 
 static void RSurf_DrawBatch_GL11_ApplyFog(int texturenumsurfaces, msurface_t **texturesurfacelist)
@@ -5620,6 +5819,32 @@ static void RSurf_DrawBatch_GL11_ApplyFog(int texturenumsurfaces, msurface_t **t
        rsurface.lightmapcolor4f_bufferoffset = 0;
 }
 
+static void RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(int texturenumsurfaces, msurface_t **texturesurfacelist)
+{
+       int texturesurfaceindex;
+       int i;
+       float f;
+       float *v, *c, *c2;
+       if (!rsurface.lightmapcolor4f)
+               return;
+       // generate color arrays for the surfaces in this list
+       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       {
+               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+               for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4, c2 += 4)
+               {
+                       f = FogPoint_Model(v);
+                       c2[0] = c[0] * f + r_refdef.fogcolor[0] * (1 - f);
+                       c2[1] = c[1] * f + r_refdef.fogcolor[1] * (1 - f);
+                       c2[2] = c[2] * f + r_refdef.fogcolor[2] * (1 - f);
+                       c2[3] = c[3];
+               }
+       }
+       rsurface.lightmapcolor4f = rsurface.array_color4f;
+       rsurface.lightmapcolor4f_bufferobject = 0;
+       rsurface.lightmapcolor4f_bufferoffset = 0;
+}
+
 static void RSurf_DrawBatch_GL11_ApplyColor(int texturenumsurfaces, msurface_t **texturesurfacelist, float r, float g, float b, float a)
 {
        int texturesurfaceindex;
@@ -5643,6 +5868,29 @@ static void RSurf_DrawBatch_GL11_ApplyColor(int texturenumsurfaces, msurface_t *
        rsurface.lightmapcolor4f_bufferoffset = 0;
 }
 
+static void RSurf_DrawBatch_GL11_ApplyAmbient(int texturenumsurfaces, msurface_t **texturesurfacelist)
+{
+       int texturesurfaceindex;
+       int i;
+       float *c, *c2;
+       if (!rsurface.lightmapcolor4f)
+               return;
+       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       {
+               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+               for (i = 0, c = (rsurface.lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4, c2 += 4)
+               {
+                       c2[0] = c[0] + r_refdef.scene.ambient / 128.0;
+                       c2[1] = c[1] + r_refdef.scene.ambient / 128.0;
+                       c2[2] = c[2] + r_refdef.scene.ambient / 128.0;
+                       c2[3] = c[3];
+               }
+       }
+       rsurface.lightmapcolor4f = rsurface.array_color4f;
+       rsurface.lightmapcolor4f_bufferobject = 0;
+       rsurface.lightmapcolor4f_bufferoffset = 0;
+}
+
 static void RSurf_DrawBatch_GL11_Lightmap(int texturenumsurfaces, msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
        // TODO: optimize
@@ -5676,7 +5924,7 @@ static void RSurf_DrawBatch_GL11_VertexColor(int texturenumsurfaces, msurface_t
        int i;
        float *c;
        // TODO: optimize
-       if (texturesurfacelist[0]->lightmapinfo && texturesurfacelist[0]->lightmapinfo->stainsamples)
+       if (texturesurfacelist[0]->lightmapinfo)
        {
                // generate color arrays for the surfaces in this list
                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
@@ -5731,12 +5979,12 @@ static void RSurf_DrawBatch_GL11_VertexColor(int texturenumsurfaces, msurface_t
        RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
 }
 
-static void RSurf_DrawBatch_GL11_VertexShade(int texturenumsurfaces, msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, msurface_t **texturesurfacelist, float *r, float *g, float *b, float *a, qboolean *applycolor)
 {
        int texturesurfaceindex;
        int i;
        float f;
-       float *v, *c, *c2;
+       float *v, *c, *c2, alpha;
        vec3_t ambientcolor;
        vec3_t diffusecolor;
        vec3_t lightdir;
@@ -5744,13 +5992,14 @@ static void RSurf_DrawBatch_GL11_VertexShade(int texturenumsurfaces, msurface_t
        // model lighting
        VectorCopy(rsurface.modellight_lightdir, lightdir);
        f = 0.5f * r_refdef.lightmapintensity;
-       ambientcolor[0] = rsurface.modellight_ambient[0] * r * f;
-       ambientcolor[1] = rsurface.modellight_ambient[1] * g * f;
-       ambientcolor[2] = rsurface.modellight_ambient[2] * b * f;
-       diffusecolor[0] = rsurface.modellight_diffuse[0] * r * f;
-       diffusecolor[1] = rsurface.modellight_diffuse[1] * g * f;
-       diffusecolor[2] = rsurface.modellight_diffuse[2] * b * f;
-       if (VectorLength2(diffusecolor) > 0)
+       ambientcolor[0] = rsurface.modellight_ambient[0] * *r * f;
+       ambientcolor[1] = rsurface.modellight_ambient[1] * *g * f;
+       ambientcolor[2] = rsurface.modellight_ambient[2] * *b * f;
+       diffusecolor[0] = rsurface.modellight_diffuse[0] * *r * f;
+       diffusecolor[1] = rsurface.modellight_diffuse[1] * *g * f;
+       diffusecolor[2] = rsurface.modellight_diffuse[2] * *b * f;
+       alpha = *a;
+       if (VectorLength2(diffusecolor) > 0 && rsurface.normal3f)
        {
                // generate color arrays for the surfaces in this list
                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
@@ -5767,27 +6016,32 @@ static void RSurf_DrawBatch_GL11_VertexShade(int texturenumsurfaces, msurface_t
                                        VectorMA(ambientcolor, f, diffusecolor, c);
                                else
                                        VectorCopy(ambientcolor, c);
-                               c[3] = a;
+                               c[3] = alpha;
                        }
                }
-               r = 1;
-               g = 1;
-               b = 1;
-               a = 1;
-               applycolor = false;
+               *r = 1;
+               *g = 1;
+               *b = 1;
+               *a = 1;
                rsurface.lightmapcolor4f = rsurface.array_color4f;
                rsurface.lightmapcolor4f_bufferobject = 0;
                rsurface.lightmapcolor4f_bufferoffset = 0;
+               *applycolor = false;
        }
        else
        {
-               r = ambientcolor[0];
-               g = ambientcolor[1];
-               b = ambientcolor[2];
+               *r = ambientcolor[0];
+               *g = ambientcolor[1];
+               *b = ambientcolor[2];
                rsurface.lightmapcolor4f = NULL;
                rsurface.lightmapcolor4f_bufferobject = 0;
                rsurface.lightmapcolor4f_bufferoffset = 0;
        }
+}
+
+static void RSurf_DrawBatch_GL11_VertexShade(int texturenumsurfaces, msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+{
+       RSurf_DrawBatch_GL11_ApplyVertexShade(texturenumsurfaces, texturesurfacelist, &r, &g, &b, &a, &applycolor);
        if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog(texturenumsurfaces, texturesurfacelist);
        if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, r, g, b, a);
        R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
@@ -6212,11 +6466,115 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, msurface_t **t
        }
 }
 
+static void R_DrawTextureSurfaceList_ShowSurfaces3(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
+{
+       float c[4];
+
+       GL_AlphaTest(false);
+       R_Mesh_ColorPointer(NULL, 0, 0);
+       R_Mesh_ResetTextureState();
+       R_SetupGenericShader(false);
+
+       if(rsurface.texture && rsurface.texture->currentskinframe)
+               memcpy(c, rsurface.texture->currentskinframe->avgcolor, sizeof(c));
+       else
+       {
+               c[0] = 1;
+               c[1] = 0;
+               c[2] = 1;
+               c[3] = 1;
+       }
+
+       if (rsurface.texture->currentskinframe->pants || rsurface.texture->currentskinframe->shirt)
+       {
+               c[0] = 0.5 * (rsurface.colormap_pantscolor[0] * 0.3 + rsurface.colormap_shirtcolor[0] * 0.7);
+               c[1] = 0.5 * (rsurface.colormap_pantscolor[1] * 0.3 + rsurface.colormap_shirtcolor[1] * 0.7);
+               c[2] = 0.5 * (rsurface.colormap_pantscolor[2] * 0.3 + rsurface.colormap_shirtcolor[2] * 0.7);
+       }
+
+       // brighten it up (as texture value 127 means "unlit")
+       c[0] *= 2 * r_refdef.view.colorscale;
+       c[1] *= 2 * r_refdef.view.colorscale;
+       c[2] *= 2 * r_refdef.view.colorscale;
+
+       if(rsurface.texture->currentmaterialflags & MATERIALFLAG_WATERALPHA)
+               c[3] *= r_wateralpha.value;
+
+       if(rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHA && c[3] != 1)
+       {
+               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               GL_DepthMask(false);
+       }
+       else if(rsurface.texture->currentmaterialflags & MATERIALFLAG_ADD)
+       {
+               GL_BlendFunc(GL_ONE, GL_ONE);
+               GL_DepthMask(false);
+       }
+       else if(rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
+       {
+               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // can't do alpha test without texture, so let's blend instead
+               GL_DepthMask(false);
+       }
+       else if(rsurface.texture->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND)
+       {
+               GL_BlendFunc(rsurface.texture->customblendfunc[0], rsurface.texture->customblendfunc[1]);
+               GL_DepthMask(false);
+       }
+       else
+       {
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_DepthMask(writedepth);
+       }
+
+       rsurface.lightmapcolor4f = NULL;
+
+       if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
+       {
+               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+
+               rsurface.lightmapcolor4f = NULL;
+               rsurface.lightmapcolor4f_bufferobject = 0;
+               rsurface.lightmapcolor4f_bufferoffset = 0;
+       }
+       else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
+       {
+               qboolean applycolor = true;
+               float one = 1.0;
+
+               RSurf_PrepareVerticesForBatch(true, false, texturenumsurfaces, texturesurfacelist);
+
+               r_refdef.lightmapintensity = 1;
+               RSurf_DrawBatch_GL11_ApplyVertexShade(texturenumsurfaces, texturesurfacelist, &one, &one, &one, &one, &applycolor);
+               r_refdef.lightmapintensity = 0; // we're in showsurfaces, after all
+       }
+       else
+       {
+               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+
+               rsurface.lightmapcolor4f = rsurface.modellightmapcolor4f;
+               rsurface.lightmapcolor4f_bufferobject = rsurface.modellightmapcolor4f_bufferobject;
+               rsurface.lightmapcolor4f_bufferoffset = rsurface.modellightmapcolor4f_bufferoffset;
+       }
+
+       if(!rsurface.lightmapcolor4f)
+               RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(texturenumsurfaces, texturesurfacelist);
+
+       RSurf_DrawBatch_GL11_ApplyAmbient(texturenumsurfaces, texturesurfacelist);
+       RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, c[0], c[1], c[2], c[3]);
+       if(r_refdef.fogenabled)
+               RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(texturenumsurfaces, texturesurfacelist);
+
+       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
+       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+}
+
 static void R_DrawTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
 {
        CHECKGLERROR
        RSurf_SetupDepthAndCulling();
-       if (r_glsl.integer && gl_support_fragment_shader)
+       if (r_showsurfaces.integer == 3)
+               R_DrawTextureSurfaceList_ShowSurfaces3(texturenumsurfaces, texturesurfacelist, writedepth);
+       else if (r_glsl.integer && gl_support_fragment_shader)
                R_DrawTextureSurfaceList_GL20(texturenumsurfaces, texturesurfacelist, writedepth);
        else if (gl_combine.integer && r_textureunits.integer >= 2)
                R_DrawTextureSurfaceList_GL13(texturenumsurfaces, texturesurfacelist, writedepth);
@@ -6238,7 +6596,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
        // to a model, knowing that they are meaningless otherwise
        if (ent == r_refdef.scene.worldentity)
                RSurf_ActiveWorldEntity();
-       else if ((ent->effects & EF_FULLBRIGHT) || r_showsurfaces.integer || VectorLength2(ent->modellight_diffuse) < (1.0f / 256.0f))
+       else if ((ent->effects & EF_FULLBRIGHT) || (r_showsurfaces.integer && r_showsurfaces.integer != 3) || VectorLength2(ent->modellight_diffuse) < (1.0f / 256.0f))
                RSurf_ActiveModelEntity(ent, false, false);
        else
                RSurf_ActiveModelEntity(ent, true, r_glsl.integer && gl_support_fragment_shader);
@@ -6281,30 +6639,38 @@ static void R_ProcessTextureSurfaceList(int texturenumsurfaces, msurface_t **tex
                RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
                RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
        }
-       else if (r_showsurfaces.integer)
+       else if (r_showsurfaces.integer && !r_refdef.view.showdebug)
        {
                RSurf_SetupDepthAndCulling();
-               GL_DepthTest(true);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_AlphaTest(false);
+               R_Mesh_ColorPointer(NULL, 0, 0);
+               R_Mesh_ResetTextureState();
+               R_SetupGenericShader(false);
+               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
                GL_DepthMask(true);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_Color(0, 0, 0, 1);
+               GL_DepthTest(writedepth);
+               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+       }
+       else if (r_showsurfaces.integer && r_showsurfaces.integer != 3)
+       {
+               RSurf_SetupDepthAndCulling();
                GL_AlphaTest(false);
                R_Mesh_ColorPointer(NULL, 0, 0);
                R_Mesh_ResetTextureState();
                R_SetupGenericShader(false);
                RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               if (!r_refdef.view.showdebug)
-               {
-                       GL_Color(0, 0, 0, 1);
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
-               }
-               else
-                       RSurf_DrawBatch_ShowSurfaces(texturenumsurfaces, texturesurfacelist);
+               GL_DepthMask(true);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_DepthTest(true);
+               RSurf_DrawBatch_ShowSurfaces(texturenumsurfaces, texturesurfacelist);
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY)
                R_DrawTextureSurfaceList_Sky(texturenumsurfaces, texturesurfacelist);
        else if (!rsurface.texture->currentnumlayers)
                return;
-       else if ((rsurface.texture->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED) && queueentity)
+       else if (((rsurface.texture->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED) || (r_showsurfaces.integer == 3 && (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST))) && queueentity)
        {
                // transparent surfaces get pushed off into the transparent queue
                int surfacelistindex;
@@ -6376,7 +6742,7 @@ float locboxvertex3f[6*4*3] =
        1,0,0, 0,0,0, 0,1,0, 1,1,0
 };
 
-int locboxelement3i[6*2*3] =
+unsigned short locboxelements[6*2*3] =
 {
         0, 1, 2, 0, 2, 3,
         4, 5, 6, 4, 6, 7,
@@ -6427,7 +6793,7 @@ void R_DrawLoc_Callback(const entity_render_t *ent, const rtlight_t *rtlight, in
                for (j = 0;j < 3;j++, i++)
                        vertex3f[i] = mins[j] + size[j] * locboxvertex3f[i];
 
-       R_Mesh_Draw(0, 6*4, 6*2, locboxelement3i, 0, 0);
+       R_Mesh_Draw(0, 6*4, 0, 6*2, NULL, locboxelements, 0, 0);
 }
 
 void R_DrawLocs(void)
@@ -6449,7 +6815,7 @@ void R_DrawDebugModel(entity_render_t *ent)
        const int *elements;
        q3mbrush_t *brush;
        msurface_t *surface;
-       model_t *model = ent->model;
+       dp_model_t *model = ent->model;
        vec3_t v;
 
        flagsmask = MATERIALFLAG_SKY | MATERIALFLAG_WALL;
@@ -6471,7 +6837,7 @@ void R_DrawDebugModel(entity_render_t *ent)
                        {
                                R_Mesh_VertexPointer(brush->colbrushf->points->v, 0, 0);
                                GL_Color((i & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((i >> 5) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((i >> 10) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, r_showcollisionbrushes.value);
-                               R_Mesh_Draw(0, brush->colbrushf->numpoints, brush->colbrushf->numtriangles, brush->colbrushf->elements, 0, 0);
+                               R_Mesh_Draw(0, brush->colbrushf->numpoints, 0, brush->colbrushf->numtriangles, brush->colbrushf->elements, NULL, 0, 0);
                        }
                }
                for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
@@ -6480,7 +6846,7 @@ void R_DrawDebugModel(entity_render_t *ent)
                        {
                                R_Mesh_VertexPointer(surface->data_collisionvertex3f, 0, 0);
                                GL_Color((i & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((i >> 5) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((i >> 10) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, r_showcollisionbrushes.value);
-                               R_Mesh_Draw(0, surface->num_collisionvertices, surface->num_collisiontriangles, surface->data_collisionelement3i, 0, 0);
+                               R_Mesh_Draw(0, surface->num_collisionvertices, 0, surface->num_collisiontriangles, surface->data_collisionelement3i, NULL, 0, 0);
                        }
                }
        }
@@ -6574,20 +6940,32 @@ void R_DrawDebugModel(entity_render_t *ent)
 }
 
 extern void R_BuildLightMap(const entity_render_t *ent, msurface_t *surface);
+int r_maxsurfacelist = 0;
+msurface_t **r_surfacelist = NULL;
 void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean addwaterplanes, qboolean debug)
 {
        int i, j, endj, f, flagsmask;
-       msurface_t *surface;
        texture_t *t;
-       model_t *model = r_refdef.scene.worldmodel;
-       const int maxsurfacelist = 1024;
+       dp_model_t *model = r_refdef.scene.worldmodel;
+       msurface_t *surfaces;
+       unsigned char *update;
        int numsurfacelist = 0;
-       msurface_t *surfacelist[1024];
        if (model == NULL)
                return;
 
+       if (r_maxsurfacelist < model->num_surfaces)
+       {
+               r_maxsurfacelist = model->num_surfaces;
+               if (r_surfacelist)
+                       Mem_Free(r_surfacelist);
+               r_surfacelist = (msurface_t **) Mem_Alloc(r_main_mempool, r_maxsurfacelist * sizeof(*r_surfacelist));
+       }
+
        RSurf_ActiveWorldEntity();
 
+       surfaces = model->data_surfaces;
+       update = model->brushq1.lightmapupdateflags;
+
        // update light styles on this submodel
        if (!skysurfaces && !depthonly && !addwaterplanes && model->brushq1.num_lightstyles && r_refdef.lightmapintensity > 0)
        {
@@ -6596,11 +6974,10 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
                {
                        if (style->value != r_refdef.scene.lightstylevalue[style->style])
                        {
-                               msurface_t *surfaces = model->data_surfaces;
                                int *list = style->surfacelist;
                                style->value = r_refdef.scene.lightstylevalue[style->style];
                                for (j = 0;j < style->numsurfaces;j++)
-                                       surfaces[list[j]].cached_dlight = true;
+                                       update[list[j]] = true;
                        }
                }
        }
@@ -6620,64 +6997,73 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
        rsurface.texture = NULL;
        rsurface.rtlight = NULL;
        numsurfacelist = 0;
+       // add visible surfaces to draw list
        j = model->firstmodelsurface;
        endj = j + model->nummodelsurfaces;
-       while (j < endj)
+       if (update)
        {
-               // quickly skip over non-visible surfaces
-               for (;j < endj && !r_refdef.viewcache.world_surfacevisible[j];j++)
-                       ;
-               // quickly iterate over visible surfaces
-               for (;j < endj && r_refdef.viewcache.world_surfacevisible[j];j++)
+               for (;j < endj;j++)
                {
-                       // process this surface
-                       surface = model->data_surfaces + j;
-                       // if this surface fits the criteria, add it to the list
-                       if (surface->num_triangles)
+                       if (r_refdef.viewcache.world_surfacevisible[j])
                        {
-                               // if lightmap parameters changed, rebuild lightmap texture
-                               if (surface->cached_dlight)
-                                       R_BuildLightMap(r_refdef.scene.worldentity, surface);
-                               // add face to draw list
-                               surfacelist[numsurfacelist++] = surface;
-                               r_refdef.stats.world_triangles += surface->num_triangles;
-                               if (numsurfacelist >= maxsurfacelist)
-                               {
-                                       r_refdef.stats.world_surfaces += numsurfacelist;
-                                       R_QueueSurfaceList(r_refdef.scene.worldentity, numsurfacelist, surfacelist, flagsmask, writedepth, depthonly, addwaterplanes);
-                                       numsurfacelist = 0;
-                               }
+                               r_surfacelist[numsurfacelist++] = surfaces + j;
+                               // update lightmap if needed
+                               if (update[j])
+                                       R_BuildLightMap(r_refdef.scene.worldentity, surfaces + j);
                        }
                }
        }
-       r_refdef.stats.world_surfaces += numsurfacelist;
-       if (numsurfacelist)
-               R_QueueSurfaceList(r_refdef.scene.worldentity, numsurfacelist, surfacelist, flagsmask, writedepth, depthonly, addwaterplanes);
+       else
+               for (;j < endj;j++)
+                       if (r_refdef.viewcache.world_surfacevisible[j])
+                               r_surfacelist[numsurfacelist++] = surfaces + j;
+       // don't do anything if there were no surfaces
+       if (!numsurfacelist)
+               return;
+       R_QueueSurfaceList(r_refdef.scene.worldentity, numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly, addwaterplanes);
        GL_AlphaTest(false);
+
+       // add to stats if desired
+       if (r_speeds.integer && !skysurfaces && !depthonly && !addwaterplanes)
+       {
+               r_refdef.stats.world_surfaces += numsurfacelist;
+               for (j = 0;j < numsurfacelist;j++)
+                       r_refdef.stats.world_triangles += r_surfacelist[j]->num_triangles;
+       }
 }
 
 void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean addwaterplanes, qboolean debug)
 {
-       int i, j, f, flagsmask;
-       msurface_t *surface, *endsurface;
+       int i, j, endj, f, flagsmask;
        texture_t *t;
-       model_t *model = ent->model;
-       const int maxsurfacelist = 1024;
+       dp_model_t *model = ent->model;
+       msurface_t *surfaces;
+       unsigned char *update;
        int numsurfacelist = 0;
-       msurface_t *surfacelist[1024];
        if (model == NULL)
                return;
 
+       if (r_maxsurfacelist < model->num_surfaces)
+       {
+               r_maxsurfacelist = model->num_surfaces;
+               if (r_surfacelist)
+                       Mem_Free(r_surfacelist);
+               r_surfacelist = (msurface_t **) Mem_Alloc(r_main_mempool, r_maxsurfacelist * sizeof(*r_surfacelist));
+       }
+
        // if the model is static it doesn't matter what value we give for
        // wantnormals and wanttangents, so this logic uses only rules applicable
        // to a model, knowing that they are meaningless otherwise
        if (ent == r_refdef.scene.worldentity)
                RSurf_ActiveWorldEntity();
-       else if ((ent->effects & EF_FULLBRIGHT) || r_showsurfaces.integer || VectorLength2(ent->modellight_diffuse) < (1.0f / 256.0f))
+       else if ((ent->effects & EF_FULLBRIGHT) || (r_showsurfaces.integer && r_showsurfaces.integer != 3) || VectorLength2(ent->modellight_diffuse) < (1.0f / 256.0f))
                RSurf_ActiveModelEntity(ent, false, false);
        else
                RSurf_ActiveModelEntity(ent, true, r_glsl.integer && gl_support_fragment_shader && !depthonly);
 
+       surfaces = model->data_surfaces;
+       update = model->brushq1.lightmapupdateflags;
+
        // update light styles
        if (!skysurfaces && !depthonly && !addwaterplanes && model->brushq1.num_lightstyles && r_refdef.lightmapintensity > 0)
        {
@@ -6686,11 +7072,10 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                {
                        if (style->value != r_refdef.scene.lightstylevalue[style->style])
                        {
-                               msurface_t *surfaces = model->data_surfaces;
                                int *list = style->surfacelist;
                                style->value = r_refdef.scene.lightstylevalue[style->style];
                                for (j = 0;j < style->numsurfaces;j++)
-                                       surfaces[list[j]].cached_dlight = true;
+                                       update[list[j]] = true;
                        }
                }
        }
@@ -6710,29 +7095,28 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
        rsurface.texture = NULL;
        rsurface.rtlight = NULL;
        numsurfacelist = 0;
-       surface = model->data_surfaces + model->firstmodelsurface;
-       endsurface = surface + model->nummodelsurfaces;
-       for (;surface < endsurface;surface++)
+       // add visible surfaces to draw list
+       j = model->firstmodelsurface;
+       endj = j + model->nummodelsurfaces;
+       for (;j < endj;j++)
+               r_surfacelist[numsurfacelist++] = surfaces + j;
+       // don't do anything if there were no surfaces
+       if (!numsurfacelist)
+               return;
+       // update lightmaps if needed
+       if (update)
+               for (j = model->firstmodelsurface;j < endj;j++)
+                       if (update[j])
+                               R_BuildLightMap(ent, surfaces + j);
+       R_QueueSurfaceList(ent, numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly, addwaterplanes);
+       GL_AlphaTest(false);
+
+       // add to stats if desired
+       if (r_speeds.integer && !skysurfaces && !depthonly && !addwaterplanes)
        {
-               // if this surface fits the criteria, add it to the list
-               if (surface->num_triangles)
-               {
-                       // if lightmap parameters changed, rebuild lightmap texture
-                       if (surface->cached_dlight)
-                               R_BuildLightMap(ent, surface);
-                       // add face to draw list
-                       surfacelist[numsurfacelist++] = surface;
-                       r_refdef.stats.entities_triangles += surface->num_triangles;
-                       if (numsurfacelist >= maxsurfacelist)
-                       {
-                               r_refdef.stats.entities_surfaces += numsurfacelist;
-                               R_QueueSurfaceList(ent, numsurfacelist, surfacelist, flagsmask, writedepth, depthonly, addwaterplanes);
-                               numsurfacelist = 0;
-                       }
-               }
+               r_refdef.stats.entities++;
+               r_refdef.stats.entities_surfaces += numsurfacelist;
+               for (j = 0;j < numsurfacelist;j++)
+                       r_refdef.stats.entities_triangles += r_surfacelist[j]->num_triangles;
        }
-       r_refdef.stats.entities_surfaces += numsurfacelist;
-       if (numsurfacelist)
-               R_QueueSurfaceList(ent, numsurfacelist, surfacelist, flagsmask, writedepth, depthonly, addwaterplanes);
-       GL_AlphaTest(false);
 }