]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
S_LoadSound now automatically tries both with and without sound/ when loading sounds...
[xonotic/darkplaces.git] / r_shadow.c
index 8d2e49f33748342cb2fbfa48c5f3c26c4ae8f5fb..1559078fca56d7a00a69ce859f19b2fc6ae2f930 100644 (file)
@@ -168,7 +168,7 @@ cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0
 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
 cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
-cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
+cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1"};
 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
@@ -176,12 +176,12 @@ cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1
 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000"};
-cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "1"};
-cvar_t r_shadow_realtime_dlight_shadows = {0, "r_shadow_realtime_dlight_shadows", "0"};
-cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
-cvar_t r_shadow_realtime_world_dlightshadows = {0, "r_shadow_realtime_world_dlightshadows", "1"};
-cvar_t r_shadow_realtime_world_lightmaps = {0, "r_shadow_realtime_world_lightmaps", "0"};
-cvar_t r_shadow_realtime_world_shadows = {0, "r_shadow_realtime_world_shadows", "1"};
+cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1"};
+cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "0"};
+cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0"};
+cvar_t r_shadow_realtime_world_dlightshadows = {CVAR_SAVE, "r_shadow_realtime_world_dlightshadows", "1"};
+cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0"};
+cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1"};
 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0"};
 cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1"};
@@ -205,11 +205,10 @@ int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
 
 float r_shadow_attenpower, r_shadow_attenscale;
 
-float varray_vertex3f2[65536*3];
-
 rtlight_t *r_shadow_compilingrtlight;
 dlight_t *r_shadow_worldlightchain;
 dlight_t *r_shadow_selectedlight;
+dlight_t r_shadow_bufferlight;
 vec3_t r_editlights_cursorlocation;
 
 rtexture_t *lighttextures[5];
@@ -392,7 +391,7 @@ void R_Shadow_Init(void)
        }
        Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
        R_Shadow_EditLights_Init();
-       r_shadow_mempool = Mem_AllocPool("R_Shadow");
+       r_shadow_mempool = Mem_AllocPool("R_Shadow", 0, NULL);
        r_shadow_worldlightchain = NULL;
        maxshadowelements = 0;
        shadowelements = NULL;
