]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
laid groundwork for a new decal system
[xonotic/darkplaces.git] / gl_rmain.c
index 5794e317410a550e16d0b5afd79015a9243a9f39..aaf4356b57cceebcd2d6dc74522a9aa4c33849ec 100644 (file)
@@ -44,6 +44,12 @@ cvar_t r_motionblur_vcoeff = {CVAR_SAVE, "r_motionblur_vcoeff", "0.05", "sliding
 cvar_t r_motionblur_maxblur = {CVAR_SAVE, "r_motionblur_maxblur", "0.88", "cap for motionblur alpha value"};
 cvar_t r_motionblur_randomize = {CVAR_SAVE, "r_motionblur_randomize", "0.1", "randomizing coefficient to workaround ghosting"};
 
+// TODO do we want a r_equalize_entities cvar that works on all ents, or would that be a cheat?
+cvar_t r_equalize_entities_fullbright = {CVAR_SAVE, "r_equalize_entities_fullbright", "0", "render fullbright entities by equalizing their lightness, not by not rendering light"};
+cvar_t r_equalize_entities_minambient = {CVAR_SAVE, "r_equalize_entities_minambient", "0.5", "light equalizing: ensure at least this ambient/diffuse ratio"};
+cvar_t r_equalize_entities_by = {CVAR_SAVE, "r_equalize_entities_by", "0.7", "light equalizing: exponent of dynamics compression (0 = no compression, 1 = full compression)"};
+cvar_t r_equalize_entities_to = {CVAR_SAVE, "r_equalize_entities_to", "0.8", "light equalizing: target light level"};
+
 cvar_t r_animcache = {CVAR_SAVE, "r_animcache", "1", "cache animation frames to save CPU usage, primarily optimizes shadows and reflections"};
 
 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"};
@@ -79,7 +85,7 @@ cvar_t r_shadows_drawafterrtlighting = {CVAR_SAVE, "r_shadows_drawafterrtlightin
 cvar_t r_shadows_castfrombmodels = {CVAR_SAVE, "r_shadows_castfrombmodels", "0", "do cast shadows from bmodels"};
 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"};
-cvar_t r_polygonoffset_submodel_offset = {0, "r_polygonoffset_submodel_offset", "2", "biases depth values of world submodels such as doors, to prevent z-fighting artifacts in Quake maps"};
+cvar_t r_polygonoffset_submodel_offset = {0, "r_polygonoffset_submodel_offset", "4", "biases depth values of world submodels such as doors, to prevent z-fighting artifacts in Quake maps"};
 cvar_t r_fog_exp2 = {0, "r_fog_exp2", "0", "uses GL_EXP2 fog (as in Nehahra) rather than realistic GL_EXP fog"};
 cvar_t r_drawfog = {CVAR_SAVE, "r_drawfog", "1", "allows one to disable fog rendering"};
 
@@ -191,8 +197,15 @@ 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];
+typedef struct r_qwskincache_s
+{
+       char name[MAX_QPATH];
+       skinframe_t *skinframe;
+}
+r_qwskincache_t;
+
+static r_qwskincache_t *r_qwskincache;
+static int r_qwskincache_size;
 
 /// vertex coordinates for a quad that covers the screen exactly
 const float r_screenvertex3f[12] =
@@ -250,22 +263,8 @@ void FOG_clear(void)
        r_refdef.fog_alpha = 1;
        r_refdef.fog_start = 0;
        r_refdef.fog_end = 0;
-}
-
-float FogForDistance(vec_t dist)
-{
-       unsigned int fogmasktableindex = (unsigned int)(dist * r_refdef.fogmasktabledistmultiplier);
-       return r_refdef.fogmasktable[min(fogmasktableindex, FOGMASKTABLEWIDTH - 1)];
-}
-
-float FogPoint_World(const vec3_t p)
-{
-       return FogForDistance(VectorDistance((p), r_refdef.view.origin));
-}
-
-float FogPoint_Model(const vec3_t p)
-{
-       return FogForDistance(VectorDistance((p), rsurface.modelorg) * Matrix4x4_ScaleFromMatrix(&rsurface.matrix));
+       r_refdef.fog_height = 1<<30;
+       r_refdef.fog_fadedepth = 128;
 }
 
 static void R_BuildBlankTextures(void)
@@ -449,7 +448,7 @@ static const char *builtinshaderstring =
 "// written by Forest 'LordHavoc' Hale\n"
 "\n"
 "// enable various extensions depending on permutation:\n"
-"\n" 
+"\n"
 "#ifdef USESHADOWMAPRECT\n"
 "# extension GL_ARB_texture_rectangle : enable\n"
 "#endif\n"
@@ -489,6 +488,14 @@ static const char *builtinshaderstring =
 "# define myhalf4 vec4\n"
 "//#endif\n"
 "\n"
+"#ifdef USEFOGINSIDE\n"
+"# define USEFOG\n"
+"#else\n"
+"# ifdef USEFOGOUTSIDE\n"
+"#  define USEFOG\n"
+"# endif\n"
+"#endif\n"
+"\n"
 "#ifdef MODE_DEPTH_OR_SHADOW\n"
 "\n"
 "# ifdef VERTEX_SHADER\n"
@@ -634,6 +641,36 @@ static const char *builtinshaderstring =
 "# endif\n"
 "\n"
 "#else // !MODE_GENERIC\n"
+"#ifdef MODE_BLOOMBLUR\n"
+"# ifdef VERTEX_SHADER\n"
+"void main(void)\n"
+"{\n"
+"      gl_FrontColor = gl_Color;\n"
+"      gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n"
+"      gl_Position = ftransform();\n"
+"}\n"
+"# endif\n"
+"# ifdef FRAGMENT_SHADER\n"
+"\n"
+"uniform sampler2D Texture_First;\n"
+"uniform vec4 BloomBlur_Parameters;\n"
+"\n"
+"void main(void)\n"
+"{\n"
+"      int i;\n"
+"      vec2 tc = gl_TexCoord[0].xy;\n"
+"      vec3 color = texture2D(Texture_First, tc).rgb;\n"
+"      tc += BloomBlur_Parameters.xy;\n"
+"      for (i = 1;i < SAMPLES;i++)\n"
+"      {\n"
+"              color += texture2D(Texture_First, tc).rgb;\n"
+"              tc += BloomBlur_Parameters.xy;\n"
+"      }\n"
+"      gl_FragColor = vec4(color * BloomBlur_Parameters.z + vec3(BloomBlur_Parameters.w), 1);\n"
+"}\n"
+"# endif\n"
+"\n"
+"#else // !MODE_BLOOMBLUR\n"
 "\n"
 "varying vec2 TexCoord;\n"
 "#ifdef USEVERTEXTEXTUREBLEND\n"
@@ -655,6 +692,7 @@ static const char *builtinshaderstring =
 "varying vec3 EyeVector;\n"
 "#ifdef USEFOG\n"
 "varying vec3 EyeVectorModelSpace;\n"
+"varying float FogPlaneVertexDist;\n"
 "#endif\n"
 "\n"
 "varying vec3 VectorS; // direction of S texcoord (sometimes crudely called tangent)\n"
@@ -681,6 +719,7 @@ static const char *builtinshaderstring =
 "uniform vec3 LightPosition;\n"
 "uniform vec3 EyePosition;\n"
 "uniform vec3 LightDir;\n"
+"uniform vec4 FogPlane;\n"
 "\n"
 "// TODO: get rid of tangentt (texcoord2) and use a crossproduct to regenerate it from tangents (texcoord1) and normal (texcoord3), this would require sending a 4 component texcoord1 with W as 1 or -1 according to which side the texcoord2 should be on\n"
 "\n"
@@ -727,6 +766,10 @@ static const char *builtinshaderstring =
 "      EyeVector.y = dot(EyeVectorModelSpace, gl_MultiTexCoord2.xyz);\n"
 "      EyeVector.z = dot(EyeVectorModelSpace, gl_MultiTexCoord3.xyz);\n"
 "\n"
+"#ifdef USEFOG\n"
+"      FogPlaneVertexDist = dot(FogPlane, gl_Vertex);\n"
+"#endif\n"
+"\n"
 "#ifdef MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
 "      VectorS = gl_MultiTexCoord1.xyz;\n"
 "      VectorT = gl_MultiTexCoord2.xyz;\n"
@@ -855,6 +898,8 @@ static const char *builtinshaderstring =
 "uniform float OffsetMapping_Scale;\n"
 "uniform float OffsetMapping_Bias;\n"
 "uniform float FogRangeRecip;\n"
+"uniform float FogPlaneViewDist;\n"
+"uniform float FogHeightFade;\n"
 "\n"
 "uniform myhalf AmbientScale;\n"
 "uniform myhalf DiffuseScale;\n"
@@ -1061,7 +1106,7 @@ static const char *builtinshaderstring =
 "#      else\n"
 "#        define texval(x, y) texture2D(Texture_ShadowMap2D, center + vec2(x, y)*ShadowMap_TextureScale).r  \n"
 "#      endif\n"
-"#      if USESHADOWMAPPCF > 1\n"       
+"#      if USESHADOWMAPPCF > 1\n"
 "    vec2 center = shadowmaptc.xy - 0.5, offset = fract(center);\n"
 "    center *= ShadowMap_TextureScale;\n"
 "    vec4 row1 = step(shadowmaptc.z, vec4(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0), texval( 2.0, -1.0)));\n"
@@ -1077,7 +1122,7 @@ static const char *builtinshaderstring =
 "    vec3 row3 = step(shadowmaptc.z, vec3(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0)));\n"
 "    vec3 cols = row2 + mix(row1, row3, offset.y);\n"
 "    f = dot(mix(cols.xy, cols.yz, offset.x), vec2(0.25));\n"
-"#      endif\n"      
+"#      endif\n"
 "#     endif\n"
 "#    else\n"
 "    f = step(shadowmaptc.z, texture2D(Texture_ShadowMap2D, shadowmaptc.xy*ShadowMap_TextureScale).r);\n"
@@ -1381,7 +1426,39 @@ static const char *builtinshaderstring =
 "\n"
 "      // apply fog after Contrastboost/SceneBrightness because its color is already modified appropriately\n"
 "#ifdef USEFOG\n"
-"      color.rgb = mix(FogColor, color.rgb, myhalf(texture2D(Texture_FogMask, myhalf2(length(EyeVectorModelSpace)*FogRangeRecip, 0.0))));\n"
+"      float fogfrac;\n"
+"#ifdef USEFOGOUTSIDE\n"
+"      fogfrac = min(0.0, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist) * min(1.0, min(0.0, FogPlaneVertexDist) * FogHeightFade);\n"
+"#else\n"
+"      fogfrac = FogPlaneViewDist / (FogPlaneViewDist - max(0.0, FogPlaneVertexDist)) * min(1.0, (min(0.0, FogPlaneVertexDist) + FogPlaneViewDist) * FogHeightFade);\n"
+"#endif\n"
+"//    float FogHeightFade1 = -0.5/1024.0;\n"
+"//    if (FogPlaneViewDist >= 0.0)\n"
+"//            fogfrac = min(0.0, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist) * min(1.0, min(0.0, FogPlaneVertexDist) * FogHeightFade1);\n"
+"//    else\n"
+"//            fogfrac = FogPlaneViewDist / (FogPlaneViewDist - max(0.0, FogPlaneVertexDist)) * min(1.0, (min(0.0, FogPlaneVertexDist) + FogPlaneViewDist) * FogHeightFade1);\n"
+"//# ifdef USEFOGABOVE\n"
+"//    if (FogPlaneViewDist >= 0.0)\n"
+"//            fogfrac = min(0.0, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist);\n"
+"//    else\n"
+"//            fogfrac = FogPlaneViewDist / (FogPlaneViewDist - max(0.0, FogPlaneVertexDist));\n"
+"//    fogfrac *= min(1.0, (min(0.0, FogPlaneVertexDist) + min(0.0, FogPlaneViewDist))*FogHeightFade1);\n"
+"//    fogfrac *= min(1.0, (max(0.0, fade*FogPlaneVertexDist) + max(0.0, fade*FogPlaneViewDist)));\n"
+"//    fogfrac *= min(1.0, (max(0.0, FogHeightFade1*FogPlaneVertexDist) + max(0.0, FogHeightFade1*FogPlaneViewDist)));\n"
+"//    fogfrac *= min(1.0, (min(0.0, FogPlaneVertexDist) + min(0.0, FogPlaneViewDist))*FogHeightFade1);\n"
+"\n"
+"      //fogfrac *= min(1.0, max(0.0, (max(-2048, min(0, FogPlaneVertexDist)) + max(-2048, min(0, FogPlaneViewDist)))/-2048.0));\n"
+"      //float fade = -0.5/128.0;\n"
+"      //fogfrac *= max(0.0, min(1.0, fade*FogPlaneVertexDist)) + max(0.0, min(1.0, fade*FogPlaneViewDist));\n"
+"      //fogfrac *= max(0.0, min(1.0, FogHeightFade1*FogPlaneVertexDist)) + max(0.0, min(1.0, FogHeightFade1*FogPlaneViewDist));\n"
+"      //fogfrac *= min(1.0, max(0.0, FogHeightFade1*FogPlaneVertexDist)) + min(1.0, max(0.0, FogHeightFade1*FogPlaneViewDist));\n"
+"      //fogfrac *= min(1.0, max(0.0, FogHeightFade1*FogPlaneVertexDist) + max(0.0, FogHeightFade1*FogPlaneViewDist));\n"
+"      //fogfrac *= min(1.0, min(1.0, max(0.0, FogHeightFade1*FogPlaneVertexDist)) + min(1.0, max(0.0, FogHeightFade1*FogPlaneViewDist)));\n"
+"      //fogfrac *= min(1.0, max(0.0, FogHeightFade1*FogPlaneVertexDist) + max(0.0, FogHeightFade1*FogPlaneViewDist));\n"
+"      //fogfrac *= min(1.0, (min(0.0, FogPlaneVertexDist) + min(0.0, FogPlaneViewDist)) * FogHeightFade1);\n"
+"      //fogfrac *= min(1.0, (min(0.0, FogPlaneVertexDist) + min(0.0, FogPlaneViewDist)) * FogHeightFade1);\n"
+"//# endif\n"
+"      color.rgb = mix(FogColor, color.rgb, myhalf(texture2D(Texture_FogMask, myhalf2(length(EyeVectorModelSpace)*fogfrac*FogRangeRecip, 0.0))));\n"
 "#endif\n"
 "\n"
 "      // reflection must come last because it already contains exactly the correct fog (the reflection render preserves camera distance from the plane, it only flips the side) and ContrastBoost/SceneBrightness\n"
