]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
optional portal based determination of lit surfaces (good speed gain, enough to make...
[xonotic/darkplaces.git] / r_shadow.c
index f8a0e3dec69b8b410ba16d5cd65d661cecdd3feb..e2964debe866f42f8fa545b4c312ab7132eb982b 100644 (file)
@@ -2,6 +2,7 @@
 #include "quakedef.h"
 #include "r_shadow.h"
 #include "cl_collision.h"
+#include "portals.h"
 
 extern void R_Shadow_EditLights_Init(void);
 
@@ -29,16 +30,21 @@ rtexture_t *r_shadow_blankbumptexture;
 rtexture_t *r_shadow_blankglosstexture;
 rtexture_t *r_shadow_blankwhitetexture;
 
-cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "2"};
-cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "2"};
+cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
+cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
 cvar_t r_shadow_realtime = {0, "r_shadow_realtime", "0"};
 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
-cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "4"};
+cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
 cvar_t r_shadow_shadownudge = {0, "r_shadow_shadownudge", "1"};
+cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
+
+int c_rt_lights, c_rt_clears, c_rt_scissored;
+int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
+int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
 
 void R_Shadow_ClearWorldLights(void);
 void R_Shadow_SaveWorldLights(void);
@@ -99,6 +105,7 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
        Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
        Cvar_RegisterVariable(&r_shadow_shadownudge);
+       Cvar_RegisterVariable(&r_shadow_portallight);
        R_Shadow_EditLights_Init();
        R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
 }
@@ -316,11 +323,15 @@ void R_Shadow_RenderVolume(int numverts, int numtris, int *elements)
                qglCullFace(GL_BACK); // quake is backwards, this culls front faces
                qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
                R_Mesh_Draw(numverts, numtris, elements);
+               c_rt_shadowmeshes++;
+               c_rt_shadowtris += numtris;
                // decrement stencil if frontface is behind depthbuffer
                qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
                qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
        }
        R_Mesh_Draw(numverts, numtris, elements);
+       c_rt_shadowmeshes++;
+       c_rt_shadowtris += numtris;
 }
 
 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
@@ -336,6 +347,8 @@ void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
                        R_Mesh_ResizeCheck(mesh->numverts);
                        memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
                        R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->elements);
+                       c_rtcached_shadowmeshes++;
+                       c_rtcached_shadowtris += mesh->numtriangles;
                }
                // decrement stencil if frontface is behind depthbuffer
                qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
@@ -346,6 +359,8 @@ void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
                R_Mesh_ResizeCheck(mesh->numverts);
                memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
                R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->elements);
+               c_rtcached_shadowmeshes++;
+               c_rtcached_shadowtris += mesh->numtriangles;
        }
 }
 
@@ -429,8 +444,8 @@ static void R_Shadow_MakeTextures(void)
        {
                for (x = 0;x < 128;x++)
                {
-                       v[0] = (x + 0.5f) * (2.0f / 128.0f) - 1.0f;
-                       v[1] = (y + 0.5f) * (2.0f / 128.0f) - 1.0f;
+                       v[0] = (x + 0.5f) * (2.0f / (128.0f - 8.0f)) - 1.0f;
+                       v[1] = (y + 0.5f) * (2.0f / (128.0f - 8.0f)) - 1.0f;
                        v[2] = 0;
                        intensity = 1.0f - sqrt(DotProduct(v, v));
                        if (intensity > 0)
@@ -443,7 +458,7 @@ static void R_Shadow_MakeTextures(void)
                        data[((0*128+y)*128+x)*4+3] = d;
                }
        }
-       r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", 128, 128, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA | TEXF_MIPMAP, NULL);
+       r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", 128, 128, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
        Mem_Free(data);
 }
 
@@ -475,6 +490,10 @@ void R_Shadow_Stage_Begin(void)
        R_Mesh_State(&m);
        GL_Color(0, 0, 0, 1);
        r_shadowstage = SHADOWSTAGE_NONE;
+
+       c_rt_lights = c_rt_clears = c_rt_scissored = 0;
+       c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
+       c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
 }
 
 void R_Shadow_Stage_ShadowVolumes(void)
@@ -494,9 +513,39 @@ void R_Shadow_Stage_ShadowVolumes(void)
        qglEnable(GL_DEPTH_TEST);
        r_shadowstage = SHADOWSTAGE_STENCIL;
        qglClear(GL_STENCIL_BUFFER_BIT);