@@ -504,8 +503,9 @@ void R_Shadow_PrepareShadowMark(int numtris)
 int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *projectorigin, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
 {
        int i, j, tris = 0, vr[3], t, outvertices = 0;
-       const int *e, *n;
        float f, temp[3];
+       const int *e, *n;
+       const float *v;
 
        if (maxvertexupdate < innumvertices)
        {
@@ -526,10 +526,12 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *
                memset(vertexremap, 0, maxvertexupdate * sizeof(int));
        }
        
+       for (i = 0;i < numshadowmarktris;i++)
+               shadowmark[shadowmarktris[i]] = shadowmarkcount;
+
        for (i = 0;i < numshadowmarktris;i++)
        {
                t = shadowmarktris[i];
-               shadowmark[t] = shadowmarkcount;
                e = inelement3i + t * 3;
                // make sure the vertices are created
                for (j = 0;j < 3;j++)
@@ -538,14 +540,24 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *
                        {
                                vertexupdate[e[j]] = vertexupdatenum;
                                vertexremap[e[j]] = outvertices;
-                               VectorSubtract(invertex3f + e[j] * 3, projectorigin, temp);
+                               v = invertex3f + e[j] * 3;
+                               // project one copy of the vertex to the sphere radius of the light
+                               // (FIXME: would projecting it to the light box be better?)
+                               VectorSubtract(v, projectorigin, temp);
                                f = projectdistance / VectorLength(temp);
-                               VectorCopy(invertex3f + e[j] * 3, outvertex3f);
+                               VectorCopy(v, outvertex3f);
                                VectorMA(projectorigin, f, temp, (outvertex3f + 3));
                                outvertex3f += 6;
                                outvertices += 2;
                        }
                }
+       }
+
+       for (i = 0;i < numshadowmarktris;i++)
+       {
+               t = shadowmarktris[i];
+               e = inelement3i + t * 3;
+               n = inneighbor3i + t * 3;
                // output the front and back triangles
                outelement3i[0] = vertexremap[e[0]];
                outelement3i[1] = vertexremap[e[1]];
@@ -555,13 +567,6 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *
                outelement3i[5] = vertexremap[e[0]] + 1;
                outelement3i += 6;
                tris += 2;
-       }
-
-       for (i = 0;i < numshadowmarktris;i++)
-       {
-               t = shadowmarktris[i];
-               e = inelement3i + t * 3;
-               n = inneighbor3i + t * 3;
                // output the sides (facing outward from this triangle)
                if (shadowmark[n[0]] != shadowmarkcount)
                {
@@ -625,39 +630,41 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f,
        R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
 }
 
-void R_Shadow_VolumeFromBox(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, const vec3_t mins, const vec3_t maxs)
+void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, vec3_t lightmins, vec3_t lightmaxs, vec3_t surfacemins, vec3_t surfacemaxs)
 {
-       int i;
+       int t, tend;
+       const int *e;
        const float *v[3];
-
-       // check which triangles are facing the , and then output
-       // triangle elements and vertices...  by clever use of elements we
-       // can construct the whole shadow from the unprojected vertices and
-       // the projected vertices
-
-       // identify lit faces within the bounding box
-       R_Shadow_PrepareShadowMark(numtris);
-       for (i = 0;i < numtris;i++)
+       if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
+               return;
+       tend = firsttriangle + numtris;
+       if (surfacemins[0] >= lightmins[0] && surfacemaxs[0] <= lightmaxs[0]
+        && surfacemins[1] >= lightmins[1] && surfacemaxs[1] <= lightmaxs[1]
+        && surfacemins[2] >= lightmins[2] && surfacemaxs[2] <= lightmaxs[2])
        {
-               v[0] = invertex3f + elements[i*3+0] * 3;
-               v[1] = invertex3f + elements[i*3+1] * 3;
-               v[2] = invertex3f + elements[i*3+2] * 3;
-               if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2]) && maxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && mins[0] < max(v[0][0], max(v[1][0], v[2][0])) && maxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && mins[1] < max(v[0][1], max(v[1][1], v[2][1])) && maxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && mins[2] < max(v[0][2], max(v[1][2], v[2][2])))
-                       shadowmarklist[numshadowmark++] = i;
+               // surface box entirely inside light box, no box cull
+               for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+                       if (PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
+                               shadowmarklist[numshadowmark++] = t;
+       }
+       else
+       {
+               // surface box not entirely inside light box, cull each triangle
+               for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+               {
+                       v[0] = invertex3f + e[0] * 3;
+                       v[1] = invertex3f + e[1] * 3;
+                       v[2] = invertex3f + e[2] * 3;
+                       if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
+                        && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0]))
+                        && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0]))
+                        && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1]))
+                        && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1]))
+                        && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2]))
+                        && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
+                               shadowmarklist[numshadowmark++] = t;
+               }
        }
-       R_Shadow_VolumeFromList(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, numshadowmark, shadowmarklist);
-}
-
-void R_Shadow_VolumeFromSphere(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, float radius)
-{
-       vec3_t mins, maxs;
-       mins[0] = projectorigin[0] - radius;
-       mins[1] = projectorigin[1] - radius;
-       mins[2] = projectorigin[2] - radius;
-       maxs[0] = projectorigin[0] + radius;
-       maxs[1] = projectorigin[1] + radius;
-       maxs[2] = projectorigin[2] + radius;
-       R_Shadow_VolumeFromBox(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, mins, maxs);
 }
 
 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
@@ -908,7 +915,7 @@ void R_Shadow_Stage_ShadowVolumes(void)
        // optimize for them as noted above
 }
 
