]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
reenabled R_UpdateAllTextureInfo calls when lighting the world model
[xonotic/darkplaces.git] / r_shadow.c
index afdd9bbbae755dcb96aa1f001cc4d832b74ec140..4964cf58e648f620326e08072102385e655f524e 100644 (file)
@@ -194,6 +194,7 @@ rtexturepool_t *r_shadow_texturepool;
 rtexture_t *r_shadow_attenuationgradienttexture;
 rtexture_t *r_shadow_attenuation2dtexture;
 rtexture_t *r_shadow_attenuation3dtexture;
+rtexture_t *r_shadow_lightcorona;
 
 // lights are reloaded when this changes
 char r_shadow_mapname[MAX_QPATH];
@@ -232,6 +233,8 @@ cvar_t r_shadow_culltriangles = {0, "r_shadow_culltriangles", "1", "performs mor
 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1", "how much to push shadow volumes into the distance when rendering, to reduce chances of zfighting artifacts (should not be less than 0)"};
 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel textures for spherical attenuation rather than cylindrical (does not affect r_glsl lighting)"};
+cvar_t r_coronas = {CVAR_SAVE, "r_coronas", "1", "brightness of corona flare effects around certain lights, 0 disables corona effects"};
+cvar_t gl_flashblend = {CVAR_SAVE, "gl_flashblend", "0", "render bright coronas for dynamic lights instead of actual lighting, fast but ugly"};
 cvar_t gl_ext_separatestencil = {0, "gl_ext_separatestencil", "1", "make use of OpenGL 2.0 glStencilOpSeparate or GL_ATI_separate_stencil extension"};
 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
 cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
@@ -253,7 +256,7 @@ static float r_shadow_attenlinearscale; // r_shadow_lightattenuationlinearscale
 static float r_shadow_attentable[ATTENTABLESIZE+1];
 
 rtlight_t *r_shadow_compilingrtlight;
-dlight_t *r_shadow_worldlightchain;
+static memexpandablearray_t r_shadow_worldlightsarray;
 dlight_t *r_shadow_selectedlight;
 dlight_t r_shadow_bufferlight;
 vec3_t r_editlights_cursorlocation;
@@ -281,6 +284,15 @@ void R_Shadow_EditLights_Reload_f(void);
 void R_Shadow_ValidateCvars(void);
 static void R_Shadow_MakeTextures(void);
 
+// VorteX: custom editor light sprites
+#define EDLIGHTSPRSIZE                 8
+cachepic_t *r_editlights_sprcursor;
+cachepic_t *r_editlights_sprlight;
+cachepic_t *r_editlights_sprnoshadowlight;
+cachepic_t *r_editlights_sprcubemaplight;
+cachepic_t *r_editlights_sprcubemapnoshadowlight;
+cachepic_t *r_editlights_sprselection;
+
 void r_shadow_start(void)
 {
        // allocate vertex processing arrays
@@ -444,6 +456,8 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_polygonfactor);
        Cvar_RegisterVariable(&r_shadow_polygonoffset);
        Cvar_RegisterVariable(&r_shadow_texture3d);
+       Cvar_RegisterVariable(&r_coronas);
+       Cvar_RegisterVariable(&gl_flashblend);
        Cvar_RegisterVariable(&gl_ext_separatestencil);
        Cvar_RegisterVariable(&gl_ext_stenciltwoside);
        if (gamemode == GAME_TENEBRAE)
@@ -453,7 +467,7 @@ void R_Shadow_Init(void)
        }
        Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f, "prints documentation on console commands and variables used by realtime lighting and shadowing system");
        R_Shadow_EditLights_Init();
-       r_shadow_worldlightchain = NULL;
+       Mem_ExpandableArray_NewArray(&r_shadow_worldlightsarray, r_main_mempool, sizeof(dlight_t), 128);
        maxshadowtriangles = 0;
        shadowelements = NULL;
        maxshadowvertices = 0;
@@ -895,11 +909,11 @@ void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *verte
        if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
        {
                // decrement stencil if backface is behind depthbuffer
-               GL_CullFace(GL_BACK); // quake is backwards, this culls front faces
+               GL_CullFace(r_view.cullface_front);
                qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
                R_Mesh_Draw(0, numvertices, numtriangles, element3i, 0, 0);
                // increment stencil if frontface is behind depthbuffer
-               GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
+               GL_CullFace(r_view.cullface_back);
                qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
        }
        R_Mesh_Draw(0, numvertices, numtriangles, element3i, 0, 0);
@@ -907,27 +921,46 @@ void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *verte
        CHECKGLERROR
 }
 
