]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
Fixed a bug in the r_shadow light entity parser which caused it to not read in
[xonotic/darkplaces.git] / r_shadow.c
index 2779a310075d149acf878ba885af6cf4e8c72259..d9553cb9cee06866e1a052f2f647c170dd86ceb9 100644 (file)
@@ -7,23 +7,25 @@ as far as the light's radius, if the light has a radius at all), capped at
 both front and back to avoid any problems (extrusion from dark faces also
 works but has a different set of problems)
 
-This is rendered using Carmack's Reverse technique, in which backfaces behind
-zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
-decrement the stencil, the result is a stencil value of zero where shadows
-did not intersect the visible geometry, suitable as a stencil mask for
-rendering lighting everywhere but shadow.
-
-In our case we use a biased stencil clear of 128 to avoid requiring the
-stencil wrap extension (but probably should support it), and to address
-Creative's patent on this sort of technology we also draw the frontfaces
-first, and backfaces second (decrement, increment).
+This is normally rendered using Carmack's Reverse technique, in which
+backfaces behind zbuffer (zfail) increment the stencil, and frontfaces behind
+zbuffer (zfail) decrement the stencil, the result is a stencil value of zero
+where shadows did not intersect the visible geometry, suitable as a stencil
+mask for rendering lighting everywhere but shadow.
+
+In our case to hopefully avoid the Creative Labs patent, we draw the backfaces
+as decrement and the frontfaces as increment, and we redefine the DepthFunc to
+GL_LESS (the patent uses GL_GEQUAL) which causes zfail when behind surfaces
+and zpass when infront (the patent draws where zpass with a GL_GEQUAL test),
+additionally we clear stencil to 128 to avoid the need for the unclamped
+incr/decr extension (not related to patent).
 
 Patent warning:
-This algorithm may be covered by Creative's patent (US Patent #6384822)
-on Carmack's Reverse paper (which I have not read), however that patent
-seems to be about drawing a stencil shadow from a model in an otherwise
-unshadowed scene, where as realtime lighting technology draws light where
-shadows do not lie.
+This algorithm may be covered by Creative's patent (US Patent #6384822),
+however that patent is quite specific about increment on backfaces and
+decrement on frontfaces where zpass with GL_GEQUAL depth test, which is
+opposite this implementation and partially opposite Carmack's Reverse paper
+(which uses GL_LESS, but increments on backfaces and decrements on frontfaces).
 
 
 
@@ -53,7 +55,8 @@ NormalMap) if supported by hardware; in our case there is support for cards
 which are incapable of DOT3, the quality is quite poor however.  Additionally
 it is desirable to have specular evaluation per pixel, per vertex
 normalization of specular halfangle vectors causes noticable distortion but
-is unavoidable on hardware without GL_ARB_fragment_program.
+is unavoidable on hardware without GL_ARB_fragment_program or
+GL_ARB_fragment_shader.
 
 
 
@@ -61,18 +64,29 @@ Terminology: Normalization CubeMap
 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
 encoded as RGB colors) for any possible direction, this technique allows per
 pixel calculation of incidence vector for per pixel lighting purposes, which
-would not otherwise be possible per pixel without GL_ARB_fragment_program.
+would not otherwise be possible per pixel without GL_ARB_fragment_program or
+GL_ARB_fragment_shader.
 
 
 
-Terminology: 2D Attenuation Texturing
+Terminology: 2D+1D Attenuation Texturing
 A very crude approximation of light attenuation with distance which results
 in cylindrical light shapes which fade vertically as a streak (some games
 such as Doom3 allow this to be rotated to be less noticable in specific
 cases), the technique is simply modulating lighting by two 2D textures (which
 can be the same) on different axes of projection (XY and Z, typically), this
-is the best technique available without 3D Attenuation Texturing or
-GL_ARB_fragment_program technology.
+is the second best technique available without 3D Attenuation Texturing,
+GL_ARB_fragment_program or GL_ARB_fragment_shader technology.
+
+
+
+Terminology: 2D+1D Inverse Attenuation Texturing
+A clever method described in papers on the Abducted engine, this has a squared
+distance texture (bright on the outside, black in the middle), which is used
+twice using GL_ADD blending, the result of this is used in an inverse modulate
+(GL_ONE_MINUS_DST_ALPHA, GL_ZERO) to implement the equation
+lighting*=(1-((X*X+Y*Y)+(Z*Z))) which is spherical (unlike 2D+1D attenuation
+texturing).
 
 
 
@@ -91,8 +105,13 @@ diffuse lighting if 3D Attenuation Textures are already used.
 
 Terminology: Light Cubemap Filtering
 A technique for modeling non-uniform light distribution according to
-direction, for example projecting a stained glass window image onto a wall,
-this is done by texturing the lighting with a cubemap.
+direction, for example a lantern may use a cubemap to describe the light
+emission pattern of the cage around the lantern (as well as soot buildup
+discoloring the light in certain areas), often also used for softened grate
+shadows and light shining through a stained glass window (done crudely by
+texturing the lighting with a cubemap), another good example would be a disco
+light.  This technique is used heavily in many games (Doom3 does not support
+this however).
 
 
 
@@ -101,14 +120,18 @@ A technique for modeling shadowing of light passing through translucent
 surfaces, allowing stained glass windows and other effects to be done more
 elegantly than possible with Light Cubemap Filtering by applying an occluder
 texture to the lighting combined with a stencil light volume to limit the lit
