]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
bouncegrid now uses high precision 48bpp accumulation array while also
[xonotic/darkplaces.git] / r_shadow.c
index 83a7517c07cd6a8062171f00184966279921d7d0..daa0114f1e383647ad336986eb195cbf5cca99ca 100644 (file)
@@ -251,7 +251,8 @@ int r_shadow_shadowmapsize; // changes for each light based on distance
 int r_shadow_shadowmaplod; // changes for each light based on distance
 
 GLuint r_shadow_prepassgeometryfbo;
-GLuint r_shadow_prepasslightingfbo;
+GLuint r_shadow_prepasslightingdiffusespecularfbo;
+GLuint r_shadow_prepasslightingdiffusefbo;
 int r_shadow_prepass_width;
 int r_shadow_prepass_height;
 rtexture_t *r_shadow_prepassgeometrydepthtexture;
@@ -298,7 +299,7 @@ cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_sh
 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
 cvar_t r_shadow_realtime_world_compileshadow = {0, "r_shadow_realtime_world_compileshadow", "1", "enables compilation of shadows from world lights for higher performance rendering"};
 cvar_t r_shadow_realtime_world_compilesvbsp = {0, "r_shadow_realtime_world_compilesvbsp", "1", "enables svbsp optimization during compilation (slower than compileportalculling but more exact)"};
-cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_world_compileportalculling", "0", "enables portal-based culling optimization during compilation (overrides compilesvbsp)"};
+cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_world_compileportalculling", "1", "enables portal-based culling optimization during compilation (overrides compilesvbsp)"};
 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"};
 cvar_t r_shadow_shadowmapping = {CVAR_SAVE, "r_shadow_shadowmapping", "0", "enables use of shadowmapping (depth texture sampling) instead of stencil shadow volumes, requires gl_fbo 1"};
 cvar_t r_shadow_shadowmapping_filterquality = {CVAR_SAVE, "r_shadow_shadowmapping_filterquality", "-1", "shadowmap filter modes: -1 = auto-select, 0 = no filtering, 1 = bilinear, 2 = bilinear 2x2 blur (fast), 3 = 3x3 blur (moderate), 4 = 4x4 blur (slow)"};
@@ -318,6 +319,32 @@ cvar_t r_shadow_sortsurfaces = {0, "r_shadow_sortsurfaces", "1", "improve perfor
 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 OpenGL 2.0 render path)"};
+cvar_t r_shadow_particletrace = {CVAR_SAVE, "r_shadow_particletrace", "0", "perform particle tracing for indirect lighting (Global Illumination / radiosity), requires r_shadow_deferred 1, requires r_shadow_realtime_world 1, EXTREMELY SLOW"};
+cvar_t r_shadow_particletrace_intensity = {CVAR_SAVE, "r_shadow_particletrace_intensity", "128", "overall brightness of particle traced radiosity"};
+cvar_t r_shadow_particletrace_size = {CVAR_SAVE, "r_shadow_particletrace_size", "32", "particles produce bounce lights of this radius"};
+cvar_t r_shadow_particletrace_radiusscale = {CVAR_SAVE, "r_shadow_particletrace_radiusscale", "1", "particles stop at this fraction of light radius"};
+cvar_t r_shadow_particletrace_maxbounce = {CVAR_SAVE, "r_shadow_particletrace_maxbounce", "1", "maximum number of bounces for a particle (minimum is 1)"};
+cvar_t r_shadow_particletrace_bounceintensity = {CVAR_SAVE, "r_shadow_particletrace_bounceintensity", "1", "amount of energy carried over after each bounce"};
+cvar_t r_shadow_particletrace_particlespacing = {CVAR_SAVE, "r_shadow_particletrace_particlespacing", "0.25", "overlap setting in terms of particle size, this affects how many particles are used"};
+cvar_t r_shadow_particletrace_updatepercentage = {CVAR_SAVE, "r_shadow_particletrace_updatepercentage", "0.01", "update this fraction of the particles of a light each frame (0 = best performance)"};
+cvar_t r_shadow_bouncegrid = {CVAR_SAVE, "r_shadow_bouncegrid", "0", "perform particle tracing for indirect lighting (Global Illumination / radiosity) using a 3D texture covering the scene, requires r_shadow_realtime_world 1"};
+cvar_t r_shadow_bouncegrid_bounceanglediffuse = {CVAR_SAVE, "r_shadow_bouncegrid_bounceanglediffuse", "0", "use random bounce direction rather than true reflection, makes some corner areas dark"};
+cvar_t r_shadow_bouncegrid_dlightparticlemultiplier = {CVAR_SAVE, "r_shadow_bouncegrid_dlightparticlemultiplier", "0", "if set to a high value like 16 this can make dlights look great, but 0 is recommended for performance reasons"};
+cvar_t r_shadow_bouncegrid_hitmodels = {CVAR_SAVE, "r_shadow_bouncegrid_hitmodels", "0", "enables hitting character model geometry (SLOW)"};
+cvar_t r_shadow_bouncegrid_intensity = {CVAR_SAVE, "r_shadow_bouncegrid_intensity", "1", "overall brightness of bouncegrid texture"};
+cvar_t r_shadow_bouncegrid_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_lightradiusscale", "2", "particles stop at this fraction of light radius (can be more than 1)"};
+cvar_t r_shadow_bouncegrid_maxbounce = {CVAR_SAVE, "r_shadow_bouncegrid_maxbounce", "3", "maximum number of bounces for a particle (minimum is 1)"};
+cvar_t r_shadow_bouncegrid_particlebounceintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particlebounceintensity", "1", "amount of energy carried over after each bounce"};
+cvar_t r_shadow_bouncegrid_particleintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particleintensity", "4", "brightness of particles contributing to bouncegrid texture"};
+cvar_t r_shadow_bouncegrid_particlespacing = {CVAR_SAVE, "r_shadow_bouncegrid_particlespacing", "32", "emit one particle per this many units (squared) of radius (squared)"};
+cvar_t r_shadow_bouncegrid_spacingx = {CVAR_SAVE, "r_shadow_bouncegrid_spacingx", "64", "unit size of bouncegrid pixel on X axis"};
+cvar_t r_shadow_bouncegrid_spacingy = {CVAR_SAVE, "r_shadow_bouncegrid_spacingy", "64", "unit size of bouncegrid pixel on Y axis"};
+cvar_t r_shadow_bouncegrid_spacingz = {CVAR_SAVE, "r_shadow_bouncegrid_spacingz", "64", "unit size of bouncegrid pixel on Z axis"};
+cvar_t r_shadow_bouncegrid_stablerandom = {CVAR_SAVE, "r_shadow_bouncegrid_stablerandom", "1", "make particle distribution consistent from frame to frame"};
+cvar_t r_shadow_bouncegrid_updateinterval = {CVAR_SAVE, "r_shadow_bouncegrid_updateinterval", "0", "update bouncegrid texture once per this many seconds, useful values are 0, 0.05, or 1000000"};
+cvar_t r_shadow_bouncegrid_x = {CVAR_SAVE, "r_shadow_bouncegrid_x", "64", "maximum texture size of bouncegrid on X axis"};
+cvar_t r_shadow_bouncegrid_y = {CVAR_SAVE, "r_shadow_bouncegrid_y", "64", "maximum texture size of bouncegrid on Y axis"};
+cvar_t r_shadow_bouncegrid_z = {CVAR_SAVE, "r_shadow_bouncegrid_z", "32", "maximum texture size of bouncegrid on Z axis"};
 cvar_t r_coronas = {CVAR_SAVE, "r_coronas", "1", "brightness of corona flare effects around certain lights, 0 disables corona effects"};
 cvar_t r_coronas_occlusionsizescale = {CVAR_SAVE, "r_coronas_occlusionsizescale", "0.1", "size of light source for corona occlusion checksum the proportion of hidden pixels controls corona intensity"};
 cvar_t r_coronas_occlusionquery = {CVAR_SAVE, "r_coronas_occlusionquery", "1", "use GL_ARB_occlusion_query extension if supported (fades coronas according to visibility)"};
