lightning beams have been replaced with a polygon effect which renders faster than...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 27 Jan 2003 02:54:54 +0000 (02:54 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 27 Jan 2003 02:54:54 +0000 (02:54 +0000)
r_speeds now reports some realtime lighting info
shadowless light support added
(unsuccessful) attempt to fix r_drawportals by moving the call to happen whether portal rendering was used or not
comment about possible optimization to shadow volumes added (rather, inverted light volumes and how to get maximum performance from them)
light styles are now checked for validity (must be in the right number range)
tiny cleanup to cubemap name handling (cubemaps are still not actually supported)
r_editlights_rtlightcolorscale and sizescale are now applied only in LoadWorldLights and SaveWorldLights, not in light creation (also somewhat to compensate for this fact, .lights loading has been tweaked to double the brightness)
torch light colors changed (more orange)
r_editlights_spawn no longer takes parameters
r_editlights_edit now has subcommands instead of setting all properties, the commands are: origin, originx, originy, originz, move, movex, movey, movez, radius, color, style, cubemap, shadows
r_editlights_toggleshadow added (useful for point and click shadow toggling)
display overlay now shown when pointing at a light in editing mode, indicating all properties of the light

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@2701 d7cf8633-e32d-0410-b094-e92efae38249

cl_main.c
cl_screen.c
gl_rmain.c
gl_rsurf.c
r_shadow.c
r_shadow.h

index 11eeadc..0a084b9 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -53,6 +53,8 @@ cvar_t r_draweffects = {0, "r_draweffects", "1"};
 cvar_t cl_explosions = {CVAR_SAVE, "cl_explosions", "1"};
 cvar_t cl_stainmaps = {CVAR_SAVE, "cl_stainmaps", "1"};
 
+cvar_t cl_beampolygons = {CVAR_SAVE, "cl_beampolygons", "1"};
+
 mempool_t *cl_scores_mempool;
 mempool_t *cl_refdef_mempool;
 mempool_t *cl_entities_mempool;
@@ -872,6 +874,9 @@ void CL_RelinkBeams (void)
                        b->start[2] += 16;
                }
 
+               if (cl_beampolygons.integer)
+                       continue;
+
                // calculate pitch and yaw
                VectorSubtract (b->end, b->start, dist);
 
@@ -916,6 +921,176 @@ void CL_RelinkBeams (void)
        }
 }
 