-area (this allows evaluating multiple translucent occluders in a scene).
+area, this technique is used by Doom3 for spotlights and flashlights, among
+other things, this can also be used more generally to render light passing
+through multiple translucent occluders in a scene (using a light volume to
+describe the area beyond the occluder, and thus mask off rendering of all
+other areas).
 
 
 
 Terminology: Doom3 Lighting
 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
-CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
-the (currently upcoming) game Doom3.
+CubeMap, 2D+1D Attenuation Texturing, and Light Projection Filtering, as
+demonstrated by the game Doom3.
 */
 
 #include "quakedef.h"
@@ -197,9 +220,11 @@ cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
 cvar_t r_shadow_visiblelighting = {0, "r_shadow_visiblelighting", "0"};
 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
 cvar_t r_shadow_glsl = {0, "r_shadow_glsl", "1"};
-cvar_t r_shadow_glsl_offsetmapping = {0, "r_shadow_glsl_offsetmapping", "1"};
+cvar_t r_shadow_glsl_offsetmapping = {0, "r_shadow_glsl_offsetmapping", "0"};
 cvar_t r_shadow_glsl_offsetmapping_scale = {0, "r_shadow_glsl_offsetmapping_scale", "-0.04"};
 cvar_t r_shadow_glsl_offsetmapping_bias = {0, "r_shadow_glsl_offsetmapping_bias", "0.04"};
+cvar_t r_shadow_glsl_usehalffloat = {0, "r_shadow_glsl_usehalffloat", "0"};
+cvar_t r_shadow_glsl_surfacenormalize = {0, "r_shadow_glsl_surfacenormalize", "1"};
 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
 cvar_t r_editlights = {0, "r_editlights", "0"};
 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024"};
@@ -207,8 +232,6 @@ cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0"};
 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4"};
 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4"};
 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
-cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
-cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
 
 float r_shadow_attenpower, r_shadow_attenscale;
 
@@ -237,7 +260,9 @@ static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
 #define SHADERPERMUTATION_FOG (1<<1)
 #define SHADERPERMUTATION_CUBEFILTER (1<<2)
 #define SHADERPERMUTATION_OFFSETMAPPING (1<<3)
-#define SHADERPERMUTATION_COUNT (1<<4)
+#define SHADERPERMUTATION_SURFACENORMALIZE (1<<4)
+#define SHADERPERMUTATION_GEFORCEFX (1<<5)
+#define SHADERPERMUTATION_COUNT (1<<6)
 
 GLhandleARB r_shadow_program_light[SHADERPERMUTATION_COUNT];
 
@@ -304,22 +329,29 @@ const char *builtinshader_light_frag =
 "// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
 "// written by Forest 'LordHavoc' Hale\n"
 "\n"
-"uniform vec3 LightColor;\n"
+"// use half floats on GEFORCEFX for math performance, otherwise don't\n"
+"#ifndef GEFORCEFX\n"
+"#define half float\n"
+"#define hvec2 vec2\n"
+"#define hvec3 vec3\n"
+"#define hvec4 vec4\n"
+"#endif\n"
 "\n"
+"uniform hvec3 LightColor;\n"
 "#ifdef USEOFFSETMAPPING\n"
-"uniform float OffsetMapping_Scale;\n"
-"uniform float OffsetMapping_Bias;\n"
+"uniform half OffsetMapping_Scale;\n"
+"uniform half OffsetMapping_Bias;\n"
 "#endif\n"
 "#ifdef USESPECULAR\n"
-"uniform float SpecularPower;\n"
+"uniform half SpecularPower;\n"
 "#endif\n"
 "#ifdef USEFOG\n"
-"uniform float FogRangeRecip;\n"
+"uniform half FogRangeRecip;\n"
 "#endif\n"
-"uniform float AmbientScale;\n"
-"uniform float DiffuseScale;\n"
+"uniform half AmbientScale;\n"
+"uniform half DiffuseScale;\n"
 "#ifdef USESPECULAR\n"
-"uniform float SpecularScale;\n"
+"uniform half SpecularScale;\n"
 "#endif\n"
 "\n"
 "uniform sampler2D Texture_Normal;\n"
@@ -351,49 +383,53 @@ const char *builtinshader_light_frag =
 "      //\n"
 "      // pow(1-(x*x+y*y+z*z), 4) is far more realistic but needs large lights to\n"
 "      // provide significant illumination, large = slow = pain.\n"
-"      float colorscale = max(1.0 - dot(CubeVector, CubeVector), 0.0);\n"
+"      half colorscale = max(1.0 - dot(CubeVector, CubeVector), 0.0);\n"
 "\n"
 "#ifdef USEFOG\n"
 "      // apply fog\n"
-"      colorscale *= texture2D(Texture_FogMask, vec2(length(EyeVector)*FogRangeRecip, 0)).x;\n"
+"      colorscale *= texture2D(Texture_FogMask, hvec2(length(EyeVector)*FogRangeRecip, 0)).x;\n"
 "#endif\n"
 "\n"
 "#ifdef USEOFFSETMAPPING\n"
 "      // this is 3 sample because of ATI Radeon 9500-9800/X300 limits\n"