@@ -1435,6 +1512,7 @@ static const char *builtinshaderstring =
 "\n"
 "#endif // FRAGMENT_SHADER\n"
 "\n"
+"#endif // !MODE_BLOOMBLUR\n"
 "#endif // !MODE_GENERIC\n"
 "#endif // !MODE_POSTPROCESS\n"
 "#endif // !MODE_SHOWDEPTH\n"
@@ -1462,29 +1540,30 @@ typedef enum shaderpermutation_e
 {
        SHADERPERMUTATION_DIFFUSE = 1<<0, ///< (lightsource) whether to use directional shading
        SHADERPERMUTATION_VERTEXTEXTUREBLEND = 1<<1, ///< indicates this is a two-layer material blend based on vertex alpha (q3bsp)
-       SHADERPERMUTATION_VIEWTINT = 1<<1, ///< view tint (postprocessing only)
-       SHADERPERMUTATION_COLORMAPPING = 1<<2, ///< indicates this is a colormapped skin
-       SHADERPERMUTATION_SATURATION = 1<<2, ///< saturation (postprocessing only)
-       SHADERPERMUTATION_FOG = 1<<3, ///< tint the color by fog color or black if using additive blend mode
-       SHADERPERMUTATION_GAMMARAMPS = 1<<3, ///< gamma (postprocessing only)
-       SHADERPERMUTATION_CUBEFILTER = 1<<4, ///< (lightsource) use cubemap light filter
-       SHADERPERMUTATION_GLOW = 1<<5, ///< (lightmap) blend in an additive glow texture
-       SHADERPERMUTATION_BLOOM = 1<<5, ///< bloom (postprocessing only)
-       SHADERPERMUTATION_SPECULAR = 1<<6, ///< (lightsource or deluxemapping) render specular effects
-       SHADERPERMUTATION_POSTPROCESSING = 1<<6, ///< user defined postprocessing (postprocessing only)
-       SHADERPERMUTATION_EXACTSPECULARMATH = 1<<7, ///< (lightsource or deluxemapping) use exact reflection map for specular effects, as opposed to the usual OpenGL approximation
-       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_SHADOWMAPRECT = 1<<11, ///< (lightsource) use shadowmap rectangle texture as light filter
-       SHADERPERMUTATION_SHADOWMAPCUBE = 1<<12, ///< (lightsource) use shadowmap cubemap texture as light filter
-       SHADERPERMUTATION_SHADOWMAP2D = 1<<13, ///< (lightsource) use shadowmap rectangle texture as light filter
-       SHADERPERMUTATION_SHADOWMAPPCF = 1<<14, //< (lightsource) use percentage closer filtering on shadowmap test results
-       SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<15, //< (lightsource) use higher quality percentage closer filtering on shadowmap test results
-       SHADERPERMUTATION_SHADOWSAMPLER = 1<<16, //< (lightsource) use hardware shadowmap test
-       SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<17, //< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
-       SHADERPERMUTATION_LIMIT = 1<<18, ///< size of permutations array
-       SHADERPERMUTATION_COUNT = 18 ///< size of shaderpermutationinfo array
+       SHADERPERMUTATION_VIEWTINT = 1<<2, ///< view tint (postprocessing only)
+       SHADERPERMUTATION_COLORMAPPING = 1<<3, ///< indicates this is a colormapped skin
+       SHADERPERMUTATION_SATURATION = 1<<4, ///< saturation (postprocessing only)
+       SHADERPERMUTATION_FOGINSIDE = 1<<5, ///< tint the color by fog color or black if using additive blend mode
+       SHADERPERMUTATION_FOGOUTSIDE = 1<<6, ///< tint the color by fog color or black if using additive blend mode
+       SHADERPERMUTATION_GAMMARAMPS = 1<<7, ///< gamma (postprocessing only)
+       SHADERPERMUTATION_CUBEFILTER = 1<<8, ///< (lightsource) use cubemap light filter
+       SHADERPERMUTATION_GLOW = 1<<9, ///< (lightmap) blend in an additive glow texture
+       SHADERPERMUTATION_BLOOM = 1<<10, ///< bloom (postprocessing only)
+       SHADERPERMUTATION_SPECULAR = 1<<11, ///< (lightsource or deluxemapping) render specular effects
+       SHADERPERMUTATION_POSTPROCESSING = 1<<12, ///< user defined postprocessing (postprocessing only)
+       SHADERPERMUTATION_EXACTSPECULARMATH = 1<<13, ///< (lightsource or deluxemapping) use exact reflection map for specular effects, as opposed to the usual OpenGL approximation
+       SHADERPERMUTATION_REFLECTION = 1<<14, ///< normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
+       SHADERPERMUTATION_OFFSETMAPPING = 1<<15, ///< adjust texcoords to roughly simulate a displacement mapped surface
+       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<16, ///< adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
+       SHADERPERMUTATION_SHADOWMAPRECT = 1<<17, ///< (lightsource) use shadowmap rectangle texture as light filter
+       SHADERPERMUTATION_SHADOWMAPCUBE = 1<<18, ///< (lightsource) use shadowmap cubemap texture as light filter
+       SHADERPERMUTATION_SHADOWMAP2D = 1<<19, ///< (lightsource) use shadowmap rectangle texture as light filter
+       SHADERPERMUTATION_SHADOWMAPPCF = 1<<20, ///< (lightsource) use percentage closer filtering on shadowmap test results
+       SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<21, ///< (lightsource) use higher quality percentage closer filtering on shadowmap test results
+       SHADERPERMUTATION_SHADOWSAMPLER = 1<<22, ///< (lightsource) use hardware shadowmap test
+       SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<23, ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
+       SHADERPERMUTATION_LIMIT = 1<<24, ///< size of permutations array
+       SHADERPERMUTATION_COUNT = 24 ///< size of shaderpermutationinfo array
 }
 shaderpermutation_t;
 
@@ -1492,12 +1571,18 @@ shaderpermutation_t;
 shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
 {
        {"#define USEDIFFUSE\n", " diffuse"},
-       {"#define USEVERTEXTEXTUREBLEND\n#define USEVIEWTINT\n", " vertextextureblend/tint"},
-       {"#define USECOLORMAPPING\n#define USESATURATION\n", " colormapping/saturation"},
-       {"#define USEFOG\n#define USEGAMMARAMPS\n", " fog/gammaramps"},
+       {"#define USEVERTEXTEXTUREBLEND\n", " vertextextureblend"},
+       {"#define USEVIEWTINT\n", " viewtint"},
+       {"#define USECOLORMAPPING\n", " colormapping"},
+       {"#define USESATURATION\n", " saturation"},
+       {"#define USEFOGINSIDE\n", " foginside"},
+       {"#define USEFOGOUTSIDE\n", " fogoutside"},
+       {"#define USEGAMMARAMPS\n", " gammaramps"},
        {"#define USECUBEFILTER\n", " cubefilter"},
-       {"#define USEGLOW\n#define USEBLOOM\n", " glow/bloom"},
-       {"#define USESPECULAR\n#define USEPOSTPROCESSING", " specular/postprocessing"},
+       {"#define USEGLOW\n", " glow"},
+       {"#define USEBLOOM\n", " bloom"},
+       {"#define USESPECULAR\n", " specular"},
+       {"#define USEPOSTPROCESSING\n", " postprocessing"},
        {"#define USEEXACTSPECULARMATH\n", " exactspecularmath"},
        {"#define USEREFLECTION\n", " reflection"},
        {"#define USEOFFSETMAPPING\n", " offsetmapping"},
@@ -1591,7 +1676,10 @@ typedef struct r_glsl_permutation_s
        int loc_EyePosition;
        int loc_Color_Pants;
        int loc_Color_Shirt;
+       int loc_FogPlane;
+       int loc_FogPlaneViewDist;
        int loc_FogRangeRecip;
+       int loc_FogHeightFade;
        int loc_AmbientScale;
        int loc_DiffuseScale;
        int loc_SpecularScale;
@@ -1786,7 +1874,10 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
                p->loc_EyePosition                = qglGetUniformLocationARB(p->program, "EyePosition");
                p->loc_Color_Pants                = qglGetUniformLocationARB(p->program, "Color_Pants");
                p->loc_Color_Shirt                = qglGetUniformLocationARB(p->program, "Color_Shirt");
+               p->loc_FogPlane                   = qglGetUniformLocationARB(p->program, "FogPlane");
+               p->loc_FogPlaneViewDist           = qglGetUniformLocationARB(p->program, "FogPlaneViewDist");
                p->loc_FogRangeRecip              = qglGetUniformLocationARB(p->program, "FogRangeRecip");
+               p->loc_FogHeightFade              = qglGetUniformLocationARB(p->program, "FogHeightFade");
                p->loc_AmbientScale               = qglGetUniformLocationARB(p->program, "AmbientScale");
                p->loc_DiffuseScale               = qglGetUniformLocationARB(p->program, "DiffuseScale");
                p->loc_SpecularPower              = qglGetUniformLocationARB(p->program, "SpecularPower");
@@ -2048,7 +2139,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                if (specularscale > 0)
                        permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
                if (r_refdef.fogenabled)
-                       permutation |= SHADERPERMUTATION_FOG;
+                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
                if (r_shadow_usingshadowmaprect || r_shadow_usingshadowmap2d || r_shadow_usingshadowmapcube)
@@ -2079,7 +2170,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                if (rsurface.texture->currentskinframe->glow && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer)
                        permutation |= SHADERPERMUTATION_GLOW;
                if (r_refdef.fogenabled)
-                       permutation |= SHADERPERMUTATION_FOG;
+                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
                if (r_glsl_offsetmapping.integer)
@@ -2103,7 +2194,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                if (specularscale > 0)
                        permutation |= SHADERPERMUTATION_SPECULAR;
                if (r_refdef.fogenabled)
-                       permutation |= SHADERPERMUTATION_FOG;
+                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
@@ -2118,7 +2209,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                if (rsurface.texture->currentskinframe->glow && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer)
                        permutation |= SHADERPERMUTATION_GLOW;
                if (r_refdef.fogenabled)
-                       permutation |= SHADERPERMUTATION_FOG;
+                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
@@ -2161,7 +2252,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                if (rsurface.texture->currentskinframe->glow && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer)
                        permutation |= SHADERPERMUTATION_GLOW;
                if (r_refdef.fogenabled)
-                       permutation |= SHADERPERMUTATION_FOG;
+                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
@@ -2229,7 +2320,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                if (r_glsl_permutation->loc_ReflectOffset >= 0) qglUniform1fARB(r_glsl_permutation->loc_ReflectOffset, rsurface.texture->reflectmin);
        }
        if (r_glsl_permutation->loc_SceneBrightness >= 0) qglUniform1fARB(r_glsl_permutation->loc_SceneBrightness, r_refdef.view.colorscale);
-       if (r_glsl_permutation->loc_EyePosition >= 0) qglUniform3fARB(r_glsl_permutation->loc_EyePosition, rsurface.modelorg[0], rsurface.modelorg[1], rsurface.modelorg[2]);
+       if (r_glsl_permutation->loc_EyePosition >= 0) qglUniform3fARB(r_glsl_permutation->loc_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);
        if (r_glsl_permutation->loc_Color_Pants >= 0)
        {
                if (rsurface.texture->currentskinframe->pants)
@@ -2244,7 +2335,10 @@ 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 * Matrix4x4_ScaleFromMatrix(&rsurface.matrix));
+       if (r_glsl_permutation->loc_FogPlane >= 0) qglUniform4fARB(r_glsl_permutation->loc_FogPlane, rsurface.fogplane[0], rsurface.fogplane[1], rsurface.fogplane[2], rsurface.fogplane[3]);
+       if (r_glsl_permutation->loc_FogPlaneViewDist >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogPlaneViewDist, rsurface.fogplaneviewdist);
+       if (r_glsl_permutation->loc_FogRangeRecip >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogRangeRecip, rsurface.fograngerecip);
+       if (r_glsl_permutation->loc_FogHeightFade >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogHeightFade, rsurface.fogheightfade);
        if(permutation & SHADERPERMUTATION_EXACTSPECULARMATH)
        {
                if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * 0.25);
@@ -2430,7 +2524,8 @@ skinframe_t *R_SkinFrame_LoadExternal_CheckAlpha(const char *name, int texturefl
        int basepixels_height;
        skinframe_t *skinframe;
 
-       *has_alpha = false;
+       if (has_alpha)
+               *has_alpha = false;
 
        if (cls.state == ca_dedicated)
                return NULL;
@@ -2474,7 +2569,8 @@ skinframe_t *R_SkinFrame_LoadExternal_CheckAlpha(const char *name, int texturefl
                if (j < basepixels_width * basepixels_height * 4)
                {
                        // has transparent pixels
-                       *has_alpha = true;
+                       if (has_alpha)
+                               *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)
                        {
@@ -2532,8 +2628,7 @@ skinframe_t *R_SkinFrame_LoadExternal_CheckAlpha(const char *name, int texturefl
 
 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);
+       return R_SkinFrame_LoadExternal_CheckAlpha(name, textureflags, complain, NULL);
 }
 
 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)
@@ -2683,6 +2778,52 @@ skinframe_t *R_SkinFrame_LoadInternalQuake(const char *name, int textureflags, i
        return skinframe;
 }
 
+skinframe_t *R_SkinFrame_LoadInternal8bit(const char *name, int textureflags, const unsigned char *skindata, int width, int height, const unsigned int *palette, const unsigned int *alphapalette)
+{
+       int i;
+       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) : 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 (developer_loading.integer)
+               Con_Printf("loading embedded 8bit image \"%s\"\n", name);
+
+       skinframe->base = skinframe->merged = R_SkinFrame_TextureForSkinLayer(skindata, width, height, skinframe->basename, palette, skinframe->textureflags, true);
+       if (textureflags & TEXF_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, 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;
+}
+
 skinframe_t *R_SkinFrame_LoadMissing(void)
 {
        skinframe_t *skinframe;
@@ -2715,8 +2856,8 @@ void gl_main_start(void)
        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));
+       r_qwskincache = NULL;
+       r_qwskincache_size = 0;
 
        // set up r_skinframe loading system for textures
        memset(&r_skinframe, 0, sizeof(r_skinframe));
@@ -2753,8 +2894,8 @@ void gl_main_shutdown(void)
        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));