@@ -331,6 +358,15 @@ cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how
 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
 
+rtexture_t *r_shadow_bouncegridtexture;
+matrix4x4_t r_shadow_bouncegridmatrix;
+vec_t r_shadow_bouncegridintensity;
+static double r_shadow_bouncegridtime;
+static int r_shadow_bouncegridresolution[3];
+static int r_shadow_bouncegridnumpixels;
+static unsigned char *r_shadow_bouncegridpixels;
+static unsigned short *r_shadow_bouncegridhighpixels;
+
 // note the table actually includes one more value, just to avoid the need to clamp the distance index due to minor math error
 #define ATTENTABLESIZE 256
 // 1D gradient, 2D circle and 3D sphere attenuation textures
@@ -420,11 +456,7 @@ void R_Shadow_SetShadowMode(void)
                                }
                        }
                        r_shadow_shadowmode = R_SHADOW_SHADOWMODE_SHADOWMAP2D;
-                       // Cg has very little choice in depth texture sampling
-                       if (vid.cgcontext)
-                               r_shadow_shadowmapsampler = false;
                        break;
-               case RENDERPATH_CGGL:
                case RENDERPATH_D3D9:
                case RENDERPATH_D3D10:
                case RENDERPATH_D3D11:
@@ -437,6 +469,8 @@ void R_Shadow_SetShadowMode(void)
                        break;
                case RENDERPATH_GL11:
                        break;
+               case RENDERPATH_GLES2:
+                       break;
                }
        }
 }
@@ -476,6 +510,10 @@ void R_Shadow_FreeShadowMaps(void)
 void r_shadow_start(void)
 {
        // allocate vertex processing arrays
+       r_shadow_bouncegridpixels = NULL;
+       r_shadow_bouncegridhighpixels = NULL;
+       r_shadow_bouncegridnumpixels = 0;
+       r_shadow_bouncegridtexture = NULL;
        r_shadow_attenuationgradienttexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
@@ -547,6 +585,10 @@ void r_shadow_shutdown(void)
        r_shadow_prepass_width = r_shadow_prepass_height = 0;
 
        CHECKGLERROR
+       r_shadow_bouncegridtexture = NULL;
+       r_shadow_bouncegridpixels = NULL;
+       r_shadow_bouncegridhighpixels = NULL;
+       r_shadow_bouncegridnumpixels = 0;
        r_shadow_attenuationgradienttexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
@@ -614,6 +656,7 @@ void r_shadow_shutdown(void)
 
 void r_shadow_newmap(void)
 {
+       if (r_shadow_bouncegridtexture) R_FreeTexture(r_shadow_bouncegridtexture);r_shadow_bouncegridtexture = NULL;
        if (r_shadow_lightcorona)                 R_SkinFrame_MarkUsed(r_shadow_lightcorona);
        if (r_editlights_sprcursor)               R_SkinFrame_MarkUsed(r_editlights_sprcursor);
        if (r_editlights_sprlight)                R_SkinFrame_MarkUsed(r_editlights_sprlight);
@@ -677,17 +720,38 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_polygonfactor);
        Cvar_RegisterVariable(&r_shadow_polygonoffset);
        Cvar_RegisterVariable(&r_shadow_texture3d);
+       Cvar_RegisterVariable(&r_shadow_particletrace);
+       Cvar_RegisterVariable(&r_shadow_particletrace_intensity);
+       Cvar_RegisterVariable(&r_shadow_particletrace_size);
+       Cvar_RegisterVariable(&r_shadow_particletrace_radiusscale);
+       Cvar_RegisterVariable(&r_shadow_particletrace_maxbounce);
+       Cvar_RegisterVariable(&r_shadow_particletrace_bounceintensity);
+       Cvar_RegisterVariable(&r_shadow_particletrace_particlespacing);
+       Cvar_RegisterVariable(&r_shadow_particletrace_updatepercentage);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_bounceanglediffuse);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dlightparticlemultiplier);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_hitmodels);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_intensity);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_lightradiusscale);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_maxbounce);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_particlebounceintensity);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_particleintensity);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_particlespacing);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_spacingx);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_spacingy);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_spacingz);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_stablerandom);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_updateinterval);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_x);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_y);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_z);
        Cvar_RegisterVariable(&r_coronas);
        Cvar_RegisterVariable(&r_coronas_occlusionsizescale);
        Cvar_RegisterVariable(&r_coronas_occlusionquery);
        Cvar_RegisterVariable(&gl_flashblend);
        Cvar_RegisterVariable(&gl_ext_separatestencil);
        Cvar_RegisterVariable(&gl_ext_stenciltwoside);
-       if (gamemode == GAME_TENEBRAE)
-       {
-               Cvar_SetValue("r_shadow_gloss", 2);
-               Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
-       }
        R_Shadow_EditLights_Init();
        Mem_ExpandableArray_NewArray(&r_shadow_worldlightsarray, r_main_mempool, sizeof(dlight_t), 128);
        maxshadowtriangles = 0;