-"      vec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
-"      vec2 TexCoordOffset = TexCoord + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).w);\n"
+"      hvec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
+"      hvec2 TexCoordOffset = TexCoord + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).w);\n"
 "      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
 "      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
 "#define TexCoord TexCoordOffset\n"
 "#endif\n"
 "\n"
-"      // get the texels - with a blendmap we'd need to blend multiple here\n"
-"      vec3 surfacenormal = -1.0 + 2.0 * vec3(texture2D(Texture_Normal, TexCoord));\n"
-"      vec3 colortexel = vec3(texture2D(Texture_Color, TexCoord));\n"
-"#ifdef USESPECULAR\n"
-"      vec3 glosstexel = vec3(texture2D(Texture_Gloss, TexCoord));\n"
+"      // get the surface normal\n"
+"#ifdef SURFACENORMALIZE\n"
+"      hvec3 surfacenormal = normalize(hvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
+"#else\n"
+"      hvec3 surfacenormal = -1.0 + 2.0 * hvec3(texture2D(Texture_Normal, TexCoord));\n"
 "#endif\n"
 "\n"
 "      // calculate shading\n"
-"      vec3 diffusenormal = normalize(LightVector);\n"
-"      vec3 color = colortexel * (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"      hvec3 diffusenormal = hvec3(normalize(LightVector));\n"
+"      hvec3 color = hvec3(texture2D(Texture_Color, TexCoord)) * (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
 "#ifdef USESPECULAR\n"
-"      color += glosstexel * (SpecularScale * pow(max(dot(surfacenormal, normalize(diffusenormal + normalize(EyeVector))), 0.0), SpecularPower));\n"
+"      hvec3 specularnormal = hvec3(normalize(diffusenormal + hvec3(normalize(EyeVector))));\n"
+"      color += hvec3(texture2D(Texture_Gloss, TexCoord)) * (SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower));\n"
 "#endif\n"
 "\n"
 "#ifdef USECUBEFILTER\n"
 "      // apply light cubemap filter\n"
-"      color *= vec3(textureCube(Texture_Cube, CubeVector));\n"
+"      color *= hvec3(textureCube(Texture_Cube, CubeVector));\n"
 "#endif\n"
 "\n"
-"      // calculate fragment color\n"
-"      gl_FragColor = vec4(LightColor * color * colorscale, 1);\n"
+"      // calculate fragment color (apply light color and attenuation/fog scaling)\n"
+"      gl_FragColor = hvec4(color * LightColor * colorscale, 1);\n"
 "}\n"
 ;
 
 void r_shadow_start(void)
 {
        int i;
+       // use half float math where available (speed gain on NVIDIA GFFX and GF6)
+       if (gl_support_half_float)
+               Cvar_SetValue("r_shadow_glsl_usehalffloat", 1);
        // allocate vertex processing arrays
        numcubemaps = 0;
        r_shadow_attenuation2dtexture = NULL;
@@ -426,10 +462,10 @@ void r_shadow_start(void)
                char *vertstring, *fragstring;
                int vertstrings_count;
                int fragstrings_count;
-               const char *vertstrings_list[SHADERPERMUTATION_COUNT];
-               const char *fragstrings_list[SHADERPERMUTATION_COUNT];
-               vertstring = FS_LoadFile("glsl/light.vert", tempmempool, false);
-               fragstring = FS_LoadFile("glsl/light.frag", tempmempool, false);
+               const char *vertstrings_list[SHADERPERMUTATION_COUNT+1];
+               const char *fragstrings_list[SHADERPERMUTATION_COUNT+1];
+               vertstring = (char *)FS_LoadFile("glsl/light.vert", tempmempool, false);
+               fragstring = (char *)FS_LoadFile("glsl/light.frag", tempmempool, false);
                for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
                {
                        vertstrings_count = 0;
@@ -454,12 +490,22 @@ void r_shadow_start(void)
                                vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING\n";
                                fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING\n";
                        }
+                       if (i & SHADERPERMUTATION_SURFACENORMALIZE)
+                       {
+                               vertstrings_list[vertstrings_count++] = "#define SURFACENORMALIZE\n";
+                               fragstrings_list[fragstrings_count++] = "#define SURFACENORMALIZE\n";
+                       }
+                       if (i & SHADERPERMUTATION_GEFORCEFX)
+                       {
+                               vertstrings_list[vertstrings_count++] = "#define GEFORCEFX\n";
+                               fragstrings_list[fragstrings_count++] = "#define GEFORCEFX\n";
+                       }
                        vertstrings_list[vertstrings_count++] = vertstring ? vertstring : builtinshader_light_vert;
                        fragstrings_list[fragstrings_count++] = fragstring ? fragstring : builtinshader_light_frag;
                        r_shadow_program_light[i] = GL_Backend_CompileProgram(vertstrings_count, vertstrings_list, fragstrings_count, fragstrings_list);
                        if (!r_shadow_program_light[i])
                        {
-                               Con_Printf("permutation %s %s %s %s failed for shader %s, some features may not work properly!\n", i & 1 ? "specular" : "", i & 2 ? "fog" : "", i & 4 ? "cubefilter" : "", i & 8 ? "offsetmapping" : "", "glsl/light");
+                               Con_Printf("permutation %s %s %s %s %s %s failed for shader %s, some features may not work properly!\n", i & 1 ? "specular" : "", i & 2 ? "fog" : "", i & 4 ? "cubefilter" : "", i & 8 ? "offsetmapping" : "", i & 16 ? "surfacenormalize" : "", i & 32 ? "geforcefx" : "", "glsl/light");
                                continue;
                        }
                        qglUseProgramObjectARB(r_shadow_program_light[i]);
@@ -574,6 +620,8 @@ void R_Shadow_Help_f(void)
 "r_shadow_glsl_offsetmapping : enables Offset Mapping bumpmap enhancement\n"
 "r_shadow_glsl_offsetmapping_scale : controls depth of Offset Mapping\n"
 "r_shadow_glsl_offsetmapping_bias : should be negative half of scale\n"
+"r_shadow_glsl_usehalffloat : use lower quality lighting\n"
+"r_shadow_glsl_surfacenormalize : makes bumpmapping slightly higher quality\n"
 "r_shadow_scissor : use scissor optimization\n"
 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
@@ -620,6 +668,8 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping);
        Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping_scale);
        Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping_bias);