+       r_qwskincache = NULL;
+       r_qwskincache_size = 0;
 
        // clear out the r_skinframe state
        Mem_ExpandableArray_FreeArray(&r_skinframe.array);
@@ -2785,6 +2926,10 @@ void gl_main_newmap(void)
        // FIXME: move this code to client
        int l;
        char *entities, entname[MAX_QPATH];
+       if (r_qwskincache)
+               Mem_Free(r_qwskincache);
+       r_qwskincache = NULL;
+       r_qwskincache_size = 0;
        if (cl.worldmodel)
        {
                strlcpy(entname, cl.worldmodel->name, sizeof(entname));
@@ -2830,6 +2975,10 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_motionblur_vcoeff);
        Cvar_RegisterVariable(&r_motionblur_randomize);
        Cvar_RegisterVariable(&r_damageblur);
+       Cvar_RegisterVariable(&r_equalize_entities_fullbright);
+       Cvar_RegisterVariable(&r_equalize_entities_minambient);
+       Cvar_RegisterVariable(&r_equalize_entities_by);
+       Cvar_RegisterVariable(&r_equalize_entities_to);
        Cvar_RegisterVariable(&r_animcache);
        Cvar_RegisterVariable(&r_depthfirst);
        Cvar_RegisterVariable(&r_useinfinitefarclip);
@@ -3228,7 +3377,8 @@ static void R_View_UpdateEntityLighting (void)
 {
        int i;
        entity_render_t *ent;
-       vec3_t tempdiffusenormal;
+       vec3_t tempdiffusenormal, avg;
+       vec_t f, fa, fd, fdd;
 
        for (i = 0;i < r_refdef.scene.numentities;i++)
        {
@@ -3257,6 +3407,47 @@ static void R_View_UpdateEntityLighting (void)
                        vec3_t org;
                        Matrix4x4_OriginFromMatrix(&ent->matrix, org);
                        r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, org, ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal);
+                       if(ent->flags & RENDER_EQUALIZE)
+                       {
+                               // first fix up ambient lighting...
+                               if(r_equalize_entities_minambient.value > 0)
+                               {
+                                       fd = 0.299f * ent->modellight_diffuse[0] + 0.587f * ent->modellight_diffuse[1] + 0.114f * ent->modellight_diffuse[2];
+                                       if(fd > 0)
+                                       {
+                                               fa = (0.299f * ent->modellight_ambient[0] + 0.587f * ent->modellight_ambient[1] + 0.114f * ent->modellight_ambient[2]);
+                                               if(fa < r_equalize_entities_minambient.value * fd)
+                                               {
+                                                       // solve:
+                                                       //   fa'/fd' = minambient
+                                                       //   fa'+0.25*fd' = fa+0.25*fd
+                                                       //   ...
+                                                       //   fa' = fd' * minambient
+                                                       //   fd'*(0.25+minambient) = fa+0.25*fd
+                                                       //   ...
+                                                       //   fd' = (fa+0.25*fd) * 1 / (0.25+minambient)
+                                                       //   fa' = (fa+0.25*fd) * minambient / (0.25+minambient)
+                                                       //   ...
+                                                       fdd = (fa + 0.25f * fd) / (0.25f + r_equalize_entities_minambient.value);
+                                                       f = fdd / fd; // f>0 because all this is additive; f<1 because fdd<fd because this follows from fa < r_equalize_entities_minambient.value * fd
+                                                       VectorMA(ent->modellight_ambient, (1-f)*0.25f, ent->modellight_diffuse, ent->modellight_ambient);
+                                                       VectorScale(ent->modellight_diffuse, f, ent->modellight_diffuse);
+                                               }
+                                       }
+                               }
+
+                               if(r_equalize_entities_to.value > 0 && r_equalize_entities_by.value != 0)
+                               {
+                                       VectorMA(ent->modellight_ambient, 0.25f, ent->modellight_diffuse, avg);
+                                       f = 0.299f * avg[0] + 0.587f * avg[1] + 0.114f * avg[2];
+                                       if(f > 0)
+                                       {
+                                               f = pow(f / r_equalize_entities_to.value, -r_equalize_entities_by.value);
+                                               VectorScale(ent->modellight_ambient, f, ent->modellight_ambient);
+                                               VectorScale(ent->modellight_diffuse, f, ent->modellight_diffuse);
+                                       }
+                               }
+                       }
                }
                else // highly rare
                        VectorSet(ent->modellight_ambient, 1, 1, 1);
@@ -3269,6 +3460,45 @@ static void R_View_UpdateEntityLighting (void)
        }
 }
 
+#define MAX_LINEOFSIGHTTRACES 64
+
+static qboolean R_CanSeeBox(int numsamples, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs)
+{
+       int i;
+       vec3_t boxmins, boxmaxs;
+       vec3_t start;
+       vec3_t end;
+       dp_model_t *model = r_refdef.scene.worldmodel;
+       
+       if (!model || !model->brush.TraceLineOfSight)
+               return true;
+
+       // expand the box a little
+       boxmins[0] = (enlarge+1) * entboxmins[0] - enlarge * entboxmaxs[0];
+       boxmaxs[0] = (enlarge+1) * entboxmaxs[0] - enlarge * entboxmins[0];
+       boxmins[1] = (enlarge+1) * entboxmins[1] - enlarge * entboxmaxs[1];
+       boxmaxs[1] = (enlarge+1) * entboxmaxs[1] - enlarge * entboxmins[1];
+       boxmins[2] = (enlarge+1) * entboxmins[2] - enlarge * entboxmaxs[2];
+       boxmaxs[2] = (enlarge+1) * entboxmaxs[2] - enlarge * entboxmins[2];
+
+       // try center
+       VectorCopy(eye, start);
+       VectorMAM(0.5f, boxmins, 0.5f, boxmaxs, end);
+       if (model->brush.TraceLineOfSight(model, start, end))
+               return true;
+
+       // try various random positions
+       for (i = 0;i < numsamples;i++)
+       {
+               VectorSet(end, lhrandom(boxmins[0], boxmaxs[0]), lhrandom(boxmins[1], boxmaxs[1]), lhrandom(boxmins[2], boxmaxs[2]));
+               if (model->brush.TraceLineOfSight(model, start, end))
+                       return true;
+       }
+
+       return false;
+}
+
+
 static void R_View_UpdateEntityVisible (void)
 {
        int i, renderimask;
@@ -3287,7 +3517,7 @@ static void R_View_UpdateEntityVisible (void)
                        ent = r_refdef.scene.entities[i];
                        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))
+                       if ((ent->flags & (RENDER_NODEPTHTEST | 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)
@@ -3295,9 +3525,9 @@ static void R_View_UpdateEntityVisible (void)
                        for (i = 0;i < r_refdef.scene.numentities;i++)
                        {
                                ent = r_refdef.scene.entities[i];
-                               if(r_refdef.viewcache.entityvisible[i] && !(ent->effects & EF_NODEPTHTEST) && !(ent->flags & (RENDER_VIEWMODEL + RENDER_NOCULL)) && !(ent->model && (ent->model->name[0] == '*')))
+                               if(r_refdef.viewcache.entityvisible[i] && !(ent->flags & (RENDER_VIEWMODEL | RENDER_NOCULL | RENDER_NODEPTHTEST)) && !(ent->model && (ent->model->name[0] == '*')))
                                {
-                                       if(Mod_CanSeeBox_Trace(r_cullentities_trace_samples.integer, r_cullentities_trace_enlarge.value, r_refdef.scene.worldmodel, r_refdef.view.origin, ent->mins, ent->maxs))
+                                       if(R_CanSeeBox(r_cullentities_trace_samples.integer, r_cullentities_trace_enlarge.value, r_refdef.view.origin, ent->mins, ent->maxs))
                                                ent->last_trace_visibility = realtime;
                                        if(ent->last_trace_visibility < realtime - r_cullentities_trace_delay.value)
                                                r_refdef.viewcache.entityvisible[i] = 0;
@@ -4014,7 +4244,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, 0, 2, NULL, polygonelements, 0, 0);
+       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
        r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
        // we now have a bloom image in the framebuffer
@@ -4059,7 +4289,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, 0, 2, NULL, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
                // copy the vertically blurred bloom view to a texture
@@ -4073,6 +4303,9 @@ void R_Bloom_MakeTexture(void)
        brighten = r_bloom_brighten.value;
        if (r_hdr.integer)
                brighten *= r_hdr_range.value;
+       brighten = sqrt(brighten);
+       if(range >= 1)
+               brighten *= (3 * range) / (2 * range - 1); // compensate for the "dot particle"
        R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
        R_Mesh_TexCoordPointer(0, 2, r_bloomstate.offsettexcoord2f, 0, 0);
 
@@ -4080,6 +4313,7 @@ void R_Bloom_MakeTexture(void)
        {
                // blend on at multiple vertical offsets to achieve a vertical blur
                // TODO: do offset blends using GLSL
+               // TODO instead of changing the texcoords, change the target positions to prevent artifacts at edges
                GL_BlendFunc(GL_ONE, GL_ZERO);
                for (x = -range;x <= range;x++)
                {
@@ -4100,10 +4334,12 @@ void R_Bloom_MakeTexture(void)
                        // black at the edges
                        // (probably not realistic but looks good enough)
                        //r = ((range*range+1)/((float)(x*x+1)))/(range*2+1);
-                       //r = (dir ? 1.0f : brighten)/(range*2+1);
-                       r = (dir ? 1.0f : brighten)/(range*2+1)*(1 - x*x/(float)(range*range));
+                       //r = brighten/(range*2+1);
+                       r = brighten / (range * 2 + 1);
+                       if(range >= 1)
+                               r *= (1 - x*x/(float)(range*range));
                        GL_Color(r, r, r, 1);
-                       R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
+                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
                        r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
                        GL_BlendFunc(GL_ONE, GL_ONE);
                }
@@ -4123,7 +4359,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, 0, 2, NULL, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
                GL_BlendFunc(GL_ONE, GL_ONE);
@@ -4131,7 +4367,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, 0, 2, NULL, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
                qglBlendEquationEXT(GL_FUNC_ADD_EXT);
 
@@ -4247,7 +4483,7 @@ static void R_BlendView(void)
                                GL_Color(1, 1, 1, cl.motionbluralpha);
                                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, 0, 2, NULL, polygonelements, 0, 0);
+                               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
                                r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
                        }
                }
@@ -4322,7 +4558,7 @@ static void R_BlendView(void)
                }
                if (r_glsl_permutation->loc_Saturation >= 0)
                        qglUniform1fARB(r_glsl_permutation->loc_Saturation, r_glsl_saturation.value);
-               R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
                return;
        }
@@ -4342,7 +4578,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, 0, 2, NULL, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
        }
        else if (r_bloomstate.texture_bloom)
@@ -4371,14 +4607,14 @@ static void R_BlendView(void)
                else
                {
                        R_SetupGenericShader(true);
-                       R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
+                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
                        r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.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, 0, 2, NULL, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
        }
        if (r_refdef.viewblend[3] >= (1.0f / 256.0f))
@@ -4390,7 +4626,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, 0, 2, NULL, polygonelements, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
        }
 }
 
@@ -4404,6 +4640,11 @@ void R_UpdateFogColor(void) // needs to be called before HDR subrender too, as t
                r_refdef.fogcolor[1] = r_refdef.fog_green;
                r_refdef.fogcolor[2] = r_refdef.fog_blue;
 
+               Vector4Set(r_refdef.fogplane, 0, 0, 1, -r_refdef.fog_height);
+               r_refdef.fogplaneviewdist = DotProduct(r_refdef.fogplane, r_refdef.view.origin) + r_refdef.fogplane[3];
+               r_refdef.fogplaneviewabove = r_refdef.fogplaneviewdist >= 0;
+               r_refdef.fogheightfade = -0.5f/max(0.125f, r_refdef.fog_fadedepth);
+
                {
                        vec3_t fogvec;
                        VectorCopy(r_refdef.fogcolor, fogvec);
@@ -4460,6 +4701,8 @@ void R_UpdateVariables(void)
                        r_refdef.fog_alpha = 1;
                        r_refdef.fog_start = 0;
                        r_refdef.fog_end = gl_skyclip.value;
+                       r_refdef.fog_height = 1<<30;
+                       r_refdef.fog_fadedepth = 128;
                }
                else if (r_refdef.oldgl_fogenable)
                {
@@ -4471,6 +4714,8 @@ void R_UpdateVariables(void)
                        r_refdef.fog_alpha = 0;
                        r_refdef.fog_start = 0;
                        r_refdef.fog_end = 0;
+                       r_refdef.fog_height = 1<<30;
+                       r_refdef.fog_fadedepth = 128;
                }
        }
 
@@ -4720,6 +4965,14 @@ void R_RenderScene(void)
 
                if (R_DrawBrushModelsSky() && r_timereport_active)
                        R_TimeReport("bmodelsky");
+
+               if (skyrendermasked && skyrenderlater)
+               {
+                       // we have to force off the water clipping plane while rendering sky
+                       R_SetupView(false);
+                       R_Sky();
+                       R_SetupView(true);
+               }
        }
 
        R_AnimCache_CacheVisibleEntities();
