]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
removed a wasted normalize in fragment shader for model lighting
[xonotic/darkplaces.git] / gl_rmain.c
index 1baaa5b31167f334d6d66b987ea8c3666ecd5278..41315e4d394fe4b0f224feb1ee59855ed68fce79 100644 (file)
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 #include "r_shadow.h"
 #include "polygon.h"
+#include "image.h"
 
 mempool_t *r_main_mempool;
 rtexturepool_t *r_main_texturepool;
@@ -33,6 +34,7 @@ r_refdef_t r_refdef;
 r_view_t r_view;
 r_viewcache_t r_viewcache;
 
+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_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%)"};
 cvar_t r_showsurfaces = {0, "r_showsurfaces", "0", "1 shows surfaces as different colors, or a value of 2 shows triangle draw order (for analyzing whether meshes are optimized for vertex cache)"};
@@ -148,6 +150,9 @@ r_glsl_permutation_t r_glsl_permutations[SHADERPERMUTATION_MAX];
 // currently selected permutation
 r_glsl_permutation_t *r_glsl_permutation;
 
+char r_qwskincache[MAX_SCOREBOARD][MAX_QPATH];
+skinframe_t *r_qwskincache_skinframe[MAX_SCOREBOARD];
+
 // vertex coordinates for a quad that covers the screen exactly
 const static float r_screenvertex3f[12] =
 {
@@ -591,7 +596,7 @@ static const char *builtinshaderstring =
 "\n"
 "      // get the surface normal and light normal\n"
 "      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - myhvec3(0.5));\n"
-"      myhvec3 diffusenormal = myhvec3(normalize(LightVector));\n"
+"      myhvec3 diffusenormal = myhvec3(LightVector);\n"
 "\n"
 "      // calculate directional shading\n"
 "      color.rgb *= AmbientColor + DiffuseColor * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0));\n"
@@ -881,10 +886,10 @@ int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, fl
                // lightmapped wall
                shaderfilename = "glsl/default.glsl";
                permutation = SHADERPERMUTATION_USES_VERTEXSHADER | SHADERPERMUTATION_USES_FRAGMENTSHADER;
-               if (r_glsl_deluxemapping.integer >= 1 && rsurface_uselightmaptexture && r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping)
+               if (r_glsl_deluxemapping.integer >= 1 && rsurface_lightmaptexture && r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping)
                {
                        // deluxemapping (light direction texture)
-                       if (rsurface_uselightmaptexture && r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping && r_refdef.worldmodel->brushq3.deluxemapping_modelspace)
+                       if (rsurface_lightmaptexture && r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping && r_refdef.worldmodel->brushq3.deluxemapping_modelspace)
                                permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE;
                        else
                                permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
@@ -1034,6 +1039,332 @@ void R_SwitchSurfaceShader(int permutation)
        }
 }
 