+       Cvar_RegisterVariable(&r_shadow_glsl_usehalffloat);
+       Cvar_RegisterVariable(&r_shadow_glsl_surfacenormalize);
        Cvar_RegisterVariable(&gl_ext_stenciltwoside);
        if (gamemode == GAME_TENEBRAE)
        {
@@ -650,7 +700,7 @@ void R_Shadow_Init(void)
        R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
 }
 
-static matrix4x4_t matrix_attenuationxyz =
+matrix4x4_t matrix_attenuationxyz =
 {
        {
                {0.5, 0.0, 0.0, 0.5},
@@ -660,7 +710,7 @@ static matrix4x4_t matrix_attenuationxyz =
        }
 };
 
-static matrix4x4_t matrix_attenuationz =
+matrix4x4_t matrix_attenuationz =
 {
        {
                {0.0, 0.0, 0.5, 0.5},
@@ -923,15 +973,15 @@ void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *verte
        GL_LockArrays(0, numvertices);
        if (r_shadowstage == R_SHADOWSTAGE_STENCIL)
        {
-               // increment stencil if backface is behind depthbuffer
+               // decrement stencil if backface is behind depthbuffer
                qglCullFace(GL_BACK); // quake is backwards, this culls front faces
-               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
+               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
                R_Mesh_Draw(0, numvertices, numtriangles, element3i);
                c_rt_shadowmeshes++;
                c_rt_shadowtris += numtriangles;
-               // decrement stencil if frontface is behind depthbuffer
+               // increment stencil if frontface is behind depthbuffer
                qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
        }
        R_Mesh_Draw(0, numvertices, numtriangles, element3i);
        c_rt_shadowmeshes++;
@@ -1089,7 +1139,7 @@ void R_Shadow_Stage_StencilShadowVolumes(void)
        //}
        //else
        //      qglDisable(GL_POLYGON_OFFSET_FILL);
-       qglDepthFunc(GL_GEQUAL);
+       qglDepthFunc(GL_LESS);
        qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
        qglEnable(GL_STENCIL_TEST);
        qglStencilFunc(GL_ALWAYS, 128, ~0);
@@ -1100,10 +1150,10 @@ void R_Shadow_Stage_StencilShadowVolumes(void)
                qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
                qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
                qglStencilMask(~0);
-               qglStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
+               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
                qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
                qglStencilMask(~0);
-               qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
+               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
        }
        else
        {
@@ -1164,7 +1214,7 @@ void R_Shadow_Stage_Lighting(int stenciltest)
                // only add a feature to the permutation if that permutation exists
                // (otherwise it might end up not using a shader at all, which looks
                // worse than using less features)
-               if (r_shadow_rtlight->specularscale && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_SPECULAR])
+               if (r_shadow_rtlight->specularscale && r_shadow_gloss.integer >= 1 && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_SPECULAR])
                        r_shadow_lightpermutation |= SHADERPERMUTATION_SPECULAR;
                //if (fog && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_FOG])
                //      r_shadow_lightpermutation |= SHADERPERMUTATION_FOG;
@@ -1172,6 +1222,10 @@ void R_Shadow_Stage_Lighting(int stenciltest)
                        r_shadow_lightpermutation |= SHADERPERMUTATION_CUBEFILTER;
                if (r_shadow_glsl_offsetmapping.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_OFFSETMAPPING])
                        r_shadow_lightpermutation |= SHADERPERMUTATION_OFFSETMAPPING;
+               if (r_shadow_glsl_surfacenormalize.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_SURFACENORMALIZE])
+                       r_shadow_lightpermutation |= SHADERPERMUTATION_SURFACENORMALIZE;
+               if (r_shadow_glsl_usehalffloat.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_GEFORCEFX])
+                       r_shadow_lightpermutation |= SHADERPERMUTATION_GEFORCEFX;
                r_shadow_lightprog = r_shadow_program_light[r_shadow_lightpermutation];
                qglUseProgramObjectARB(r_shadow_lightprog);CHECKGLERROR
                // TODO: support fog (after renderer is converted to texture fog)
@@ -1198,7 +1252,7 @@ void R_Shadow_Stage_Lighting(int stenciltest)
                        qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "OffsetMapping_Bias"), r_shadow_glsl_offsetmapping_bias.value);CHECKGLERROR
                }
        }