-static unsigned char R_Shadow_MakeTextures_SamplePoint(float x, float y, float z)
+static void R_Shadow_MakeTextures_MakeCorona(void)
+{
+       float dx, dy;
+       int x, y, a;
+       unsigned char pixels[32][32][4];
+       for (y = 0;y < 32;y++)
+       {
+               dy = (y - 15.5f) * (1.0f / 16.0f);
+               for (x = 0;x < 32;x++)
+               {
+                       dx = (x - 15.5f) * (1.0f / 16.0f);
+                       a = (int)(((1.0f / (dx * dx + dy * dy + 0.2f)) - (1.0f / (1.0f + 0.2))) * 32.0f / (1.0f / (1.0f + 0.2)));
+                       a = bound(0, a, 255);
+                       pixels[y][x][0] = a;
+                       pixels[y][x][1] = a;
+                       pixels[y][x][2] = a;
+                       pixels[y][x][3] = 255;
+               }
+       }
+       r_shadow_lightcorona = R_LoadTexture2D(r_shadow_texturepool, "lightcorona", 32, 32, &pixels[0][0][0], TEXTYPE_BGRA, TEXF_PRECACHE, NULL);
+}
+
+static unsigned int R_Shadow_MakeTextures_SamplePoint(float x, float y, float z)
 {
        float dist = sqrt(x*x+y*y+z*z);
        float intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
-       return (unsigned char)bound(0, intensity * 256.0f, 255);
+       // note this code could suffer byte order issues except that it is multiplying by an integer that reads the same both ways
+       return (unsigned char)bound(0, intensity * 256.0f, 255) * 0x01010101;
 }
 
 static void R_Shadow_MakeTextures(void)
 {
        int x, y, z;
        float intensity, dist;
-       unsigned char *data;
-       unsigned int palette[256];
+       unsigned int *data;
        R_FreeTexturePool(&r_shadow_texturepool);
        r_shadow_texturepool = R_AllocTexturePool();
        r_shadow_attenlinearscale = r_shadow_lightattenuationlinearscale.value;
        r_shadow_attendividebias = r_shadow_lightattenuationdividebias.value;
-       // note this code could suffer byte order issues except that it is multiplying by an integer that reads the same both ways
-       for (x = 0;x < 256;x++)
-               palette[x] = x * 0x01010101;
-       data = (unsigned char *)Mem_Alloc(tempmempool, max(max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE, ATTEN2DSIZE*ATTEN2DSIZE), ATTEN1DSIZE));
+       data = (unsigned int *)Mem_Alloc(tempmempool, max(max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE, ATTEN2DSIZE*ATTEN2DSIZE), ATTEN1DSIZE) * 4);
        // the table includes one additional value to avoid the need to clamp indexing due to minor math errors
        for (x = 0;x <= ATTENTABLESIZE;x++)
        {
@@ -938,12 +971,12 @@ static void R_Shadow_MakeTextures(void)
        // 1D gradient texture
        for (x = 0;x < ATTEN1DSIZE;x++)
                data[x] = R_Shadow_MakeTextures_SamplePoint((x + 0.5f) * (1.0f / ATTEN1DSIZE) * (1.0f / 0.9375), 0, 0);
-       r_shadow_attenuationgradienttexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation1d", ATTEN1DSIZE, 1, data, TEXTYPE_PALETTE, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, palette);
+       r_shadow_attenuationgradienttexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation1d", ATTEN1DSIZE, 1, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
        // 2D circle texture
        for (y = 0;y < ATTEN2DSIZE;y++)
                for (x = 0;x < ATTEN2DSIZE;x++)
                        data[y*ATTEN2DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), 0);
-       r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_PALETTE, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, palette);
+       r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
        // 3D sphere texture
        if (r_shadow_texture3d.integer && gl_texture3d)
        {
@@ -951,11 +984,21 @@ static void R_Shadow_MakeTextures(void)
                        for (y = 0;y < ATTEN3DSIZE;y++)
                                for (x = 0;x < ATTEN3DSIZE;x++)
                                        data[(z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375));
-               r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_PALETTE, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, palette);
+               r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
        }
        else
                r_shadow_attenuation3dtexture = NULL;
        Mem_Free(data);
+
+       R_Shadow_MakeTextures_MakeCorona();
+
+       // Editor light sprites
+       r_editlights_sprcursor = Draw_CachePic("gfx/editlights/cursor", true);
+       r_editlights_sprlight = Draw_CachePic("gfx/editlights/light", true);
+       r_editlights_sprnoshadowlight = Draw_CachePic("gfx/editlights/noshadow", true);
+       r_editlights_sprcubemaplight = Draw_CachePic("gfx/editlights/cubemaplight", true);
+       r_editlights_sprcubemapnoshadowlight = Draw_CachePic("gfx/editlights/cubemapnoshadowlight", true);
+       r_editlights_sprselection = Draw_CachePic("gfx/editlights/selection", true);
 }
 
 void R_Shadow_ValidateCvars(void)
@@ -1033,7 +1076,7 @@ void R_Shadow_RenderMode_Reset(void)
        qglStencilMask(~0);CHECKGLERROR
        qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
        qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
-       GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
+       GL_CullFace(r_view.cullface_back);
        GL_Color(1, 1, 1, 1);
        GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
        GL_BlendFunc(GL_ONE, GL_ZERO);