@@ -4875,11 +5128,13 @@ void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, floa
 {
        int i;
        float *v, *c, f1, f2, vertex3f[8*3], color4f[8*4];
+
+       RSurf_ActiveWorldEntity();
+
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        GL_DepthMask(false);
        GL_DepthRange(0, 1);
        GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
-       R_Mesh_Matrix(&identitymatrix);
        R_Mesh_ResetTextureState();
 
        vertex3f[ 0] = mins[0];vertex3f[ 1] = mins[1];vertex3f[ 2] = mins[2]; //
@@ -4895,7 +5150,7 @@ void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, floa
        {
                for (i = 0, v = vertex3f, c = color4f;i < 8;i++, v += 3, c += 4)
                {
-                       f1 = FogPoint_World(v);
+                       f1 = RSurf_FogVertex(v);
                        f2 = 1 - f1;
                        c[0] = c[0] * f1 + r_refdef.fogcolor[0] * f2;
                        c[1] = c[1] * f1 + r_refdef.fogcolor[1] * f2;
@@ -4977,7 +5232,19 @@ static void R_DrawEntityBBoxes(void)
        prog = prog_save;
 }
 
-unsigned short nomodelelements[24] =
+static const int nomodelelement3i[24] =
+{
+       5, 2, 0,
+       5, 1, 2,
+       5, 0, 3,
+       5, 3, 1,
+       0, 2, 4,
+       2, 1, 4,
+       3, 0, 4,
+       1, 3, 4
+};
+
+static const unsigned short nomodelelement3s[24] =
 {
        5, 2, 0,
        5, 1, 2,
@@ -4989,7 +5256,7 @@ unsigned short nomodelelements[24] =
        1, 3, 4
 };
 
-float nomodelvertex3f[6*3] =
+static const float nomodelvertex3f[6*3] =
 {
        -16,   0,   0,
         16,   0,   0,
@@ -4999,7 +5266,7 @@ float nomodelvertex3f[6*3] =
          0,   0,  16
 };
 
-float nomodelcolor4f[6*4] =
+static const float nomodelcolor4f[6*4] =
 {
        0.0f, 0.0f, 0.5f, 1.0f,
        0.0f, 0.0f, 0.5f, 1.0f,
@@ -5014,16 +5281,18 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
        int i;
        float f1, f2, *c;
        float color4f[6*4];
+
+       RSurf_ActiveCustomEntity(&ent->matrix, &ent->inversematrix, ent->flags, ent->shadertime, ent->colormod[0], ent->colormod[1], ent->colormod[2], ent->alpha, 6, nomodelvertex3f, NULL, NULL, NULL, NULL, nomodelcolor4f, 8, nomodelelement3i, nomodelelement3s, false, false);
+
        // this is only called once per entity so numsurfaces is always 1, and
        // surfacelist is always {0}, so this code does not handle batches
-       R_Mesh_Matrix(&ent->matrix);
 
-       if (ent->flags & EF_ADDITIVE)
+       if (rsurface.ent_flags & RENDER_ADDITIVE)
        {
                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
                GL_DepthMask(false);
        }
-       else if (ent->alpha < 1)
+       else if (rsurface.ent_color[3] < 1)
        {
                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                GL_DepthMask(false);
@@ -5033,49 +5302,44 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
                GL_BlendFunc(GL_ONE, GL_ZERO);
                GL_DepthMask(true);
        }
-       GL_DepthRange(0, (ent->flags & RENDER_VIEWMODEL) ? 0.0625 : 1);
-       GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
-       GL_DepthTest(!(ent->effects & EF_NODEPTHTEST));
-       GL_CullFace((ent->effects & EF_DOUBLESIDED) ? GL_NONE : r_refdef.view.cullface_back);
+       GL_DepthRange(0, (rsurface.ent_flags & RENDER_VIEWMODEL) ? 0.0625 : 1);
+       GL_PolygonOffset(rsurface.basepolygonfactor, rsurface.basepolygonoffset);
+       GL_DepthTest(!(rsurface.ent_flags & RENDER_NODEPTHTEST));
+       GL_CullFace((rsurface.ent_flags & RENDER_DOUBLESIDED) ? GL_NONE : r_refdef.view.cullface_back);
        R_SetupGenericShader(false);
-       R_Mesh_VertexPointer(nomodelvertex3f, 0, 0);
+       R_Mesh_VertexPointer(rsurface.vertex3f, rsurface.vertex3f_bufferobject, rsurface.vertex3f_bufferoffset);
+       memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
+       R_Mesh_ColorPointer(color4f, 0, 0);
+       for (i = 0, c = color4f;i < 6;i++, c += 4)
+       {
+               c[0] *= rsurface.ent_color[0];
+               c[1] *= rsurface.ent_color[1];
+               c[2] *= rsurface.ent_color[2];
+               c[3] *= rsurface.ent_color[3];
+       }
        if (r_refdef.fogenabled)
        {
-               vec3_t org;
-               memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
-               R_Mesh_ColorPointer(color4f, 0, 0);
-               Matrix4x4_OriginFromMatrix(&ent->matrix, org);
-               f1 = FogPoint_World(org);
-               f2 = 1 - f1;
                for (i = 0, c = color4f;i < 6;i++, c += 4)
                {
+                       f1 = RSurf_FogVertex(rsurface.vertex3f + 3*i);
+                       f2 = 1 - f1;
                        c[0] = (c[0] * f1 + r_refdef.fogcolor[0] * f2);
                        c[1] = (c[1] * f1 + r_refdef.fogcolor[1] * f2);
                        c[2] = (c[2] * f1 + r_refdef.fogcolor[2] * f2);
-                       c[3] *= ent->alpha;
                }
        }
-       else if (ent->alpha != 1)
-       {
-               memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
-               R_Mesh_ColorPointer(color4f, 0, 0);
-               for (i = 0, c = color4f;i < 6;i++, c += 4)
-                       c[3] *= ent->alpha;
-       }
-       else
-               R_Mesh_ColorPointer(nomodelcolor4f, 0, 0);
        R_Mesh_ResetTextureState();
-       R_Mesh_Draw(0, 6, 0, 8, NULL, nomodelelements, 0, 0);
+       R_Mesh_Draw(0, 6, 0, 8, nomodelelement3i, nomodelelement3s, 0, 0);
 }
 
 void R_DrawNoModel(entity_render_t *ent)
 {
        vec3_t org;
        Matrix4x4_OriginFromMatrix(&ent->matrix, org);
-       //if ((ent->effects & EF_ADDITIVE) || (ent->alpha < 1))
-               R_MeshQueue_AddTransparent(ent->effects & EF_NODEPTHTEST ? r_refdef.view.origin : org, R_DrawNoModel_TransparentCallback, ent, 0, rsurface.rtlight);
-       //else
-       //      R_DrawNoModelCallback(ent, 0);
+       if ((ent->flags & RENDER_ADDITIVE) || (ent->alpha < 1))
+               R_MeshQueue_AddTransparent(ent->flags & RENDER_NODEPTHTEST ? r_refdef.view.origin : org, R_DrawNoModel_TransparentCallback, ent, 0, rsurface.rtlight);
+       else
+               R_DrawNoModel_TransparentCallback(ent, rsurface.rtlight, 0, NULL);
 }
 
 void R_CalcBeam_Vertex3f (float *vert, const vec3_t org1, const vec3_t org2, float width)
@@ -5108,27 +5372,8 @@ void R_CalcBeam_Vertex3f (float *vert, const vec3_t org1, const vec3_t org2, flo
        vert[11] = org2[2] + width * right2[2];
 }
 
-float spritetexcoord2f[4*2] = {0, 1, 0, 0, 1, 0, 1, 1};
-
-void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_t *fogtexture, qboolean depthdisable, qboolean depthshort, const vec3_t origin, const vec3_t left, const vec3_t up, float scalex1, float scalex2, float scaley1, float scaley2, float cr, float cg, float cb, float ca)
+void R_CalcSprite_Vertex3f(float *vertex3f, const vec3_t origin, const vec3_t left, const vec3_t up, float scalex1, float scalex2, float scaley1, float scaley2)
 {
-       // NOTE: this must not call qglDepthFunc (see r_shadow.c, R_BeginCoronaQuery) thanks to ATI
-       float fog = 1.0f;
-       float vertex3f[12];
-
-       if (r_refdef.fogenabled && !depthdisable) // TODO maybe make the unfog effect a separate flag?
-               fog = FogPoint_World(origin);
-
-       R_Mesh_Matrix(&identitymatrix);
-       GL_BlendFunc(blendfunc1, blendfunc2);
-
-       GL_CullFace(GL_NONE);
-
-       GL_DepthMask(false);
-       GL_DepthRange(0, depthshort ? 0.0625 : 1);
-       GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
-       GL_DepthTest(!depthdisable);
-
        vertex3f[ 0] = origin[0] + left[0] * scalex2 + up[0] * scaley1;
        vertex3f[ 1] = origin[1] + left[1] * scalex2 + up[1] * scaley1;
        vertex3f[ 2] = origin[2] + left[2] * scalex2 + up[2] * scaley1;
@@ -5141,25 +5386,6 @@ void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_
        vertex3f[ 9] = origin[0] + left[0] * scalex1 + up[0] * scaley1;
        vertex3f[10] = origin[1] + left[1] * scalex1 + up[1] * scaley1;
        vertex3f[11] = origin[2] + left[2] * scalex1 + up[2] * scaley1;
-
-       R_Mesh_VertexPointer(vertex3f, 0, 0);
-       R_Mesh_ColorPointer(NULL, 0, 0);
-       R_Mesh_ResetTextureState();
-       R_SetupGenericShader(true);
-       R_Mesh_TexBind(0, R_GetTexture(texture));
-       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, 0, 2, NULL, polygonelements, 0, 0);
-
-       if (blendfunc2 == GL_ONE_MINUS_SRC_ALPHA)
-       {
-               R_Mesh_TexBind(0, R_GetTexture(fogtexture));
-               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, 0, 2, NULL, polygonelements, 0, 0);
-       }
 }
 
 int R_Mesh_AddVertex(rmesh_t *mesh, float x, float y, float z)
@@ -5238,7 +5464,7 @@ void R_Mesh_AddBrushMeshFromPlanes(rmesh_t *mesh, int numplanes, mplane_t *plane
        // figure out how large a bounding box we need to properly compute this brush
        maxdist = 0;
        for (w = 0;w < numplanes;w++)
-               maxdist = max(maxdist, planes[w].dist);
+               maxdist = max(maxdist, fabs(planes[w].dist));
        // now make it large enough to enclose the entire brush, and round it off to a reasonable multiple of 1024
        maxdist = floor(maxdist * (4.0 / 1024.0) + 1) * 1024.0;
        for (planenum = 0, plane = planes;planenum < numplanes;planenum++, plane++)
@@ -5368,6 +5594,33 @@ void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcmod_t *t
        Matrix4x4_Concat(texmatrix, &matrix, &temp);
 }
 
+void R_LoadQWSkin(r_qwskincache_t *cache, const char *skinname)
+{
+       int textureflags = TEXF_PRECACHE | (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_PICMIP | TEXF_COMPRESS;
+       char name[MAX_QPATH];
+       skinframe_t *skinframe;
+       unsigned char pixels[296*194];
+       strlcpy(cache->name, skinname, sizeof(cache->name));
+       dpsnprintf(name, sizeof(name), "skins/%s.pcx", cache->name);
+       if (developer_loading.integer)
+               Con_Printf("loading %s\n", name);
+       skinframe = R_SkinFrame_Find(name, textureflags, 0, 0, 0, false);
+       if (!skinframe || !skinframe->base)
+       {
+               unsigned char *f;
+               fs_offset_t filesize;
+               skinframe = NULL;
+               f = FS_LoadFile(name, tempmempool, true, &filesize);
+               if (f)
+               {
+                       if (LoadPCX_QWSkin(f, filesize, pixels, 296, 194))
+                               skinframe = R_SkinFrame_LoadInternalQuake(name, textureflags, true, r_fullbrights.integer, pixels, image_width, image_height);
+                       Mem_Free(f);
+               }
+       }
+       cache->skinframe = skinframe;
+}
+
 texture_t *R_GetCurrentTexture(texture_t *t)
 {
        int i;
@@ -5383,7 +5636,7 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        // switch to an alternate material if this is a q1bsp animated material
        {
                texture_t *texture = t;
-               int s = ent->skinnum;
+               int s = rsurface.ent_skinnum;
                if ((unsigned int)s >= (unsigned int)model->numskins)
                        s = 0;
                if (model->skinscenes)
@@ -5399,7 +5652,7 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                {
                        // use an alternate animation if the entity's frame is not 0,
                        // and only if the texture has an alternate animation
-                       if (ent->framegroupblend[0].frame != 0 && t->anim_total[1])
+                       if (rsurface.frameblend[0].subframe != 0 && t->anim_total[1])
                                t = t->anim_frames[1][(t->anim_total[1] >= 2) ? ((int)(r_refdef.scene.time * 5.0f) % t->anim_total[1]) : 0];
                        else
                                t = t->anim_frames[0][(t->anim_total[0] >= 2) ? ((int)(r_refdef.scene.time * 5.0f) % t->anim_total[0]) : 0];
@@ -5408,51 +5661,52 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        }
 
        // update currentskinframe to be a qw skin or animation frame
-       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 (rsurface.ent_qwskin >= 0)
        {
-               if (strcmp(r_qwskincache[i], cl.scores[i].qw_skin))
+               i = rsurface.ent_qwskin;
+               if (!r_qwskincache || r_qwskincache_size != cl.maxclients)
                {
-                       strlcpy(r_qwskincache[i], cl.scores[i].qw_skin, sizeof(r_qwskincache[i]));
-                       if (developer_loading.integer)
-                               Con_Printf("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 | TEXF_COMPRESS, developer.integer > 0);
+                       r_qwskincache_size = cl.maxclients;
+                       if (r_qwskincache)
+                               Mem_Free(r_qwskincache);
+                       r_qwskincache = Mem_Alloc(r_main_mempool, sizeof(*r_qwskincache) * r_qwskincache_size);
                }
-               t->currentskinframe = r_qwskincache_skinframe[i];
+               if (strcmp(r_qwskincache[i].name, cl.scores[i].qw_skin))
+                       R_LoadQWSkin(&r_qwskincache[i], cl.scores[i].qw_skin);
+               t->currentskinframe = r_qwskincache[i].skinframe;
                if (t->currentskinframe == NULL)
-                       t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->shadertime)) % t->numskinframes];
+                       t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - rsurface.ent_shadertime)) % t->numskinframes];
        }
        else if (t->numskinframes >= 2)
-               t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->shadertime)) % t->numskinframes];
+               t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - rsurface.ent_shadertime)) % t->numskinframes];
        if (t->backgroundnumskinframes >= 2)
-               t->backgroundcurrentskinframe = t->backgroundskinframes[(int)(t->backgroundskinframerate * (cl.time - ent->shadertime)) % t->backgroundnumskinframes];
+               t->backgroundcurrentskinframe = t->backgroundskinframes[(int)(t->backgroundskinframerate * (cl.time - rsurface.ent_shadertime)) % t->backgroundnumskinframes];
 
        t->currentmaterialflags = t->basematerialflags;
-       t->currentalpha = ent->alpha;
+       t->currentalpha = rsurface.ent_color[3];
        if (t->basematerialflags & MATERIALFLAG_WATERALPHA && (model->brush.supportwateralpha || r_novis.integer))
                t->currentalpha *= r_wateralpha.value;
        if(t->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay)
                t->currentalpha *= t->r_water_wateralpha;
        if(!r_waterstate.enabled || r_refdef.view.isoverlay)
                t->currentmaterialflags &= ~(MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION);