+       c_rt_clears++;
+       // LordHavoc note: many shadow volumes reside entirely inside the world
+       // (that is to say they are entirely bounded by their lit surfaces),
+       // which can be optimized by handling things as an inverted light volume,
+       // with the shadow boundaries of the world being simulated by an altered
+       // (129) bias to stencil clearing on such lights
+       // FIXME: generate inverted light volumes for use as shadow volumes and
+       // optimize for them as noted above
+}
+
+void R_Shadow_Stage_LightWithoutShadows(void)
+{
+       rmeshstate_t m;
+       memset(&m, 0, sizeof(m));
+       R_Mesh_TextureState(&m);
+       qglActiveTexture(GL_TEXTURE0_ARB);
+
+       qglEnable(GL_BLEND);
+       qglBlendFunc(GL_ONE, GL_ONE);
+       GL_Color(1, 1, 1, 1);
+       qglColorMask(1, 1, 1, 1);
+       qglDepthMask(0);
+       qglDepthFunc(GL_EQUAL);
+       qglDisable(GL_STENCIL_TEST);
+       qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+       qglStencilFunc(GL_EQUAL, 128, 0xFF);
+       qglEnable(GL_CULL_FACE);
+       qglEnable(GL_DEPTH_TEST);
+       r_shadowstage = SHADOWSTAGE_LIGHT;
+       c_rt_lights++;
 }
 
-void R_Shadow_Stage_Light(void)
+void R_Shadow_Stage_LightWithShadows(void)
 {
        rmeshstate_t m;
        memset(&m, 0, sizeof(m));
@@ -517,6 +566,7 @@ void R_Shadow_Stage_Light(void)
        qglEnable(GL_CULL_FACE);
        qglEnable(GL_DEPTH_TEST);
        r_shadowstage = SHADOWSTAGE_LIGHT;
+       c_rt_lights++;
 }
 
 void R_Shadow_Stage_End(void)
@@ -682,6 +732,7 @@ int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const
        // set up the scissor rectangle
        qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
        qglEnable(GL_SCISSOR_TEST);
+       c_rt_scissored++;
        return false;
 }
 
@@ -780,8 +831,8 @@ void R_Shadow_GenTexCoords_LightCubeMap(float *out, int numverts, const float *v
 
 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, float lightradius, const float *lightcolor, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
 {
-       int renders, mult;
-       float scale, colorscale;
+       int renders;
+       float color[3];
        rmeshstate_t m;
        memset(&m, 0, sizeof(m));
        if (!bumptexture)
@@ -807,6 +858,8 @@ void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *element
                R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
                R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[2], varray_texcoord[3], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
                R_Mesh_Draw(numverts, numtriangles, elements);
+               c_rt_lightmeshes++;
+               c_rt_lighttris += numtriangles;
 
                m.tex[0] = R_GetTexture(basetexture);
                m.texcubemap[1] = R_GetTexture(lightcubemap);
@@ -815,18 +868,20 @@ void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *element
                m.tex[2] = 0;
                m.tex[3] = 0;
                R_Mesh_TextureState(&m);
-               qglColorMask(1,1,1,1);
+               qglColorMask(1,1,1,0);
                qglBlendFunc(GL_DST_ALPHA, GL_ONE);
                qglEnable(GL_BLEND);
                if (lightcubemap)
                        R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
 
-               colorscale = r_colorscale * r_shadow_lightintensityscale.value;
-               for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
-               colorscale *= scale;
-               GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
-               for (renders = 0;renders < mult;renders++)
+               VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
+               for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
+               {
+                       GL_Color(color[0], color[1], color[2], 1);
                        R_Mesh_Draw(numverts, numtriangles, elements);
+                       c_rt_lightmeshes++;
+                       c_rt_lighttris += numtriangles;
+               }
        }
        else
        {
@@ -839,6 +894,8 @@ void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *element
                GL_Color(1,1,1,1);
                R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
                R_Mesh_Draw(numverts, numtriangles, elements);
+               c_rt_lightmeshes++;
+               c_rt_lighttris += numtriangles;
 
                m.tex[0] = R_GetTexture(bumptexture);
                m.tex[1] = 0;
@@ -851,30 +908,34 @@ void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *element
                memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
                R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
                R_Mesh_Draw(numverts, numtriangles, elements);
+               c_rt_lightmeshes++;
+               c_rt_lighttris += numtriangles;
 
                m.tex[0] = R_GetTexture(basetexture);
                m.texcubemap[1] = R_GetTexture(lightcubemap);
                m.texcombinergb[0] = GL_MODULATE;
                m.texcombinergb[1] = GL_MODULATE;
                R_Mesh_TextureState(&m);
-               qglColorMask(1,1,1,1);
+               qglColorMask(1,1,1,0);
                qglBlendFunc(GL_DST_ALPHA, GL_ONE);
                if (lightcubemap)
                        R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
 
-               colorscale = r_colorscale * r_shadow_lightintensityscale.value;
-               for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
-               colorscale *= scale;
-               GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
-               for (renders = 0;renders < mult;renders++)
+               VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
+               for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
+               {
+                       GL_Color(color[0], color[1], color[2], 1);
                        R_Mesh_Draw(numverts, numtriangles, elements);
+                       c_rt_lightmeshes++;
+                       c_rt_lighttris += numtriangles;
+               }
        }
 }
 
 void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
 {
-       int renders, mult;
-       float scale, colorscale;
+       int renders;
+       float color[3];
        rmeshstate_t m;
        memset(&m, 0, sizeof(m));
        if (!bumptexture)
@@ -896,6 +957,8 @@ void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elemen
                memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
                R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, relativeeyeorigin);
                R_Mesh_Draw(numverts, numtriangles, elements);