+#define SKINFRAME_HASH 1024
+
+struct
+{
+       int loadsequence; // incremented each level change
+       memexpandablearray_t array;
+       skinframe_t *hash[SKINFRAME_HASH];
+}
+r_skinframe;
+
+void R_SkinFrame_PrepareForPurge(void)
+{
+       r_skinframe.loadsequence++;
+       // wrap it without hitting zero
+       if (r_skinframe.loadsequence >= 200)
+               r_skinframe.loadsequence = 1;
+}
+
+void R_SkinFrame_MarkUsed(skinframe_t *skinframe)
+{
+       if (!skinframe)
+               return;
+       // mark the skinframe as used for the purging code
+       skinframe->loadsequence = r_skinframe.loadsequence;
+}
+
+void R_SkinFrame_Purge(void)
+{
+       int i;
+       skinframe_t *s;
+       for (i = 0;i < SKINFRAME_HASH;i++)
+       {
+               for (s = r_skinframe.hash[i];s;s = s->next)
+               {
+                       if (s->loadsequence && s->loadsequence != r_skinframe.loadsequence)
+                       {
+                               if (s->base == r_texture_notexture)     s->base   = NULL;
+                               if (s->nmap == r_texture_blanknormalmap)s->nmap   = NULL;
+                               if (s->merged == s->base)               s->merged = NULL;
+                               if (s->stain ) R_FreeTexture(s->stain );s->stain  = NULL;
+                               if (s->merged) R_FreeTexture(s->merged);s->merged = NULL;
+                               if (s->base  ) R_FreeTexture(s->base  );s->base   = NULL;
+                               if (s->pants ) R_FreeTexture(s->pants );s->pants  = NULL;
+                               if (s->shirt ) R_FreeTexture(s->shirt );s->shirt  = NULL;
+                               if (s->nmap  ) R_FreeTexture(s->nmap  );s->nmap   = NULL;
+                               if (s->gloss ) R_FreeTexture(s->gloss );s->gloss  = NULL;
+                               if (s->glow  ) R_FreeTexture(s->glow  );s->glow   = NULL;
+                               if (s->fog   ) R_FreeTexture(s->fog   );s->fog    = NULL;
+                               s->loadsequence = 0;
+                       }
+               }
+       }
+}
+
+skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewidth, int compareheight, int comparecrc, qboolean add)
+{
+       skinframe_t *item;
+       int hashindex;
+       char basename[MAX_QPATH];
+
+       Image_StripImageExtension(name, basename, sizeof(basename));
+
+       hashindex = CRC_Block((unsigned char *)basename, strlen(basename)) & (SKINFRAME_HASH - 1);
+       for (item = r_skinframe.hash[hashindex];item;item = item->next)
+               if (!strcmp(item->basename, basename) && item->textureflags == textureflags && item->comparewidth == comparewidth && item->compareheight == compareheight && item->comparecrc == comparecrc)
+                       break;
+       if (!item)
+       {
+               if (!add)
+                       return NULL;
+               item = (skinframe_t *)Mem_ExpandableArray_AllocRecord(&r_skinframe.array);
+               memset(item, 0, sizeof(*item));
+               strlcpy(item->basename, basename, sizeof(item->basename));
+               item->textureflags = textureflags;
+               item->comparewidth = comparewidth;
+               item->compareheight = compareheight;
+               item->comparecrc = comparecrc;
+               item->next = r_skinframe.hash[hashindex];
+               r_skinframe.hash[hashindex] = item;
+       }
+       R_SkinFrame_MarkUsed(item);
+       return item;
+}
+
+skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboolean complain)
+{
+       // FIXME: it should be possible to disable loading various layers using
+       // cvars, to prevent wasted loading time and memory usage if the user does
+       // not want them
+       qboolean loadnormalmap = true;
+       qboolean loadgloss = true;
+       qboolean loadpantsandshirt = true;
+       qboolean loadglow = true;
+       int j;
+       unsigned char *pixels;
+       unsigned char *bumppixels;
+       unsigned char *basepixels = NULL;
+       int basepixels_width;
+       int basepixels_height;
+       skinframe_t *skinframe;
+
+       if (cls.state == ca_dedicated)
+               return NULL;
+
+       // return an existing skinframe if already loaded
+       // if loading of the first image fails, don't make a new skinframe as it
+       // would cause all future lookups of this to be missing
+       skinframe = R_SkinFrame_Find(name, textureflags, 0, 0, 0, false);
+       if (skinframe && skinframe->base)
+               return skinframe;
+
+       basepixels = loadimagepixels(name, complain, 0, 0);
+       if (basepixels == NULL)
+               return NULL;
+
+       // we've got some pixels to store, so really allocate this new texture now
+       if (!skinframe)
+               skinframe = R_SkinFrame_Find(name, textureflags, 0, 0, 0, true);
+       skinframe->stain = NULL;
+       skinframe->merged = NULL;
+       skinframe->base = r_texture_notexture;
+       skinframe->pants = NULL;
+       skinframe->shirt = NULL;
+       skinframe->nmap = r_texture_blanknormalmap;
+       skinframe->gloss = NULL;
+       skinframe->glow = NULL;
+       skinframe->fog = NULL;
+
+       basepixels_width = image_width;
+       basepixels_height = image_height;
+       skinframe->base = R_LoadTexture2D (r_main_texturepool, skinframe->basename, basepixels_width, basepixels_height, basepixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);
+
+       if (textureflags & TEXF_ALPHA)
+       {
+               for (j = 3;j < basepixels_width * basepixels_height * 4;j += 4)
+                       if (basepixels[j] < 255)
+                               break;
+               if (j < basepixels_width * basepixels_height * 4)
+               {
+                       // has transparent pixels
+                       pixels = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4);
+                       for (j = 0;j < image_width * image_height * 4;j += 4)
+                       {
+                               pixels[j+0] = 255;
+                               pixels[j+1] = 255;
+                               pixels[j+2] = 255;
+                               pixels[j+3] = basepixels[j+3];
+                       }
+                       skinframe->fog = R_LoadTexture2D (r_main_texturepool, va("%s_mask", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);
+                       Mem_Free(pixels);
+               }
+       }
+
+       // _norm is the name used by tenebrae and has been adopted as standard
+       if (loadnormalmap)
+       {
+               if ((pixels = loadimagepixels(va("%s_norm", skinframe->basename), false, 0, 0)) != NULL)
+               {
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);
+                       Mem_Free(pixels);
+                       pixels = NULL;
+               }
+               else if (r_shadow_bumpscale_bumpmap.value > 0 && (bumppixels = loadimagepixels(va("%s_bump", skinframe->basename), false, 0, 0)) != NULL)
+               {
+                       pixels = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4);
+                       Image_HeightmapToNormalmap(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_RGBA, skinframe->textureflags, NULL);
+                       Mem_Free(pixels);
+                       Mem_Free(bumppixels);
+               }
+               else if (r_shadow_bumpscale_basetexture.value > 0)
+               {
+                       pixels = (unsigned char *)Mem_Alloc(tempmempool, basepixels_width * basepixels_height * 4);
+                       Image_HeightmapToNormalmap(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_RGBA, skinframe->textureflags, NULL);
+                       Mem_Free(pixels);
+               }
+       }
+       // _luma is supported for tenebrae compatibility
+       // (I think it's a very stupid name, but oh well)
+       // _glow is the preferred name
+       if (loadglow          && ((pixels = loadimagepixels(va("%s_glow", skinframe->basename), false, 0, 0)) != NULL || (pixels = loadimagepixels(va("%s_luma", skinframe->basename), false, 0, 0)) != NULL)) {skinframe->glow = R_LoadTexture2D (r_main_texturepool, va("%s_glow", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
+       if (loadgloss         && (pixels = loadimagepixels(va("%s_gloss", skinframe->basename), false, 0, 0)) != NULL) {skinframe->gloss = R_LoadTexture2D (r_main_texturepool, va("%s_gloss", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
+       if (loadpantsandshirt && (pixels = loadimagepixels(va("%s_pants", skinframe->basename), false, 0, 0)) != NULL) {skinframe->pants = R_LoadTexture2D (r_main_texturepool, va("%s_pants", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
+       if (loadpantsandshirt && (pixels = loadimagepixels(va("%s_shirt", skinframe->basename), false, 0, 0)) != NULL) {skinframe->shirt = R_LoadTexture2D (r_main_texturepool, va("%s_shirt", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
+
+       if (basepixels)
+               Mem_Free(basepixels);
+
+       return skinframe;
+}
+
+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;
+       if (!force)
+       {
+               for (i = 0;i < width*height;i++)
+                       if (((unsigned char *)&palette[in[i]])[3] > 0)
+                               break;
+               if (i == width*height)
+                       return NULL;
+       }
+       return R_LoadTexture2D (r_main_texturepool, name, width, height, in, TEXTYPE_PALETTE, textureflags, palette);
+}
+
+skinframe_t *R_SkinFrame_LoadInternal(const char *name, int textureflags, int loadpantsandshirt, int loadglowtexture, const unsigned char *skindata, int width, int height, int bitsperpixel, const unsigned int *palette, const unsigned int *alphapalette)
+{
+       int i;
+       unsigned char *temp1, *temp2;
+       skinframe_t *skinframe;
+
+       if (cls.state == ca_dedicated)
+               return NULL;
+
+       // if already loaded just return it, otherwise make a new skinframe
+       skinframe = R_SkinFrame_Find(name, textureflags, width, height, skindata ? CRC_Block(skindata, width*height*bitsperpixel/8) : 0, true);
+       if (skinframe && skinframe->base)
+               return skinframe;
+
+       skinframe->stain = NULL;
+       skinframe->merged = NULL;
+       skinframe->base = r_texture_notexture;
+       skinframe->pants = NULL;
+       skinframe->shirt = NULL;
+       skinframe->nmap = r_texture_blanknormalmap;
+       skinframe->gloss = NULL;
+       skinframe->glow = NULL;
+       skinframe->fog = NULL;
+
+       // if no data was provided, then clearly the caller wanted to get a blank skinframe
+       if (!skindata)
+               return NULL;
+
+       if (bitsperpixel == 32)
+       {
+               if (r_shadow_bumpscale_basetexture.value > 0)
+               {
+                       temp1 = (unsigned char *)Mem_Alloc(tempmempool, width * height * 8);
+                       temp2 = temp1 + width * height * 4;
+                       Image_HeightmapToNormalmap(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
+                       skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_RGBA, textureflags | TEXF_ALPHA, NULL);
+                       Mem_Free(temp1);
+               }
+               skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_RGBA, textureflags, NULL);
+               if (textureflags & TEXF_ALPHA)
+               {
+                       for (i = 3;i < width * height * 4;i += 4)
+                               if (skindata[i] < 255)
+                                       break;
+                       if (i < width * height * 4)
+                       {
+                               unsigned char *fogpixels = (unsigned char *)Mem_Alloc(tempmempool, width * height * 4);
+                               memcpy(fogpixels, skindata, width * height * 4);
+                               for (i = 0;i < width * height * 4;i += 4)
+                                       fogpixels[i] = fogpixels[i+1] = fogpixels[i+2] = 255;
+                               skinframe->fog = R_LoadTexture2D(r_main_texturepool, va("%s_fog", skinframe->basename), width, height, fogpixels, TEXTYPE_RGBA, textureflags, NULL);
+                               Mem_Free(fogpixels);
+                       }
+               }
+       }
+       else if (bitsperpixel == 8)
+       {
+               if (r_shadow_bumpscale_basetexture.value > 0)
+               {
+                       temp1 = (unsigned char *)Mem_Alloc(tempmempool, width * height * 8);
+                       temp2 = temp1 + width * height * 4;
+                       if (bitsperpixel == 32)
+                               Image_HeightmapToNormalmap(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
+                       else
+                       {
+                               // use either a custom palette or the quake palette
+                               Image_Copy8bitRGBA(skindata, temp1, width * height, palette ? palette : palette_complete);
+                               Image_HeightmapToNormalmap(temp1, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
+                       }
+                       skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_RGBA, textureflags | TEXF_ALPHA, NULL);
+                       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), palette ? palette : (loadglowtexture ? palette_nofullbrights : ((textureflags & TEXF_ALPHA) ? palette_transparent : palette_complete)), textureflags, true); // all
+               if (!palette && loadglowtexture)
+                       skinframe->glow = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_glow", skinframe->basename), palette_onlyfullbrights, textureflags, false); // glow
+               if (!palette && loadpantsandshirt)
+               {
+                       skinframe->pants = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_pants", skinframe->basename), palette_pantsaswhite, textureflags, false); // pants
+                       skinframe->shirt = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_shirt", skinframe->basename), palette_shirtaswhite, textureflags, false); // shirt
+               }
+               if (skinframe->pants || skinframe->shirt)
+                       skinframe->base = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_nospecial", skinframe->basename),loadglowtexture ? palette_nocolormapnofullbrights : palette_nocolormap, textureflags, false); // no special colors
+               if (textureflags & TEXF_ALPHA)
+               {
+                       // if not using a custom alphapalette, use the quake one
+                       if (!alphapalette)
+                               alphapalette = palette_alpha;
+                       for (i = 0;i < width * height;i++)
+                               if (((unsigned char *)alphapalette)[skindata[i]*4+3] < 255)
+                                       break;
+                       if (i < width * height)
+                               skinframe->fog = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_fog", skinframe->basename), alphapalette, textureflags, true); // fog mask
+               }
+       }
+
+       return skinframe;
+}
+
+skinframe_t *R_SkinFrame_LoadMissing(void)
+{
+       skinframe_t *skinframe;
+
+       if (cls.state == ca_dedicated)
+               return NULL;
+
+       skinframe = R_SkinFrame_Find("missing", TEXF_PRECACHE, 0, 0, 0, true);
+       skinframe->stain = NULL;
+       skinframe->merged = NULL;
+       skinframe->base = r_texture_notexture;
+       skinframe->pants = NULL;
+       skinframe->shirt = NULL;
+       skinframe->nmap = r_texture_blanknormalmap;
+       skinframe->gloss = NULL;
+       skinframe->glow = NULL;
+       skinframe->fog = NULL;
+
+       return skinframe;
+}
+
 void gl_main_start(void)
 {
        int x;
@@ -1048,6 +1379,14 @@ void gl_main_start(void)
                r_refdef.fogmasktable[x] = bound(0, alpha, 1);
        }
 
+       memset(r_qwskincache, 0, sizeof(r_qwskincache));
+       memset(r_qwskincache_skinframe, 0, sizeof(r_qwskincache_skinframe));
+
+       // set up r_skinframe loading system for textures
+       memset(&r_skinframe, 0, sizeof(r_skinframe));
+       r_skinframe.loadsequence = 1;
+       Mem_ExpandableArray_NewArray(&r_skinframe.array, r_main_mempool, sizeof(skinframe_t), 256);
+
        r_main_texturepool = R_AllocTexturePool();
        R_BuildBlankTextures();
        R_BuildNoTexture();
@@ -1064,6 +1403,13 @@ void gl_main_start(void)
 
 void gl_main_shutdown(void)
 {
+       memset(r_qwskincache, 0, sizeof(r_qwskincache));
+       memset(r_qwskincache_skinframe, 0, sizeof(r_qwskincache_skinframe));
+
+       // clear out the r_skinframe state
+       Mem_ExpandableArray_FreeArray(&r_skinframe.array);
+       memset(&r_skinframe, 0, sizeof(r_skinframe));
+
        if (r_svbsp.nodes)
                Mem_Free(r_svbsp.nodes);
        memset(&r_svbsp, 0, sizeof (r_svbsp));
@@ -1118,6 +1464,7 @@ void GL_Main_Init(void)
                Cvar_RegisterVariable (&gl_fogstart);
                Cvar_RegisterVariable (&gl_fogend);
        }
+       Cvar_RegisterVariable(&r_depthfirst);
        Cvar_RegisterVariable(&r_nearclip);
        Cvar_RegisterVariable(&r_showbboxes);
        Cvar_RegisterVariable(&r_showsurfaces);
@@ -1339,7 +1686,14 @@ static void R_UpdateEntityLighting(entity_render_t *ent)
 
        // move the light direction into modelspace coordinates for lighting code
        Matrix4x4_Transform3x3(&ent->inversematrix, tempdiffusenormal, ent->modellight_lightdir);
-       VectorNormalize(ent->modellight_lightdir);
+       if(VectorLength2(ent->modellight_lightdir) > 0)
+       {
+               VectorNormalize(ent->modellight_lightdir);
+       }
+       else
+       {
+               VectorSet(ent->modellight_lightdir, 0, 0, 1); // have to set SOME valid vector here
+       }
 
        // scale ambient and directional light contributions according to rendering variables
        ent->modellight_ambient[0] *= ent->colormod[0] * r_refdef.lightmapintensity;
@@ -1442,6 +1796,25 @@ void R_DrawModels(void)
        }
 }
 
+void R_DrawModelsDepth(void)
+{
+       int i;
+       entity_render_t *ent;
+
+       if (!r_drawentities.integer)
+               return;
+
+       for (i = 0;i < r_refdef.numentities;i++)
+       {
+               if (!r_viewcache.entityvisible[i])
+                       continue;
+               ent = r_refdef.entities[i];
+               r_refdef.stats.entities++;
+               if (ent->model && ent->model->DrawDepth != NULL)
+                       ent->model->DrawDepth(ent);
+       }
+}
+
 static void R_View_SetFrustum(void)
 {
        double slopex, slopey;
@@ -2218,13 +2591,26 @@ void R_RenderScene(void)
 
                if (R_DrawBrushModelsSky() && r_timereport_active)
                        R_TimeReport("bmodelsky");
+       }
 
-               if (r_refdef.worldmodel && r_refdef.worldmodel->Draw)
-               {
-                       r_refdef.worldmodel->Draw(r_refdef.worldentity);
-                       if (r_timereport_active)
-                               R_TimeReport("world");
-               }
+       if (r_depthfirst.integer >= 1 && cl.csqc_vidvars.drawworld && r_refdef.worldmodel && r_refdef.worldmodel->DrawDepth)
+       {
+               r_refdef.worldmodel->DrawDepth(r_refdef.worldentity);
+               if (r_timereport_active)
+                       R_TimeReport("worlddepth");
+       }
+       if (r_depthfirst.integer >= 2)
+       {
+               R_DrawModelsDepth();
+               if (r_timereport_active)
+                       R_TimeReport("modeldepth");
+       }
+
+       if (cl.csqc_vidvars.drawworld && r_refdef.worldmodel && r_refdef.worldmodel->Draw)
+       {
+               r_refdef.worldmodel->Draw(r_refdef.worldentity);
+               if (r_timereport_active)
+                       R_TimeReport("world");
        }
 
        // don't let sound skip if going slow
@@ -2740,6 +3126,7 @@ static void R_Texture_AddLayer(texture_t *t, qboolean depthmask, int blendfunc1,
 
 void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
 {
+       int i;
        model_t *model = ent->model;
 
        // switch to an alternate material if this is a q1bsp animated material
@@ -2769,11 +3156,23 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
                texture->currentframe = t;
        }
 
-       // pick a new currentskinframe if the material is animated
-       if (t->numskinframes >= 2)
-               t->currentskinframe = t->skinframes + ((int)(t->skinframerate * (cl.time - ent->frame2time)) % t->numskinframes);
+       // update currentskinframe to be a qw skin or animation frame
+       if ((i = ent->entitynumber - 1) >= 0 && i < cl.maxclients)
+       {
+               if (strcmp(r_qwskincache[i], cl.scores[i].qw_skin))
+               {
+                       strlcpy(r_qwskincache[i], cl.scores[i].qw_skin, sizeof(r_qwskincache[i]));
+                       Con_DPrintf("loading skins/%s\n", r_qwskincache[i]);
+                       r_qwskincache_skinframe[i] = R_SkinFrame_LoadExternal(va("skins/%s", r_qwskincache[i]), TEXF_PRECACHE | (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_PICMIP, developer.integer > 0);
+               }
+               t->currentskinframe = r_qwskincache_skinframe[i];
+               if (t->currentskinframe == NULL)
+                       t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->frame2time)) % t->numskinframes];
+       }
+       else if (t->numskinframes >= 2)
+               t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->frame2time)) % t->numskinframes];
        if (t->backgroundnumskinframes >= 2)
-               t->backgroundcurrentskinframe = t->backgroundskinframes + ((int)(t->backgroundskinframerate * (cl.time - ent->frame2time)) % t->backgroundnumskinframes);
+               t->backgroundcurrentskinframe = t->backgroundskinframes[(int)(t->backgroundskinframerate * (cl.time - ent->frame2time)) % t->backgroundnumskinframes];
 
        t->currentmaterialflags = t->basematerialflags;
        t->currentalpha = ent->alpha;
@@ -2918,7 +3317,7 @@ void R_UpdateAllTextureInfo(entity_render_t *ent)
 {
        int i;
        if (ent->model)
-               for (i = 0;i < ent->model->num_textures;i++)
+               for (i = 0;i < ent->model->num_texturesperskin;i++)
                        R_UpdateTextureInfo(ent, ent->model->data_textures + i);
 }
 
@@ -2987,7 +3386,8 @@ qboolean rsurface_generatedvertex;
 const entity_render_t *rsurface_entity;
 const model_t *rsurface_model;
 texture_t *rsurface_texture;
-qboolean rsurface_uselightmaptexture;
+rtexture_t *rsurface_lightmaptexture;
+rtexture_t *rsurface_deluxemaptexture;
 rsurfmode_t rsurface_mode;
 int rsurface_lightmode; // 0 = lightmap or fullbright, 1 = color array from q3bsp, 2 = vertex shaded model
 
@@ -3000,7 +3400,8 @@ void RSurf_CleanUp(void)
        }
        GL_AlphaTest(false);
        rsurface_mode = RSURFMODE_NONE;
-       rsurface_uselightmaptexture = false;
+       rsurface_lightmaptexture = NULL;
+       rsurface_deluxemaptexture = NULL;
        rsurface_texture = NULL;
 }
 
@@ -3290,112 +3691,6 @@ void RSurf_DrawBatch_Simple(int texturenumsurfaces, msurface_t **texturesurfacel
        }
 }
 