@@ -1895,11 +1959,11 @@ void R_Shadow_RenderMode_Begin(void)
        switch(vid.renderpath)
        {
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
        case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
                break;
        case RENDERPATH_GL13:
@@ -2103,8 +2167,8 @@ init_done:
        case RENDERPATH_GL11:
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
        case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                GL_CullFace(r_refdef.view.cullface_back);
                // OpenGL lets us scissor larger than the viewport, so go ahead and clear all views at once
                if ((clear & ((2 << side) - 1)) == (1 << side)) // only clear if the side is the first in the mask
@@ -2210,7 +2274,7 @@ void R_Shadow_RenderMode_DrawDeferredLight(qboolean stenciltest, qboolean shadow
        // only draw light where this geometry was already rendered AND the
        // stencil is 128 (values other than this mean shadow)
        R_SetStencil(stenciltest, 255, GL_KEEP, GL_KEEP, GL_KEEP, GL_EQUAL, 128, 255);
-       R_Mesh_SetRenderTargets(r_shadow_prepasslightingfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
+       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusespecularfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
 
        r_shadow_usingshadowmap2d = shadowmapping;
 
@@ -2229,6 +2293,495 @@ void R_Shadow_RenderMode_DrawDeferredLight(qboolean stenciltest, qboolean shadow
        R_Mesh_Draw(0, 8, 0, 12, NULL, NULL, 0, bboxelements, NULL, 0);
 }
 
+static void R_Shadow_UpdateBounceGridTexture(void)
+{
+#define MAXBOUNCEGRIDPARTICLESPERLIGHT 1048576
+       dlight_t *light;
+       int flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
+       int bouncecount;
+       int c[3];
+       int hitsupercontentsmask;
+       int maxbounce;
+       int numpixels;
+       int pixelindex;
+       int resolution[3];
+       int shootparticles;
+       int shotparticles;
+       int tex[3];
+       trace_t cliptrace;
+       unsigned char *pixel;
+       unsigned char *pixels;
+       unsigned short *highpixel;
+       unsigned short *highpixels;
+       unsigned int lightindex;
+       unsigned int range;
+       unsigned int range1;
+       unsigned int range2;
+       unsigned int seed = (unsigned int)(realtime * 1000.0f);
+       vec3_t shotcolor;
+       vec3_t baseshotcolor;
+       vec3_t clipend;
+       vec3_t clipstart;
+       vec3_t clipdiff;
+       vec3_t ispacing;
+       vec3_t maxs;
+       vec3_t mins;
+       vec3_t size;
+       vec3_t spacing;
+       vec3_t lightcolor;
+       vec_t radius;
+       vec_t s;
+       vec_t lightintensity;
+       float m[16];
+       qboolean isstatic = r_shadow_bouncegrid_updateinterval.value > 1.0f;
+       rtlight_t *rtlight;
+       if (!r_shadow_bouncegrid.integer || !vid.support.ext_texture_3d)
+       {
+               if (r_shadow_bouncegridtexture)
+               {
+                       R_FreeTexture(r_shadow_bouncegridtexture);
+                       r_shadow_bouncegridtexture = NULL;
+               }
+               if (r_shadow_bouncegridpixels)
+                       Mem_Free(r_shadow_bouncegridpixels);
+               r_shadow_bouncegridpixels = NULL;
+               if (r_shadow_bouncegridhighpixels)
+                       Mem_Free(r_shadow_bouncegridhighpixels);
+               r_shadow_bouncegridhighpixels = NULL;
+               r_shadow_bouncegridnumpixels = 0;
+               return;
+       }
+       if (r_refdef.scene.worldmodel && isstatic)
+       {
+               VectorSet(spacing, bound(1, r_shadow_bouncegrid_spacingx.value, 512), bound(1, r_shadow_bouncegrid_spacingy.value, 512), bound(1, r_shadow_bouncegrid_spacingz.value, 512));
+               VectorMA(r_refdef.scene.worldmodel->normalmins, -2.0f, spacing, mins);
+               VectorMA(r_refdef.scene.worldmodel->normalmaxs, 2.0f, spacing, maxs);
+               VectorSubtract(maxs, mins, size);
+               resolution[0] = (int)floor(size[0] / spacing[0] + 0.5f);
+               resolution[1] = (int)floor(size[1] / spacing[1] + 0.5f);
+               resolution[2] = (int)floor(size[2] / spacing[2] + 0.5f);
+               resolution[0] = min(resolution[0], bound(4, r_shadow_bouncegrid_x.integer, (int)vid.maxtexturesize_3d));
+               resolution[1] = min(resolution[1], bound(4, r_shadow_bouncegrid_y.integer, (int)vid.maxtexturesize_3d));
+               resolution[2] = min(resolution[2], bound(4, r_shadow_bouncegrid_z.integer, (int)vid.maxtexturesize_3d));
+               spacing[0] = size[0] / resolution[0];
+               spacing[1] = size[1] / resolution[1];
+               spacing[2] = size[2] / resolution[2];
+               ispacing[0] = 1.0f / spacing[0];
+               ispacing[1] = 1.0f / spacing[1];
+               ispacing[2] = 1.0f / spacing[2];
+       }
+       else
+       {
+               VectorSet(resolution, bound(4, r_shadow_bouncegrid_x.integer, (int)vid.maxtexturesize_3d), bound(4, r_shadow_bouncegrid_y.integer, (int)vid.maxtexturesize_3d), bound(4, r_shadow_bouncegrid_z.integer, (int)vid.maxtexturesize_3d));
+               VectorSet(spacing, bound(1, r_shadow_bouncegrid_spacingx.value, 512), bound(1, r_shadow_bouncegrid_spacingy.value, 512), bound(1, r_shadow_bouncegrid_spacingz.value, 512));
+               VectorMultiply(resolution, spacing, size);
+               ispacing[0] = 1.0f / spacing[0];
+               ispacing[1] = 1.0f / spacing[1];
+               ispacing[2] = 1.0f / spacing[2];
+               mins[0] = floor(r_refdef.view.origin[0] * ispacing[0] + 0.5f) * spacing[0] - 0.5f * size[0];
+               mins[1] = floor(r_refdef.view.origin[1] * ispacing[1] + 0.5f) * spacing[1] - 0.5f * size[1];
+               mins[2] = floor(r_refdef.view.origin[2] * ispacing[2] + 0.5f) * spacing[2] - 0.5f * size[2];
+               VectorAdd(mins, size, maxs);
+       }
+       r_shadow_bouncegridintensity = r_shadow_bouncegrid_intensity.value;
+       if (r_shadow_bouncegridtexture && realtime < r_shadow_bouncegridtime + r_shadow_bouncegrid_updateinterval.value && resolution[0] == r_shadow_bouncegridresolution[0] && resolution[1] == r_shadow_bouncegridresolution[1] && resolution[2] == r_shadow_bouncegridresolution[2])
+               return;
+       // we're going to update the bouncegrid, update the matrix...
+       memset(m, 0, sizeof(m));
+       m[0] = 1.0f / size[0];
+       m[3] = -mins[0] * m[0];
+       m[5] = 1.0f / size[1];
+       m[7] = -mins[1] * m[5];
+       m[10] = 1.0f / size[2];
+       m[11] = -mins[2] * m[10];
+       m[15] = 1.0f;
+       Matrix4x4_FromArrayFloatD3D(&r_shadow_bouncegridmatrix, m);
+       numpixels = resolution[0]*resolution[1]*resolution[2];
+       // reallocate pixels for this update if needed...
+       if (r_shadow_bouncegridnumpixels != numpixels || !r_shadow_bouncegridpixels || !r_shadow_bouncegridhighpixels)
+       {
+               r_shadow_bouncegridpixels = (unsigned char *)Mem_Realloc(r_main_mempool, r_shadow_bouncegridpixels, numpixels * sizeof(unsigned char[4]));
+               r_shadow_bouncegridhighpixels = (unsigned short *)Mem_Realloc(r_main_mempool, r_shadow_bouncegridhighpixels, numpixels * sizeof(unsigned short[4]));
+       }
+       r_shadow_bouncegridnumpixels = numpixels;
+       pixels = r_shadow_bouncegridpixels;
+       highpixels = r_shadow_bouncegridhighpixels;
+       memset(pixels, 0, numpixels * sizeof(unsigned char[4]));
+       memset(highpixels, 0, numpixels * sizeof(unsigned short[3]));
+       // figure out what we want to interact with
+       if (r_shadow_bouncegrid_hitmodels.integer)
+               hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK;
+       else
+               hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK;
+       maxbounce = bound(1, r_shadow_bouncegrid_maxbounce.integer, 16);
+       // iterate world rtlights
+       range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
+       range1 = isstatic ? 0 : r_refdef.scene.numlights;
+       range2 = range + range1;
+       for (lightindex = 0;lightindex < range2;lightindex++)
+       {
+               if (isstatic)
+               {
+                       light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+                       if (!light || !(light->flags & flag))
+                               continue;
+                       rtlight = &light->rtlight;
+                       // when static, we skip styled lights because they tend to change...
+                       if (rtlight->style > 0)
+                               continue;
+                       VectorScale(rtlight->color, (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) * (rtlight->style >= 0 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1), lightcolor);
+               }
+               else
+               {
+                       if (lightindex < range)
+                       {
+                               light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+                               rtlight = &light->rtlight;
+                       }
+                       else
+                               rtlight = r_refdef.scene.lights[lightindex - range];
+                       // draw only visible lights (major speedup)
+                       if (!rtlight->draw)
+                               continue;
+                       VectorScale(rtlight->currentcolor, rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale, lightcolor);
+               }
+               if (!VectorLength2(lightcolor))
+                       continue;
+               // shoot particles from this light
+               // use a calculation for the number of particles that will not
+               // vary with lightstyle, otherwise we get randomized particle
+               // distribution, the seeded random is only consistent for a
+               // consistent number of particles on this light...
+               radius = rtlight->radius * bound(0.0001f, r_shadow_bouncegrid_lightradiusscale.value, 1024.0f);
+               s = rtlight->radius / bound(1.0f, r_shadow_bouncegrid_particlespacing.value, 1048576.0f);
+               lightintensity = VectorLength(rtlight->color) * rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale;
+               if (lightindex >= range)
+                       lightintensity *= r_shadow_bouncegrid_dlightparticlemultiplier.value;
+               shootparticles = (int)bound(0, lightintensity * s *s, MAXBOUNCEGRIDPARTICLESPERLIGHT);
+               if (!shootparticles)
+                       continue;
+               s = 65535.0f * r_shadow_bouncegrid_particleintensity.value / shootparticles;
+               VectorScale(lightcolor, s, baseshotcolor);
+               if (VectorLength2(baseshotcolor) < 3.0f)
+                       break;
+               r_refdef.stats.bouncegrid_lights++;
+               r_refdef.stats.bouncegrid_particles += shootparticles;
+               for (shotparticles = 0;shotparticles < shootparticles;shotparticles++)
+               {
+                       if (r_shadow_bouncegrid_stablerandom.integer > 0)
+                               seed = lightindex * 11937 + shotparticles;
+                       VectorCopy(baseshotcolor, shotcolor);
+                       VectorCopy(rtlight->shadoworigin, clipstart);
+                       if (r_shadow_bouncegrid_stablerandom.integer < 0)
+                               VectorRandom(clipend);
+                       else
+                               VectorCheeseRandom(clipend);
+                       VectorMA(clipstart, radius, clipend, clipend);
+                       for (bouncecount = 0;;bouncecount++)
+                       {
+                               r_refdef.stats.bouncegrid_traces++;
+                               cliptrace = CL_TraceLine(clipstart, clipend, r_shadow_bouncegrid_hitmodels.integer ? MOVE_HITMODEL : MOVE_NOMONSTERS, NULL, hitsupercontentsmask, true, false, NULL, true);
+                               //Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask);
+                               if (cliptrace.fraction >= 1.0f)
+                                       break;
+                               r_refdef.stats.bouncegrid_hits++;
+                               if (bouncecount > 0)
+                               {
+                                       r_refdef.stats.bouncegrid_splats++;
+                                       // figure out which texture pixel this is in
+                                       tex[0] = (int)((cliptrace.endpos[0] - mins[0]) * ispacing[0]);
+                                       tex[1] = (int)((cliptrace.endpos[1] - mins[1]) * ispacing[1]);
+                                       tex[2] = (int)((cliptrace.endpos[2] - mins[2]) * ispacing[2]);
+                                       if (tex[0] >= 1 && tex[1] >= 1 && tex[2] >= 1 && tex[0] < resolution[0] - 1 && tex[1] < resolution[1] - 1 && tex[2] < resolution[2] - 1)
+                                       {
+                                               // it is within bounds...
+                                               pixelindex = ((tex[2]*resolution[1]+tex[1])*resolution[0]+tex[0]);
+                                               pixel = pixels + 4 * pixelindex;
+                                               highpixel = highpixels + 3 * pixelindex;
+                                               // add to the high precision pixel color
+                                               c[0] = highpixel[0] + (int)shotcolor[2];
+                                               c[1] = highpixel[1] + (int)shotcolor[1];
+                                               c[2] = highpixel[2] + (int)shotcolor[0];
+                                               highpixel[0] = (unsigned short)min(c[0], 65535);
+                                               highpixel[1] = (unsigned short)min(c[1], 65535);
+                                               highpixel[2] = (unsigned short)min(c[2], 65535);
+                                               // update the low precision pixel color
+                                               pixel[0] = highpixel[0] >> 8;
+                                               pixel[1] = highpixel[1] >> 8;
+                                               pixel[2] = highpixel[2] >> 8;
+                                               pixel[3] = 255;
+                                       }
+                               }
+                               if (bouncecount >= maxbounce)
+                                       break;
+                               // scale down shot color by bounce intensity and texture color
+                               VectorScale(shotcolor, r_shadow_bouncegrid_particlebounceintensity.value, shotcolor);
+                               if (cliptrace.hittexture && cliptrace.hittexture->currentskinframe)
+                                       VectorMultiply(shotcolor, rsurface.texture->currentskinframe->avgcolor, shotcolor);
+                               if (VectorLength2(shotcolor) < 3.0f)
+                                       break;
+                               r_refdef.stats.bouncegrid_bounces++;
+                               if (r_shadow_bouncegrid_bounceanglediffuse.integer)
+                               {
+                                       // random direction, primarily along plane normal
+                                       s = VectorDistance(cliptrace.endpos, clipend);
+                                       if (r_shadow_bouncegrid_stablerandom.integer < 0)
+                                               VectorRandom(clipend);
+                                       else
+                                               VectorCheeseRandom(clipend);
+                                       VectorMA(cliptrace.plane.normal, 0.95f, clipend, clipend);
+                                       VectorNormalize(clipend);
+                                       VectorScale(clipend, s, clipend);
+                               }
+                               else
+                               {
+                                       // reflect the remaining portion of the line across plane normal
+                                       VectorSubtract(clipend, cliptrace.endpos, clipdiff);
+                                       VectorReflect(clipdiff, 1.0, cliptrace.plane.normal, clipend);
+                               }
+                               // calculate the new line start and end
+                               VectorCopy(cliptrace.endpos, clipstart);
+                               VectorAdd(clipstart, clipend, clipend);
+                       }
+               }
+       }
+       if (r_shadow_bouncegridtexture && r_shadow_bouncegridresolution[0] == resolution[0] && r_shadow_bouncegridresolution[1] == resolution[1] && r_shadow_bouncegridresolution[2] == resolution[2])
+               R_UpdateTexture(r_shadow_bouncegridtexture, pixels, 0, 0, 0, resolution[0], resolution[1], resolution[2]);
+       else
+       {
+               VectorCopy(resolution, r_shadow_bouncegridresolution);
+               if (r_shadow_bouncegridtexture)
+                       R_FreeTexture(r_shadow_bouncegridtexture);
+               r_shadow_bouncegridtexture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2], pixels, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL);
+       }
+       r_shadow_bouncegridtime = realtime;
+}
+
+#define MAXPARTICLESPERLIGHT 262144
+#define MAXLIGHTSPERDRAW 1024
+
+static void R_Shadow_RenderParticlesForLight(rtlight_t *rtlight)
+{
+       int batchcount;
+       int i;
+       int j;
+       int bouncecount;
+       int hitsupercontentsmask;
+       int n;
+       int shotparticles;
+       int shootparticles = 0;
+       int bouncelimit;
+       int maxbounce;
+       unsigned int seed = 0;
+       static unsigned short bouncelight_elements[MAXLIGHTSPERDRAW*36];
+       static float vertex3f[MAXLIGHTSPERDRAW*24];
+       static float lightorigin4f[MAXLIGHTSPERDRAW*32];
+       static float color4f[MAXLIGHTSPERDRAW*32];
+       float scaledpoints[8][3];
+       float *v3f;
+       float *lo4f;
+       float *c4f;
+       rtlight_particle_t *p;
+       vec_t wantparticles = 0;
+       vec_t s;
+       vec_t radius;
+       vec_t particlesize;
+       vec_t iparticlesize;
+//     vec3_t offset;
+//     vec3_t right;
+//     vec3_t up;
+       vec4_t org;
+       vec4_t color;
+       vec3_t currentcolor;
+       vec3_t clipstart;
+       vec3_t clipend;
+       vec3_t shotcolor;
+       trace_t cliptrace;
+       if (!rtlight->draw || !rtlight->isstatic || !r_shadow_usingdeferredprepass)
+               return;
+       if (r_shadow_particletrace.integer)
+       {
+               radius = rtlight->radius * bound(0.0001f, r_shadow_particletrace_radiusscale.value, 1.0f) - r_shadow_particletrace_size.value;
+               s = rtlight->radius / bound(1.0f, r_shadow_particletrace_particlespacing.value * r_shadow_particletrace_size.value, 1048576.0f);
+               wantparticles = s*s;
+               n = (int)bound(0, wantparticles, MAXPARTICLESPERLIGHT);
+       }
+       else
+               n = 0;
+       shootparticles = (int)(n * r_shadow_particletrace_updatepercentage.value);
+       if ((n && !rtlight->particlecache_particles) || rtlight->particlecache_maxparticles != n)
+       {
+               if (rtlight->particlecache_particles)
+                       Mem_Free(rtlight->particlecache_particles);
+               rtlight->particlecache_particles = NULL;
+               rtlight->particlecache_numparticles = 0;
+               rtlight->particlecache_maxparticles = n;
+               rtlight->particlecache_updateparticle = 0;
+               if (rtlight->particlecache_maxparticles)
+                       rtlight->particlecache_particles = (rtlight_particle_t *)Mem_Alloc(r_main_mempool, rtlight->particlecache_maxparticles * sizeof(*rtlight->particlecache_particles));
+               shootparticles = n * 16;
+       }
+
+       if (!rtlight->particlecache_maxparticles)
+               return;
+
+//     if (rtlight->particlecache_numparticles < rtlight->particlecache_maxparticles)
+//             shootparticles = rtlight->particlecache_maxparticles;
+
+//     if (rtlight->particlecache_numparticles >= rtlight->particlecache_maxparticles)
+//             shootparticles = 0;
+
+       maxbounce = bound(1, r_shadow_particletrace_maxbounce.integer, 16);
+       //r_refdef.stats.lights_bouncelightsupdated += shootparticles;
+       for (shotparticles = 0;shotparticles < shootparticles;shotparticles++)
+       {
+               seed = rtlight->particlecache_updateparticle;
+               VectorSet(shotcolor, 1.0f, 1.0f, 1.0f);
+               VectorCopy(rtlight->shadoworigin, clipstart);
+               VectorRandom(clipend);
+               VectorMA(clipstart, radius, clipend, clipend);
+               hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK;
+               bouncelimit = 1 + (rtlight->particlecache_updateparticle % maxbounce);
+               for (bouncecount = 0;;bouncecount++)
+               {
+                       cliptrace = CL_TraceLine(clipstart, clipend, MOVE_NOMONSTERS, NULL, hitsupercontentsmask, true, false, NULL, true);
+                       //Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask);
+                       if (cliptrace.fraction >= 1.0f)
+                               break;
+                       if (VectorLength2(shotcolor) < (1.0f / 262144.0f))
+                               break;
+                       if (bouncecount >= bouncelimit)
+                       {
+                               VectorCopy(cliptrace.endpos, rtlight->particlecache_particles[rtlight->particlecache_updateparticle].origin);
+                               VectorCopy(shotcolor, rtlight->particlecache_particles[rtlight->particlecache_updateparticle].color);
+                               rtlight->particlecache_updateparticle++;
+                               if (rtlight->particlecache_numparticles < rtlight->particlecache_updateparticle)
+                                       rtlight->particlecache_numparticles = rtlight->particlecache_updateparticle;
+                               if (rtlight->particlecache_updateparticle >= rtlight->particlecache_maxparticles)
+                               {
+                                       rtlight->particlecache_updateparticle = 0;
+                                       shotparticles = shootparticles;
+                               }
+                               break;
+                       }
+                       // scale down shot color by bounce intensity and texture color
+                       VectorScale(shotcolor, r_shadow_particletrace_bounceintensity.value, shotcolor);
+                       if (cliptrace.hittexture && cliptrace.hittexture->currentskinframe)
+                               VectorMultiply(shotcolor, rsurface.texture->currentskinframe->avgcolor, shotcolor);
+                       // reflect the remaining portion of the line across plane normal
+                       //VectorSubtract(clipend, cliptrace.endpos, clipdiff);
+                       //VectorReflect(clipdiff, 1.0, cliptrace.plane.normal, clipend);
+                       // random direction, primarily along plane normal
+                       s = VectorDistance(cliptrace.endpos, clipend);
+                       VectorRandom(clipend);
+                       VectorMA(cliptrace.plane.normal, 0.95f, clipend, clipend);
+                       VectorNormalize(clipend);
+                       VectorScale(clipend, s, clipend);
+                       // calculate the new line start and end
+                       VectorCopy(cliptrace.endpos, clipstart);
+                       VectorAdd(clipstart, clipend, clipend);
+               }
+       }
+
+       if (!rtlight->particlecache_numparticles)
+               return;
+
+       // render the particles as deferred lights
+// do global setup needed for the chosen lighting mode
+       R_Shadow_RenderMode_Reset();
+       r_shadow_rendermode = r_shadow_lightingrendermode;
+       r_shadow_usingshadowmap2d = false;
+       R_EntityMatrix(&identitymatrix);
+       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+       // only draw light where this geometry was already rendered AND the
+       // stencil is 128 (values other than this mean shadow)
+       R_SetStencil(false, 255, GL_KEEP, GL_KEEP, GL_KEEP, GL_EQUAL, 128, 255);
+       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusefbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, NULL, NULL, NULL);
+       R_SetupShader_DeferredBounceLight();
+       GL_ColorMask(1,1,1,1);
+       GL_DepthMask(false);
+       GL_DepthRange(0, 1);
+       GL_PolygonOffset(0, 0);
+       GL_DepthTest(true);
+       GL_DepthFunc(GL_GREATER);
+       GL_CullFace(r_refdef.view.cullface_back);
+       s = r_shadow_particletrace_intensity.value / (float)rtlight->particlecache_numparticles;
+       VectorScale(rtlight->currentcolor, s, currentcolor);
+       particlesize = bound(0.0001f, r_shadow_particletrace_size.value, 1024.0f);
+       iparticlesize = 1.0f / particlesize;
+//     VectorScale(r_refdef.view.forward, particlesize, offset);
+//     VectorScale(r_refdef.view.left, -particlesize, right);
+//     VectorScale(r_refdef.view.up, particlesize, up);
+       org[3] = iparticlesize;
+       color[3] = 1.0f;
+       v3f = vertex3f;
+       lo4f = lightorigin4f;
+       c4f = color4f;
+       batchcount = 0;
+       if (!bouncelight_elements[1])
+               for (i = 0;i < MAXLIGHTSPERDRAW;i++)
+                       for (j = 0;j < 36;j++)
+                               bouncelight_elements[i*36+j] = i*8+bboxelements[j];
+       for (j = 0;j < 8;j++)
+               VectorScale(bboxpoints[j], particlesize, scaledpoints[j]);
+       //r_refdef.stats.lights_bouncelightscounted += rtlight->particlecache_numparticles;
+       for (j = 0, p = rtlight->particlecache_particles, n = rtlight->particlecache_numparticles;j < n;j++, p++)
+       {
+               VectorCopy(p->origin, org);
+               // org[3] is set above
+               VectorMultiply(p->color, currentcolor, color);
+               // color[3] is set above
+               VectorAdd(scaledpoints[0], org, v3f +  0);
+               VectorAdd(scaledpoints[1], org, v3f +  3);
+               VectorAdd(scaledpoints[2], org, v3f +  6);
+               VectorAdd(scaledpoints[3], org, v3f +  9);
+               VectorAdd(scaledpoints[4], org, v3f + 12);
+               VectorAdd(scaledpoints[5], org, v3f + 15);
+               VectorAdd(scaledpoints[6], org, v3f + 18);
+               VectorAdd(scaledpoints[7], org, v3f + 21);
+               Vector4Copy(org, lo4f +  0);
+               Vector4Copy(org, lo4f +  4);
+               Vector4Copy(org, lo4f +  8);
+               Vector4Copy(org, lo4f + 12);
+               Vector4Copy(org, lo4f + 16);
+               Vector4Copy(org, lo4f + 20);
+               Vector4Copy(org, lo4f + 24);
+               Vector4Copy(org, lo4f + 28);
+               Vector4Copy(color, c4f + 0);
+               Vector4Copy(color, c4f + 4);
+               Vector4Copy(color, c4f + 8);
+               Vector4Copy(color, c4f + 12);
+               Vector4Copy(color, c4f + 16);
+               Vector4Copy(color, c4f + 20);
+               Vector4Copy(color, c4f + 24);
+               Vector4Copy(color, c4f + 28);
+               v3f += 24;
+               lo4f += 32;
+               c4f += 32;
+               batchcount++;
+               if (batchcount >= MAXLIGHTSPERDRAW)
+               {
+                       //r_refdef.stats.lights_bouncelightsdrawn += batchcount;
+                       R_Mesh_PrepareVertices_BounceLight_Arrays(batchcount*8, vertex3f, color4f, lightorigin4f);
+                       R_Mesh_Draw(0, batchcount*8, 0, batchcount*12, NULL, NULL, 0, bouncelight_elements, NULL, 0);
+                       v3f = vertex3f;
+                       lo4f = lightorigin4f;
+                       c4f = color4f;
+                       batchcount = 0;
+               }
+       }
+       if (batchcount)
+       {
+               //r_refdef.stats.lights_bouncelightsdrawn += batchcount;
+               R_Mesh_PrepareVertices_BounceLight_Arrays(batchcount*8, vertex3f, color4f, lightorigin4f);
+               R_Mesh_Draw(0, batchcount*8, 0, batchcount*12, NULL, NULL, 0, bouncelight_elements, NULL, 0);
+               v3f = vertex3f;
+               lo4f = lightorigin4f;
+               c4f = color4f;
+               batchcount = 0;
+       }
+}
+
 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
 {
        R_Shadow_RenderMode_Reset();
@@ -2676,7 +3229,7 @@ void R_Shadow_RenderLighting(int texturenumsurfaces, const msurface_t **textures
                case RENDERPATH_GL11:
                case RENDERPATH_GL13:
                case RENDERPATH_GL20:
-               case RENDERPATH_CGGL:
+               case RENDERPATH_GLES2:
                        qglBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
                        break;
                case RENDERPATH_D3D9:
@@ -2722,7 +3275,7 @@ void R_Shadow_RenderLighting(int texturenumsurfaces, const msurface_t **textures
                case RENDERPATH_GL11:
                case RENDERPATH_GL13:
                case RENDERPATH_GL20:
-               case RENDERPATH_CGGL:
+               case RENDERPATH_GLES2:
                        qglBlendEquationEXT(GL_FUNC_ADD_EXT);
                        break;
                case RENDERPATH_D3D9:
@@ -3238,7 +3791,6 @@ void R_Shadow_SetupEntityLight(const entity_render_t *ent)
 {
        // set up properties for rendering light onto this entity
        RSurf_ActiveModelEntity(ent, true, true, false);
-       GL_AlphaTest(false);
        Matrix4x4_Concat(&rsurface.entitytolight, &rsurface.rtlight->matrix_worldtolight, &ent->matrix);
        Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
        Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
@@ -3252,7 +3804,6 @@ void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned c
 
        // set up properties for rendering light onto this entity
        RSurf_ActiveWorldEntity();
-       GL_AlphaTest(false);
        rsurface.entitytolight = rsurface.rtlight->matrix_worldtolight;
        Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
        Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
@@ -3746,6 +4297,9 @@ void R_Shadow_DrawLight(rtlight_t *rtlight)
                else
                        R_Shadow_RenderMode_DrawDeferredLight(false, false);
        }
+
+       if (r_shadow_particletrace.integer)
+               R_Shadow_RenderParticlesForLight(rtlight);
 }
 
 static void R_Shadow_FreeDeferred(void)
@@ -3753,8 +4307,11 @@ static void R_Shadow_FreeDeferred(void)
        R_Mesh_DestroyFramebufferObject(r_shadow_prepassgeometryfbo);
        r_shadow_prepassgeometryfbo = 0;
 
-       R_Mesh_DestroyFramebufferObject(r_shadow_prepasslightingfbo);
-       r_shadow_prepasslightingfbo = 0;
+       R_Mesh_DestroyFramebufferObject(r_shadow_prepasslightingdiffusespecularfbo);
+       r_shadow_prepasslightingdiffusespecularfbo = 0;
+
+       R_Mesh_DestroyFramebufferObject(r_shadow_prepasslightingdiffusefbo);
+       r_shadow_prepasslightingdiffusefbo = 0;
 
        if (r_shadow_prepassgeometrydepthtexture)
                R_FreeTexture(r_shadow_prepassgeometrydepthtexture);
@@ -3788,7 +4345,6 @@ void R_Shadow_DrawPrepass(void)
        entity_render_t *ent;
        float clearcolor[4];
 
-       GL_AlphaTest(false);
        R_Mesh_ResetTextureState();
        GL_DepthMask(true);
        GL_ColorMask(1,1,1,1);
@@ -3822,7 +4378,7 @@ void R_Shadow_DrawPrepass(void)
        GL_ColorMask(1,1,1,1);
        GL_Color(1,1,1,1);
        GL_DepthTest(true);
-       R_Mesh_SetRenderTargets(r_shadow_prepasslightingfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
+       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusespecularfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
        Vector4Set(clearcolor, 0, 0, 0, 0);
        GL_Clear(GL_COLOR_BUFFER_BIT, clearcolor, 1.0f, 0);
        if (r_timereport_active)
@@ -3885,11 +4441,11 @@ void R_Shadow_PrepareLights(void)
        switch (vid.renderpath)
        {
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
        case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                if (!r_shadow_deferred.integer || r_shadow_shadowmode == R_SHADOW_SHADOWMODE_STENCIL || !vid.support.ext_framebuffer_object || vid.maxdrawbuffers < 2)
                {
                        r_shadow_usingdeferredprepass = false;
@@ -3937,8 +4493,8 @@ void R_Shadow_PrepareLights(void)
                        }
 
                        // set up the lighting pass fbo (diffuse + specular)
-                       r_shadow_prepasslightingfbo = R_Mesh_CreateFramebufferObject(r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
-                       R_Mesh_SetRenderTargets(r_shadow_prepasslightingfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
+                       r_shadow_prepasslightingdiffusespecularfbo = R_Mesh_CreateFramebufferObject(r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
+                       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusespecularfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
                        // render diffuse into one texture and specular into another,
                        // with depth and normalmap bound as textures,
                        // with depth bound as attachment as well
@@ -3954,6 +4510,25 @@ void R_Shadow_PrepareLights(void)
                                        r_shadow_usingdeferredprepass = false;
                                }
                        }
+
+                       // set up the lighting pass fbo (diffuse)
+                       r_shadow_prepasslightingdiffusefbo = R_Mesh_CreateFramebufferObject(r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, NULL, NULL, NULL);
+                       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusefbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, NULL, NULL, NULL);
+                       // render diffuse into one texture,
+                       // with depth and normalmap bound as textures,
+                       // with depth bound as attachment as well
+                       if (qglDrawBuffersARB)
+                       {
+                               qglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);CHECKGLERROR
+                               qglReadBuffer(GL_NONE);CHECKGLERROR
+                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
+                               if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+                               {
+                                       Con_Printf("R_PrepareRTLights: glCheckFramebufferStatusEXT returned %i\n", status);
+                                       Cvar_SetValueQuick(&r_shadow_deferred, 0);
+                                       r_shadow_usingdeferredprepass = false;
+                               }
+                       }
                }
                break;
        case RENDERPATH_GL13:
@@ -3999,6 +4574,8 @@ void R_Shadow_PrepareLights(void)
 
        if (r_editlights.integer)
                R_Shadow_DrawLightSprites();
+
+       R_Shadow_UpdateBounceGridTexture();
 }
 
 void R_Shadow_DrawLights(void)
@@ -4281,8 +4858,8 @@ void R_DrawModelShadowMaps(void)
        case RENDERPATH_GL11:
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
        case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                break;
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
@@ -4458,7 +5035,7 @@ void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequery)
                case RENDERPATH_GL20:
                case RENDERPATH_GL13:
                case RENDERPATH_GL11:
-               case RENDERPATH_CGGL:
+               case RENDERPATH_GLES2:
                        CHECKGLERROR
                        // NOTE: GL_DEPTH_TEST must be enabled or ATI won't count samples, so use GL_DepthFunc instead
                        qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, rtlight->corona_queryindex_allpixels);
@@ -4506,7 +5083,7 @@ void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                case RENDERPATH_GL20:
                case RENDERPATH_GL13:
                case RENDERPATH_GL11:
-               case RENDERPATH_CGGL:
+               case RENDERPATH_GLES2:
                        CHECKGLERROR
                        qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, &visiblepixels);
                        qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, &allpixels);
@@ -4550,7 +5127,7 @@ void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                        case RENDERPATH_GL11:
                        case RENDERPATH_GL13:
                        case RENDERPATH_GL20:
-                       case RENDERPATH_CGGL:
+                       case RENDERPATH_GLES2:
                                qglBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
                                break;
                        case RENDERPATH_D3D9:
@@ -4579,7 +5156,7 @@ void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                        case RENDERPATH_GL11:
                        case RENDERPATH_GL13:
                        case RENDERPATH_GL20:
-                       case RENDERPATH_CGGL:
+                       case RENDERPATH_GLES2:
                                qglBlendEquationEXT(GL_FUNC_ADD_EXT);
                                break;
                        case RENDERPATH_D3D9:
@@ -4627,7 +5204,7 @@ void R_Shadow_DrawCoronas(void)
        case RENDERPATH_GL11:
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
+       case RENDERPATH_GLES2:
                usequery = vid.support.arb_occlusion_query && r_coronas_occlusionquery.integer;
                if (usequery)
                {
@@ -4664,6 +5241,7 @@ void R_Shadow_DrawCoronas(void)
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
        case RENDERPATH_SOFT:
+               usequery = false;
                //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
        }
@@ -6036,26 +6614,60 @@ LIGHT SAMPLING
 =============================================================================
 */
 
-void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const vec3_t p, const int flags)
+void R_CompleteLightPoint(vec3_t ambient, vec3_t diffuse, vec3_t lightdir, const vec3_t p, const int flags)
 {
        int i, numlights, flag;
-       float f, relativepoint[3], dist, dist2, lightradius2;
        rtlight_t *light;
        dlight_t *dlight;
+       float relativepoint[3];
+       float color[3];
+       float dir[3];
+       float dist;
+       float dist2;
+       float intensity;
+       float sample[5*3];
+       float lightradius2;
 
-       VectorClear(diffusecolor);
-       VectorClear(diffusenormal);
+       if (r_fullbright.integer)
+       {
+               VectorSet(ambient, 1, 1, 1);
+               VectorClear(diffuse);
+               VectorClear(lightdir);
+               return;
+       }
 
        if (flags & LP_LIGHTMAP)
        {
-               if (!r_fullbright.integer && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
-               {
-                       ambientcolor[0] = ambientcolor[1] = ambientcolor[2] = r_refdef.scene.ambient;
-                       r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
-               }
-               else
-                       VectorSet(ambientcolor, 1, 1, 1);
+               VectorSet(ambient, r_refdef.scene.ambient, r_refdef.scene.ambient, r_refdef.scene.ambient);
+               VectorClear(diffuse);
+               VectorClear(lightdir);
+               if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
+                       r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, p, ambient, diffuse, lightdir);
+               return;
        }
+
+       memset(sample, 0, sizeof(sample));
+       VectorSet(sample, r_refdef.scene.ambient, r_refdef.scene.ambient, r_refdef.scene.ambient);
+
+       if ((flags & LP_LIGHTMAP) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
+       {
+               vec3_t tempambient;
+               VectorClear(tempambient);
+               VectorClear(color);
+               VectorClear(relativepoint);
+               r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, p, tempambient, color, relativepoint);
+               VectorScale(tempambient, r_refdef.lightmapintensity, tempambient);
+               VectorScale(color, r_refdef.lightmapintensity, color);
+               VectorAdd(sample, tempambient, sample);
+               VectorMA(sample    , 0.5f            , color, sample    );
+               VectorMA(sample + 3, relativepoint[0], color, sample + 3);
+               VectorMA(sample + 6, relativepoint[1], color, sample + 6);
+               VectorMA(sample + 9, relativepoint[2], color, sample + 9);
+               // calculate a weighted average light direction as well
+               intensity = VectorLength(color);
+               VectorMA(sample + 12, intensity, relativepoint, sample + 12);
+       }
+
        if (flags & LP_RTWORLD)
        {
                flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
@@ -6075,14 +6687,25 @@ void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffu
                        if (dist2 >= lightradius2)
                                continue;
                        dist = sqrt(dist2) / light->radius;
-                       f = dist < 1 ? (r_shadow_lightintensityscale.value * ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist))) : 0;
-                       if (f <= 0)
+                       intensity = min(1.0f, (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) * r_shadow_lightintensityscale.value;
+                       if (intensity <= 0.0f)
                                continue;
-                       // todo: add to both ambient and diffuse
-                       if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1)
-                               VectorMA(ambientcolor, f, light->currentcolor, ambientcolor);
+                       if (light->shadow && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
+                               continue;
+                       // scale down intensity to add to both ambient and diffuse
+                       //intensity *= 0.5f;
+                       VectorNormalize(relativepoint);
+                       VectorScale(light->currentcolor, intensity, color);
+                       VectorMA(sample    , 0.5f            , color, sample    );
+                       VectorMA(sample + 3, relativepoint[0], color, sample + 3);
+                       VectorMA(sample + 6, relativepoint[1], color, sample + 6);
+                       VectorMA(sample + 9, relativepoint[2], color, sample + 9);
+                       // calculate a weighted average light direction as well
+                       intensity *= VectorLength(color);
+                       VectorMA(sample + 12, intensity, relativepoint, sample + 12);
                }
        }