+               c_rt_lightmeshes++;
+               c_rt_lighttris += numtriangles;
 
                m.tex[0] = 0;
                m.texcubemap[1] = 0;
@@ -907,10 +970,16 @@ void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elemen
                // these comments are a test run through this math for intensity 0.5
                // 0.5 * 0.5 = 0.25
                R_Mesh_Draw(numverts, numtriangles, elements);
+               c_rt_lightmeshes++;
+               c_rt_lighttris += numtriangles;
                // 0.25 * 0.25 = 0.0625
                R_Mesh_Draw(numverts, numtriangles, elements);
+               c_rt_lightmeshes++;
+               c_rt_lighttris += numtriangles;
                // 0.0625 * 0.0625 = 0.00390625
                R_Mesh_Draw(numverts, numtriangles, elements);
+               c_rt_lightmeshes++;
+               c_rt_lighttris += numtriangles;
 
                m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
@@ -918,22 +987,26 @@ void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elemen
                qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
                R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
                R_Mesh_Draw(numverts, numtriangles, elements);
+               c_rt_lightmeshes++;
+               c_rt_lighttris += numtriangles;
 
                m.tex[0] = R_GetTexture(glosstexture);
                m.texcubemap[1] = R_GetTexture(lightcubemap);
                R_Mesh_TextureState(&m);
-               qglColorMask(1,1,1,1);
+               qglColorMask(1,1,1,0);
                qglBlendFunc(GL_DST_ALPHA, GL_ONE);
                memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
                if (lightcubemap)
                        R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
 
-               colorscale = r_colorscale * r_shadow_lightintensityscale.value;
-               for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
-               colorscale *= scale;
-               GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
-               for (renders = 0;renders < mult;renders++)
+               VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
+               for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
+               {
+                       GL_Color(color[0], color[1], color[2], 1);
                        R_Mesh_Draw(numverts, numtriangles, elements);
+                       c_rt_lightmeshes++;
+                       c_rt_lighttris += numtriangles;
+               }
        }
 }
 
@@ -949,12 +1022,14 @@ cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "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"};
 worldlight_t *r_shadow_worldlightchain;
 worldlight_t *r_shadow_selectedlight;
 vec3_t r_editlights_cursorlocation;
 
 static int castshadowcount = 1;