@@ -1051,17 +1094,17 @@ void R_Shadow_RenderMode_StencilShadowVolumes(qboolean clearstencil)
        if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SEPARATESTENCIL)
        {
                GL_CullFace(GL_NONE);
-               qglStencilOpSeparate(GL_BACK, GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR // quake is backwards, this is front faces
-               qglStencilOpSeparate(GL_FRONT, GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR // quake is backwards, this is back faces
+               qglStencilOpSeparate(r_view.cullface_front, GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
+               qglStencilOpSeparate(r_view.cullface_back, GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
        }
        else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
        {
                GL_CullFace(GL_NONE);
                qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
-               qglActiveStencilFaceEXT(GL_BACK);CHECKGLERROR // quake is backwards, this is front faces
+               qglActiveStencilFaceEXT(r_view.cullface_front);CHECKGLERROR
                qglStencilMask(~0);CHECKGLERROR
                qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
-               qglActiveStencilFaceEXT(GL_FRONT);CHECKGLERROR // quake is backwards, this is back faces
+               qglActiveStencilFaceEXT(r_view.cullface_back);CHECKGLERROR
                qglStencilMask(~0);CHECKGLERROR
                qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
        }
@@ -1150,14 +1193,41 @@ void R_Shadow_RenderMode_End(void)
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 }
 
+int bboxedges[12][2] =
+{
+       // top
+       {0, 1}, // +X
+       {0, 2}, // +Y
+       {1, 3}, // Y, +X
+       {2, 3}, // X, +Y
+       // bottom
+       {4, 5}, // +X
+       {4, 6}, // +Y
+       {5, 7}, // Y, +X
+       {6, 7}, // X, +Y
+       // verticals
+       {0, 4}, // +Z
+       {1, 5}, // X, +Z
+       {2, 6}, // Y, +Z
+       {3, 7}, // XY, +Z
+};
+
 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
 {
        int i, ix1, iy1, ix2, iy2;
        float x1, y1, x2, y2;
        vec4_t v, v2;
-       rmesh_t mesh;
-       mplane_t planes[11];
-       float vertex3f[256*3];
+       float vertex[20][3];
+       int j, k;
+       vec4_t plane4f;
+       int numvertices;
+       float corner[8][4];
+       float dist[8];
+       int sign[8];
+       float f;
+
+       if (!r_shadow_scissor.integer)
+               return false;
 
        // if view is inside the light box, just say yes it's visible
        if (BoxesOverlap(r_view.origin, r_view.origin, mins, maxs))
@@ -1166,40 +1236,52 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
                return false;
        }
 
-       // create a temporary brush describing the area the light can affect in worldspace
-       VectorNegate(r_view.frustum[0].normal, planes[ 0].normal);planes[ 0].dist = -r_view.frustum[0].dist;
-       VectorNegate(r_view.frustum[1].normal, planes[ 1].normal);planes[ 1].dist = -r_view.frustum[1].dist;
-       VectorNegate(r_view.frustum[2].normal, planes[ 2].normal);planes[ 2].dist = -r_view.frustum[2].dist;
-       VectorNegate(r_view.frustum[3].normal, planes[ 3].normal);planes[ 3].dist = -r_view.frustum[3].dist;
-       VectorNegate(r_view.frustum[4].normal, planes[ 4].normal);planes[ 4].dist = -r_view.frustum[4].dist;
-       VectorSet   (planes[ 5].normal,  1, 0, 0);         planes[ 5].dist =  maxs[0];
-       VectorSet   (planes[ 6].normal, -1, 0, 0);         planes[ 6].dist = -mins[0];
-       VectorSet   (planes[ 7].normal, 0,  1, 0);         planes[ 7].dist =  maxs[1];
-       VectorSet   (planes[ 8].normal, 0, -1, 0);         planes[ 8].dist = -mins[1];
-       VectorSet   (planes[ 9].normal, 0, 0,  1);         planes[ 9].dist =  maxs[2];
-       VectorSet   (planes[10].normal, 0, 0, -1);         planes[10].dist = -mins[2];
-
-       // turn the brush into a mesh
-       memset(&mesh, 0, sizeof(rmesh_t));
-       mesh.maxvertices = 256;
-       mesh.vertex3f = vertex3f;
-       mesh.epsilon2 = (1.0f / (32.0f * 32.0f));
-       R_Mesh_AddBrushMeshFromPlanes(&mesh, 11, planes);
-
-       // if that mesh is empty, the light is not visible at all
-       if (!mesh.numvertices)
-               return true;
+       x1 = y1 = x2 = y2 = 0;
 
-       if (!r_shadow_scissor.integer)
-               return false;
+       // transform all corners that are infront of the nearclip plane
+       VectorNegate(r_view.frustum[4].normal, plane4f);
+       plane4f[3] = r_view.frustum[4].dist;
+       numvertices = 0;
+       for (i = 0;i < 8;i++)
+       {
+               Vector4Set(corner[i], (i & 1) ? maxs[0] : mins[0], (i & 2) ? maxs[1] : mins[1], (i & 4) ? maxs[2] : mins[2], 1);
+               dist[i] = DotProduct4(corner[i], plane4f);
+               sign[i] = dist[i] > 0;
+               if (!sign[i])
+               {
+                       VectorCopy(corner[i], vertex[numvertices]);
+                       numvertices++;
+               }
+       }
+       // if some points are behind the nearclip, add clipped edge points to make
+       // sure that the scissor boundary is complete
+       if (numvertices > 0 && numvertices < 8)
+       {
+               // add clipped edge points
+               for (i = 0;i < 12;i++)
+               {
+                       j = bboxedges[i][0];
+                       k = bboxedges[i][1];
+                       if (sign[j] != sign[k])
+                       {
+                               f = dist[j] / (dist[j] - dist[k]);
+                               VectorLerp(corner[j], f, corner[k], vertex[numvertices]);
+                               numvertices++;
+                       }
+               }
+       }
+
+       // if we have no points to check, the light is behind the view plane
+       if (!numvertices)
+               return true;
 
-       // if that mesh is not empty, check what area of the screen it covers
+       // if we have some points to transform, check what screen area is covered
        x1 = y1 = x2 = y2 = 0;
        v[3] = 1.0f;
-       //Con_Printf("%i vertices to transform...\n", mesh.numvertices);
-       for (i = 0;i < mesh.numvertices;i++)
+       //Con_Printf("%i vertices to transform...\n", numvertices);
+       for (i = 0;i < numvertices;i++)
        {
-               VectorCopy(mesh.vertex3f + i * 3, v);
+               VectorCopy(vertex[i], v);
                GL_TransformToScreen(v, v2);
                //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
                if (i)
@@ -1463,7 +1545,16 @@ static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvert
 static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
 {
        // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
-       R_SetupSurfaceShader(lightcolorbase, false, ambientscale, diffusescale, specularscale);
+       R_SetupSurfaceShader(lightcolorbase, false, ambientscale, diffusescale, specularscale, RSURFPASS_RTLIGHT);
+       R_Mesh_TexMatrix(0, &rsurface.texture->currenttexmatrix);
+       R_Mesh_TexBind(0, R_GetTexture(rsurface.texture->currentskinframe->nmap));
+       R_Mesh_TexBind(1, R_GetTexture(rsurface.texture->basetexture));
+       R_Mesh_TexBind(2, R_GetTexture(rsurface.texture->glosstexture));
+       R_Mesh_TexBindCubeMap(3, R_GetTexture(rsurface.rtlight->currentcubemap));
+       R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation));
+       R_Mesh_TexBind(5, R_GetTexture(rsurface.texture->currentskinframe->pants));
+       R_Mesh_TexBind(6, R_GetTexture(rsurface.texture->currentskinframe->shirt));
+       R_Mesh_TexBind(10, R_GetTexture(r_shadow_attenuationgradienttexture));
        R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
        R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
        R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
@@ -2241,10 +2332,7 @@ void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles,
        }
        if ((ambientscale + diffusescale) * VectorLength2(lightcolorbase) + specularscale * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
                return;
-       GL_DepthRange(0, (rsurface.texture->currentmaterialflags & MATERIALFLAG_SHORTDEPTHRANGE) ? 0.0625 : 1);
-       GL_PolygonOffset(rsurface.texture->currentpolygonfactor, rsurface.texture->currentpolygonoffset);
-       GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
-       GL_CullFace((rsurface.texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : GL_FRONT); // quake is backwards, this culls back faces
+       RSurf_SetupDepthAndCulling();
        nmap = rsurface.texture->currentskinframe->nmap;
        if (gl_lightmaps.integer)
                nmap = r_texture_blanknormalmap;
@@ -2474,9 +2562,15 @@ void R_RTLight_Uncompile(rtlight_t *rtlight)
 
 void R_Shadow_UncompileWorldLights(void)
 {
+       size_t lightindex;
        dlight_t *light;
-       for (light = r_shadow_worldlightchain;light;light = light->next)
+       for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
+       {
+               light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+               if (!light)
+                       continue;
                R_RTLight_Uncompile(&light->rtlight);
+       }
 }
 
 void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
@@ -2660,11 +2754,11 @@ void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned
                        if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
                        {
                                // decrement stencil if backface is behind depthbuffer
-                               GL_CullFace(GL_BACK); // quake is backwards, this culls front faces
+                               GL_CullFace(r_view.cullface_front);
                                qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
                                R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->ebo, 0);
                                // increment stencil if frontface is behind depthbuffer
-                               GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
+                               GL_CullFace(r_view.cullface_back);
                                qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
                        }
                        R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->ebo, 0);
@@ -2736,13 +2830,14 @@ void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned c
        r_refdef.worldmodel->DrawLight(r_refdef.worldentity, numsurfaces, surfacelist, trispvs);
 }
 
-void R_Shadow_DrawEntityLight(entity_render_t *ent, int numsurfaces, int *surfacelist)
+void R_Shadow_DrawEntityLight(entity_render_t *ent)
 {
        model_t *model = ent->model;
        if (!model->DrawLight)
                return;
 
        R_Shadow_SetupEntityLight(ent);
+       R_UpdateAllTextureInfo(ent);
 
        model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist, NULL);
 }