-       if (!(ent->flags & RENDER_LIGHT))
+       if (!(rsurface.ent_flags & RENDER_LIGHT))
                t->currentmaterialflags |= MATERIALFLAG_FULLBRIGHT;
        else if (rsurface.modeltexcoordlightmap2f == NULL)
        {
                // pick a model lighting mode
-               if (VectorLength2(ent->modellight_diffuse) >= (1.0f / 256.0f))
+               if (VectorLength2(rsurface.modellight_diffuse) >= (1.0f / 256.0f))
                        t->currentmaterialflags |= MATERIALFLAG_MODELLIGHT | MATERIALFLAG_MODELLIGHT_DIRECTIONAL;
                else
                        t->currentmaterialflags |= MATERIALFLAG_MODELLIGHT;
        }
-       if (ent->effects & EF_ADDITIVE)
+       if (rsurface.ent_flags & RENDER_ADDITIVE)
                t->currentmaterialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
        else if (t->currentalpha < 1)
                t->currentmaterialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
-       if (ent->effects & EF_DOUBLESIDED)
+       if (rsurface.ent_flags & RENDER_DOUBLESIDED)
                t->currentmaterialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
-       if (ent->effects & EF_NODEPTHTEST)
-               t->currentmaterialflags |= MATERIALFLAG_SHORTDEPTHRANGE;
-       if (ent->flags & RENDER_VIEWMODEL)
+       if (rsurface.ent_flags & (RENDER_NODEPTHTEST | RENDER_VIEWMODEL))
                t->currentmaterialflags |= MATERIALFLAG_SHORTDEPTHRANGE;
        if (t->backgroundnumskinframes)
                t->currentmaterialflags |= MATERIALFLAG_VERTEXTEXTUREBLEND;
@@ -5470,7 +5724,7 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                t->currenttexmatrix = r_waterscrollmatrix;
                t->currentbackgroundtexmatrix = r_waterscrollmatrix;
        }
-       else
+       else if (!(t->currentmaterialflags & MATERIALFLAG_CUSTOMSURFACE))
        {
                Matrix4x4_CreateIdentity(&t->currenttexmatrix);
                Matrix4x4_CreateIdentity(&t->currentbackgroundtexmatrix);
@@ -5481,7 +5735,7 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        for (i = 0, tcmod = t->backgroundtcmods;i < Q3MAXTCMODS && tcmod->tcmod;i++, tcmod++)
                R_tcMod_ApplyToMatrix(&t->currentbackgroundtexmatrix, tcmod, t->currentmaterialflags);
 
-       t->colormapping = VectorLength2(ent->colormap_pantscolor) + VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f);
+       t->colormapping = VectorLength2(rsurface.colormap_pantscolor) + VectorLength2(rsurface.colormap_shirtcolor) >= (1.0f / 1048576.0f);
        t->basetexture = (!t->colormapping && t->currentskinframe->merged) ? t->currentskinframe->merged : t->currentskinframe->base;
        t->glosstexture = r_texture_black;
        t->backgroundbasetexture = t->backgroundnumskinframes ? ((!t->colormapping && t->backgroundcurrentskinframe->merged) ? t->backgroundcurrentskinframe->merged : t->backgroundcurrentskinframe->base) : r_texture_white;
@@ -5508,6 +5762,8 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                        t->specularpower = r_shadow_gloss2exponent.value;
                }
        }
+       t->specularscale *= t->specularscalemod;
+       t->specularpower *= t->specularpowermod;
 
        // lightmaps mode looks bad with dlights using actual texturing, so turn
        // off the colormap and glossmap, but leave the normalmap on as it still
@@ -5520,7 +5776,7 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                t->currentmaterialflags = MATERIALFLAG_WALL | (t->currentmaterialflags & (MATERIALFLAG_NOCULLFACE | MATERIALFLAG_MODELLIGHT | MATERIALFLAG_MODELLIGHT_DIRECTIONAL | MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_SHORTDEPTHRANGE));
        }
 
-       Vector4Set(t->lightmapcolor, ent->colormod[0], ent->colormod[1], ent->colormod[2], t->currentalpha);
+       Vector4Set(t->lightmapcolor, rsurface.ent_color[0], rsurface.ent_color[1], rsurface.ent_color[2], t->currentalpha);
        VectorClear(t->dlightcolor);
        t->currentnumlayers = 0;
        if (t->currentmaterialflags & MATERIALFLAG_WALL)
@@ -5555,23 +5811,23 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                {
                        // fullbright is not affected by r_refdef.lightmapintensity
                        R_Texture_AddLayer(t, depthmask, blendfunc1, blendfunc2, TEXTURELAYERTYPE_TEXTURE, t->basetexture, &t->currenttexmatrix, t->lightmapcolor[0], t->lightmapcolor[1], t->lightmapcolor[2], t->lightmapcolor[3]);
-                       if (VectorLength2(ent->colormap_pantscolor) >= (1.0f / 1048576.0f) && t->currentskinframe->pants)
-                               R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->pants, &t->currenttexmatrix, ent->colormap_pantscolor[0] * t->lightmapcolor[0], ent->colormap_pantscolor[1] * t->lightmapcolor[1], ent->colormap_pantscolor[2] * t->lightmapcolor[2], t->lightmapcolor[3]);
-                       if (VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f) && t->currentskinframe->shirt)
-                               R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->shirt, &t->currenttexmatrix, ent->colormap_shirtcolor[0] * t->lightmapcolor[0], ent->colormap_shirtcolor[1] * t->lightmapcolor[1], ent->colormap_shirtcolor[2] * t->lightmapcolor[2], t->lightmapcolor[3]);
+                       if (VectorLength2(rsurface.colormap_pantscolor) >= (1.0f / 1048576.0f) && t->currentskinframe->pants)
+                               R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->pants, &t->currenttexmatrix, rsurface.colormap_pantscolor[0] * t->lightmapcolor[0], rsurface.colormap_pantscolor[1] * t->lightmapcolor[1], rsurface.colormap_pantscolor[2] * t->lightmapcolor[2], t->lightmapcolor[3]);
+                       if (VectorLength2(rsurface.colormap_shirtcolor) >= (1.0f / 1048576.0f) && t->currentskinframe->shirt)
+                               R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->shirt, &t->currenttexmatrix, rsurface.colormap_shirtcolor[0] * t->lightmapcolor[0], rsurface.colormap_shirtcolor[1] * t->lightmapcolor[1], rsurface.colormap_shirtcolor[2] * t->lightmapcolor[2], t->lightmapcolor[3]);
                }
                else
                {
                        vec3_t ambientcolor;
                        float colorscale;
                        // set the color tint used for lights affecting this surface
-                       VectorSet(t->dlightcolor, ent->colormod[0] * t->lightmapcolor[3], ent->colormod[1] * t->lightmapcolor[3], ent->colormod[2] * t->lightmapcolor[3]);
+                       VectorSet(t->dlightcolor, rsurface.ent_color[0] * t->lightmapcolor[3], rsurface.ent_color[1] * t->lightmapcolor[3], rsurface.ent_color[2] * t->lightmapcolor[3]);
                        colorscale = 2;
                        // q3bsp has no lightmap updates, so the lightstylevalue that
                        // would normally be baked into the lightmap must be
                        // applied to the color
                        // FIXME: r_glsl 1 rendering doesn't support overbright lightstyles with this (the default light style is not overbright)
-                       if (ent->model->type == mod_brushq3)
+                       if (model->type == mod_brushq3)
                                colorscale *= r_refdef.scene.rtlightstylevalue[0];
                        colorscale *= r_refdef.lightmapintensity;
                        VectorScale(t->lightmapcolor, r_refdef.scene.ambient * (1.0f / 64.0f), ambientcolor);
@@ -5579,18 +5835,18 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                        // basic lit geometry
                        R_Texture_AddLayer(t, depthmask, blendfunc1, blendfunc2, TEXTURELAYERTYPE_LITTEXTURE, t->basetexture, &t->currenttexmatrix, t->lightmapcolor[0], t->lightmapcolor[1], t->lightmapcolor[2], t->lightmapcolor[3]);
                        // add pants/shirt if needed
-                       if (VectorLength2(ent->colormap_pantscolor) >= (1.0f / 1048576.0f) && t->currentskinframe->pants)
-                               R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_LITTEXTURE, t->currentskinframe->pants, &t->currenttexmatrix, ent->colormap_pantscolor[0] * t->lightmapcolor[0], ent->colormap_pantscolor[1] * t->lightmapcolor[1], ent->colormap_pantscolor[2]  * t->lightmapcolor[2], t->lightmapcolor[3]);
-                       if (VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f) && t->currentskinframe->shirt)
-                               R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_LITTEXTURE, t->currentskinframe->shirt, &t->currenttexmatrix, ent->colormap_shirtcolor[0] * t->lightmapcolor[0], ent->colormap_shirtcolor[1] * t->lightmapcolor[1], ent->colormap_shirtcolor[2] * t->lightmapcolor[2], t->lightmapcolor[3]);
+                       if (VectorLength2(rsurface.colormap_pantscolor) >= (1.0f / 1048576.0f) && t->currentskinframe->pants)
+                               R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_LITTEXTURE, t->currentskinframe->pants, &t->currenttexmatrix, rsurface.colormap_pantscolor[0] * t->lightmapcolor[0], rsurface.colormap_pantscolor[1] * t->lightmapcolor[1], rsurface.colormap_pantscolor[2]  * t->lightmapcolor[2], t->lightmapcolor[3]);
+                       if (VectorLength2(rsurface.colormap_shirtcolor) >= (1.0f / 1048576.0f) && t->currentskinframe->shirt)
+                               R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_LITTEXTURE, t->currentskinframe->shirt, &t->currenttexmatrix, rsurface.colormap_shirtcolor[0] * t->lightmapcolor[0], rsurface.colormap_shirtcolor[1] * t->lightmapcolor[1], rsurface.colormap_shirtcolor[2] * t->lightmapcolor[2], t->lightmapcolor[3]);
                        // now add ambient passes if needed
                        if (VectorLength2(ambientcolor) >= (1.0f/1048576.0f))
                        {
                                R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->basetexture, &t->currenttexmatrix, ambientcolor[0], ambientcolor[1], ambientcolor[2], t->lightmapcolor[3]);
-                               if (VectorLength2(ent->colormap_pantscolor) >= (1.0f / 1048576.0f) && t->currentskinframe->pants)
-                                       R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->pants, &t->currenttexmatrix, ent->colormap_pantscolor[0] * ambientcolor[0], ent->colormap_pantscolor[1] * ambientcolor[1], ent->colormap_pantscolor[2] * ambientcolor[2], t->lightmapcolor[3]);
-                               if (VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f) && t->currentskinframe->shirt)
-                                       R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->shirt, &t->currenttexmatrix, ent->colormap_shirtcolor[0] * ambientcolor[0], ent->colormap_shirtcolor[1] * ambientcolor[1], ent->colormap_shirtcolor[2] * ambientcolor[2], t->lightmapcolor[3]);
+                               if (VectorLength2(rsurface.colormap_pantscolor) >= (1.0f / 1048576.0f) && t->currentskinframe->pants)
+                                       R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->pants, &t->currenttexmatrix, rsurface.colormap_pantscolor[0] * ambientcolor[0], rsurface.colormap_pantscolor[1] * ambientcolor[1], rsurface.colormap_pantscolor[2] * ambientcolor[2], t->lightmapcolor[3]);
+                               if (VectorLength2(rsurface.colormap_shirtcolor) >= (1.0f / 1048576.0f) && t->currentskinframe->shirt)
+                                       R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->shirt, &t->currenttexmatrix, rsurface.colormap_shirtcolor[0] * ambientcolor[0], rsurface.colormap_shirtcolor[1] * ambientcolor[1], rsurface.colormap_shirtcolor[2] * ambientcolor[2], t->lightmapcolor[3]);
                        }
                }
                if (t->currentskinframe->glow != NULL && !gl_lightmaps.integer)
@@ -5645,12 +5901,24 @@ void RSurf_ActiveWorldEntity(void)
        //if (rsurface.entity == r_refdef.scene.worldentity)
        //      return;
        rsurface.entity = r_refdef.scene.worldentity;
+       rsurface.ent_skinnum = 0;
+       rsurface.ent_qwskin = -1;
+       rsurface.ent_shadertime = 0;
+       Vector4Set(rsurface.ent_color, 1, 1, 1, 1);
+       rsurface.ent_flags = r_refdef.scene.worldentity->flags;
        if (rsurface.array_size < model->surfmesh.num_vertices)
                R_Mesh_ResizeArrays(model->surfmesh.num_vertices);
        rsurface.matrix = identitymatrix;
        rsurface.inversematrix = identitymatrix;
+       rsurface.matrixscale = 1;
+       rsurface.inversematrixscale = 1;
        R_Mesh_Matrix(&identitymatrix);
-       VectorCopy(r_refdef.view.origin, rsurface.modelorg);
+       VectorCopy(r_refdef.view.origin, rsurface.localvieworigin);
+       Vector4Copy(r_refdef.fogplane, rsurface.fogplane);
+       rsurface.fograngerecip = r_refdef.fograngerecip;
+       rsurface.fogheightfade = r_refdef.fogheightfade;
+       rsurface.fogplaneviewdist = r_refdef.fogplaneviewdist;
+       rsurface.fogmasktabledistmultiplier = FOGMASKTABLEWIDTH * rsurface.fograngerecip;
        VectorSet(rsurface.modellight_ambient, 0, 0, 0);
        VectorSet(rsurface.modellight_diffuse, 0, 0, 0);
        VectorSet(rsurface.modellight_lightdir, 0, 0, 1);
@@ -5712,18 +5980,25 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
        //if (rsurface.entity == ent && (!model->surfmesh.isanimated || (!wantnormals && !wanttangents)))
        //      return;
        rsurface.entity = (entity_render_t *)ent;
+       rsurface.ent_skinnum = ent->skinnum;
+       rsurface.ent_qwskin = (ent->entitynumber <= cl.maxclients && ent->entitynumber >= 1 && cls.protocol == PROTOCOL_QUAKEWORLD && cl.scores[ent->entitynumber - 1].qw_skin[0] && !strcmp(ent->model->name, "progs/player.mdl")) ? (ent->entitynumber - 1) : -1;
+       rsurface.ent_shadertime = ent->shadertime;
+       Vector4Set(rsurface.ent_color, ent->colormod[0], ent->colormod[1], ent->colormod[2], ent->alpha);
+       rsurface.ent_flags = ent->flags;
        if (rsurface.array_size < model->surfmesh.num_vertices)
                R_Mesh_ResizeArrays(model->surfmesh.num_vertices);
        rsurface.matrix = ent->matrix;
        rsurface.inversematrix = ent->inversematrix;