-void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname)
+void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
 {
        int i, j, k, l, maxverts, *mark, tris;
        float *verts, *v, f, temp[3], radius2;
@@ -976,13 +1051,25 @@ void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style
        VectorCopy(origin, e->origin);
        VectorCopy(color, e->light);
        e->lightradius = radius;
-       VectorCopy(origin, e->mins);
-       VectorCopy(origin, e->maxs);
-       e->cullradius = 0;
        e->style = style;
+       if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
+       {
+               Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
+               e->style = 0;
+       }
+       e->castshadows = castshadow;
+
+       e->cullradius = e->lightradius;
+       e->mins[0] = e->origin[0] - e->lightradius;
+       e->maxs[0] = e->origin[0] + e->lightradius;
+       e->mins[1] = e->origin[1] - e->lightradius;
+       e->maxs[1] = e->origin[1] + e->lightradius;
+       e->mins[2] = e->origin[2] - e->lightradius;
+       e->maxs[2] = e->origin[2] + e->lightradius;
+
        e->next = r_shadow_worldlightchain;
        r_shadow_worldlightchain = e;
-       if (cubemapname)
+       if (cubemapname && cubemapname[0])
        {
                e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
                strcpy(e->cubemapname, cubemapname);
@@ -991,38 +1078,82 @@ void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style
        if (cl.worldmodel)
        {
                castshadowcount++;
-               leaf = Mod_PointInLeaf(origin, cl.worldmodel);
-               pvs = Mod_LeafPVS(leaf, cl.worldmodel);
-               for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
+               if (r_shadow_portallight.integer)
+               {
+                       qbyte *byteleafpvs;
+                       qbyte *bytesurfacepvs;
+                       byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->numleafs + 1);
+                       bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->numsurfaces);
+                       Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, e->lightradius);
+
+                       for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
+                       {
+                               if (byteleafpvs[i+1])
+                               {
+                                       temp[0] = bound(leaf->mins[0], e->origin[0], leaf->maxs[0]) - e->origin[0];
+                                       temp[1] = bound(leaf->mins[1], e->origin[1], leaf->maxs[1]) - e->origin[1];
+                                       temp[2] = bound(leaf->mins[2], e->origin[2], leaf->maxs[2]) - e->origin[2];
+                                       if (DotProduct(temp, temp) < e->lightradius * e->lightradius)
+                                               leaf->worldnodeframe = castshadowcount;
+                               }
+                       }
+
+                       for (i = 0, surf = cl.worldmodel->surfaces;i < cl.worldmodel->numsurfaces;i++, surf++)
+                       {
+                               if (bytesurfacepvs[i])
+                               {
+                                       f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
+                                       if (surf->flags & SURF_PLANEBACK)
+                                               f = -f;
+                                       if (f > 0 && f < e->lightradius)
+                                       {
+                                               temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
+                                               temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
+                                               temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
+                                               if (DotProduct(temp, temp) < e->lightradius * e->lightradius)
+                                                       surf->castshadow = castshadowcount;
+                                       }
+                               }
+                       }
+
+                       Mem_Free(byteleafpvs);
+                       Mem_Free(bytesurfacepvs);
+               }
+               else
                {
-                       if (pvs[i >> 3] & (1 << (i & 7)))
+                       leaf = Mod_PointInLeaf(origin, cl.worldmodel);
+                       pvs = Mod_LeafPVS(leaf, cl.worldmodel);
+                       for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
                        {
-                               VectorCopy(origin, temp);
-                               if (temp[0] < leaf->mins[0]) temp[0] = leaf->mins[0];
-                               if (temp[0] > leaf->maxs[0]) temp[0] = leaf->maxs[0];
-                               if (temp[1] < leaf->mins[1]) temp[1] = leaf->mins[1];
-                               if (temp[1] > leaf->maxs[1]) temp[1] = leaf->maxs[1];
-                               if (temp[2] < leaf->mins[2]) temp[2] = leaf->mins[2];
-                               if (temp[2] > leaf->maxs[2]) temp[2] = leaf->maxs[2];
-                               VectorSubtract(temp, origin, temp);
-                               if (DotProduct(temp, temp) < e->lightradius * e->lightradius)
+                               if (pvs[i >> 3] & (1 << (i & 7)))
                                {
-                                       leaf->worldnodeframe = castshadowcount;
-                                       for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
+                                       VectorCopy(origin, temp);
+                                       if (temp[0] < leaf->mins[0]) temp[0] = leaf->mins[0];
+                                       if (temp[0] > leaf->maxs[0]) temp[0] = leaf->maxs[0];
+                                       if (temp[1] < leaf->mins[1]) temp[1] = leaf->mins[1];
+                                       if (temp[1] > leaf->maxs[1]) temp[1] = leaf->maxs[1];
+                                       if (temp[2] < leaf->mins[2]) temp[2] = leaf->mins[2];
+                                       if (temp[2] > leaf->maxs[2]) temp[2] = leaf->maxs[2];
+                                       VectorSubtract(temp, origin, temp);
+                                       if (DotProduct(temp, temp) < e->lightradius * e->lightradius)
                                        {
-                                               surf = cl.worldmodel->surfaces + *mark;
-                                               if (surf->castshadow != castshadowcount)
+                                               leaf->worldnodeframe = castshadowcount;
+                                               for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
                                                {
-                                                       f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
-                                                       if (surf->flags & SURF_PLANEBACK)
-                                                               f = -f;
-                                                       if (f > 0 && f < e->lightradius)
+                                                       surf = cl.worldmodel->surfaces + *mark;
+                                                       if (surf->castshadow != castshadowcount)
                                                        {
-                                                               temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
-                                                               temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
-                                                               temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
-                                                               if (DotProduct(temp, temp) < e->lightradius * e->lightradius)
-                                                                       surf->castshadow = castshadowcount;
+                                                               f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
+                                                               if (surf->flags & SURF_PLANEBACK)
+                                                                       f = -f;
+                                                               if (f > 0 && f < e->lightradius)
+                                                               {
+                                                                       temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
+                                                                       temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
+                                                                       temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
+                                                                       if (DotProduct(temp, temp) < e->lightradius * e->lightradius)
+                                                                               surf->castshadow = castshadowcount;
+                                                               }
                                                        }
                                                }
                                        }
@@ -1079,62 +1210,64 @@ void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style
                if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
                if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
                if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
-               Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
-               // clip shadow volumes against eachother to remove unnecessary
-               // polygons (and sections of polygons)
-               maxverts = 256;
-               verts = NULL;
-               castshadowcount++;
-               for (j = 0;j < e->numsurfaces;j++)
+
+               if (e->castshadows)
                {
-                       surf = e->surfaces[j];
-                       if (surf->flags & SURF_SHADOWCAST)
+                       maxverts = 256;
+                       verts = NULL;
+                       castshadowcount++;
+                       for (j = 0;j < e->numsurfaces;j++)
                        {
-                               surf->castshadow = castshadowcount;
-                               if (maxverts < surf->poly_numverts)
-                                       maxverts = surf->poly_numverts;
+                               surf = e->surfaces[j];
+                               if (surf->flags & SURF_SHADOWCAST)
+                               {
+                                       surf->castshadow = castshadowcount;
+                                       if (maxverts < surf->poly_numverts)
+                                               maxverts = surf->poly_numverts;
+                               }
                        }
-               }
-               e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
-               // make a mesh to cast a shadow volume from
-               castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
-               for (j = 0;j < e->numsurfaces;j++)
-                       if (e->surfaces[j]->castshadow == castshadowcount)
-                               for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
-                                       Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->numverts, surfmesh->verts, surfmesh->numtriangles, surfmesh->index);
-               castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
-
-               // cast shadow volume from castmesh
-               for (mesh = castmesh;mesh;mesh = mesh->next)
-               {
-                       R_Shadow_ResizeTriangleFacingLight(castmesh->numtriangles);
-                       R_Shadow_ResizeShadowElements(castmesh->numtriangles);
-
-                       if (maxverts < castmesh->numverts * 2)
+                       e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
+                       // make a mesh to cast a shadow volume from
+                       castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
+                       for (j = 0;j < e->numsurfaces;j++)
+                               if (e->surfaces[j]->castshadow == castshadowcount)
+                                       for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
+                                               Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->numverts, surfmesh->verts, surfmesh->numtriangles, surfmesh->index);
+                       castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
+
+                       // cast shadow volume from castmesh
+                       for (mesh = castmesh;mesh;mesh = mesh->next)
                        {
-                               maxverts = castmesh->numverts * 2;
-                               if (verts)
-                                       Mem_Free(verts);
-                               verts = NULL;
+                               R_Shadow_ResizeTriangleFacingLight(castmesh->numtriangles);
+                               R_Shadow_ResizeShadowElements(castmesh->numtriangles);
+
+                               if (maxverts < castmesh->numverts * 2)
+                               {
+                                       maxverts = castmesh->numverts * 2;
+                                       if (verts)
+                                               Mem_Free(verts);
+                                       verts = NULL;
+                               }
+                               if (verts == NULL && maxverts > 0)
+                                       verts = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[4]));
+
+                               // now that we have the buffers big enough, construct shadow volume mesh
+                               memcpy(verts, castmesh->verts, castmesh->numverts * sizeof(float[4]));
+                               R_Shadow_ProjectVertices(verts, castmesh->numverts, e->origin, 10000000.0f);//, e->lightradius);
+                               R_Shadow_MakeTriangleShadowFlags(castmesh->elements, verts, castmesh->numtriangles, trianglefacinglight, e->origin, e->lightradius);
+                               tris = R_Shadow_BuildShadowVolumeTriangles(castmesh->elements, castmesh->neighbors, castmesh->numtriangles, castmesh->numverts, trianglefacinglight, shadowelements);
+                               // add the constructed shadow volume mesh
+                               Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, castmesh->numverts, verts, tris, shadowelements);
                        }