@@ -2778,7 +2873,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
 
        // look up the light style value at this time
-       f = (rtlight->style >= 0 ? r_refdef.lightstylevalue[rtlight->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value;
+       f = (rtlight->style >= 0 ? r_refdef.rtlightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value;
        VectorScale(rtlight->color, f, rtlight->currentcolor);
        /*
        if (rtlight->selected)
@@ -2963,7 +3058,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                        // draw lighting in the unmasked areas
                        R_Shadow_RenderMode_Lighting(true, false);
                        for (i = 0;i < numlightentities_noselfshadow;i++)
-                               R_Shadow_DrawEntityLight(lightentities_noselfshadow[i], numsurfaces, surfacelist);
+                               R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
 
                        // optionally draw the illuminated areas
                        // for performance analysis by level designers
@@ -2971,7 +3066,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                        {
                                R_Shadow_RenderMode_VisibleLighting(!r_showdisabledepthtest.integer, false);
                                for (i = 0;i < numlightentities_noselfshadow;i++)
-                                       R_Shadow_DrawEntityLight(lightentities_noselfshadow[i], numsurfaces, surfacelist);
+                                       R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
                        }
 
                        R_Shadow_RenderMode_StencilShadowVolumes(false);
@@ -2986,7 +3081,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                        if (numsurfaces)
                                R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
                        for (i = 0;i < numlightentities;i++)
-                               R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
+                               R_Shadow_DrawEntityLight(lightentities[i]);
 
                        // optionally draw the illuminated areas
                        // for performance analysis by level designers
@@ -2996,7 +3091,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                                if (numsurfaces)
                                        R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
                                for (i = 0;i < numlightentities;i++)
-                                       R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
+                                       R_Shadow_DrawEntityLight(lightentities[i]);
                        }
                }
        }
@@ -3009,9 +3104,9 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                        if (numsurfaces)
                                R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
                        for (i = 0;i < numlightentities;i++)
-                               R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
+                               R_Shadow_DrawEntityLight(lightentities[i]);
                        for (i = 0;i < numlightentities_noselfshadow;i++)
-                               R_Shadow_DrawEntityLight(lightentities_noselfshadow[i], numsurfaces, surfacelist);
+                               R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
 
                        // optionally draw the illuminated areas
                        // for performance analysis by level designers
@@ -3021,9 +3116,9 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                                if (numsurfaces)
                                        R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
                                for (i = 0;i < numlightentities;i++)
-                                       R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
+                                       R_Shadow_DrawEntityLight(lightentities[i]);
                                for (i = 0;i < numlightentities_noselfshadow;i++)
-                                       R_Shadow_DrawEntityLight(lightentities_noselfshadow[i], numsurfaces, surfacelist);
+                                       R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
                        }
                }
        }
@@ -3032,7 +3127,9 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
 void R_Shadow_DrawLightSprites(void);
 void R_ShadowVolumeLighting(qboolean visible)
 {
-       int lnum, flag;
+       int flag;
+       int lnum;
+       size_t lightindex;
        dlight_t *light;
 
        if (r_refdef.worldmodel && strncmp(r_refdef.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
@@ -3046,14 +3143,20 @@ void R_ShadowVolumeLighting(qboolean visible)
        flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
        if (r_shadow_debuglight.integer >= 0)
        {
-               for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
-                       if (lnum == r_shadow_debuglight.integer && (light->flags & flag))
-                               R_DrawRTLight(&light->rtlight, visible);
+               lightindex = r_shadow_debuglight.integer;
+               light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+               if (light && (light->flags & flag))
+                       R_DrawRTLight(&light->rtlight, visible);
        }
        else
-               for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
-                       if (light->flags & flag)
+       {
+               for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
+               {
+                       light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+                       if (light && (light->flags & flag))
                                R_DrawRTLight(&light->rtlight, visible);
+               }
+       }
        if (r_refdef.rtdlight)
                for (lnum = 0;lnum < r_refdef.numlights;lnum++)
                        R_DrawRTLight(&r_refdef.lights[lnum], visible);
@@ -3061,7 +3164,7 @@ void R_ShadowVolumeLighting(qboolean visible)
        R_Shadow_RenderMode_End();
 }
 
-extern void R_SetupView(const matrix4x4_t *matrix);
+extern void R_SetupView(void);
 extern cvar_t r_shadows_throwdistance;
 void R_DrawModelShadows(void)
 {
@@ -3143,12 +3246,72 @@ void R_DrawModelShadows(void)
        R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
 
        // restoring the perspective view is done by R_RenderScene
-       //R_SetupView(&r_view.matrix);
+       //R_SetupView();
 
        // restore other state to normal
        R_Shadow_RenderMode_End();
 }
 
+void R_DrawCoronas(void)
+{
+       int i, flag;
+       float cscale, scale;
+       size_t lightindex;
+       dlight_t *light;
+       rtlight_t *rtlight;
+       if (r_coronas.value < (1.0f / 256.0f) && !gl_flashblend.integer)
+               return;
+       R_Mesh_Matrix(&identitymatrix);
+       flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
+       // FIXME: these traces should scan all render entities instead of cl.world
+       for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
+       {
+               light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+               if (!light)
+                       continue;
+               rtlight = &light->rtlight;
+               if (!(rtlight->flags & flag))
+                       continue;
+               if (rtlight->corona * r_coronas.value <= 0)
+                       continue;
+               if (r_shadow_debuglight.integer >= 0 && r_shadow_debuglight.integer != (int)lightindex)
+                       continue;
+               cscale = rtlight->corona * r_coronas.value* 0.25f;
+               scale = rtlight->radius * rtlight->coronasizescale;
+               if (VectorDistance2(rtlight->shadoworigin, r_view.origin) < 16.0f * 16.0f)
+                       continue;
+               if (CL_Move(r_view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
+                       continue;
+               R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_view.right, r_view.up, scale, -scale, -scale, scale, rtlight->color[0] * cscale, rtlight->color[1] * cscale, rtlight->color[2] * cscale, 1);
+       }
+       for (i = 0;i < r_refdef.numlights;i++)
+       {
+               rtlight = &r_refdef.lights[i];
+               if (!(rtlight->flags & flag))
+                       continue;
+               if (rtlight->corona <= 0)
+                       continue;
+               if (VectorDistance2(rtlight->shadoworigin, r_view.origin) < 32.0f * 32.0f)
+                       continue;
+               if (gl_flashblend.integer)
+               {
+                       cscale = rtlight->corona * 1.0f;
+                       scale = rtlight->radius * rtlight->coronasizescale * 2.0f;
+               }
+               else
+               {
+                       cscale = rtlight->corona * r_coronas.value* 0.25f;
+                       scale = rtlight->radius * rtlight->coronasizescale;
+               }
+               if (VectorLength(rtlight->color) * cscale < (1.0f / 256.0f))
+                       continue;
+               if (CL_Move(r_view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
+                       continue;
+               R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_view.right, r_view.up, scale, -scale, -scale, scale, rtlight->color[0] * cscale, rtlight->color[1] * cscale, rtlight->color[2] * cscale, 1);
+       }
+}
+
+
 
 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
 typedef struct suffixinfo_s
@@ -3190,7 +3353,7 @@ static int componentorder[4] = {0, 1, 2, 3};
 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
 {
        int i, j, cubemapsize;
-       unsigned char *cubemappixels, *image_rgba;
+       unsigned char *cubemappixels, *image_buffer;
        rtexture_t *cubemaptexture;
        char name[256];
        // must start 0 so the first loadimagepixels has no requested width/height
@@ -3206,10 +3369,10 @@ rtexture_t *R_Shadow_LoadCubemap(const char *basename)
                        // generate an image name based on the base and and suffix
                        dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
                        // load it
-                       if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
+                       if ((image_buffer = loadimagepixelsbgra(name, false, false)))
                        {
                                // an image loaded, make sure width and height are equal
-                               if (image_width == image_height)
+                               if (image_width == image_height && (!cubemappixels || image_width == cubemapsize))
                                {
                                        // if this is the first image to load successfully, allocate the cubemap memory
                                        if (!cubemappixels && image_width >= 1)
@@ -3220,12 +3383,12 @@ rtexture_t *R_Shadow_LoadCubemap(const char *basename)
                                        }
                                        // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
                                        if (cubemappixels)
-                                               Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_rgba, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
+                                               Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_buffer, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
                                }
                                else
                                        Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
                                // free the image
-                               Mem_Free(image_rgba);
+                               Mem_Free(image_buffer);
                        }
                }
        }
@@ -3234,7 +3397,7 @@ rtexture_t *R_Shadow_LoadCubemap(const char *basename)
        {
                if (!r_shadow_filters_texturepool)
                        r_shadow_filters_texturepool = R_AllocTexturePool();
-               cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE | (gl_texturecompression_lightcubemaps.integer ? TEXF_COMPRESS : 0), NULL);
+               cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_BGRA, TEXF_PRECACHE | (gl_texturecompression_lightcubemaps.integer ? TEXF_COMPRESS : 0), NULL);
                Mem_Free(cubemappixels);
        }
        else
@@ -3272,11 +3435,7 @@ void R_Shadow_FreeCubemaps(void)
 
 dlight_t *R_Shadow_NewWorldLight(void)
 {
-       dlight_t *light;
-       light = (dlight_t *)Mem_Alloc(r_main_mempool, sizeof(dlight_t));
-       light->next = r_shadow_worldlightchain;
-       r_shadow_worldlightchain = light;
-       return light;
+       return (dlight_t *)Mem_ExpandableArray_AllocRecord(&r_shadow_worldlightsarray);
 }
 
 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, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
@@ -3317,19 +3476,22 @@ void R_Shadow_UpdateWorldLight(dlight_t *light, vec3_t origin, vec3_t angles, ve
 
 void R_Shadow_FreeWorldLight(dlight_t *light)
 {
-       dlight_t **lightpointer;
+       if (r_shadow_selectedlight == light)
+               r_shadow_selectedlight = NULL;
        R_RTLight_Uncompile(&light->rtlight);
-       for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
-       if (*lightpointer != light)
-               Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain");
-       *lightpointer = light->next;
-       Mem_Free(light);
+       Mem_ExpandableArray_FreeRecord(&r_shadow_worldlightsarray, light);
 }
 
 void R_Shadow_ClearWorldLights(void)
 {
-       while (r_shadow_worldlightchain)
-               R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
+       size_t lightindex;
+       dlight_t *light;
+       for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
+       {
+               light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+               if (light)
+                       R_Shadow_FreeWorldLight(light);
+       }
        r_shadow_selectedlight = NULL;
        R_Shadow_FreeCubemaps();
 }
@@ -3346,42 +3508,69 @@ void R_Shadow_SelectLight(dlight_t *light)
 void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
 {
        // this is never batched (there can be only one)
-       float scale = r_editlights_cursorgrid.value * 0.5f;
-       R_DrawSprite(GL_SRC_ALPHA, GL_ONE, r_crosshairs[1]->tex, NULL, false, false, r_editlights_cursorlocation, r_view.right, r_view.up, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
+       R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, r_editlights_sprcursor->tex, r_editlights_sprcursor->tex, false, false, r_editlights_cursorlocation, r_view.right, r_view.up, EDLIGHTSPRSIZE, -EDLIGHTSPRSIZE, -EDLIGHTSPRSIZE, EDLIGHTSPRSIZE, 1, 1, 1, 1);
 }
 
 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
 {
+       float intensity;
+       float s;
+       vec3_t spritecolor;
+       cachepic_t *pic;
+
        // this is never batched (due to the ent parameter changing every time)
        // so numsurfaces == 1 and surfacelist[0] == lightnumber
-       float intensity;
        const dlight_t *light = (dlight_t *)ent;
-       intensity = 0.5;
+       s = EDLIGHTSPRSIZE;
+       intensity = 0.5f;
+       VectorScale(light->color, intensity, spritecolor);
+       if (VectorLength(spritecolor) < 0.1732f)
+               VectorSet(spritecolor, 0.1f, 0.1f, 0.1f);
+       if (VectorLength(spritecolor) > 1.0f)
+               VectorNormalize(spritecolor);
+
+       // draw light sprite
+       if (light->cubemapname[0] && !light->shadow)
+               pic = r_editlights_sprcubemapnoshadowlight;
+       else if (light->cubemapname[0])
+               pic = r_editlights_sprcubemaplight;
+       else if (!light->shadow)
+               pic = r_editlights_sprnoshadowlight;
+       else
+               pic = r_editlights_sprlight;
+       R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, pic->tex, pic->tex, false, false, light->origin, r_view.right, r_view.up, s, -s, -s, s, spritecolor[0], spritecolor[1], spritecolor[2], 1);
+       // draw selection sprite if light is selected
        if (light->selected)
-               intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
-       if (!light->shadow)
-               intensity *= 0.5f;
-       R_DrawSprite(GL_SRC_ALPHA, GL_ONE, r_crosshairs[surfacelist[0]]->tex, NULL, false, false, light->origin, r_view.right, r_view.up, 8, -8, -8, 8, intensity, intensity, intensity, 0.5f);
+               R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, r_editlights_sprselection->tex, r_editlights_sprselection->tex, false, false, light->origin, r_view.right, r_view.up, s, -s, -s, s, 1, 1, 1, 1);
+       // VorteX todo: add normalmode/realtime mode light overlay sprites?
 }
 
 void R_Shadow_DrawLightSprites(void)
 {
-       int i;
+       size_t lightindex;
        dlight_t *light;
-
-       for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next)
-               R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 1+(i % 5), &light->rtlight);
+       for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
+       {
+               light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+               if (light)
+                       R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 5, &light->rtlight);
+       }
        R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
 }
 
 void R_Shadow_SelectLightInView(void)
 {
        float bestrating, rating, temp[3];
-       dlight_t *best, *light;
+       dlight_t *best;
+       size_t lightindex;
+       dlight_t *light;
        best = NULL;
        bestrating = 0;
-       for (light = r_shadow_worldlightchain;light;light = light->next)
+       for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
        {
+               light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+               if (!light)
+                       continue;
                VectorSubtract(light->origin, r_view.origin, temp);
                rating = (DotProduct(temp, r_view.forward) / sqrt(DotProduct(temp, temp)));
                if (rating >= 0.95)
@@ -3492,12 +3681,13 @@ void R_Shadow_LoadWorldLights(void)
 
 void R_Shadow_SaveWorldLights(void)
 {
+       size_t lightindex;
        dlight_t *light;
        size_t bufchars, bufmaxchars;
        char *buf, *oldbuf;
        char name[MAX_QPATH];
        char line[MAX_INPUTLINE];
-       if (!r_shadow_worldlightchain)
+       if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
                return;
        if (r_refdef.worldmodel == NULL)
        {
@@ -3508,8 +3698,11 @@ void R_Shadow_SaveWorldLights(void)
        strlcat (name, ".rtlights", sizeof (name));
        bufchars = bufmaxchars = 0;
        buf = NULL;
-       for (light = r_shadow_worldlightchain;light;light = light->next)
+       for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
        {
+               light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+               if (!light)
+                       continue;
                if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
                        sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f %f %f %f %f %i\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius, light->color[0], light->color[1], light->color[2], light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2], light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
                else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
@@ -3614,7 +3807,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                data = r_refdef.worldmodel->brush.entities;
        if (!data)
                return;
-       for (entnum = 0;COM_ParseToken_Simple(&data, false) && com_token[0] == '{';entnum++)
+       for (entnum = 0;COM_ParseToken_Simple(&data, false, false) && com_token[0] == '{';entnum++)
        {
                type = LIGHTTYPE_MINUSX;
                origin[0] = origin[1] = origin[2] = 0;
@@ -3632,7 +3825,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                islight = false;
                while (1)
                {
-                       if (!COM_ParseToken_Simple(&data, false))
+                       if (!COM_ParseToken_Simple(&data, false, false))
                                break; // error
                        if (com_token[0] == '}')
                                break; // end of entity
@@ -3642,7 +3835,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                strlcpy(key, com_token, sizeof(key));
                        while (key[strlen(key)-1] == ' ') // remove trailing spaces
                                key[strlen(key)-1] = 0;
-                       if (!COM_ParseToken_Simple(&data, false))
+                       if (!COM_ParseToken_Simple(&data, false, false))
                                break; // error
                        strlcpy(value, com_token, sizeof(value));
 
@@ -3859,10 +4052,10 @@ void R_Shadow_EditLights_Reload_f(void)
        strlcpy(r_shadow_mapname, r_refdef.worldmodel->name, sizeof(r_shadow_mapname));
        R_Shadow_ClearWorldLights();
        R_Shadow_LoadWorldLights();
-       if (r_shadow_worldlightchain == NULL)
+       if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
        {
                R_Shadow_LoadLightsFile();
-               if (r_shadow_worldlightchain == NULL)
+               if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
                        R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
        }
 }
@@ -4220,6 +4413,7 @@ void R_Shadow_EditLights_Edit_f(void)
 
 void R_Shadow_EditLights_EditAll_f(void)
 {
+       size_t lightindex;
        dlight_t *light;
 
        if (!r_editlights.integer)
@@ -4228,8 +4422,11 @@ void R_Shadow_EditLights_EditAll_f(void)
                return;
        }
 
-       for (light = r_shadow_worldlightchain;light;light = light->next)
+       for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
        {
+               light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+               if (!light)
+                       continue;
                R_Shadow_SelectLight(light);
                R_Shadow_EditLights_Edit_f();
        }
@@ -4238,34 +4435,44 @@ void R_Shadow_EditLights_EditAll_f(void)
 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
 {
        int lightnumber, lightcount;
+       size_t lightindex;
        dlight_t *light;
        float x, y;
        char temp[256];
        if (!r_editlights.integer)
                return;
-       x = 0;
-       y = con_vislines;
+       x = vid_conwidth.value - 240;
+       y = 5;
+       DrawQ_Pic(x-5, y-5, NULL, 250, 155, 0, 0, 0, 0.75, 0);
        lightnumber = -1;
        lightcount = 0;
-       for (lightcount = 0, light = r_shadow_worldlightchain;light;lightcount++, light = light->next)
+       for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
+       {
+               light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+               if (!light)
+                       continue;
                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, NULL, true);y += 8;
+                       lightnumber = lightindex;
+               lightcount++;
+       }
+       sprintf(temp, "Cursor origin: %.0f %.0f %.0f", 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, NULL, false);y += 8;
+       sprintf(temp, "Total lights : %i active (%i total)", lightcount, (int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray)); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, false);y += 8;
+       y += 8;
        if (r_shadow_selectedlight == NULL)
                return;
-       sprintf(temp, "Light #%i properties", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
-       sprintf(temp, "Origin       : %f %f %f\n", 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, NULL, true);y += 8;
-       sprintf(temp, "Angles       : %f %f %f\n", 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, NULL, true);y += 8;
-       sprintf(temp, "Color        : %f %f %f\n", 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, NULL, true);y += 8;
-       sprintf(temp, "Radius       : %f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
-       sprintf(temp, "Corona       : %f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
+       sprintf(temp, "Light #%i properties:", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
+       sprintf(temp, "Origin       : %.0f %.0f %.0f\n", 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, NULL, true);y += 8;
+       sprintf(temp, "Angles       : %.0f %.0f %.0f\n", 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, NULL, true);y += 8;
+       sprintf(temp, "Color        : %.2f %.2f %.2f\n", 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, NULL, true);y += 8;
+       sprintf(temp, "Radius       : %.0f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
+       sprintf(temp, "Corona       : %.0f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
        sprintf(temp, "Style        : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
        sprintf(temp, "Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
        sprintf(temp, "Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
-       sprintf(temp, "CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
-       sprintf(temp, "Ambient      : %f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
-       sprintf(temp, "Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
-       sprintf(temp, "Specular     : %f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
+       sprintf(temp, "CoronaSize   : %.2f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
+       sprintf(temp, "Ambient      : %.2f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
+       sprintf(temp, "Diffuse      : %.2f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
+       sprintf(temp, "Specular     : %.2f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
        sprintf(temp, "NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
        sprintf(temp, "RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
 }
@@ -4439,3 +4646,41 @@ void R_Shadow_EditLights_Init(void)
        Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f, "apply the stored properties onto the selected light (making it exactly identical except for origin)");
 }
 
+
+
+/*
+=============================================================================
+
+LIGHT SAMPLING
+
+=============================================================================
+*/
+
+void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const vec3_t p, int dynamic)
+{
+       VectorClear(diffusecolor);
+       VectorClear(diffusenormal);
+
+       if (!r_fullbright.integer && r_refdef.worldmodel && r_refdef.worldmodel->brush.LightPoint)
+       {
+               ambientcolor[0] = ambientcolor[1] = ambientcolor[2] = r_ambient.value * (2.0f / 128.0f);
+               r_refdef.worldmodel->brush.LightPoint(r_refdef.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
+       }
+       else
+               VectorSet(ambientcolor, 1, 1, 1);
+
+       if (dynamic)
+       {
+               int i;
+               float f, v[3];
+               rtlight_t *light;
+               for (i = 0;i < r_refdef.numlights;i++)
+               {
+                       light = &r_refdef.lights[i];
+                       Matrix4x4_Transform(&light->matrix_worldtolight, p, v);
+                       f = 1 - VectorLength2(v);
+                       if (f > 0 && CL_Move(p, vec3_origin, vec3_origin, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1)
+                               VectorMA(ambientcolor, f, light->currentcolor, ambientcolor);
+               }
+       }
+}