+
        if (flags & LP_DYNLIGHT)
        {
                // sample dlights
@@ -6096,12 +6719,35 @@ void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffu
                        if (dist2 >= lightradius2)
                                continue;
                        dist = sqrt(dist2) / light->radius;
-                       f = dist < 1 ? (r_shadow_lightintensityscale.value * ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist))) : 0;
-                       if (f <= 0)
+                       intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist) * r_shadow_lightintensityscale.value;
+                       if (intensity <= 0.0f)
                                continue;
-                       // todo: add to both ambient and diffuse
-                       if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1)
-                               VectorMA(ambientcolor, f, light->color, ambientcolor);
+                       if (light->shadow && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
+                               continue;
+                       // scale down intensity to add to both ambient and diffuse
+                       //intensity *= 0.5f;
+                       VectorNormalize(relativepoint);
+                       VectorScale(light->currentcolor, intensity, color);
+                       VectorMA(sample    , 0.5f            , color, sample    );
+                       VectorMA(sample + 3, relativepoint[0], color, sample + 3);
+                       VectorMA(sample + 6, relativepoint[1], color, sample + 6);
+                       VectorMA(sample + 9, relativepoint[2], color, sample + 9);
+                       // calculate a weighted average light direction as well
+                       intensity *= VectorLength(color);
+                       VectorMA(sample + 12, intensity, relativepoint, sample + 12);
                }
        }
+
+       // calculate the direction we'll use to reduce the sample to a directional light source
+       VectorCopy(sample + 12, dir);
+       //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
+       VectorNormalize(dir);
+       // extract the diffuse color along the chosen direction and scale it
+       diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
+       diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
+       diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
+       // subtract some of diffuse from ambient
+       VectorMA(sample, -0.333f, diffuse, ambient);
+       // store the normalized lightdir
+       VectorCopy(dir, lightdir);
 }