+cvar_t r_lightningbeam_thickness = {CVAR_SAVE, "r_lightningbeam_thickness", "4"};
+cvar_t r_lightningbeam_scroll = {CVAR_SAVE, "r_lightningbeam_scroll", "5"};
+cvar_t r_lightningbeam_repeatdistance = {CVAR_SAVE, "r_lightningbeam_repeatdistance", "1024"};
+cvar_t r_lightningbeam_color_red = {CVAR_SAVE, "r_lightningbeam_color_red", "1"};
+cvar_t r_lightningbeam_color_green = {CVAR_SAVE, "r_lightningbeam_color_green", "1"};
+cvar_t r_lightningbeam_color_blue = {CVAR_SAVE, "r_lightningbeam_color_blue", "1"};
+
+rtexture_t *r_lightningbeamtexture;
+rtexturepool_t *r_lightningbeamtexturepool;
+
+int r_lightningbeamelements[18] = {0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11};
+
+void r_lightningbeams_start(void)
+{
+       float r, g, b, intensity, fx, width, center;
+       int x, y;
+       qbyte *data, *noise1, *noise2;
+       data = Mem_Alloc(tempmempool, 32 * 512 * 4);
+       noise1 = Mem_Alloc(tempmempool, 512 * 512);
+       noise2 = Mem_Alloc(tempmempool, 512 * 512);
+       fractalnoise(noise1, 512, 8);
+       fractalnoise(noise2, 512, 16);
+
+       for (y = 0;y < 512;y++)
+       {
+               width = noise1[y * 512] * (1.0f / 256.0f) * 3.0f + 3.0f;
+               center = (noise1[y * 512 + 64] / 256.0f) * (32.0f - (width + 1.0f) * 2.0f) + (width + 1.0f);
+               for (x = 0;x < 32;x++, fx++)
+               {
+                       fx = (x - center) / width;
+                       intensity = (1.0f - fx * fx) * (noise2[y*512+x] * (1.0f / 256.0f) * 0.33f + 0.66f);
+                       intensity = bound(0, intensity, 1);
+                       r = intensity * 2.0f - 1.0f;
+                       g = intensity * 3.0f - 1.0f;
+                       b = intensity * 3.0f;
+                       data[(y * 32 + x) * 4 + 0] = (qbyte)(bound(0, r, 1) * 255.0f);
+                       data[(y * 32 + x) * 4 + 1] = (qbyte)(bound(0, g, 1) * 255.0f);
+                       data[(y * 32 + x) * 4 + 2] = (qbyte)(bound(0, b, 1) * 255.0f);
+                       data[(y * 32 + x) * 4 + 3] = (qbyte)255;
+               }
+       }
+
+       r_lightningbeamtexturepool = R_AllocTexturePool();
+       r_lightningbeamtexture = R_LoadTexture2D(r_lightningbeamtexturepool, "lightningbeam", 32, 512, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
+       Mem_Free(noise1);
+       Mem_Free(noise2);
+       Mem_Free(data);
+}
+
+void r_lightningbeams_shutdown(void)
+{
+       r_lightningbeamtexture = NULL;
+       R_FreeTexturePool(&r_lightningbeamtexturepool);
+}
+
+void r_lightningbeams_newmap(void)
+{
+}
+
+void R_LightningBeams_Init(void)
+{
+       Cvar_RegisterVariable(&r_lightningbeam_thickness);
+       Cvar_RegisterVariable(&r_lightningbeam_scroll);
+       Cvar_RegisterVariable(&r_lightningbeam_repeatdistance);
+       Cvar_RegisterVariable(&r_lightningbeam_color_red);
+       Cvar_RegisterVariable(&r_lightningbeam_color_green);
+       Cvar_RegisterVariable(&r_lightningbeam_color_blue);
+       R_RegisterModule("R_LightningBeams", r_lightningbeams_start, r_lightningbeams_shutdown, r_lightningbeams_newmap);
+}
+
+void R_CalcLightningBeamPolygonVertices(float *v, float *tc, const float *start, const float *end, const float *offset, float t1, float t2)
+{
+       VectorAdd     (start, offset, (v +  0));tc[ 0] = 0;tc[ 1] = t1;
+       VectorSubtract(start, offset, (v +  4));tc[ 4] = 1;tc[ 5] = t1;
+       VectorSubtract(end  , offset, (v +  8));tc[ 8] = 1;tc[ 9] = t2;
+       VectorAdd     (end  , offset, (v + 12));tc[12] = 0;tc[13] = t2;
+}
+
+void R_FogLightningBeamColors(const float *v, float *c, int numverts, float r, float g, float b, float a)
+{
+       int i;
+       vec3_t fogvec;
+       float ifog;
+       for (i = 0;i < numverts;i++, v += 4, c += 4)
+       {
+               VectorSubtract(v, r_origin, fogvec);
+               ifog = 1 - exp(fogdensity/DotProduct(fogvec,fogvec));
+               c[0] = r * ifog;
+               c[1] = g * ifog;
+               c[2] = b * ifog;
+               c[3] = a;
+       }
+}
+
+float beamrepeatscale;
+
+void R_DrawLightningBeamCallback(const void *calldata1, int calldata2)
+{
+       const beam_t *b = calldata1;
+       rmeshstate_t m;
+       vec3_t beamdir, right, up, offset;
+       float length, t1, t2;
+       memset(&m, 0, sizeof(m));
+       m.blendfunc1 = GL_SRC_ALPHA;
+       m.blendfunc2 = GL_ONE;
+       m.tex[0] = R_GetTexture(r_lightningbeamtexture);
+       R_Mesh_State(&m);
+       R_Mesh_Matrix(&r_identitymatrix);
+       VectorSubtract(b->end, b->start, beamdir);
+       length = sqrt(DotProduct(beamdir, beamdir));
+       t1 = 1.0f / length;
+       VectorScale(beamdir, t1, beamdir);
+       VectorSubtract(r_origin, b->start, up);
+       t1 = -DotProduct(up, beamdir);
+       VectorMA(up, t1, beamdir, up);
+       VectorNormalizeFast(up);
+       CrossProduct(beamdir, up, right);
+       //VectorVectors(beamdir, right, up);
+
+       t1 = cl.time * -r_lightningbeam_scroll.value;
+       t1 = t1 - (int) t1;
+       t2 = t1 + beamrepeatscale * length;
+
+       // horizontal
+       VectorScale(right, r_lightningbeam_thickness.value, offset);
+       R_CalcLightningBeamPolygonVertices(varray_vertex, varray_texcoord[0], b->start, b->end, offset, t1, t2);
+       // diagonal up-right/down-left
+       VectorAdd(right, up, offset);
+       VectorScale(offset, r_lightningbeam_thickness.value * 0.70710681f, offset);
+       R_CalcLightningBeamPolygonVertices(varray_vertex + 16, varray_texcoord[0] + 16, b->start, b->end, offset, t1 + 0.33, t2 + 0.33);
+       // diagonal down-right/up-left
+       VectorSubtract(right, up, offset);
+       VectorScale(offset, r_lightningbeam_thickness.value * 0.70710681f, offset);
+       R_CalcLightningBeamPolygonVertices(varray_vertex + 32, varray_texcoord[0] + 32, b->start, b->end, offset, t1 + 0.66, t2 + 0.66);
+
+       if (fogenabled)
+       {
+               GL_UseColorArray();
+               R_FogLightningBeamColors(varray_vertex, varray_color, 12, r_lightningbeam_color_red.value, r_lightningbeam_color_green.value, r_lightningbeam_color_blue.value, 1);
+       }
+       else
+               GL_Color(r_lightningbeam_color_red.value, r_lightningbeam_color_green.value, r_lightningbeam_color_blue.value, 1);
+
+       //qglDisable(GL_CULL_FACE);
+       R_Mesh_Draw(12, 6, r_lightningbeamelements);
+       //qglEnable(GL_CULL_FACE);
+}
+
+void R_DrawLightningBeams (void)
+{
+       int i;
+       beam_t *b;
+       vec3_t org;
+
+       if (!cl_beampolygons.integer)
+               return;
+
+       beamrepeatscale = 1.0f / r_lightningbeam_repeatdistance.value;
+       for (i = 0, b = cl_beams;i < cl_max_beams;i++, b++)
+       {
+               if (b->model && b->endtime >= cl.time)
+               {
+                       VectorAdd(b->start, b->end, org);
+                       VectorScale(org, 0.5f, org);
+                       R_MeshQueue_AddTransparent(org, R_DrawLightningBeamCallback, b, 0);
+               }
+       }
+}
+
+
 void CL_LerpPlayer(float frac)
 {
        int i;
@@ -1234,6 +1409,9 @@ void CL_Init (void)
        Cvar_RegisterVariable(&r_draweffects);
        Cvar_RegisterVariable(&cl_explosions);
        Cvar_RegisterVariable(&cl_stainmaps);
+       Cvar_RegisterVariable(&cl_beampolygons);
+
+       R_LightningBeams_Init();
 
        CL_Parse_Init();
        CL_Particles_Init();
index 86081c2..4c369fe 100644 (file)
@@ -350,6 +350,10 @@ void R_TimeReport(char *desc)
        }
 }
 