-void R_Shadow_Stage_LightWithoutShadows(void)
+void R_Shadow_Stage_Light(int shadowtest)
 {
        rmeshstate_t m;
        memset(&m, 0, sizeof(m));
@@ -923,31 +930,10 @@ void R_Shadow_Stage_LightWithoutShadows(void)
        qglDepthFunc(GL_EQUAL);
        qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
        qglEnable(GL_CULL_FACE);
-       qglDisable(GL_STENCIL_TEST);
-       if (gl_support_stenciltwoside)
-               qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
-       qglStencilMask(~0);
-       qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
-       qglStencilFunc(GL_EQUAL, 128, ~0);
-       r_shadowstage = SHADOWSTAGE_LIGHT;
-       c_rt_lights++;
-}
-
-void R_Shadow_Stage_LightWithShadows(void)
-{
-       rmeshstate_t m;
-       memset(&m, 0, sizeof(m));
-       R_Mesh_State(&m);
-       GL_BlendFunc(GL_ONE, GL_ONE);
-       GL_DepthMask(false);
-       GL_DepthTest(true);
-       qglPolygonOffset(0, 0);
-       //qglDisable(GL_POLYGON_OFFSET_FILL);
-       GL_Color(1, 1, 1, 1);
-       GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
-       qglDepthFunc(GL_EQUAL);
-       qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-       qglEnable(GL_STENCIL_TEST);
+       if (shadowtest)
+               qglEnable(GL_STENCIL_TEST);
+       else
+               qglDisable(GL_STENCIL_TEST);
        if (gl_support_stenciltwoside)
                qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
        qglStencilMask(~0);
@@ -1312,6 +1298,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                bumptexture = r_shadow_blankbumptexture;
        if (!glosstexture)
                glosstexture = r_shadow_blankglosstexture;
+       // FIXME: support EF_NODEPTHTEST
        GL_DepthMask(false);
        GL_DepthTest(true);
        if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
@@ -2042,7 +2029,7 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
        int i, shadow;
        entity_render_t *ent;
        float f;
-       vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
+       vec3_t relativelightorigin, relativeeyeorigin, lightcolor, lightcolor2;
        rtexture_t *cubemaptexture;
        matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
        int numclusters, numsurfaces;
@@ -2125,23 +2112,7 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
        }
        */
 
-#if 1
-       shadow = rtlight->shadow && (rtlight->isstatic ? r_shadow_realtime_world_shadows.integer : (r_shadow_realtime_world.integer ? r_shadow_realtime_world_dlightshadows.integer : r_shadow_realtime_dlight_shadows.integer));
-#else
-       shadow = false;
-       if (rtlight->shadow)
-       {
-               if (rtlight->isstatic)
-                       shadow = r_shadow_realtime_world_shadows.integer;
-               else
-               {
-                       if (r_shadow_realtime_world.integer)
-                               shadow = r_shadow_realtime_world_dlightshadows.integer;
-                       else
-                               shadow = r_shadow_realtime_dlight_shadows.integer;
-               }
-       }
-#endif
+       shadow = rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows);
 
        if (shadow && (gl_stencil || visiblevolumes))
        {
@@ -2175,7 +2146,7 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
                                GL_LockArrays(0, 0);
                        }
                }
-               else
+               else if (numsurfaces)
                {
                        Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
                        ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, numsurfaces, surfacelist);
@@ -2206,13 +2177,10 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
 
        if (!visiblevolumes)
        {
-               if (shadow && gl_stencil)
-                       R_Shadow_Stage_LightWithShadows();
-               else
-                       R_Shadow_Stage_LightWithoutShadows();
+               R_Shadow_Stage_Light(shadow && gl_stencil);
 
                ent = &cl_entities[0].render;
-               if (ent->model && ent->model->DrawLight)
+               if (ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
                {
                        Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
                        Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
@@ -2233,14 +2201,17 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
                        for (i = 0;i < r_refdef.numentities;i++)
                        {
                                ent = r_refdef.entities[i];
-                               if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
+                               // can't draw transparent entity lighting here because
+                               // transparent meshes are deferred for later
+                               if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & (RENDER_LIGHT | RENDER_TRANSPARENT)) == RENDER_LIGHT)
                                {
+                                       VectorScale(lightcolor, ent->alpha, lightcolor2);
                                        Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
                                        Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
                                        Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
                                        Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
                                        Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
-                                       ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, ent->model->nummodelsurfaces, ent->model->surfacelist);
+                                       ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor2, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, ent->model->nummodelsurfaces, ent->model->surfacelist);
                                }
                        }
                }
@@ -2269,7 +2240,7 @@ void R_ShadowVolumeLighting(int visiblevolumes)
        }
        else
                R_Shadow_Stage_Begin();
-       if (r_shadow_realtime_world.integer)
+       if (r_rtworld)
        {
                if (r_shadow_debuglight.integer >= 0)
                {
@@ -2281,7 +2252,7 @@ void R_ShadowVolumeLighting(int visiblevolumes)
                        for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
                                R_DrawRTLight(&light->rtlight, visiblevolumes);
        }
-       if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
+       if (r_rtdlight)
                for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
                        R_DrawRTLight(&light->rtlight, visiblevolumes);
 
@@ -2412,21 +2383,25 @@ void R_Shadow_FreeCubemaps(void)
        R_FreeTexturePool(&r_shadow_filters_texturepool);
 }
 