-       else if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
+       else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
                r_shadowstage = R_SHADOWSTAGE_LIGHT_DOT3;
        else
                r_shadowstage = R_SHADOWSTAGE_LIGHT_VERTEX;
@@ -1262,171 +1316,90 @@ void R_Shadow_Stage_End(void)
        r_shadowstage = R_SHADOWSTAGE_NONE;
 }
 
-int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
+qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
 {
        int i, ix1, iy1, ix2, iy2;
-       float x1, y1, x2, y2, x, y, f;
-       vec3_t smins, smaxs;
+       float x1, y1, x2, y2;
        vec4_t v, v2;
-       if (!r_shadow_scissor.integer)
-               return false;
-       // if view is inside the box, just say yes it's visible
+       rmesh_t mesh;
+       mplane_t planes[11];
+       float vertex3f[256*3];
+
+       // if view is inside the light box, just say yes it's visible
        if (BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
        {
                GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
                return false;
        }
-       for (i = 0;i < 3;i++)
+
+       // create a temporary brush describing the area the light can affect in worldspace
+       VectorNegate(frustum[0].normal, planes[ 0].normal);planes[ 0].dist = -frustum[0].dist;
+       VectorNegate(frustum[1].normal, planes[ 1].normal);planes[ 1].dist = -frustum[1].dist;
+       VectorNegate(frustum[2].normal, planes[ 2].normal);planes[ 2].dist = -frustum[2].dist;
+       VectorNegate(frustum[3].normal, planes[ 3].normal);planes[ 3].dist = -frustum[3].dist;
+       VectorNegate(frustum[4].normal, planes[ 4].normal);planes[ 4].dist = -frustum[4].dist;
+       VectorSet   (planes[ 5].normal,  1, 0, 0);         planes[ 5].dist =  maxs[0];
+       VectorSet   (planes[ 6].normal, -1, 0, 0);         planes[ 6].dist = -mins[0];
+       VectorSet   (planes[ 7].normal, 0,  1, 0);         planes[ 7].dist =  maxs[1];
+       VectorSet   (planes[ 8].normal, 0, -1, 0);         planes[ 8].dist = -mins[1];
+       VectorSet   (planes[ 9].normal, 0, 0,  1);         planes[ 9].dist =  maxs[2];
+       VectorSet   (planes[10].normal, 0, 0, -1);         planes[10].dist = -mins[2];
+
+       // turn the brush into a mesh
+       memset(&mesh, 0, sizeof(rmesh_t));
+       mesh.maxvertices = 256;
+       mesh.vertex3f = vertex3f;
+       mesh.epsilon2 = (1.0f / (32.0f * 32.0f));
+       R_Mesh_AddBrushMeshFromPlanes(&mesh, 11, planes);
+
+       // if that mesh is empty, the light is not visible at all
+       if (!mesh.numvertices)
+               return true;
+
+       if (!r_shadow_scissor.integer)
+               return false;
+
+       // if that mesh is not empty, check what area of the screen it covers
+       x1 = y1 = x2 = y2 = 0;
+       v[3] = 1.0f;
+       for (i = 0;i < mesh.numvertices;i++)
        {
-               if (r_viewforward[i] >= 0)
+               VectorCopy(mesh.vertex3f + i * 3, v);
+               GL_TransformToScreen(v, v2);
+               //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
+               if (i)
                {
-                       v[i] = mins[i];
-                       v2[i] = maxs[i];
+                       if (x1 > v2[0]) x1 = v2[0];
+                       if (x2 < v2[0]) x2 = v2[0];
+                       if (y1 > v2[1]) y1 = v2[1];
+                       if (y2 < v2[1]) y2 = v2[1];
                }
                else
                {
-                       v[i] = maxs[i];
-                       v2[i] = mins[i];
+                       x1 = x2 = v2[0];
+                       y1 = y2 = v2[1];
                }
        }
-       f = DotProduct(r_viewforward, r_vieworigin) + 1;
-       if (DotProduct(r_viewforward, v2) <= f)
-       {
-               // entirely behind nearclip plane
-               return true;
-       }
-       if (DotProduct(r_viewforward, v) >= f)
-       {
-               // entirely infront of nearclip plane
-               x1 = y1 = x2 = y2 = 0;
-               for (i = 0;i < 8;i++)
-               {
-                       v[0] = (i & 1) ? mins[0] : maxs[0];
-                       v[1] = (i & 2) ? mins[1] : maxs[1];
-                       v[2] = (i & 4) ? mins[2] : maxs[2];
-                       v[3] = 1.0f;
-                       GL_TransformToScreen(v, v2);
-                       //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
-                       x = v2[0];
-                       y = v2[1];
-                       if (i)
-                       {
-                               if (x1 > x) x1 = x;
-                               if (x2 < x) x2 = x;
-                               if (y1 > y) y1 = y;
-                               if (y2 < y) y2 = y;
-                       }
-                       else
-                       {
-                               x1 = x2 = x;
-                               y1 = y2 = y;
-                       }
-               }
-       }
-       else
-       {
-               // clipped by nearclip plane
-               // this is nasty and crude...
-               // create viewspace bbox
-               for (i = 0;i < 8;i++)
-               {
-                       v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
-                       v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
-                       v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
-                       v2[0] = -DotProduct(v, r_viewleft);
-                       v2[1] = DotProduct(v, r_viewup);
-                       v2[2] = DotProduct(v, r_viewforward);
-                       if (i)
-                       {
-                               if (smins[0] > v2[0]) smins[0] = v2[0];
-                               if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
-                               if (smins[1] > v2[1]) smins[1] = v2[1];
-                               if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
-                               if (smins[2] > v2[2]) smins[2] = v2[2];
-                               if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
-                       }
-                       else
-                       {
-                               smins[0] = smaxs[0] = v2[0];
-                               smins[1] = smaxs[1] = v2[1];
-                               smins[2] = smaxs[2] = v2[2];
-                       }
-               }
-               // now we have a bbox in viewspace
-               // clip it to the view plane
-               if (smins[2] < 1)
-                       smins[2] = 1;
-               // return true if that culled the box
-               if (smins[2] >= smaxs[2])
-                       return true;
-               // ok some of it is infront of the view, transform each corner back to
-               // worldspace and then to screenspace and make screen rect
-               // initialize these variables just to avoid compiler warnings
-               x1 = y1 = x2 = y2 = 0;
-               for (i = 0;i < 8;i++)
-               {
-                       v2[0] = (i & 1) ? smins[0] : smaxs[0];
-                       v2[1] = (i & 2) ? smins[1] : smaxs[1];
-                       v2[2] = (i & 4) ? smins[2] : smaxs[2];
-                       v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
-                       v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
-                       v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
-                       v[3] = 1.0f;
-                       GL_TransformToScreen(v, v2);
-                       //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
-                       x = v2[0];
-                       y = v2[1];
-                       if (i)
-                       {
-                               if (x1 > x) x1 = x;
-                               if (x2 < x) x2 = x;
-                               if (y1 > y) y1 = y;
-                               if (y2 < y) y2 = y;
-                       }
-                       else
-                       {
-                               x1 = x2 = x;
-                               y1 = y2 = y;
-                       }
-               }
-               /*
-               // this code doesn't handle boxes with any points behind view properly
-               x1 = 1000;x2 = -1000;
-               y1 = 1000;y2 = -1000;
-               for (i = 0;i < 8;i++)
-               {
-                       v[0] = (i & 1) ? mins[0] : maxs[0];
-                       v[1] = (i & 2) ? mins[1] : maxs[1];
-                       v[2] = (i & 4) ? mins[2] : maxs[2];
-                       v[3] = 1.0f;
-                       GL_TransformToScreen(v, v2);
-                       //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
-                       if (v2[2] > 0)
-                       {
-                               x = v2[0];
-                               y = v2[1];
 
-                               if (x1 > x) x1 = x;
-                               if (x2 < x) x2 = x;
-                               if (y1 > y) y1 = y;
-                               if (y2 < y) y2 = y;
-                       }
-               }
-               */
-       }
+       // now convert the scissor rectangle to integer screen coordinates
        ix1 = x1 - 1.0f;
        iy1 = y1 - 1.0f;
        ix2 = x2 + 1.0f;
        iy2 = y2 + 1.0f;
        //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
+
+       // clamp it to the screen
        if (ix1 < r_view_x) ix1 = r_view_x;
        if (iy1 < r_view_y) iy1 = r_view_y;
        if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
        if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
+
+       // if it is inside out, it's not visible
        if (ix2 <= ix1 || iy2 <= iy1)
                return true;
-       // set up the scissor rectangle
-       GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
+
+       // the light area is visible, set up the scissor rectangle
+       GL_Scissor(ix1, vid.height - iy2, ix2 - ix1, iy2 - iy1);
        //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
        //qglEnable(GL_SCISSOR_TEST);
        c_rt_scissored++;
@@ -1614,9 +1587,9 @@ static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numve
        for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
        {
                VectorSubtract(vertex3f, relativelightorigin, lightdir);
-               VectorNormalizeFast(lightdir);
+               VectorNormalize(lightdir);
                VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
-               VectorNormalizeFast(eyedir);
+               VectorNormalize(eyedir);
                VectorAdd(lightdir, eyedir, halfdir);
                // the cubemap normalizes this for us
                out3f[0] = DotProduct(svector3f, halfdir);
@@ -1630,7 +1603,7 @@ void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles,
        int renders;
        float color[3], color2[3], colorscale, specularscale;
        rmeshstate_t m;
-       // FIXME: support EF_NODEPTHTEST
+       // FIXME: support MATERIALFLAG_NODEPTHTEST
        if (!basetexture)
                basetexture = r_texture_white;
        if (!bumptexture)
@@ -1657,8 +1630,16 @@ void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles,
        {
                int passes = 0;
                if (r_shadow_glsl.integer && r_shadow_program_light[0])
-                       passes++; // GLSL shader path (GFFX5200, Radeon 9500)
-               else if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
+               {
+                       // GLSL shader path (GFFX5200, Radeon 9500)
+                       // TODO: add direct pants/shirt rendering
+                       if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
+                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, NULL, NULL, bumptexture, NULL);
+                       if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
+                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, NULL, NULL, bumptexture, NULL);
+                       passes++;
+               }
+               else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
                {
                        // TODO: add direct pants/shirt rendering
                        if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
@@ -1770,6 +1751,11 @@ void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles,
        else if (r_shadowstage == R_SHADOWSTAGE_LIGHT_GLSL)
        {
                // GLSL shader path (GFFX5200, Radeon 9500)
+               // TODO: add direct pants/shirt rendering
+               if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
+                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, NULL, NULL, bumptexture, NULL);
+               if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
+                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, NULL, NULL, bumptexture, NULL);
                R_Mesh_VertexPointer(vertex3f);
                R_Mesh_TexCoordPointer(0, 2, texcoord2f);
                R_Mesh_TexCoordPointer(1, 3, svector3f);
@@ -1787,23 +1773,6 @@ void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles,
                R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                c_rt_lightmeshes++;
                c_rt_lighttris += numtriangles;
-               // TODO: add direct pants/shirt rendering
-               if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
-               {
-                       R_Mesh_TexBind(1, R_GetTexture(pantstexture));
-                       qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightColor"), lightcolorpants[0], lightcolorpants[1], lightcolorpants[2]);CHECKGLERROR
-                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-               }
-               if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
-               {
-                       R_Mesh_TexBind(1, R_GetTexture(shirttexture));
-                       qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightColor"), lightcolorshirt[0], lightcolorshirt[1], lightcolorshirt[2]);CHECKGLERROR
-                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-               }
                GL_LockArrays(0, 0);
        }
        else if (r_shadowstage == R_SHADOWSTAGE_LIGHT_DOT3)
@@ -2690,7 +2659,7 @@ void R_RTLight_Compile(rtlight_t *rtlight)
        {
                // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
                r_shadow_compilingrtlight = rtlight;
-               R_Shadow_EnlargeLeafSurfaceBuffer(model->brush.num_leafs, model->brush.num_surfaces);
+               R_Shadow_EnlargeLeafSurfaceBuffer(model->brush.num_leafs, model->num_surfaces);
                model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
                numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
                data = Mem_Alloc(r_shadow_mempool, sizeof(int) * numleafs + numleafpvsbytes + sizeof(int) * numsurfaces);
@@ -2800,15 +2769,15 @@ void R_Shadow_DrawEntityShadow(entity_render_t *ent, rtlight_t *rtlight, int num
                                GL_LockArrays(0, mesh->numverts);
                                if (r_shadowstage == R_SHADOWSTAGE_STENCIL)
                                {
-                                       // increment stencil if backface is behind depthbuffer
+                                       // decrement stencil if backface is behind depthbuffer
                                        qglCullFace(GL_BACK); // quake is backwards, this culls front faces
-                                       qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
+                                       qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
                                        R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
                                        c_rtcached_shadowmeshes++;
                                        c_rtcached_shadowtris += mesh->numtriangles;
-                                       // decrement stencil if frontface is behind depthbuffer
+                                       // increment stencil if frontface is behind depthbuffer
                                        qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-                                       qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+                                       qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
                                }
                                R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
                                c_rtcached_shadowmeshes++;
@@ -2930,7 +2899,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        {
                // dynamic light, world available and can receive realtime lighting
                // calculate lit surfaces and leafs
-               R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->brush.num_surfaces);
+               R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces);
                r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
                leaflist = r_shadow_buffer_leaflist;
                leafpvs = r_shadow_buffer_leafpvs;
@@ -3161,6 +3130,8 @@ rtexture_t *R_Shadow_Cubemap(const char *basename)
        numcubemaps++;
        strcpy(cubemaps[i].basename, basename);
        cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
+       if (!cubemaps[i].texture)
+               cubemaps[i].texture = r_texture_whitecube;
        return cubemaps[i].texture;
 }
 
@@ -3310,7 +3281,7 @@ void R_Shadow_LoadWorldLights(void)
        }
        FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
        strlcat (name, ".rtlights", sizeof (name));