-                       if (verts == NULL && maxverts > 0)
-                               verts = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[4]));
-
-                       // now that we have the buffers big enough, construct shadow volume mesh
-                       memcpy(verts, castmesh->verts, castmesh->numverts * sizeof(float[4]));
-                       R_Shadow_ProjectVertices(verts, castmesh->numverts, e->origin, 1000000.0f);//, e->lightradius);
-                       R_Shadow_MakeTriangleShadowFlags(castmesh->elements, verts, castmesh->numtriangles, trianglefacinglight, e->origin, e->lightradius);
-                       tris = R_Shadow_BuildShadowVolumeTriangles(castmesh->elements, castmesh->neighbors, castmesh->numtriangles, castmesh->numverts, trianglefacinglight, shadowelements);
-                       // add the constructed shadow volume mesh
-                       Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, castmesh->numverts, verts, tris, shadowelements);
+                       // we're done with castmesh now
+                       Mod_ShadowMesh_Free(castmesh);
+                       e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
+                       for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
+                               l += mesh->numtriangles;
+                       Con_Printf("static shadow volume built containing %i triangles\n", l);
                }
-               // we're done with castmesh now
-               Mod_ShadowMesh_Free(castmesh);
-               e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
-               for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
-                       l += mesh->numtriangles;
-               Con_Printf("static shadow volume built containing %i triangles\n", l);
        }
+       Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
 }
 
 void R_Shadow_FreeWorldLight(worldlight_t *light)
@@ -1171,14 +1304,6 @@ void R_Shadow_SelectLight(worldlight_t *light)
                r_shadow_selectedlight->selected = true;
 }
 
-void R_Shadow_FreeSelectedWorldLight(void)
-{
-       if (r_shadow_selectedlight)
-       {
-               R_Shadow_FreeWorldLight(r_shadow_selectedlight);
-               r_shadow_selectedlight = NULL;
-       }
-}
 
 void R_DrawLightSprite(int texnum, const vec3_t origin, vec_t scale, float cr, float cg, float cb, float ca)
 {
@@ -1285,9 +1410,14 @@ void R_Shadow_SelectLightInView(void)
 
 void R_Shadow_LoadWorldLights(void)
 {
-       int n, a, style;
+       int n, a, style, shadow;
        char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
        float origin[3], radius, color[3];
+       if (cl.worldmodel == NULL)
+       {
+               Con_Printf("No map loaded.\n");
+               return;
+       }
        COM_StripExtension(cl.worldmodel->name, name);
        strcat(name, ".rtlights");
        lightsstring = COM_LoadFile(name, false);
@@ -1303,7 +1433,14 @@ void R_Shadow_LoadWorldLights(void)
                        if (!*s)
                                break;
                        *s = 0;
-                       a = sscanf(t, "%f %f %f %f %f %f %f %d %s", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, &cubemapname);
+                       shadow = true;
+                       // check for modifier flags
+                       if (*t == '!')
+                       {
+                               shadow = false;
+                               t++;
+                       }
+                       a = sscanf(t, "%f %f %f %f %f %f %f %d %s", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname);
                        if (a < 9)
                                cubemapname[0] = 0;
                        *s = '\n';
@@ -1312,7 +1449,9 @@ void R_Shadow_LoadWorldLights(void)
                                Con_Printf("found %d parameters on line %i, should be 8 or 9 parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style cubemapname)\n", a, n + 1);
                                break;
                        }
-                       R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
+                       VectorScale(color, r_editlights_rtlightscolorscale.value, color);
+                       radius *= r_editlights_rtlightssizescale.value;
+                       R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
                        s++;
                        n++;
                }