-void R_Shadow_NewWorldLight(vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname)
+dlight_t *R_Shadow_NewWorldLight(void)
 {
        dlight_t *light;
-
-       if (radius < 15 || DotProduct(color, color) < 0.03)
-       {
-               Con_Print("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
-               return;
-       }
-
        light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
+       light->next = r_shadow_worldlightchain;
+       r_shadow_worldlightchain = light;
+       return light;
+}
+
+void R_Shadow_UpdateWorldLight(dlight_t *light, vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname)
+{
        VectorCopy(origin, light->origin);
-       VectorCopy(angles, light->angles);
-       VectorCopy(color, light->color);
-       light->radius = radius;
+       light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
+       light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
+       light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
+       light->color[0] = max(color[0], 0);
+       light->color[1] = max(color[1], 0);
+       light->color[2] = max(color[2], 0);
+       light->radius = max(radius, 0);
        light->style = style;
        if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
        {
@@ -2435,11 +2410,10 @@ void R_Shadow_NewWorldLight(vec3_t origin, vec3_t angles, vec3_t color, vec_t ra
        }
        light->shadow = shadowenable;
        light->corona = corona;
-       if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname))
-               strcpy(light->cubemapname, cubemapname);
+       if (!cubemapname)
+               cubemapname = "";
+       strlcpy(light->cubemapname, cubemapname, strlen(light->cubemapname));
        Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
-       light->next = r_shadow_worldlightchain;
-       r_shadow_worldlightchain = light;
 
        R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
 }
@@ -2504,8 +2478,8 @@ void R_Shadow_DrawLightSprites(void)
                        lighttextures[i] = pic->tex;
        }
 
-       for (light = r_shadow_worldlightchain;light;light = light->next)
-               R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
+       for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next)
+               R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, i % 5);
        R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
 }
 
@@ -2595,7 +2569,7 @@ void R_Shadow_LoadWorldLights(void)
                        }
                        VectorScale(color, r_editlights_rtlightscolorscale.value, color);
                        radius *= r_editlights_rtlightssizescale.value;
-                       R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
+                       R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname);
                        s++;
                        n++;
                }
@@ -2625,7 +2599,10 @@ void R_Shadow_SaveWorldLights(void)
        buf = NULL;
        for (light = r_shadow_worldlightchain;light;light = light->next)
        {
-               sprintf(line, "%s%f %f %f %f %f %f %f %d %s %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname[0] ? light->cubemapname : "\"\"", light->corona, light->angles[0], light->angles[1], light->angles[2]);
+               if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
+                       sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2]);
+               else
+                       sprintf(line, "%s%f %f %f %f %f %f %f %d\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style);
                if (bufchars + (int) strlen(line) > bufmaxchars)
                {
                        bufmaxchars = bufchars + strlen(line) + 2048;
@@ -2685,7 +2662,7 @@ void R_Shadow_LoadLightsFile(void)
                        radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
                        radius = bound(15, radius, 4096);
                        VectorScale(color, (2.0f / (8388608.0f)), color);
-                       R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
+                       R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL);
                        s++;
                        n++;
                }
@@ -2701,16 +2678,23 @@ typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX,
 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
 {
        int entnum, style, islight, skin, pflags, effects, type, n;
-       char key[256], value[1024];
-       float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
+       char *entfiledata;
        const char *data;
+       float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
+       char key[256], value[1024];
 
        if (cl.worldmodel == NULL)
        {
                Con_Print("No map loaded.\n");
                return;
        }
-       data = cl.worldmodel->brush.entities;
+       // try to load a .ent file first
+       FS_StripExtension (cl.worldmodel->name, key, sizeof (key));
+       strlcat (key, ".ent", sizeof (key));
+       data = entfiledata = FS_LoadFile(key, tempmempool, true);
+       // and if that is not found, fall back to the bsp file entity string
+       if (!data)
+               data = cl.worldmodel->brush.entities;
        if (!data)
                return;
        for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
@@ -2902,8 +2886,10 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                }
                VectorAdd(origin, originhack, origin);
                if (radius >= 1)
-                       R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
+                       R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
        }
+       if (entfiledata)
+               Mem_Free(entfiledata);
 }
 
 
@@ -2993,7 +2979,7 @@ void R_Shadow_EditLights_Spawn_f(void)
                return;
        }
        color[0] = color[1] = color[2] = 1;
-       R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
+       R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
 }
 
 void R_Shadow_EditLights_Edit_f(void)