+       rsurface.matrixscale = Matrix4x4_ScaleFromMatrix(&rsurface.matrix);
+       rsurface.inversematrixscale = 1.0f / rsurface.matrixscale;
        R_Mesh_Matrix(&rsurface.matrix);
-       Matrix4x4_Transform(&rsurface.inversematrix, r_refdef.view.origin, rsurface.modelorg);
-       rsurface.modellight_ambient[0] = ent->modellight_ambient[0] * ent->colormod[0];
-       rsurface.modellight_ambient[1] = ent->modellight_ambient[1] * ent->colormod[1];
-       rsurface.modellight_ambient[2] = ent->modellight_ambient[2] * ent->colormod[2];
-       rsurface.modellight_diffuse[0] = ent->modellight_diffuse[0] * ent->colormod[0];
-       rsurface.modellight_diffuse[1] = ent->modellight_diffuse[1] * ent->colormod[1];
-       rsurface.modellight_diffuse[2] = ent->modellight_diffuse[2] * ent->colormod[2];
+       Matrix4x4_Transform(&rsurface.inversematrix, r_refdef.view.origin, rsurface.localvieworigin);
+       Matrix4x4_TransformStandardPlane(&rsurface.inversematrix, r_refdef.fogplane[0], r_refdef.fogplane[1], r_refdef.fogplane[2], r_refdef.fogplane[3], rsurface.fogplane);
+       rsurface.fogplaneviewdist *= rsurface.inversematrixscale;
+       rsurface.fograngerecip = r_refdef.fograngerecip * rsurface.matrixscale;
+       rsurface.fogheightfade = r_refdef.fogheightfade * rsurface.matrixscale;
+       rsurface.fogmasktabledistmultiplier = FOGMASKTABLEWIDTH * rsurface.fograngerecip;
+       VectorCopy(ent->modellight_ambient, rsurface.modellight_ambient);
        VectorCopy(ent->modellight_diffuse, rsurface.modellight_diffuse);
        VectorCopy(ent->modellight_lightdir, rsurface.modellight_lightdir);
        VectorCopy(ent->colormap_pantscolor, rsurface.colormap_pantscolor);
@@ -5828,8 +6103,141 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
        rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
 }
 
+void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inversematrix, int entflags, double shadertime, float r, float g, float b, float a, int numvertices, const float *vertex3f, const float *texcoord2f, const float *normal3f, const float *svector3f, const float *tvector3f, const float *color4f, int numtriangles, const int *element3i, const unsigned short *element3s, qboolean wantnormals, qboolean wanttangents)
+{
+       rsurface.entity = r_refdef.scene.worldentity;
+       rsurface.ent_skinnum = 0;
+       rsurface.ent_qwskin = -1;
+       rsurface.ent_shadertime = shadertime;
+       Vector4Set(rsurface.ent_color, r, g, b, a);
+       rsurface.ent_flags = entflags;
+       rsurface.modelnum_vertices = numvertices;
+       rsurface.modelnum_triangles = numtriangles;
+       if (rsurface.array_size < rsurface.modelnum_vertices)
+               R_Mesh_ResizeArrays(rsurface.modelnum_vertices);
+       rsurface.matrix = *matrix;
+       rsurface.inversematrix = *inversematrix;
+       rsurface.matrixscale = Matrix4x4_ScaleFromMatrix(&rsurface.matrix);
+       rsurface.inversematrixscale = 1.0f / rsurface.matrixscale;
+       R_Mesh_Matrix(&rsurface.matrix);
+       Matrix4x4_Transform(&rsurface.inversematrix, r_refdef.view.origin, rsurface.localvieworigin);
+       Matrix4x4_TransformStandardPlane(&rsurface.inversematrix, r_refdef.fogplane[0], r_refdef.fogplane[1], r_refdef.fogplane[2], r_refdef.fogplane[3], rsurface.fogplane);
+       rsurface.fogplaneviewdist *= rsurface.inversematrixscale;
+       rsurface.fograngerecip = r_refdef.fograngerecip * rsurface.matrixscale;
+       rsurface.fogheightfade = r_refdef.fogheightfade * rsurface.matrixscale;
+       rsurface.fogmasktabledistmultiplier = FOGMASKTABLEWIDTH * rsurface.fograngerecip;
+       VectorSet(rsurface.modellight_ambient, 0, 0, 0);
+       VectorSet(rsurface.modellight_diffuse, 0, 0, 0);
+       VectorSet(rsurface.modellight_lightdir, 0, 0, 1);
+       VectorSet(rsurface.colormap_pantscolor, 0, 0, 0);
+       VectorSet(rsurface.colormap_shirtcolor, 0, 0, 0);
+       VectorSet(rsurface.glowmod, 1, 1, 1);
+       memset(rsurface.frameblend, 0, sizeof(rsurface.frameblend));
+       rsurface.frameblend[0].lerp = 1;
+       rsurface.basepolygonfactor = r_refdef.polygonfactor;
+       rsurface.basepolygonoffset = r_refdef.polygonoffset;
+       if (wanttangents)
+       {
+               rsurface.modelvertex3f = vertex3f;
+               rsurface.modelsvector3f = svector3f ? svector3f : rsurface.array_modelsvector3f;
+               rsurface.modeltvector3f = tvector3f ? tvector3f : rsurface.array_modeltvector3f;
+               rsurface.modelnormal3f = normal3f ? normal3f : rsurface.array_modelnormal3f;
+       }
+       else if (wantnormals)
+       {
+               rsurface.modelvertex3f = vertex3f;
+               rsurface.modelsvector3f = NULL;
+               rsurface.modeltvector3f = NULL;
+               rsurface.modelnormal3f = normal3f ? normal3f : rsurface.array_modelnormal3f;
+       }
+       else
+       {
+               rsurface.modelvertex3f = vertex3f;
+               rsurface.modelsvector3f = NULL;
+               rsurface.modeltvector3f = NULL;
+               rsurface.modelnormal3f = NULL;
+       }
+       rsurface.modelvertex3f_bufferobject = 0;
+       rsurface.modelvertex3f_bufferoffset = 0;
+       rsurface.modelsvector3f_bufferobject = 0;
+       rsurface.modelsvector3f_bufferoffset = 0;
+       rsurface.modeltvector3f_bufferobject = 0;
+       rsurface.modeltvector3f_bufferoffset = 0;
+       rsurface.modelnormal3f_bufferobject = 0;
+       rsurface.modelnormal3f_bufferoffset = 0;
+       rsurface.generatedvertex = true;
+       rsurface.modellightmapcolor4f  = color4f;
+       rsurface.modellightmapcolor4f_bufferobject = 0;
+       rsurface.modellightmapcolor4f_bufferoffset = 0;
+       rsurface.modeltexcoordtexture2f  = texcoord2f;
+       rsurface.modeltexcoordtexture2f_bufferobject = 0;
+       rsurface.modeltexcoordtexture2f_bufferoffset = 0;
+       rsurface.modeltexcoordlightmap2f  = NULL;
+       rsurface.modeltexcoordlightmap2f_bufferobject = 0;
+       rsurface.modeltexcoordlightmap2f_bufferoffset = 0;
+       rsurface.modelelement3i = element3i;
+       rsurface.modelelement3s = element3s;
+       rsurface.modelelement3i_bufferobject = 0;
+       rsurface.modelelement3s_bufferobject = 0;
+       rsurface.modellightmapoffsets = NULL;
+       rsurface.modelsurfaces = NULL;
+       rsurface.vertex3f  = rsurface.modelvertex3f;
+       rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
+       rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
+       rsurface.svector3f = rsurface.modelsvector3f;
+       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
+       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
+       rsurface.tvector3f = rsurface.modeltvector3f;
+       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
+       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
+       rsurface.normal3f  = rsurface.modelnormal3f;
+       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
+       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
+       rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
+
+       if (rsurface.modelnum_vertices && rsurface.modelelement3i)
+       {
+               if ((wantnormals || wanttangents) && !normal3f)
+                       Mod_BuildNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modelelement3i, rsurface.array_modelnormal3f, r_smoothnormals_areaweighting.integer != 0);
+               if (wanttangents && !svector3f)
+                       Mod_BuildTextureVectorsFromNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modeltexcoordtexture2f, rsurface.modelnormal3f, rsurface.modelelement3i, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f, r_smoothnormals_areaweighting.integer != 0);
+       }
+}
+
+float RSurf_FogPoint(const float *v)
+{
+       // this code is identical to the USEFOGINSIDE/USEFOGOUTSIDE code in the shader
+       float FogPlaneViewDist = r_refdef.fogplaneviewdist;
+       float FogPlaneVertexDist = DotProduct(r_refdef.fogplane, v) + r_refdef.fogplane[3];
+       float FogHeightFade = r_refdef.fogheightfade;
+       float fogfrac;
+       unsigned int fogmasktableindex;
+       if (r_refdef.fogplaneviewabove)
+               fogfrac = min(0.0f, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist) * min(1.0f, min(0.0f, FogPlaneVertexDist) * FogHeightFade);
+       else
+               fogfrac = FogPlaneViewDist / (FogPlaneViewDist - max(0.0f, FogPlaneVertexDist)) * min(1.0f, (min(0.0f, FogPlaneVertexDist) + FogPlaneViewDist) * FogHeightFade);
+       fogmasktableindex = (unsigned int)(VectorDistance(r_refdef.view.origin, v) * fogfrac * r_refdef.fogmasktabledistmultiplier);
+       return r_refdef.fogmasktable[min(fogmasktableindex, FOGMASKTABLEWIDTH - 1)];
+}
+
+float RSurf_FogVertex(const float *v)
+{
+       // this code is identical to the USEFOGINSIDE/USEFOGOUTSIDE code in the shader
+       float FogPlaneViewDist = rsurface.fogplaneviewdist;
+       float FogPlaneVertexDist = DotProduct(rsurface.fogplane, v) + rsurface.fogplane[3];
+       float FogHeightFade = rsurface.fogheightfade;
+       float fogfrac;
+       unsigned int fogmasktableindex;
+       if (r_refdef.fogplaneviewabove)
+               fogfrac = min(0.0f, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist) * min(1.0f, min(0.0f, FogPlaneVertexDist) * FogHeightFade);
+       else
+               fogfrac = FogPlaneViewDist / (FogPlaneViewDist - max(0.0f, FogPlaneVertexDist)) * min(1.0f, (min(0.0f, FogPlaneVertexDist) + FogPlaneViewDist) * FogHeightFade);
+       fogmasktableindex = (unsigned int)(VectorDistance(rsurface.localvieworigin, v) * fogfrac * rsurface.fogmasktabledistmultiplier);
+       return r_refdef.fogmasktable[min(fogmasktableindex, FOGMASKTABLEWIDTH - 1)];
+}
+
 static const int quadedges[6][2] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}};
-void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generatetangents, int texturenumsurfaces, msurface_t **texturesurfacelist)
+void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generatetangents, int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        int deformindex;
        int texturesurfaceindex;
@@ -6027,7 +6435,7 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                        VectorSubtract(end, start, up);
                                        VectorNormalize(up);
                                        // calculate a forward vector to use instead of the original plane normal (this is how we get a new right vector)
-                                       VectorSubtract(rsurface.modelorg, center, forward);
+                                       VectorSubtract(rsurface.localvieworigin, center, forward);
                                        //Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, forward);
                                        VectorNegate(forward, forward);
                                        VectorReflect(forward, 0, up, forward);
@@ -6213,7 +6621,7 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
 
                                float viewer[3], d, reflected[3], worldreflected[3];
 
-                               VectorSubtract(rsurface.modelorg, vertex, viewer);
+                               VectorSubtract(rsurface.localvieworigin, vertex, viewer);
                                // VectorNormalize(viewer);
 
                                d = DotProduct(normal, viewer);
@@ -6265,7 +6673,7 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
        R_Mesh_VertexPointer(rsurface.vertex3f, rsurface.vertex3f_bufferobject, rsurface.vertex3f_bufferoffset);
 }
 
-void RSurf_DrawBatch_Simple(int texturenumsurfaces, msurface_t **texturesurfacelist)
+void RSurf_DrawBatch_Simple(int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        int i, j;
        const msurface_t *surface = texturesurfacelist[0];
@@ -6339,14 +6747,14 @@ void RSurf_DrawBatch_Simple(int texturenumsurfaces, msurface_t **texturesurfacel
        }
 }
 
-static void RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(int texturenumsurfaces, msurface_t **texturesurfacelist, int lightmaptexunit, int deluxemaptexunit, int refractiontexunit, int reflectiontexunit)
+static void RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(int texturenumsurfaces, const msurface_t **texturesurfacelist, int lightmaptexunit, int deluxemaptexunit, int refractiontexunit, int reflectiontexunit)
 {
        int i, planeindex, vertexindex;
        float d, bestd;
        vec3_t vert;
        const float *v;
        r_waterstate_waterplane_t *p, *bestp;
-       msurface_t *surface;
+       const msurface_t *surface;
        if (r_waterstate.renderingscene)
                return;
        for (i = 0;i < texturenumsurfaces;i++)
@@ -6392,7 +6800,7 @@ static void RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(int
        }
 }
 
-static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, msurface_t **texturesurfacelist, int lightmaptexunit, int deluxemaptexunit)
+static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, const msurface_t **texturesurfacelist, int lightmaptexunit, int deluxemaptexunit)
 {
        int i;
        int j;
@@ -6498,7 +6906,7 @@ static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, msurfa
        }
 }
 