+extern int c_rt_lights, c_rt_clears, c_rt_scissored;
+extern int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
+extern int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
+extern int r_shadow_lightingmode;
 void R_TimeReport_Start(void)
 {
        r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
@@ -367,6 +371,16 @@ void R_TimeReport_Start(void)
                        c_faces, c_nodes, c_leafs, c_light_polys,
                        c_models, c_bmodels, c_sprites, c_particles, c_dlights,
                        c_alias_polys, c_meshs, c_meshelements / 3);
+               if (r_shadow_lightingmode)
+               {
+                       sprintf(r_speeds_string + strlen(r_speeds_string),
+                               "realtime lighting:%4i lights%4i clears%4i scissored\n"
+                               "dynamic: %6i shadowmeshes%6i shadowtris%6i lightmeshes%6i lighttris\n"
+                               "precomputed: %6i shadowmeshes%6i shadowtris\n",
+                               c_rt_lights, c_rt_clears, c_rt_scissored,
+                               c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris,
+                               c_rtcached_shadowmeshes, c_rtcached_shadowtris);
+               }
 
                c_alias_polys = 0;
                c_light_polys = 0;
@@ -933,6 +947,7 @@ void CL_SetupScreenSize(void)
        SCR_CalcRefdef();
 }
 
+extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
 void CL_UpdateScreen(void)
 {
        if (!scr_initialized || !con_initialized || vid_hidden)
@@ -984,6 +999,7 @@ void CL_UpdateScreen(void)
                        R_TimeReport_End();
                        R_TimeReport_Start();
                }