-       lightsstring = FS_LoadFile(name, tempmempool, false);
+       lightsstring = (char *)FS_LoadFile(name, tempmempool, false);
        if (lightsstring)
        {
                s = lightsstring;
@@ -3376,8 +3347,6 @@ void R_Shadow_LoadWorldLights(void)
                                Con_Printf("found %d parameters on line %i, should be 8 or more parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style \"cubemapname\" corona angles[0] angles[1] angles[2] coronasizescale ambientscale diffusescale specularscale flags)\n", a, n + 1);
                                break;
                        }
-                       VectorScale(color, r_editlights_rtlightscolorscale.value, color);
-                       radius *= r_editlights_rtlightssizescale.value;
                        R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
                        if (*s == '\r')
                                s++;
@@ -3394,7 +3363,7 @@ void R_Shadow_LoadWorldLights(void)
 void R_Shadow_SaveWorldLights(void)
 {
        dlight_t *light;
-       int bufchars, bufmaxchars;
+       size_t bufchars, bufmaxchars;
        char *buf, *oldbuf;
        char name[MAX_QPATH];
        char line[1024];
@@ -3412,12 +3381,12 @@ void R_Shadow_SaveWorldLights(void)
        for (light = r_shadow_worldlightchain;light;light = light->next)
        {
                if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
-                       sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f %f %f %f %f %i\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2], light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
+                       sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f %f %f %f %f %i\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius, light->color[0], light->color[1], light->color[2], light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2], light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
                else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