-static void RSurf_DrawBatch_ShowSurfaces(int texturenumsurfaces, msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_ShowSurfaces(int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        int j;
        int texturesurfaceindex;
@@ -6528,11 +6936,12 @@ static void RSurf_DrawBatch_ShowSurfaces(int texturenumsurfaces, msurface_t **te
        }
 }
 
-static void RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(int texturenumsurfaces, msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        int texturesurfaceindex;
        int i;
-       float *v, *c2;
+       const float *v;
+       float *c2;
        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
        {
                const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
@@ -6549,12 +6958,14 @@ static void RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(int texturenum
        rsurface.lightmapcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_GL11_ApplyFog(int texturenumsurfaces, msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_GL11_ApplyFog(int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        int texturesurfaceindex;
        int i;
        float f;
-       float *v, *c, *c2;
+       const float *v;
+       const float *c;
+       float *c2;
        if (rsurface.lightmapcolor4f)
        {
                // generate color arrays for the surfaces in this list
@@ -6563,7 +6974,7 @@ static void RSurf_DrawBatch_GL11_ApplyFog(int texturenumsurfaces, msurface_t **t
                        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);
+                               f = RSurf_FogVertex(v);
                                c2[0] = c[0] * f;
                                c2[1] = c[1] * f;
                                c2[2] = c[2] * f;
@@ -6578,7 +6989,7 @@ static void RSurf_DrawBatch_GL11_ApplyFog(int texturenumsurfaces, msurface_t **t
                        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)
                        {
-                               f = FogPoint_Model(v);
+                               f = RSurf_FogVertex(v);
                                c2[0] = f;
                                c2[1] = f;
                                c2[2] = f;
@@ -6591,12 +7002,14 @@ 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)
+static void RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        int texturesurfaceindex;
        int i;
        float f;
-       float *v, *c, *c2;
+       const float *v;
+       const float *c;
+       float *c2;
        if (!rsurface.lightmapcolor4f)
                return;
        // generate color arrays for the surfaces in this list
@@ -6605,7 +7018,7 @@ static void RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(int texturenumsu
                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);
+                       f = RSurf_FogVertex(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);
@@ -6617,11 +7030,12 @@ static void RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(int texturenumsu
        rsurface.lightmapcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_GL11_ApplyColor(int texturenumsurfaces, msurface_t **texturesurfacelist, float r, float g, float b, float a)
+static void RSurf_DrawBatch_GL11_ApplyColor(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a)
 {
        int texturesurfaceindex;
        int i;
-       float *c, *c2;
+       const float *c;
+       float *c2;
        if (!rsurface.lightmapcolor4f)
                return;
        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
@@ -6640,11 +7054,12 @@ 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)
+static void RSurf_DrawBatch_GL11_ApplyAmbient(int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        int texturesurfaceindex;
        int i;
-       float *c, *c2;
+       const float *c;
+       float *c2;
        if (!rsurface.lightmapcolor4f)
                return;
        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
@@ -6663,7 +7078,7 @@ static void RSurf_DrawBatch_GL11_ApplyAmbient(int texturenumsurfaces, msurface_t
        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)
+static void RSurf_DrawBatch_GL11_Lightmap(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
        // TODO: optimize
        rsurface.lightmapcolor4f = NULL;
@@ -6676,7 +7091,7 @@ static void RSurf_DrawBatch_GL11_Lightmap(int texturenumsurfaces, msurface_t **t
        RSurf_DrawBatch_WithLightmapSwitching(texturenumsurfaces, texturesurfacelist, 0, -1);
 }
 
-static void RSurf_DrawBatch_GL11_Unlit(int texturenumsurfaces, msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_Unlit(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
        // TODO: optimize applyfog && applycolor case
        // just apply fog if necessary, and tint the fog color array if necessary
@@ -6690,7 +7105,7 @@ static void RSurf_DrawBatch_GL11_Unlit(int texturenumsurfaces, msurface_t **text
        RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
 }
 
-static void RSurf_DrawBatch_GL11_VertexColor(int texturenumsurfaces, msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_VertexColor(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
        int texturesurfaceindex;
        int i;
@@ -6751,12 +7166,15 @@ static void RSurf_DrawBatch_GL11_VertexColor(int texturenumsurfaces, msurface_t
        RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
 }
 
-static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, msurface_t **texturesurfacelist, float *r, float *g, float *b, float *a, qboolean *applycolor)
+static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, const msurface_t **texturesurfacelist, float *r, float *g, float *b, float *a, qboolean *applycolor)
 {
        int texturesurfaceindex;
        int i;
        float f;
-       float *v, *c, *c2, alpha;
+       float alpha;
+       const float *v;
+       const float *n;
+       float *c;
        vec3_t ambientcolor;
        vec3_t diffusecolor;
        vec3_t lightdir;
@@ -6779,12 +7197,12 @@ static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, msurfa
                        const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
                        int numverts = surface->num_vertices;
                        v = rsurface.vertex3f + 3 * surface->num_firstvertex;
-                       c2 = rsurface.normal3f + 3 * surface->num_firstvertex;
+                       n = rsurface.normal3f + 3 * surface->num_firstvertex;
                        c = rsurface.array_color4f + 4 * surface->num_firstvertex;
                        // q3-style directional shading
-                       for (i = 0;i < numverts;i++, v += 3, c2 += 3, c += 4)
+                       for (i = 0;i < numverts;i++, v += 3, n += 3, c += 4)
                        {
-                               if ((f = DotProduct(c2, lightdir)) > 0)
+                               if ((f = DotProduct(n, lightdir)) > 0)
                                        VectorMA(ambientcolor, f, diffusecolor, c);
                                else
                                        VectorCopy(ambientcolor, c);
@@ -6811,7 +7229,7 @@ static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, msurfa
        }
 }
 
-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_VertexShade(int texturenumsurfaces, const 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);
@@ -6832,22 +7250,13 @@ void RSurf_SetupDepthAndCulling(void)
        GL_CullFace((rsurface.texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : r_refdef.view.cullface_back);
 }
 
-static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, msurface_t **texturesurfacelist)
+static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        // transparent sky would be ridiculous
        if (rsurface.texture->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED)
                return;
        R_SetupGenericShader(false);
-       if (skyrendernow)
-       {
-               skyrendernow = false;
-               // we have to force off the water clipping plane while rendering sky
-               R_SetupView(false);
-               R_Sky();
-               R_SetupView(true);
-               // restore entity matrix
-               R_Mesh_Matrix(&rsurface.matrix);
-       }
+       skyrenderlater = true;
        RSurf_SetupDepthAndCulling();
        GL_DepthMask(true);
        // LordHavoc: HalfLife maps have freaky skypolys so don't use
@@ -6885,7 +7294,7 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, msurface_t **te
        GL_Color(1, 1, 1, 1);
 }
 
-static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
+static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
        if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION)))
                return;
@@ -6988,7 +7397,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, msurface_t **t
        GL_LockArrays(0, 0);
 }
 
-static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
+static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
        // OpenGL 1.3 path - anything not completely ancient
        int texturesurfaceindex;
@@ -7084,11 +7493,13 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, msurface_t **t
                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                        {
                                int i;
-                               float f, *v, *c;
+                               float f;
+                               const float *v;
+                               float *c;
                                const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
                                for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                {
-                                       f = 1 - FogPoint_Model(v);
+                                       f = 1 - RSurf_FogVertex(v);
                                        c[0] = layercolor[0];
                                        c[1] = layercolor[1];
                                        c[2] = layercolor[2];
@@ -7110,7 +7521,7 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, msurface_t **t
        }
 }
 
-static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
+static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
        // OpenGL 1.1 - crusty old voodoo path
        int texturesurfaceindex;
@@ -7213,11 +7624,13 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, msurface_t **t
                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                        {
                                int i;
-                               float f, *v, *c;
+                               float f;
+                               const float *v;
+                               float *c;
                                const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
                                for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                {
-                                       f = 1 - FogPoint_Model(v);
+                                       f = 1 - RSurf_FogVertex(v);
                                        c[0] = layer->color[0];
                                        c[1] = layer->color[1];
                                        c[2] = layer->color[2];
@@ -7239,7 +7652,7 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, msurface_t **t
        }
 }
 
-static void R_DrawTextureSurfaceList_ShowSurfaces3(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
+static void R_DrawTextureSurfaceList_ShowSurfaces3(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
        float c[4];
 
@@ -7344,7 +7757,7 @@ static void R_DrawTextureSurfaceList_ShowSurfaces3(int texturenumsurfaces, msurf
        RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
 }
 
-static void R_DrawWorldTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
+static void R_DrawWorldTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
        CHECKGLERROR
        RSurf_SetupDepthAndCulling();
@@ -7359,7 +7772,7 @@ static void R_DrawWorldTextureSurfaceList(int texturenumsurfaces, msurface_t **t
        CHECKGLERROR
 }
 
-static void R_DrawModelTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
+static void R_DrawModelTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
        CHECKGLERROR
        RSurf_SetupDepthAndCulling();
@@ -7379,8 +7792,8 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
        int i, j;
        int texturenumsurfaces, endsurface;
        texture_t *texture;
-       msurface_t *surface;
-       msurface_t *texturesurfacelist[1024];
+       const msurface_t *surface;
+       const msurface_t *texturesurfacelist[1024];
 
        // if the model is static it doesn't matter what value we give for
        // wantnormals and wanttangents, so this logic uses only rules applicable
@@ -7420,7 +7833,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
        GL_AlphaTest(false);
 }
 
-static void R_ProcessWorldTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly)
+static void R_ProcessWorldTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly)
 {
        const entity_render_t *queueentity = r_refdef.scene.worldentity;
        CHECKGLERROR
@@ -7489,7 +7902,7 @@ static void R_ProcessWorldTextureSurfaceList(int texturenumsurfaces, msurface_t
        CHECKGLERROR
 }
 
-void R_QueueWorldSurfaceList(int numsurfaces, msurface_t **surfacelist, int flagsmask, qboolean writedepth, qboolean depthonly)
+void R_QueueWorldSurfaceList(int numsurfaces, const msurface_t **surfacelist, int flagsmask, qboolean writedepth, qboolean depthonly)
 {
        int i, j;
        texture_t *texture;
@@ -7519,7 +7932,7 @@ void R_QueueWorldSurfaceList(int numsurfaces, msurface_t **surfacelist, int flag
        }
 }
 
-static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly, const entity_render_t *queueentity)
+static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly, const entity_render_t *queueentity)
 {
        CHECKGLERROR
        if (depthonly)
@@ -7593,7 +8006,7 @@ static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, msurface_t
        CHECKGLERROR
 }
 
-void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, msurface_t **surfacelist, int flagsmask, qboolean writedepth, qboolean depthonly)
+void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, const msurface_t **surfacelist, int flagsmask, qboolean writedepth, qboolean depthonly)
 {
        int i, j;
        texture_t *texture;
@@ -7700,12 +8113,312 @@ void R_DrawLocs(void)
        }
 }
 
-void R_DrawDebugModel(entity_render_t *ent)
+void R_DecalSystem_Reset(decalsystem_t *decalsystem)
+{
+       if (decalsystem->decals)
+               Mem_Free(decalsystem->decals);
+       memset(decalsystem, 0, sizeof(*decalsystem));
+}
+
+void R_DecalSystem_SpawnTriangle(decalsystem_t *decalsystem, const float *v0, const float *v1, const float *v2, const float *t0, const float *t1, const float *t2, const float *c0, const float *c1, const float *c2, int triangleindex)
+{
+       float *v3f;
+       float *tc2f;
+       tridecal_t *decal;
+       tridecal_t *decals;
+       int i;
+       int maxdecals;
+
+       // expand or initialize the system
+       if (decalsystem->maxdecals <= decalsystem->numdecals)
+       {
+               decalsystem_t old = *decalsystem;
+               qboolean useshortelements;
+               decalsystem->maxdecals = max(16, decalsystem->maxdecals * 2);
+               useshortelements = decalsystem->maxdecals * 3 <= 65536;
+               decalsystem->decals = Mem_Alloc(r_main_mempool, decalsystem->maxdecals * (sizeof(tridecal_t) + sizeof(float[3][3]) + sizeof(float[3][2]) + sizeof(float[3][4]) + sizeof(int[3]) + useshortelements ? sizeof(unsigned short[3]) : 0));
+               decalsystem->vertex3f = (float *)(decalsystem->decals + decalsystem->maxdecals);
+               decalsystem->texcoord2f = (float *)(decalsystem->vertex3f + decalsystem->maxdecals*9);
+               decalsystem->color4f = (float *)(decalsystem->texcoord2f + decalsystem->maxdecals*6);
+               decalsystem->element3i = (int *)(decalsystem->color4f + decalsystem->maxdecals*12);
+               decalsystem->element3s = useshortelements ? (unsigned short *)(decalsystem->element3i + decalsystem->maxdecals*3) : NULL;
+               if (decalsystem->numdecals)
+               {
+                       memcpy(decalsystem->decals, old.decals, decalsystem->numdecals * sizeof(tridecal_t));
+                       memcpy(decalsystem->vertex3f, old.vertex3f, decalsystem->numdecals * sizeof(float[3][3]));
+                       memcpy(decalsystem->texcoord2f, old.texcoord2f, decalsystem->numdecals * sizeof(float[3][2]));
+                       memcpy(decalsystem->color4f, old.color4f, decalsystem->numdecals * sizeof(float[3][4]));
+               }
+               for (i = 0;i < decalsystem->maxdecals*3;i++)
+                       decalsystem->element3i[i] = i;
+               if (useshortelements)
+                       for (i = 0;i < decalsystem->maxdecals*3;i++)
+                               decalsystem->element3s[i] = i;
+       }
+
+       // grab a decal and search for another free slot for the next one
+       maxdecals = decalsystem->maxdecals;
+       decals = decalsystem->decals;
+       decal = decalsystem->decals + (i = decalsystem->freedecal++);
+       v3f = decalsystem->vertex3f + 9*i;
+       tc2f = decalsystem->texcoord2f + 6*i;
+       for (i = decalsystem->freedecal;i < maxdecals && decals[i].alpha;i++)
+               ;
+       decalsystem->freedecal = i;
+       if (decalsystem->numdecals <= i)
+               decalsystem->numdecals = i + 1;
+
+       // initialize the decal
+       decal->fade = cl_decals_time.value;
+       decal->alpha = 1.0f;
+       decal->colors[0][0] = (unsigned char)(c0[0]*255.0f);
+       decal->colors[0][1] = (unsigned char)(c0[1]*255.0f);
+       decal->colors[0][2] = (unsigned char)(c0[2]*255.0f);
+       decal->colors[0][3] = (unsigned char)(c0[3]*255.0f);
+       decal->colors[1][0] = (unsigned char)(c1[0]*255.0f);
+       decal->colors[1][1] = (unsigned char)(c1[1]*255.0f);
+       decal->colors[1][2] = (unsigned char)(c1[2]*255.0f);
+       decal->colors[1][3] = (unsigned char)(c1[3]*255.0f);
+       decal->colors[2][0] = (unsigned char)(c2[0]*255.0f);
+       decal->colors[2][1] = (unsigned char)(c2[1]*255.0f);
+       decal->colors[2][2] = (unsigned char)(c2[2]*255.0f);
+       decal->colors[2][3] = (unsigned char)(c2[3]*255.0f);
+       v3f[0] = v0[0];
+       v3f[1] = v0[1];
+       v3f[2] = v0[2];
+       v3f[3] = v1[0];
+       v3f[4] = v1[1];
+       v3f[5] = v1[2];
+       v3f[6] = v2[0];
+       v3f[7] = v2[1];
+       v3f[8] = v2[2];
+       tc2f[0] = t0[0];
+       tc2f[1] = t0[1];
+       tc2f[2] = t1[0];
+       tc2f[3] = t1[1];
+       tc2f[4] = t2[0];
+       tc2f[5] = t2[1];
+}
+
+void R_DecalSystem_Splat(decalsystem_t *decalsystem, int numvertices, const float *vertex3f, int numtriangles, const int *element3i, const matrix4x4_t *projection, float r, float g, float b, float a, qboolean dynamic)
+{
+       int vertexindex;
+       int triangleindex;
+       int cornerindex;
+       int index;
+       int numpoints;
+       const int *e;
+       float v[9][3];
+       float tc[9][3]; // third coord is distance fade
+       float c[9][4];
+       float *temp;
+       float origin[3];
+       float normal[3];
+       float planes[6][3];
+       float f;
+       float points[2][9][3];
+       temp = Mem_Alloc(tempmempool, numvertices * sizeof(float[3]));
+       for (vertexindex = 0;vertexindex < numvertices;vertexindex++)
+               Matrix4x4_Transform(projection, vertex3f + 3*vertexindex, temp + 3*vertexindex);
+       for (triangleindex = 0, e = element3i;triangleindex < numtriangles;triangleindex++, e += 3)
+       {
+               for (cornerindex = 0;cornerindex < 3;cornerindex++)
+               {
+                       index = 3*e[cornerindex];
+                       VectorCopy(temp + index, tc[cornerindex]);
+               }
+               // cull triangles that are entirely outside the projection
+               if (min(tc[0][0], min(tc[1][0], tc[2][0])) >=  1)
+                       continue;
+               if (min(tc[0][1], min(tc[1][1], tc[2][1])) >=  1)
+                       continue;
+               if (min(tc[0][2], min(tc[1][2], tc[2][2])) >=  1)
+                       continue;
+               if (max(tc[0][0], max(tc[1][0], tc[2][0])) <= -1)
+                       continue;
+               if (max(tc[0][1], max(tc[1][1], tc[2][1])) <= -1)
+                       continue;
+               if (max(tc[0][2], max(tc[1][2], tc[2][2])) <= -1)
+                       continue;
+               // cull backfaces
+               TriangleNormal(tc[0], tc[1], tc[2], normal);
+               if (normal[2] < 0)
+                       continue;
+               // we accept this triangle
+               for (cornerindex = 0;cornerindex < 3;cornerindex++)
+               {
+                       index = 3*e[cornerindex];
+                       VectorCopy(vertex3f + index, v[cornerindex]);
+                       // calculate distance fade from the projection origin
+                       f = a * (1-fabs(tc[cornerindex][2]));
+                       c[cornerindex][0] = r * f;
+                       c[cornerindex][1] = g * f;
+                       c[cornerindex][2] = b * f;
+                       c[cornerindex][3] = 1;
+               }
+               if (dynamic)
+               {
+                       R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[1], v[2], tc[0], tc[1], tc[2], c[0], c[1], c[2], triangleindex);
+               }
+               else
+               {
+                       // clip by each of the box planes formed from the projection matrix
+                       Matrix4x4_ToVectors(projection, planes[0], planes[2], planes[4], origin);
+                       VectorNegate(planes[0], planes[1]);
+                       VectorNegate(planes[2], planes[3]);
+                       VectorNegate(planes[4], planes[5]);
+                       VectorCopy(planes[4], normal);
+                       VectorNormalize(normal);
+                       VectorMA(v[0], 0.125f, planes[4], v[0]);
+                       VectorMA(v[1], 0.125f, planes[4], v[1]);
+                       VectorMA(v[2], 0.125f, planes[4], v[2]);
+                       numpoints = PolygonF_Clip(3        , v[0]        , planes[0][0], planes[0][1], planes[0][2], DotProduct(planes[0], origin) - 1, 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+                       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[1][0], planes[1][1], planes[1][2], DotProduct(planes[1], origin) - 1, 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
+                       numpoints = PolygonF_Clip(numpoints, points[0][0], planes[2][0], planes[2][1], planes[2][2], DotProduct(planes[2], origin) - 1, 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+                       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[3][0], planes[3][1], planes[3][2], DotProduct(planes[3], origin) - 1, 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
+                       numpoints = PolygonF_Clip(numpoints, points[0][0], planes[4][0], planes[4][1], planes[4][2], DotProduct(planes[4], origin) - 1, 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+                       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[5][0], planes[5][1], planes[5][2], DotProduct(planes[5], origin) - 1, 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), v[0]);
+                       for (cornerindex = 0;cornerindex < numpoints;cornerindex++)
+                       {
+                               // convert vertex positions to texcoords
+                               Matrix4x4_Transform(projection, v[cornerindex], tc[cornerindex]);
+                               // calculate distance fade from the projection origin
+                               f = a * (1-fabs(tc[cornerindex][2]));
+                               c[cornerindex][0] = r * f;
+                               c[cornerindex][1] = g * f;
+                               c[cornerindex][2] = b * f;
+                               c[cornerindex][3] = 1;
+                       }
+                       for (cornerindex = 0;cornerindex < numpoints-2;cornerindex++)
+                               R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[cornerindex+1], v[cornerindex+2], tc[0], tc[cornerindex+1], tc[cornerindex+2], c[0], c[cornerindex+1], c[cornerindex+2], -1);
+               }
+       }
+}
+
+extern skinframe_t *decalskinframe;
+void RSurf_DrawTriDecals(void)
 {
+       int i;
+       decalsystem_t *decalsystem = &rsurface.entity->decalsystem;
+       int numdecals = decalsystem->numdecals;
+       tridecal_t *decal = decalsystem->decals;
+       float frametime = cl.time - decalsystem->lastupdatetime;
+       float decalfade;
+       float alphascale;
+       float ca;
+       float *v3f;
+       float *c4f;
+       const int *e;
+
+       decalsystem->lastupdatetime = cl.time;
+
+       if (!decalsystem->numdecals)
+               return;
+
+       decalfade = 1.0f / max(0.001f, cl_decals_fadetime.value);
+       alphascale = (1.0f / 255.0f);
+
+       if (rsurface.ent_color[3] < 1 || (rsurface.ent_flags & RENDER_ADDITIVE))
+       {
+               Mem_Free(decalsystem->decals);
+               memset(decalsystem, 0, sizeof(*decalsystem));
+               return;
+       }
+
+       if (decalfade < 0)
+               decalfade = 0;
+
+       for (i = 0, decal = decalsystem->decals;i < numdecals;i++, decal++)
+       {
+               if (!decal->alpha)
+                       continue;
+
+               decal->fade -= frametime;
+               if (decal->fade <= 0)
+               {
+                       decal->alpha -= decalfade;
+                       if (decal->alpha <= 0)
+                       {
+                               // kill the decal by zeroing vertex data
+                               memset(decalsystem->vertex3f + 9*i, 0, sizeof(float[3][3]));
+                               memset(decalsystem->texcoord2f + 6*i, 0, sizeof(float[3][2]));
+                               memset(decalsystem->color4f + 12*i, 0, sizeof(float[3][4]));
+                               memset(decal, 0, sizeof(*decal));
+                               if (decalsystem->freedecal > i)
+                                       decalsystem->freedecal = i;
+                               continue;
+                       }
+               }
+
+               // update color values for fading decals
+               ca = decal->alpha * alphascale;
+               c4f = decalsystem->color4f + 12*i;
+               c4f[ 0] = decal->colors[0][0] * ca;
+               c4f[ 1] = decal->colors[0][1] * ca;
+               c4f[ 2] = decal->colors[0][2] * ca;
+               c4f[ 3] = 1;
+               c4f[ 4] = decal->colors[1][0] * ca;
+               c4f[ 5] = decal->colors[1][1] * ca;
+               c4f[ 6] = decal->colors[1][2] * ca;
+               c4f[ 7] = 1;
+               c4f[ 8] = decal->colors[2][0] * ca;
+               c4f[ 9] = decal->colors[2][1] * ca;
+               c4f[10] = decal->colors[2][2] * ca;
+               c4f[11] = 1;
+
+               // update vertex positions for animated models
+               if (decal->triangleindex >= 0 && decal->triangleindex < rsurface.modelnum_triangles)
+               {
+                       e = rsurface.modelelement3i + 3*decal->triangleindex;
+                       v3f = decalsystem->vertex3f + i*i;
+                       VectorCopy(rsurface.vertex3f + 3*e[0], v3f);
+                       VectorCopy(rsurface.vertex3f + 3*e[1], v3f + 3);
+                       VectorCopy(rsurface.vertex3f + 3*e[2], v3f + 6);
+               }
+
+               r_refdef.stats.decals++;
+       }
+
+       // reduce numdecals if possible
+       while (numdecals > 0 && !decalsystem->decals[numdecals - 1].alpha)
+               numdecals--;
+       decalsystem->numdecals = numdecals;
+
+       if (numdecals > 0)
+       {
+               // now render the decals all at once
+               // (this assumes they all use one particle font texture!)
+               RSurf_ActiveCustomEntity(&rsurface.matrix, &rsurface.inversematrix, rsurface.ent_flags, rsurface.ent_shadertime, 1, 1, 1, 1, numdecals*3, decalsystem->vertex3f, decalsystem->texcoord2f, NULL, NULL, NULL, decalsystem->color4f, numdecals, decalsystem->element3i, decalsystem->element3s, false, false);
+               R_Mesh_ResetTextureState();
+               R_Mesh_VertexPointer(decalsystem->vertex3f, 0, 0);
+               R_Mesh_TexCoordPointer(0, 2, decalsystem->texcoord2f, 0, 0);
+               R_Mesh_ColorPointer(decalsystem->color4f, 0, 0);
+               R_SetupGenericShader(true);
+               GL_DepthMask(false);
+               GL_DepthRange(0, 1);
+               GL_PolygonOffset(0, 0);
+               GL_DepthTest(true);
+               GL_CullFace(GL_NONE);
+               GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
+               R_Mesh_TexBind(0, R_GetTexture(decalskinframe->base));
+               GL_LockArrays(0, numdecals * 3);
+               R_Mesh_Draw(0, numdecals * 3, 0, numdecals, decalsystem->element3i, decalsystem->element3s, 0, 0);
+               GL_LockArrays(0, 0);
+       }
+       else
+       {
+               // if there are no decals left, reset decalsystem
+               R_DecalSystem_Reset(decalsystem);
+       }
+}
+
+void R_DrawDebugModel(void)
+{
+       entity_render_t *ent = rsurface.entity;
        int i, j, k, l, flagsmask;
        const int *elements;
        q3mbrush_t *brush;
-       msurface_t *surface;
+       const msurface_t *surface;
        dp_model_t *model = ent->model;
        vec3_t v;
 
@@ -7772,12 +8485,12 @@ void R_DrawDebugModel(entity_render_t *ent)
                                                GL_Color(r_refdef.view.colorscale, r_refdef.view.colorscale, r_refdef.view.colorscale, r_showtris.value);
                                        else
                                                GL_Color(0, r_refdef.view.colorscale, 0, r_showtris.value);
-                                       elements = (ent->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);
+                                       elements = (model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);
                                        R_Mesh_VertexPointer(rsurface.vertex3f, 0, 0);
                                        R_Mesh_ColorPointer(NULL, 0, 0);
                                        R_Mesh_TexCoordPointer(0, 0, NULL, 0, 0);
                                        qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-                                       //R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, ent->model->surfmesh.data_element3i, NULL, 0, 0);
+                                       //R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, model->surfmesh.data_element3i, NULL, 0, 0);
                                        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);
                                        qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
                                        CHECKGLERROR