@@ -3210,15 +3196,19 @@ void R_Shadow_EditLights_Edit_f(void)
                Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
                return;
        }
-       R_Shadow_FreeWorldLight(r_shadow_selectedlight);
-       r_shadow_selectedlight = NULL;
-       R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
+       R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname);
 }
 
 void R_Shadow_EditLights_EditAll_f(void)
 {
        dlight_t *light;
 
+       if (!r_editlights.integer)
+       {
+               Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
+               return;
+       }
+
        for (light = r_shadow_worldlightchain;light;light = light->next)
        {
                R_Shadow_SelectLight(light);
@@ -3228,14 +3218,23 @@ void R_Shadow_EditLights_EditAll_f(void)
 
 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
 {
+       int lightnumber, lightcount;
+       dlight_t *light;
        float x, y;
        char temp[256];
+       if (!r_editlights.integer)
+               return;
        x = 0;
        y = con_vislines;
-       sprintf(temp, "Cursor  %f %f %f", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
+       lightnumber = -1;
+       lightcount = 0;
+       for (lightcount = 0, light = r_shadow_worldlightchain;light;lightcount++, light = light->next)
+               if (light == r_shadow_selectedlight)
+                       lightnumber = lightcount;
+       sprintf(temp, "Cursor  %f %f %f  Total Lights %i", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2], lightcount);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
        if (r_shadow_selectedlight == NULL)
                return;
-       sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
+       sprintf(temp, "Light #%i properties", lightnumber);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, "Angles  %f %f %f", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
        sprintf(temp, "Color   %f %f %f", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
@@ -3258,9 +3257,7 @@ void R_Shadow_EditLights_ToggleShadow_f(void)
                Con_Print("No selected light.\n");
                return;
        }
-       R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, r_shadow_selectedlight->corona, r_shadow_selectedlight->style, !r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
-       R_Shadow_FreeWorldLight(r_shadow_selectedlight);
-       r_shadow_selectedlight = NULL;
+       R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, r_shadow_selectedlight->corona, r_shadow_selectedlight->style, !r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
 }
 
 void R_Shadow_EditLights_ToggleCorona_f(void)
@@ -3275,9 +3272,7 @@ void R_Shadow_EditLights_ToggleCorona_f(void)
                Con_Print("No selected light.\n");
                return;
        }
-       R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, !r_shadow_selectedlight->corona, r_shadow_selectedlight->style, r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
-       R_Shadow_FreeWorldLight(r_shadow_selectedlight);
-       r_shadow_selectedlight = NULL;
+       R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, !r_shadow_selectedlight->corona, r_shadow_selectedlight->style, r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
 }
 
 void R_Shadow_EditLights_Remove_f(void)
@@ -3343,6 +3338,45 @@ void R_Shadow_EditLights_Help_f(void)
        );
 }
 
+void R_Shadow_EditLights_CopyInfo_f(void)
+{
+       if (!r_editlights.integer)
+       {
+               Con_Print("Cannot copy light info when not in editing mode.  Set r_editlights to 1.\n");
+               return;
+       }
+       if (!r_shadow_selectedlight)
+       {
+               Con_Print("No selected light.\n");
+               return;
+       }
+       VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
+       VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
+       r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
+       r_shadow_bufferlight.style = r_shadow_selectedlight->style;
+       if (r_shadow_selectedlight->cubemapname)
+               strcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname);
+       else
+               r_shadow_bufferlight.cubemapname[0] = 0;
+       r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
+       r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
+}
+
+void R_Shadow_EditLights_PasteInfo_f(void)
+{
+       if (!r_editlights.integer)
+       {
+               Con_Print("Cannot paste light info when not in editing mode.  Set r_editlights to 1.\n");
+               return;
+       }
+       if (!r_shadow_selectedlight)
+       {
+               Con_Print("No selected light.\n");
+               return;
+       }
+       R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_bufferlight.angles, r_shadow_bufferlight.color, r_shadow_bufferlight.radius, r_shadow_bufferlight.corona, r_shadow_bufferlight.style, r_shadow_bufferlight.shadow, r_shadow_bufferlight.cubemapname);
+}
+
 void R_Shadow_EditLights_Init(void)
 {
        Cvar_RegisterVariable(&r_editlights);
@@ -3365,5 +3399,7 @@ void R_Shadow_EditLights_Init(void)
        Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
        Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
        Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
+       Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f);
+       Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f);
 }