-                       sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2]);
+                       sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius, light->color[0], light->color[1], light->color[2], light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2]);
                else
-                       sprintf(line, "%s%f %f %f %f %f %f %f %d\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style);
-               if (bufchars + (int) strlen(line) > bufmaxchars)
+                       sprintf(line, "%s%f %f %f %f %f %f %f %d\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius, light->color[0], light->color[1], light->color[2], light->style);
+               if (bufchars + strlen(line) > bufmaxchars)
                {
                        bufmaxchars = bufchars + strlen(line) + 2048;
                        oldbuf = buf;
@@ -3436,7 +3405,7 @@ void R_Shadow_SaveWorldLights(void)
                }
        }
        if (bufchars)
-               FS_WriteFile(name, buf, bufchars);
+               FS_WriteFile(name, buf, (fs_offset_t)bufchars);
        if (buf)
                Mem_Free(buf);
 }
@@ -3453,7 +3422,7 @@ void R_Shadow_LoadLightsFile(void)
        }
        FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
        strlcat (name, ".lights", sizeof (name));
-       lightsstring = FS_LoadFile(name, tempmempool, false);
+       lightsstring = (char *)FS_LoadFile(name, tempmempool, false);
        if (lightsstring)
        {
                s = lightsstring;
@@ -3509,7 +3478,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
        // try to load a .ent file first
        FS_StripExtension (r_refdef.worldmodel->name, key, sizeof (key));
        strlcat (key, ".ent", sizeof (key));
-       data = entfiledata = FS_LoadFile(key, tempmempool, true);
+       data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true);
        // and if that is not found, fall back to the bsp file entity string
        if (!data)
                data = r_refdef.worldmodel->brush.entities;