@@ -7844,7 +8557,7 @@ 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;
+const msurface_t **r_surfacelist = NULL;
 void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean debug)
 {
        int i, j, endj, f, flagsmask;
@@ -7861,7 +8574,7 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
                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));
+               r_surfacelist = (const msurface_t **) Mem_Alloc(r_main_mempool, r_maxsurfacelist * sizeof(*r_surfacelist));
        }
 
        RSurf_ActiveWorldEntity();
@@ -7889,7 +8602,7 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
 
        if (debug)
        {
-               R_DrawDebugModel(r_refdef.scene.worldentity);
+               R_DrawDebugModel();
                rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
                return;
        }
@@ -7929,6 +8642,10 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
                for (j = 0;j < numsurfacelist;j++)
                        r_refdef.stats.world_triangles += r_surfacelist[j]->num_triangles;
        }
+
+       // draw decals
+       RSurf_DrawTriDecals();
+
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
@@ -7948,7 +8665,7 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                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));
+               r_surfacelist = (const 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
@@ -7984,7 +8701,7 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
 
        if (debug)
        {
-               R_DrawDebugModel(ent);
+               R_DrawDebugModel();
                rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
                return;
        }
@@ -8019,5 +8736,36 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                for (j = 0;j < numsurfacelist;j++)
                        r_refdef.stats.entities_triangles += r_surfacelist[j]->num_triangles;
        }
+
+       // draw decals
+       RSurf_DrawTriDecals();
+
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
+
+void R_DrawCustomSurface(skinframe_t *skinframe, const matrix4x4_t *texmatrix, int materialflags, int firstvertex, int numvertices, int firsttriangle, int numtriangles, qboolean writedepth)
+{
+       static texture_t texture;
+       static msurface_t surface;
+       const msurface_t *surfacelist = &surface;
+
+       // fake enough texture and surface state to render this geometry
+
+       texture.update_lastrenderframe = -1; // regenerate this texture
+       texture.basematerialflags = materialflags | MATERIALFLAG_CUSTOMSURFACE | MATERIALFLAG_WALL;
+       texture.currentskinframe = skinframe;
+       texture.currenttexmatrix = *texmatrix; // requires MATERIALFLAG_CUSTOMSURFACE
+       texture.specularscalemod = 1;
+       texture.specularpowermod = 1;
+
+       surface.texture = &texture;
+       surface.num_triangles = numtriangles;
+       surface.num_firsttriangle = firsttriangle;
+       surface.num_vertices = numvertices;
+       surface.num_firstvertex = firstvertex;
+
+       // now render it
+       rsurface.texture = R_GetCurrentTexture(surface.texture);
+       rsurface.uselightmaptexture = false;
+       R_DrawModelTextureSurfaceList(1, &surfacelist, writedepth);
+}