-static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, msurface_t **texturesurfacelist, int lightmaptexunit, int deluxemaptexunit)
-{
-       int i;
-       int j;
-       const msurface_t *surface = texturesurfacelist[0];
-       const msurface_t *surface2;
-       int firstvertex;
-       int endvertex;
-       int numvertices;
-       int numtriangles;
-       // TODO: lock all array ranges before render, rather than on each surface
-       if (texturenumsurfaces == 1)
-       {
-               R_Mesh_TexBind(lightmaptexunit, R_GetTexture(surface->lightmaptexture));
-               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_model->surfmesh.data_element3i + 3 * surface->num_firsttriangle), rsurface_model->surfmesh.ebo, (sizeof(int[3]) * surface->num_firsttriangle));
-       }
-       else if (r_batchmode.integer == 2)
-       {
-               #define MAXBATCHTRIANGLES 4096
-               int batchtriangles = 0;
-               int batchelements[MAXBATCHTRIANGLES*3];
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       R_Mesh_TexBind(lightmaptexunit, R_GetTexture(surface->lightmaptexture));
-                       if (deluxemaptexunit >= 0)
-                               R_Mesh_TexBind(deluxemaptexunit, R_GetTexture(surface->deluxemaptexture));
-                       j = i + 1;
-                       if (surface->num_triangles > MAXBATCHTRIANGLES)
-                       {
-                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface_model->surfmesh.data_element3i + 3 * surface->num_firsttriangle), rsurface_model->surfmesh.ebo, (sizeof(int[3]) * surface->num_firsttriangle));
-                               continue;
-                       }
-                       memcpy(batchelements, rsurface_model->surfmesh.data_element3i + 3 * surface->num_firsttriangle, surface->num_triangles * sizeof(int[3]));
-                       batchtriangles = surface->num_triangles;
-                       firstvertex = surface->num_firstvertex;
-                       endvertex = surface->num_firstvertex + surface->num_vertices;
-                       for (;j < texturenumsurfaces;j++)
-                       {
-                               surface2 = texturesurfacelist[j];
-                               if (surface2->lightmaptexture != surface->lightmaptexture || batchtriangles + surface2->num_triangles > MAXBATCHTRIANGLES)
-                                       break;
-                               memcpy(batchelements + batchtriangles * 3, rsurface_model->surfmesh.data_element3i + 3 * surface2->num_firsttriangle, surface2->num_triangles * sizeof(int[3]));
-                               batchtriangles += surface2->num_triangles;
-                               firstvertex = min(firstvertex, surface2->num_firstvertex);
-                               endvertex = max(endvertex, surface2->num_firstvertex + surface2->num_vertices);
-                       }
-                       surface2 = texturesurfacelist[j-1];
-                       numvertices = endvertex - firstvertex;
-                       R_Mesh_Draw(firstvertex, numvertices, batchtriangles, batchelements, 0, 0);
-               }
-       }
-       else if (r_batchmode.integer == 1)
-       {
-#if 0
-               Con_Printf("%s batch sizes ignoring lightmap:", rsurface_texture->name);
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       for (j = i + 1, surface2 = surface + 1;j < texturenumsurfaces;j++, surface2++)
-                               if (texturesurfacelist[j] != surface2)
-                                       break;
-                       Con_Printf(" %i", j - i);
-               }
-               Con_Printf("\n");
-               Con_Printf("%s batch sizes honoring lightmap:", rsurface_texture->name);
-#endif
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       R_Mesh_TexBind(lightmaptexunit, R_GetTexture(surface->lightmaptexture));
-                       if (deluxemaptexunit >= 0)
-                               R_Mesh_TexBind(deluxemaptexunit, R_GetTexture(surface->deluxemaptexture));
-                       for (j = i + 1, surface2 = surface + 1;j < texturenumsurfaces;j++, surface2++)
-                               if (texturesurfacelist[j] != surface2 || texturesurfacelist[j]->lightmaptexture != surface->lightmaptexture)
-                                       break;
-#if 0
-                       Con_Printf(" %i", j - i);
-#endif
-                       surface2 = texturesurfacelist[j-1];
-                       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_model->surfmesh.data_element3i + 3 * surface->num_firsttriangle), rsurface_model->surfmesh.ebo, (sizeof(int[3]) * surface->num_firsttriangle));
-               }
-#if 0
-               Con_Printf("\n");
-#endif
-       }
-       else
-       {
-               for (i = 0;i < texturenumsurfaces;i++)
-               {
-                       surface = texturesurfacelist[i];
-                       R_Mesh_TexBind(lightmaptexunit, R_GetTexture(surface->lightmaptexture));
-                       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_model->surfmesh.data_element3i + 3 * surface->num_firsttriangle), rsurface_model->surfmesh.ebo, (sizeof(int[3]) * surface->num_firsttriangle));
-               }
-       }
-}
-
 static void RSurf_DrawBatch_ShowSurfaces(int texturenumsurfaces, msurface_t **texturesurfacelist)
 {
        int j;
@@ -3501,7 +3796,8 @@ static void RSurf_DrawBatch_GL11_Lightmap(int texturenumsurfaces, msurface_t **t
        if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, r, g, b, a);
        R_Mesh_ColorPointer(rsurface_lightmapcolor4f, rsurface_lightmapcolor4f_bufferobject, rsurface_lightmapcolor4f_bufferoffset);
        GL_Color(r, g, b, a);
-       RSurf_DrawBatch_WithLightmapSwitching(texturenumsurfaces, texturesurfacelist, 0, -1);
+       R_Mesh_TexBind(0, R_GetTexture(rsurface_lightmaptexture));
+       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
 }
 
 static void RSurf_DrawBatch_GL11_Unlit(int texturenumsurfaces, msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
@@ -3727,9 +4023,9 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, msurface_t **t
                return;
 
        if (rsurface_lightmode == 2)
-               RSurf_PrepareVerticesForBatch(true, r_glsl_permutation->loc_Texture_Normal, texturenumsurfaces, texturesurfacelist);
+               RSurf_PrepareVerticesForBatch(true, r_glsl_permutation->loc_Texture_Normal >= 0, texturenumsurfaces, texturesurfacelist);
        else
-               RSurf_PrepareVerticesForBatch(r_glsl_permutation->loc_Texture_Normal, r_glsl_permutation->loc_Texture_Normal, texturenumsurfaces, texturesurfacelist);
+               RSurf_PrepareVerticesForBatch(r_glsl_permutation->loc_Texture_Normal >= 0, r_glsl_permutation->loc_Texture_Normal >= 0, texturenumsurfaces, texturesurfacelist);
        R_Mesh_TexCoordPointer(0, 2, rsurface_model->surfmesh.data_texcoordtexture2f, rsurface_model->surfmesh.vbo, rsurface_model->surfmesh.vbooffset_texcoordtexture2f);
        R_Mesh_TexCoordPointer(1, 3, rsurface_svector3f, rsurface_svector3f_bufferobject, rsurface_svector3f_bufferoffset);
        R_Mesh_TexCoordPointer(2, 3, rsurface_tvector3f, rsurface_tvector3f_bufferobject, rsurface_tvector3f_bufferoffset);
@@ -3743,11 +4039,11 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, msurface_t **t
                        R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap));
                R_Mesh_ColorPointer(NULL, 0, 0);
        }