@@ -1331,13 +1470,18 @@ void R_Shadow_SaveWorldLights(void)
        char line[1024];
        if (!r_shadow_worldlightchain)
                return;
+       if (cl.worldmodel == NULL)
+       {
+               Con_Printf("No map loaded.\n");
+               return;
+       }
        COM_StripExtension(cl.worldmodel->name, name);
        strcat(name, ".rtlights");
        bufchars = bufmaxchars = 0;
        buf = NULL;
        for (light = r_shadow_worldlightchain;light;light = light->next)
        {
-               sprintf(line, "%g %g %g %g %g %g %g %d %s\n", light->origin[0], light->origin[1], light->origin[2], light->lightradius, light->light[0], light->light[1], light->light[2], light->style, light->cubemapname ? light->cubemapname : "");
+               sprintf(line, "%s%g %g %g %g %g %g %g %d %s\n", light->castshadows ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->lightradius / r_editlights_rtlightssizescale.value, light->light[0] / r_editlights_rtlightscolorscale.value, light->light[1] / r_editlights_rtlightscolorscale.value, light->light[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname ? light->cubemapname : "");
                if (bufchars + strlen(line) > bufmaxchars)
                {
                        bufmaxchars = bufchars + strlen(line) + 2048;
@@ -1367,6 +1511,11 @@ void R_Shadow_LoadLightsFile(void)
        int n, a, style;
        char name[MAX_QPATH], *lightsstring, *s, *t;
        float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
+       if (cl.worldmodel == NULL)
+       {
+               Con_Printf("No map loaded.\n");
+               return;
+       }
        COM_StripExtension(cl.worldmodel->name, name);
        strcat(name, ".lights");
        lightsstring = COM_LoadFile(name, false);
@@ -1391,8 +1540,8 @@ void R_Shadow_LoadLightsFile(void)
                        }
                        radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
                        radius = bound(15, radius, 4096);
-                       VectorScale(color, (1.0f / (8388608.0f)), color);
-                       R_Shadow_NewWorldLight(origin, radius, color, style, NULL);
+                       VectorScale(color, (2.0f / (8388608.0f)), color);
+                       R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
                        s++;
                        n++;
                }
@@ -1409,6 +1558,11 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
        float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
        const char *data;
 
+       if (cl.worldmodel == NULL)
+       {
+               Con_Printf("No map loaded.\n");
+               return;
+       }
        data = cl.worldmodel->entities;
        if (!data)
                return;
@@ -1485,8 +1639,8 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                                originhack[1] = 0;
                                                originhack[2] = 48;
                                                overridecolor[0] = 1;
-                                               overridecolor[1] = 0.7;
-                                               overridecolor[2] = 0.2;
+                                               overridecolor[1] = 0.5;
+                                               overridecolor[2] = 0.1;
                                        }
                                        if (!strcmp(value, "light_flame_small_yellow"))
                                        {
@@ -1494,8 +1648,8 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                                originhack[1] = 0;
                                                originhack[2] = 40;
                                                overridecolor[0] = 1;
-                                               overridecolor[1] = 0.7;
-                                               overridecolor[2] = 0.2;
+                                               overridecolor[1] = 0.5;
+                                               overridecolor[2] = 0.1;
                                        }
                                        if (!strcmp(value, "light_torch_small_white"))
                                        {
@@ -1503,8 +1657,8 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                                originhack[1] = 0;
                                                originhack[2] = 40;
                                                overridecolor[0] = 1;
-                                               overridecolor[1] = 0.9;
-                                               overridecolor[2] = 0.7;
+                                               overridecolor[1] = 0.5;
+                                               overridecolor[2] = 0.1;
                                        }
                                        if (!strcmp(value, "light_torch_small_walltorch"))
                                        {
@@ -1512,8 +1666,8 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                                originhack[1] = 0;
                                                originhack[2] = 40;
                                                overridecolor[0] = 1;
-                                               overridecolor[1] = 0.7;
-                                               overridecolor[2] = 0.2;
+                                               overridecolor[1] = 0.5;
+                                               overridecolor[2] = 0.1;
                                        }
                                }
                        }
@@ -1529,7 +1683,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                VectorScale(color, light, color);
                VectorAdd(origin, originhack, origin);
                if (radius >= 15)