+               R_Shadow_EditLights_DrawSelectedLightProperties();
        }
        SCR_DrawConsole();
 
index fe0052d..9ccd05c 100644 (file)
@@ -982,20 +982,26 @@ void R_ShadowVolumeLighting (int visiblevolumes)
                        VectorScale(lightcolor, f, lightcolor);
                }
 
-               if (!visiblevolumes)
-                       R_Shadow_Stage_ShadowVolumes();
-               ent = &cl_entities[0].render;
-               if (wl->shadowvolume && r_shadow_staticworldlights.integer)
-                       R_Shadow_DrawWorldLightShadowVolume(&ent->matrix, wl);
-               else
-                       R_TestAndDrawShadowVolume(ent, wl->origin, cullradius, lightradius, wl->mins, wl->maxs, clipmins, clipmaxs);
-               if (r_drawentities.integer)
-                       for (i = 0;i < r_refdef.numentities;i++)
-                               R_TestAndDrawShadowVolume(r_refdef.entities[i], wl->origin, cullradius, lightradius, wl->mins, wl->maxs, clipmins, clipmaxs);
+               if (wl->castshadows)
+               {
+                       if (!visiblevolumes)
+                               R_Shadow_Stage_ShadowVolumes();
+                       ent = &cl_entities[0].render;
+                       if (wl->shadowvolume && r_shadow_staticworldlights.integer)
+                               R_Shadow_DrawWorldLightShadowVolume(&ent->matrix, wl);
+                       else
+                               R_TestAndDrawShadowVolume(ent, wl->origin, cullradius, lightradius, wl->mins, wl->maxs, clipmins, clipmaxs);
+                       if (r_drawentities.integer)
+                               for (i = 0;i < r_refdef.numentities;i++)
+                                       R_TestAndDrawShadowVolume(r_refdef.entities[i], wl->origin, cullradius, lightradius, wl->mins, wl->maxs, clipmins, clipmaxs);
+               }
 
                if (!visiblevolumes)
                {
-                       R_Shadow_Stage_Light();
+                       if (wl->castshadows)
+                               R_Shadow_Stage_LightWithShadows();
+                       else
+                               R_Shadow_Stage_LightWithoutShadows();
                        ent = &cl_entities[0].render;
                        if (ent->model && ent->model->DrawLight)
                        {
@@ -1123,7 +1129,7 @@ void R_ShadowVolumeLighting (int visiblevolumes)
 
                if (!visiblevolumes)
                {
-                       R_Shadow_Stage_Light();
+                       R_Shadow_Stage_LightWithShadows();
                        ent = &cl_entities[0].render;
                        if (ent->model && ent->model->DrawLight)
                        {
@@ -1245,6 +1251,7 @@ R_RenderView
 r_refdef must be set before the first call
 ================
 */
+extern void R_DrawLightningBeams (void);
 void R_RenderView (void)
 {
        entity_render_t *world;
@@ -1338,6 +1345,9 @@ void R_RenderView (void)
                R_TimeReport("dynlight");
        }
 
+       R_DrawLightningBeams();
+       R_TimeReport("lightning");
+
        R_DrawParticles();
        R_TimeReport("particles");
 
index cf63d96..5931325 100644 (file)
@@ -1709,8 +1709,6 @@ static void R_PortalWorldNode(entity_render_t *ent, mleaf_t *viewleaf)
                        }
                }
        }
-       if (r_drawportals.integer)
-               R_DrawPortals(ent);
 }
 
 void R_PVSUpdate (entity_render_t *ent, mleaf_t *viewleaf)
@@ -1785,6 +1783,8 @@ void R_DrawWorld (entity_render_t *ent)
        R_PrepareSurfaces(ent);
        R_DrawSurfaces(ent, SHADERSTAGE_SKY, ent->model->pvstexturechains);
        R_DrawSurfaces(ent, SHADERSTAGE_NORMAL, ent->model->pvstexturechains);
+       if (r_drawportals.integer)
+               R_DrawPortals(ent);
 }
 
 void R_Model_Brush_DrawSky (entity_render_t *ent)
index 4272ead..11305fe 100644 (file)
@@ -40,6 +40,10 @@ cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
 cvar_t r_shadow_shadownudge = {0, "r_shadow_shadownudge", "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);
 void R_Shadow_LoadWorldLights(void);
@@ -316,11 +320,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 +344,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 +356,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;
        }
 }
 