@@ -3652,6 +3621,12 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                        }
                        else if (!strcmp("style", key))
                                style = atoi(value);
+                       else if (!strcmp("skin", key))
+                               skin = (int)atof(value);
+                       else if (!strcmp("pflags", key))
+                               pflags = (int)atof(value);
+                       else if (!strcmp("effects", key))
+                               effects = (int)atof(value);
                        else if (r_refdef.worldmodel->type == mod_brushq3)
                        {
                                if (!strcmp("scale", key))
@@ -3659,12 +3634,6 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                if (!strcmp("fade", key))
                                        fadescale = atof(value);
                        }
-                       else if (!strcmp("skin", key))
-                               skin = (int)atof(value);
-                       else if (!strcmp("pflags", key))
-                               pflags = (int)atof(value);
-                       else if (!strcmp("effects", key))
-                               effects = (int)atof(value);
                }
                if (!islight)
                        continue;
@@ -3727,6 +3696,10 @@ void R_Shadow_SetCursorLocationForView(void)
                push = -push;
                VectorMA(trace.endpos, push, r_viewforward, endpos);
                VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
+       } 
+       else 
+       {
+               VectorClear( endpos );
        }
        r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
        r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
@@ -3969,6 +3942,36 @@ void R_Shadow_EditLights_Edit_f(void)
                }
                radius = atof(Cmd_Argv(2));
        }
+       else if (!strcmp(Cmd_Argv(1), "colorscale"))
+       {
+               if (Cmd_Argc() == 3)
+               {
+                       double scale = atof(Cmd_Argv(2));
+                       color[0] *= scale;
+                       color[1] *= scale;
+                       color[2] *= scale;
+               }
+               else
+               {
+                       if (Cmd_Argc() != 5)
+                       {
+                               Con_Printf("usage: r_editlights_edit %s red green blue  (OR grey instead of red green blue)\n", Cmd_Argv(1));
+                               return;
+                       }
+                       color[0] *= atof(Cmd_Argv(2));
+                       color[1] *= atof(Cmd_Argv(3));
+                       color[2] *= atof(Cmd_Argv(4));
+               }
+       }
+       else if (!strcmp(Cmd_Argv(1), "radiusscale") || !strcmp(Cmd_Argv(1), "sizescale"))
+       {
+               if (Cmd_Argc() != 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
+                       return;
+               }
+               radius *= atof(Cmd_Argv(2));
+       }
        else if (!strcmp(Cmd_Argv(1), "style"))
        {
                if (Cmd_Argc() != 3)
@@ -4195,8 +4198,6 @@ void R_Shadow_EditLights_Help_f(void)
 "r_editlights_cursorpushoff : push cursor off surface this far\n"
 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
-"r_editlights_rtlightssizescale : imported rtlight size scaling\n"
-"r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
 "Commands:\n"
 "r_editlights_help : this help\n"
 "r_editlights_clear : remove all lights\n"
@@ -4223,6 +4224,10 @@ void R_Shadow_EditLights_Help_f(void)
 "anglesz z: set z component of light angles\n"
 "color r g b : set color of light (can be brighter than 1 1 1)\n"
 "radius radius : set radius (size) of light\n"
+"colorscale grey : multiply color of light (1 does nothing)\n"
+"colorscale r g b : multiply color of light (1 1 1 does nothing)\n"
+"radiusscale scale : multiply radius (size) of light (1 does nothing)\n"
+"sizescale scale : multiply radius (size) of light (1 does nothing)\n"
 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
 "cubemap basename : set filter cubemap of light (not yet supported)\n"
 "shadows 1/0 : turn on/off shadows\n"
@@ -4289,8 +4294,6 @@ void R_Shadow_EditLights_Init(void)
        Cvar_RegisterVariable(&r_editlights_cursorpushoff);
        Cvar_RegisterVariable(&r_editlights_cursorgrid);
        Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
-       Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
-       Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
        Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
        Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
        Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);