-       else if (rsurface_uselightmaptexture)
+       else if (rsurface_lightmaptexture)
        {
-               R_Mesh_TexBind(7, R_GetTexture(texturesurfacelist[0]->lightmaptexture));
+               R_Mesh_TexBind(7, R_GetTexture(rsurface_lightmaptexture));
                if (r_glsl_permutation->loc_Texture_Deluxemap >= 0)
-                       R_Mesh_TexBind(8, R_GetTexture(texturesurfacelist[0]->deluxemaptexture));
+                       R_Mesh_TexBind(8, R_GetTexture(rsurface_deluxemaptexture));
                R_Mesh_ColorPointer(NULL, 0, 0);
        }
        else
@@ -3758,10 +4054,13 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, msurface_t **t
                R_Mesh_ColorPointer(rsurface_model->surfmesh.data_lightmapcolor4f, rsurface_model->surfmesh.vbo, rsurface_model->surfmesh.vbooffset_lightmapcolor4f);
        }
 
-       if (rsurface_uselightmaptexture && !(rsurface_texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
-               RSurf_DrawBatch_WithLightmapSwitching(texturenumsurfaces, texturesurfacelist, 7, r_glsl_permutation->loc_Texture_Deluxemap >= 0 ? 8 : -1);
-       else
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+       if (rsurface_lightmaptexture && !(rsurface_texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
+       {
+               R_Mesh_TexBind(7, R_GetTexture(rsurface_lightmaptexture));
+               if (r_glsl_permutation->loc_Texture_Deluxemap >= 0)
+                       R_Mesh_TexBind(8, R_GetTexture(rsurface_deluxemaptexture));
+       }
+       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
        if (rsurface_texture->backgroundnumskinframes && !(rsurface_texture->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED))
        {
        }
@@ -3831,7 +4130,7 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, msurface_t **t
                        R_Mesh_TextureState(&m);
                        if (rsurface_lightmode == 2)
                                RSurf_DrawBatch_GL11_VertexShade(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
-                       else if (rsurface_uselightmaptexture)
+                       else if (rsurface_lightmaptexture)
                                RSurf_DrawBatch_GL11_Lightmap(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
                        else
                                RSurf_DrawBatch_GL11_VertexColor(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
@@ -3932,7 +4231,7 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, msurface_t **t
                                R_Mesh_TextureState(&m);
                                if (rsurface_lightmode == 2)
                                        RSurf_DrawBatch_GL11_VertexShade(texturenumsurfaces, texturesurfacelist, 1, 1, 1, 1, false, false);
-                               else if (rsurface_uselightmaptexture)
+                               else if (rsurface_lightmaptexture)
                                        RSurf_DrawBatch_GL11_Lightmap(texturenumsurfaces, texturesurfacelist, 1, 1, 1, 1, false, false);
                                else
                                        RSurf_DrawBatch_GL11_VertexColor(texturenumsurfaces, texturesurfacelist, 1, 1, 1, 1, false, false);
@@ -4020,24 +4319,75 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, msurface_t **t
        }
 }
 
-static void R_DrawTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist)
+static void R_DrawTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly)
 {
        if (rsurface_texture->currentmaterialflags & MATERIALFLAG_NODRAW)
                return;
        r_shadow_rtlight = NULL;
-       r_refdef.stats.entities_surfaces += texturenumsurfaces;
        CHECKGLERROR
-       if (r_showsurfaces.integer)
+       if (depthonly)
+       {
+               if ((rsurface_texture->currentmaterialflags & (MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHATEST)))
+                       return;
+               if (rsurface_mode != RSURFMODE_MULTIPASS)
+                       rsurface_mode = RSURFMODE_MULTIPASS;
+               if (r_depthfirst.integer == 3)
+               {
+                       int i = (int)(texturesurfacelist[0] - rsurface_model->data_surfaces);
+                       GL_Color(((i >> 6) & 7) / 7.0f, ((i >> 3) & 7) / 7.0f, (i & 7) / 7.0f,1);
+               }
+               else
+               {
+                       GL_ColorMask(0,0,0,0);
+                       GL_Color(1,1,1,1);
+               }
+               GL_DepthRange(0, (rsurface_texture->currentmaterialflags & MATERIALFLAG_SHORTDEPTHRANGE) ? 0.0625 : 1);
+               GL_CullFace((rsurface_texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : GL_FRONT); // quake is backwards, this culls back faces
+               GL_DepthTest(true);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_DepthMask(true);
+               GL_AlphaTest(false);
+               R_Mesh_ColorPointer(NULL, 0, 0);
+               R_Mesh_ResetTextureState();
+               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+               GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
+               r_refdef.stats.entities_surfaces += texturenumsurfaces;
+       }
+       else if (r_depthfirst.integer == 3)
+               return;
+       else if (r_showsurfaces.integer)
+       {
+               if (rsurface_mode != RSURFMODE_MULTIPASS)
+                       rsurface_mode = RSURFMODE_MULTIPASS;
+               GL_DepthRange(0, (rsurface_texture->currentmaterialflags & MATERIALFLAG_SHORTDEPTHRANGE) ? 0.0625 : 1);
+               GL_DepthTest(true);
+               GL_CullFace((rsurface_texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : GL_FRONT); // quake is backwards, this culls back faces
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_DepthMask(writedepth);
+               GL_Color(1,1,1,1);
+               GL_AlphaTest(false);
+               R_Mesh_ColorPointer(NULL, 0, 0);
+               R_Mesh_ResetTextureState();
+               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
                R_DrawTextureSurfaceList_ShowSurfaces(texturenumsurfaces, texturesurfacelist);
+               r_refdef.stats.entities_surfaces += texturenumsurfaces;
+       }
        else if (rsurface_texture->currentmaterialflags & MATERIALFLAG_SKY)
+       {
                R_DrawTextureSurfaceList_Sky(texturenumsurfaces, texturesurfacelist);
+               r_refdef.stats.entities_surfaces += texturenumsurfaces;
+       }
        else if (rsurface_texture->currentnumlayers)
        {
+               // write depth for anything we skipped on the depth-only pass earlier
+               if (!writedepth && (rsurface_texture->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHATEST)))
+                       writedepth = true;
                GL_DepthRange(0, (rsurface_texture->currentmaterialflags & MATERIALFLAG_SHORTDEPTHRANGE) ? 0.0625 : 1);
                GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
                GL_CullFace((rsurface_texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : GL_FRONT); // quake is backwards, this culls back faces
                GL_BlendFunc(rsurface_texture->currentlayers[0].blendfunc1, rsurface_texture->currentlayers[0].blendfunc2);
-               GL_DepthMask(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_BLENDED));
+               GL_DepthMask(writedepth && !(rsurface_texture->currentmaterialflags & MATERIALFLAG_BLENDED));
                GL_Color(rsurface_entity->colormod[0], rsurface_entity->colormod[1], rsurface_entity->colormod[2], rsurface_texture->currentalpha);
                GL_AlphaTest((rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
                // FIXME: identify models using a better check than rsurface_model->brush.shadowmesh
@@ -4048,6 +4398,7 @@ static void R_DrawTextureSurfaceList(int texturenumsurfaces, msurface_t **textur
                        R_DrawTextureSurfaceList_GL13(texturenumsurfaces, texturesurfacelist);
                else
                        R_DrawTextureSurfaceList_GL11(texturenumsurfaces, texturesurfacelist);
+               r_refdef.stats.entities_surfaces += texturenumsurfaces;
        }
        CHECKGLERROR
        GL_LockArrays(0, 0);
@@ -4078,7 +4429,8 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                texture = surface->texture;
                R_UpdateTextureInfo(ent, texture);
                rsurface_texture = texture->currentframe;
-               rsurface_uselightmaptexture = surface->lightmaptexture != NULL;
+               rsurface_lightmaptexture = surface->lightmaptexture;
+               rsurface_deluxemaptexture = surface->deluxemaptexture;
                // scan ahead until we find a different texture
                endsurface = min(i + 1024, numsurfaces);
                texturenumsurfaces = 0;
@@ -4086,18 +4438,18 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                for (;j < endsurface;j++)
                {
                        surface = rsurface_model->data_surfaces + surfacelist[j];
-                       if (texture != surface->texture || rsurface_uselightmaptexture != (surface->lightmaptexture != NULL))
+                       if (texture != surface->texture || rsurface_lightmaptexture != surface->lightmaptexture)
                                break;
                        texturesurfacelist[texturenumsurfaces++] = surface;
                }
                // render the range of surfaces
-               R_DrawTextureSurfaceList(texturenumsurfaces, texturesurfacelist);
+               R_DrawTextureSurfaceList(texturenumsurfaces, texturesurfacelist, true, false);
        }
 
        RSurf_CleanUp();
 }
 
-void R_QueueSurfaceList(int numsurfaces, msurface_t **surfacelist, int flagsmask)
+void R_QueueSurfaceList(int numsurfaces, msurface_t **surfacelist, int flagsmask, qboolean writedepth, qboolean depthonly)
 {
        int i, j;
        vec3_t tempcenter, center;
@@ -4112,7 +4464,8 @@ void R_QueueSurfaceList(int numsurfaces, msurface_t **surfacelist, int flagsmask
                // use skin 1 instead)
                texture = surfacelist[i]->texture;
                rsurface_texture = texture->currentframe;
-               rsurface_uselightmaptexture = surfacelist[i]->lightmaptexture != NULL;
+               rsurface_lightmaptexture = surfacelist[i]->lightmaptexture;
+               rsurface_deluxemaptexture = surfacelist[i]->deluxemaptexture;
                if (!(rsurface_texture->currentmaterialflags & flagsmask))
                {
                        // if this texture is not the kind we want, skip ahead to the next one
@@ -4124,6 +4477,8 @@ void R_QueueSurfaceList(int numsurfaces, msurface_t **surfacelist, int flagsmask
                {
                        // transparent surfaces get pushed off into the transparent queue
                        const msurface_t *surface = surfacelist[i];
+                       if (depthonly)
+                               continue;
                        tempcenter[0] = (surface->mins[0] + surface->maxs[0]) * 0.5f;
                        tempcenter[1] = (surface->mins[1] + surface->maxs[1]) * 0.5f;
                        tempcenter[2] = (surface->mins[2] + surface->maxs[2]) * 0.5f;
@@ -4133,10 +4488,10 @@ void R_QueueSurfaceList(int numsurfaces, msurface_t **surfacelist, int flagsmask
                else
                {
                        // simply scan ahead until we find a different texture or lightmap state
-                       for (;j < numsurfaces && texture == surfacelist[j]->texture && rsurface_uselightmaptexture == (surfacelist[j]->lightmaptexture != NULL);j++)
+                       for (;j < numsurfaces && texture == surfacelist[j]->texture && rsurface_lightmaptexture == surfacelist[j]->lightmaptexture;j++)
                                ;
                        // render the range of surfaces
-                       R_DrawTextureSurfaceList(j - i, surfacelist + i);
+                       R_DrawTextureSurfaceList(j - i, surfacelist + i, writedepth, depthonly);
                }
        }
 }
@@ -4276,9 +4631,10 @@ void R_DrawTrianglesAndNormals(entity_render_t *ent, qboolean drawtris, qboolean
                                qglBegin(GL_LINES);
                                for (k = 0;k < surface->num_triangles;k++, elements += 3)
                                {
-                                       qglArrayElement(elements[0]);qglArrayElement(elements[1]);
-                                       qglArrayElement(elements[1]);qglArrayElement(elements[2]);
-                                       qglArrayElement(elements[2]);qglArrayElement(elements[0]);
+#define GLVERTEXELEMENT(n) qglVertex3f(rsurface_vertex3f[elements[n]*3+0], rsurface_vertex3f[elements[n]*3+1], rsurface_vertex3f[elements[n]*3+2])
+                                       GLVERTEXELEMENT(0);GLVERTEXELEMENT(1);
+                                       GLVERTEXELEMENT(1);GLVERTEXELEMENT(2);
+                                       GLVERTEXELEMENT(2);GLVERTEXELEMENT(0);
                                }
                                qglEnd();
                                CHECKGLERROR
@@ -4325,7 +4681,7 @@ void R_DrawTrianglesAndNormals(entity_render_t *ent, qboolean drawtris, qboolean
 }
 
 extern void R_BuildLightMap(const entity_render_t *ent, msurface_t *surface);
-void R_DrawWorldSurfaces(qboolean skysurfaces)
+void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean depthonly)
 {
        int i, j, endj, f, flagsmask;
        int counttriangles = 0;
@@ -4341,7 +4697,7 @@ void R_DrawWorldSurfaces(qboolean skysurfaces)
        RSurf_ActiveWorldEntity();
 
        // update light styles
-       if (!skysurfaces && model->brushq1.light_styleupdatechains)
+       if (!skysurfaces && !depthonly && model->brushq1.light_styleupdatechains)
        {
                for (i = 0;i < model->brushq1.light_styles;i++)
                {
@@ -4359,7 +4715,8 @@ void R_DrawWorldSurfaces(qboolean skysurfaces)
        flagsmask = skysurfaces ? MATERIALFLAG_SKY : (MATERIALFLAG_WATER | MATERIALFLAG_WALL);
        f = 0;
        t = NULL;
-       rsurface_uselightmaptexture = false;
+       rsurface_lightmaptexture = NULL;
+       rsurface_deluxemaptexture = NULL;
        rsurface_texture = NULL;
        numsurfacelist = 0;
        j = model->firstmodelsurface;
@@ -4385,25 +4742,25 @@ void R_DrawWorldSurfaces(qboolean skysurfaces)
                                counttriangles += surface->num_triangles;
                                if (numsurfacelist >= maxsurfacelist)
                                {
-                                       R_QueueSurfaceList(numsurfacelist, surfacelist, flagsmask);
+                                       R_QueueSurfaceList(numsurfacelist, surfacelist, flagsmask, writedepth, depthonly);
                                        numsurfacelist = 0;
                                }
                        }
                }
        }
        if (numsurfacelist)
-               R_QueueSurfaceList(numsurfacelist, surfacelist, flagsmask);
+               R_QueueSurfaceList(numsurfacelist, surfacelist, flagsmask, writedepth, depthonly);
        r_refdef.stats.entities_triangles += counttriangles;
        RSurf_CleanUp();
 
-       if (r_showcollisionbrushes.integer && !skysurfaces)
+       if (r_showcollisionbrushes.integer && !skysurfaces && !depthonly)
                R_DrawCollisionBrushes(r_refdef.worldentity);
 
-       if (r_showtris.integer || r_shownormals.integer)
+       if ((r_showtris.integer || r_shownormals.integer) && !depthonly)
                R_DrawTrianglesAndNormals(r_refdef.worldentity, r_showtris.integer, r_shownormals.integer, flagsmask);
 }
 
-void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces)
+void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean writedepth, qboolean depthonly)
 {
        int i, f, flagsmask;
        int counttriangles = 0;
@@ -4424,10 +4781,10 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces)
        else if ((ent->effects & EF_FULLBRIGHT) || r_showsurfaces.integer || 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);
+               RSurf_ActiveModelEntity(ent, true, r_glsl.integer && gl_support_fragment_shader && !depthonly);
 
        // update light styles
-       if (!skysurfaces && model->brushq1.light_styleupdatechains)
+       if (!skysurfaces && !depthonly && model->brushq1.light_styleupdatechains)
        {
                for (i = 0;i < model->brushq1.light_styles;i++)
                {
@@ -4445,7 +4802,8 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces)
        flagsmask = skysurfaces ? MATERIALFLAG_SKY : (MATERIALFLAG_WATER | MATERIALFLAG_WALL);
        f = 0;
        t = NULL;
-       rsurface_uselightmaptexture = false;
+       rsurface_lightmaptexture = NULL;
+       rsurface_deluxemaptexture = NULL;
        rsurface_texture = NULL;
        numsurfacelist = 0;
        surface = model->data_surfaces + model->firstmodelsurface;
@@ -4463,19 +4821,19 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces)
                        counttriangles += surface->num_triangles;
                        if (numsurfacelist >= maxsurfacelist)
                        {
-                               R_QueueSurfaceList(numsurfacelist, surfacelist, flagsmask);
+                               R_QueueSurfaceList(numsurfacelist, surfacelist, flagsmask, writedepth, depthonly);
                                numsurfacelist = 0;
                        }
                }
        }
        if (numsurfacelist)
-               R_QueueSurfaceList(numsurfacelist, surfacelist, flagsmask);
+               R_QueueSurfaceList(numsurfacelist, surfacelist, flagsmask, writedepth, depthonly);
        r_refdef.stats.entities_triangles += counttriangles;
        RSurf_CleanUp();
 
-       if (r_showcollisionbrushes.integer && !skysurfaces)
+       if (r_showcollisionbrushes.integer && !skysurfaces && !depthonly)
                R_DrawCollisionBrushes(ent);
 
-       if (r_showtris.integer || r_shownormals.integer)
+       if ((r_showtris.integer || r_shownormals.integer) && !depthonly)
                R_DrawTrianglesAndNormals(ent, r_showtris.integer, r_shownormals.integer, flagsmask);
 }