@@ -475,6 +487,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 +510,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_Light(void)
+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_LightWithShadows(void)
 {
        rmeshstate_t m;
        memset(&m, 0, sizeof(m));
@@ -517,6 +563,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 +729,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;
 }
 
@@ -807,6 +855,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);
@@ -826,6 +876,8 @@ void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *element
                {
                        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 +891,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,6 +905,8 @@ 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);
@@ -867,6 +923,8 @@ void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *element
                {
                        GL_Color(color[0], color[1], color[2], 1);
                        R_Mesh_Draw(numverts, numtriangles, elements);
+                       c_rt_lightmeshes++;
+                       c_rt_lighttris += numtriangles;
                }
        }
 }
@@ -896,6 +954,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 +967,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,6 +984,8 @@ 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);
@@ -933,6 +1001,8 @@ void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elemen
                {
                        GL_Color(color[0], color[1], color[2], 1);
                        R_Mesh_Draw(numverts, numtriangles, elements);
+                       c_rt_lightmeshes++;
+                       c_rt_lighttris += numtriangles;
                }
        }
 }
@@ -956,7 +1026,7 @@ 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,8 +1046,16 @@ void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style
 
        e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
        VectorCopy(origin, e->origin);
-       VectorScale(color, r_editlights_rtlightscolorscale.value, e->light);
-       e->lightradius = radius * r_editlights_rtlightssizescale.value;
+       VectorCopy(color, e->light);
+       e->lightradius = radius;
+       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;
@@ -986,10 +1064,9 @@ void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style
        e->mins[2] = e->origin[2] - e->lightradius;
        e->maxs[2] = e->origin[2] + e->lightradius;
 
-       e->style = style;
        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);
@@ -1087,58 +1164,61 @@ void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style
                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;
 
-               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, 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);
+                       // 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);
 }
@@ -1177,14 +1257,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)
 {
@@ -1291,7 +1363,7 @@ 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)
@@ -1314,6 +1386,13 @@ void R_Shadow_LoadWorldLights(void)
                        if (!*s)
                                break;
                        *s = 0;
+                       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;
@@ -1323,7 +1402,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++;
                }
@@ -1353,7 +1434,7 @@ void R_Shadow_SaveWorldLights(void)
        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 / 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 : "");
+               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;
@@ -1412,8 +1493,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++;
                }
@@ -1511,8 +1592,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"))
                                        {
@@ -1520,8 +1601,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"))
                                        {
@@ -1529,8 +1610,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"))
                                        {
@@ -1538,8 +1619,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;
                                        }
                                }
                        }
@@ -1555,7 +1636,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);
        }
 }
 
@@ -1635,60 +1716,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");
@@ -1699,45 +1747,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;
+               }
+               origin[1] += atof(Cmd_Argv(2));
+       }
+       else if (!strcmp(Cmd_Argv(1), "movez"))
+       {
+               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), "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 (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))
+               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)
                {
-                       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;
                }
+               shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
        }
-       Con_Printf("usage: r_editlights_edit radius red green blue [style [cubemap]]\n");
+       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)
@@ -1752,7 +1946,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)
@@ -1771,6 +1966,7 @@ void R_Shadow_EditLights_Init(void)
        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);
 }
index 96480aa..1c6ded7 100644 (file)
@@ -20,17 +20,23 @@ void R_Shadow_RenderVolume(int numverts, int numtris, int *elements);
 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *mesh);
 void R_Shadow_Stage_Begin(void);
 void R_Shadow_Stage_ShadowVolumes(void);
-void R_Shadow_Stage_Light(void);
+void R_Shadow_Stage_LightWithShadows(void);
+void R_Shadow_Stage_LightWithoutShadows(void);
 void R_Shadow_Stage_End(void);
 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius);
 
 typedef struct worldlight_s
 {
+       // saved properties
        vec3_t origin;
+       vec_t lightradius;
        vec3_t light;
+       int castshadows;
+       char *cubemapname;
+
+       // generated properties
        vec3_t mins;
        vec3_t maxs;
-       vec_t lightradius;
        vec_t cullradius;
        struct worldlight_s *next;
        msurface_t **surfaces;
@@ -38,7 +44,6 @@ typedef struct worldlight_s
        mleaf_t **leafs;
        int numleafs;
        rtexture_t *cubemap;
-       char *cubemapname;
        int style;
        shadowmesh_t *shadowvolume;
        int selected;