-                       R_Shadow_NewWorldLight(origin, radius, color, style, NULL);
+                       R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
        }
 }
 
@@ -1569,9 +1723,9 @@ void R_Shadow_UpdateLightingMode(void)
 
 void R_Shadow_UpdateWorldLightSelection(void)
 {
+       R_Shadow_SetCursorLocationForView();
        if (r_editlights.integer)
        {
-               R_Shadow_SetCursorLocationForView();
                R_Shadow_SelectLightInView();
                R_Shadow_DrawLightSprites();
        }
@@ -1609,60 +1763,27 @@ void R_Shadow_EditLights_ImportLightsFile_f(void)
 
 void R_Shadow_EditLights_Spawn_f(void)
 {
-       vec3_t origin, color;
-       vec_t radius;
-       int style;
-       const char *cubemapname;
+       vec3_t color;
        if (!r_editlights.integer)
        {
                Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
                return;
        }
-       if (Cmd_Argc() <= 7)
+       if (Cmd_Argc() != 1)
        {
-               radius = 200;
-               color[0] = color[1] = color[2] = 1;
-               style = 0;
-               cubemapname = NULL;
-               if (Cmd_Argc() >= 2)
-               {
-                       radius = atof(Cmd_Argv(1));
-                       if (Cmd_Argc() >= 3)
-                       {
-                               color[0] = atof(Cmd_Argv(2));
-                               color[1] = color[0];
-                               color[2] = color[0];
-                               if (Cmd_Argc() >= 5)
-                               {
-                                       color[1] = atof(Cmd_Argv(3));
-                                       color[2] = atof(Cmd_Argv(4));
-                                       if (Cmd_Argc() >= 6)
-                                       {
-                                               style = atoi(Cmd_Argv(5));
-                                               if (Cmd_Argc() >= 7)
-                                                       cubemapname = Cmd_Argv(6);
-                                       }
-                               }
-                       }
-               }
-               if (cubemapname && !cubemapname[0])
-                       cubemapname = NULL;
-               if (radius >= 16 && color[0] >= 0 && color[1] >= 0 && color[2] >= 0 && style >= 0 && style < 256 && (color[0] >= 0.1 || color[1] >= 0.1 || color[2] >= 0.1))
-               {
-                       VectorCopy(r_editlights_cursorlocation, origin);
-                       R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
-                       return;
-               }
+               Con_Printf("r_editlights_spawn does not take parameters\n");
+               return;
        }
-       Con_Printf("usage: r_editlights_spawn radius red green blue [style [cubemap]]\n");
+       color[0] = color[1] = color[2] = 1;
+       R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
 }
 
 void R_Shadow_EditLights_Edit_f(void)
 {
        vec3_t origin, color;
        vec_t radius;
-       int style;
-       const char *cubemapname;
+       int style, shadows;
+       char cubemapname[1024];
        if (!r_editlights.integer)
        {
                Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
@@ -1673,45 +1794,191 @@ void R_Shadow_EditLights_Edit_f(void)
                Con_Printf("No selected light.\n");
                return;
        }
-       if (Cmd_Argc() <= 7)
+       VectorCopy(r_shadow_selectedlight->origin, origin);
+       radius = r_shadow_selectedlight->lightradius;
+       VectorCopy(r_shadow_selectedlight->light, color);
+       style = r_shadow_selectedlight->style;
+       if (r_shadow_selectedlight->cubemapname)
+               strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
+       else
+               cubemapname[0] = 0;
+       shadows = r_shadow_selectedlight->castshadows;
+       if (!strcmp(Cmd_Argv(1), "origin"))
        {
-               radius = 200;
-               color[0] = color[1] = color[2] = 1;
-               style = 0;
-               cubemapname = NULL;
-               if (Cmd_Argc() >= 2)
+               if (Cmd_Argc() != 5)
                {
-                       radius = atof(Cmd_Argv(1));
-                       if (Cmd_Argc() >= 3)
-                       {
-                               color[0] = atof(Cmd_Argv(2));
-                               color[1] = color[0];
-                               color[2] = color[0];
-                               if (Cmd_Argc() >= 5)
-                               {
-                                       color[1] = atof(Cmd_Argv(3));
-                                       color[2] = atof(Cmd_Argv(4));
-                                       if (Cmd_Argc() >= 6)
-                                       {
-                                               style = atoi(Cmd_Argv(5));
-                                               if (Cmd_Argc() >= 7)
-                                                       cubemapname = Cmd_Argv(6);
-                                       }
-                               }
-                       }
+                       Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
+                       return;
+               }
+               origin[0] = atof(Cmd_Argv(2));
+               origin[1] = atof(Cmd_Argv(3));
+               origin[2] = atof(Cmd_Argv(4));
+       }
+       else if (!strcmp(Cmd_Argv(1), "originx"))
+       {
+               if (Cmd_Argc() != 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
+                       return;
+               }
+               origin[0] = atof(Cmd_Argv(2));
+       }
+       else if (!strcmp(Cmd_Argv(1), "originy"))
+       {
+               if (Cmd_Argc() != 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
+                       return;
+               }
+               origin[1] = atof(Cmd_Argv(2));
+       }
+       else if (!strcmp(Cmd_Argv(1), "originz"))
+       {
+               if (Cmd_Argc() != 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
+                       return;
+               }
+               origin[2] = atof(Cmd_Argv(2));
+       }
+       else if (!strcmp(Cmd_Argv(1), "move"))
+       {
+               if (Cmd_Argc() != 5)
+               {
+                       Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
+                       return;
+               }
+               origin[0] += atof(Cmd_Argv(2));
+               origin[1] += atof(Cmd_Argv(3));
+               origin[2] += atof(Cmd_Argv(4));
+       }
+       else if (!strcmp(Cmd_Argv(1), "movex"))
+       {
+               if (Cmd_Argc() != 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
+                       return;
+               }
+               origin[0] += atof(Cmd_Argv(2));
+       }
+       else if (!strcmp(Cmd_Argv(1), "movey"))
+       {
+               if (Cmd_Argc() != 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
+                       return;
                }
-               if (cubemapname && !cubemapname[0])
-                       cubemapname = NULL;
-               if (radius >= 16 && color[0] >= 0 && color[1] >= 0 && color[2] >= 0 && style >= 0 && style < 256 && (color[0] >= 0.1 || color[1] >= 0.1 || color[2] >= 0.1))
+               origin[1] += atof(Cmd_Argv(2));
+       }
+       else if (!strcmp(Cmd_Argv(1), "movez"))
+       {
+               if (Cmd_Argc() != 3)
                {
-                       VectorCopy(r_shadow_selectedlight->origin, origin);
-                       R_Shadow_FreeWorldLight(r_shadow_selectedlight);
-                       r_shadow_selectedlight = NULL;
-                       R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
                        return;
                }
+               origin[2] += atof(Cmd_Argv(2));
        }
-       Con_Printf("usage: r_editlights_edit radius red green blue [style [cubemap]]\n");
+       else if (!strcmp(Cmd_Argv(1), "color"))
+       {
+               if (Cmd_Argc() != 5)
+               {
+                       Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
+                       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), "radius"))
+       {
+               if (Cmd_Argc() != 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
+                       return;
+               }
+               radius = atof(Cmd_Argv(2));
+       }
+       else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
+       {
+               if (Cmd_Argc() != 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
+                       return;
+               }
+               style = atoi(Cmd_Argv(2));
+       }
+       else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
+       {
+               if (Cmd_Argc() > 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
+                       return;
+               }
+               if (Cmd_Argc() == 3)
+                       strcpy(cubemapname, Cmd_Argv(2));
+               else
+                       cubemapname[0] = 0;
+       }
+       else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
+       {
+               if (Cmd_Argc() != 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
+                       return;
+               }
+               shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
+       }
+       else
+       {
+               Con_Printf("usage: r_editlights_edit [property] [value]\n");
+               Con_Printf("Selected light's properties:\n");
+               Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
+               Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
+               Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
+               Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
+               Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
+               Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
+               return;
+       }
+       R_Shadow_FreeWorldLight(r_shadow_selectedlight);
+       r_shadow_selectedlight = NULL;
+       R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
+}
+
+extern int con_vislines;
+void R_Shadow_EditLights_DrawSelectedLightProperties(void)
+{
+       float x, y;
+       char temp[256];
+       if (r_shadow_selectedlight == NULL)
+               return;
+       x = 0;
+       y = con_vislines;
+       sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
+       sprintf(temp, "Origin %f %f %f", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
+       sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
+       sprintf(temp, "Color %f %f %f", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
+       sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
+       sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
+       sprintf(temp, "Shadows %s", r_shadow_selectedlight->castshadows ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
+}
+
+void R_Shadow_EditLights_ToggleShadow_f(void)
+{
+       if (!r_editlights.integer)
+       {
+               Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
+               return;
+       }
+       if (!r_shadow_selectedlight)
+       {
+               Con_Printf("No selected light.\n");
+               return;
+       }
+       R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->lightradius, r_shadow_selectedlight->light, r_shadow_selectedlight->style, r_shadow_selectedlight->cubemapname, !r_shadow_selectedlight->castshadows);
+       R_Shadow_FreeWorldLight(r_shadow_selectedlight);
+       r_shadow_selectedlight = NULL;
 }
 
 void R_Shadow_EditLights_Remove_f(void)
@@ -1726,7 +1993,8 @@ void R_Shadow_EditLights_Remove_f(void)
                Con_Printf("No selected light.\n");
                return;
        }
-       R_Shadow_FreeSelectedWorldLight();
+       R_Shadow_FreeWorldLight(r_shadow_selectedlight);
+       r_shadow_selectedlight = NULL;
 }
 
 void R_Shadow_EditLights_Init(void)
@@ -1737,12 +2005,15 @@ 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_clear", R_Shadow_EditLights_Clear_f);
        Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
        Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
        Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
        Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
        Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
+       Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
        Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
        Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
 }