X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=r_shadow.c;h=a5e9922585124fce072e32ddbcc5e01a709237a8;hb=9467a5d4d63a1d3587eff10afae41a853dea68f5;hp=8b0106dd78c69e73a29274d0dd16178d446a7815;hpb=28e02ce5a65a4eb60187acc1bca5e493d7cb78f9;p=xonotic%2Fdarkplaces.git diff --git a/r_shadow.c b/r_shadow.c index 8b0106dd..a5e99225 100644 --- a/r_shadow.c +++ b/r_shadow.c @@ -298,6 +298,7 @@ cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_ cvar_t r_shadow_realtime_dlight_svbspculling = {0, "r_shadow_realtime_dlight_svbspculling", "0", "enables svbsp optimization on dynamic lights (very slow!)"}; cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0", "enables portal optimization on dynamic lights (slow!)"}; cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0", "enables rendering of full world lighting (whether loaded from the map, or a .rtlights file, or a .ent file, or a .lights file produced by hlight)"}; +cvar_t r_shadow_realtime_world_importlightentitiesfrommap = {0, "r_shadow_realtime_world_importlightentitiesfrommap", "1", "load lights from .ent file or map entities at startup if no .rtlights or .lights file is present (if set to 2, always use the .ent or map entities)"}; cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0", "brightness to render lightmaps when using full world lighting, try 0.5 for a tenebrae-like appearance"}; cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"}; cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"}; @@ -325,25 +326,31 @@ cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0", "how much to 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_bouncegrid = {CVAR_SAVE, "r_shadow_bouncegrid", "0", "perform particle tracing for indirect lighting (Global Illumination / radiosity) using a 3D texture covering the scene, only active on levels with realtime lights active (r_shadow_realtime_world is usually required for these)"}; +cvar_t r_shadow_bouncegrid_blur = {CVAR_SAVE, "r_shadow_bouncegrid_blur", "1", "apply a 1-radius blur on bouncegrid to denoise it and deal with boundary issues with surfaces"}; 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_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_directionalshading", "0", "use diffuse shading rather than ambient, 3D texture becomes 8x as many pixels to hold the additional data"}; -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_dynamic_culllightpaths = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_culllightpaths", "1", "skip accumulating light in the bouncegrid texture where the light paths are out of view (dynamic mode only)"}; +cvar_t r_shadow_bouncegrid_dynamic_dlightparticlemultiplier = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_dlightparticlemultiplier", "1", "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_dynamic_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_directionalshading", "0", "use diffuse shading rather than ambient, 3D texture becomes 8x as many pixels to hold the additional data"}; +cvar_t r_shadow_bouncegrid_dynamic_hitmodels = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_hitmodels", "0", "enables hitting character model geometry (SLOW)"}; +cvar_t r_shadow_bouncegrid_dynamic_energyperphoton = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_energyperphoton", "10000", "amount of light that one photon should represent"}; +cvar_t r_shadow_bouncegrid_dynamic_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_lightradiusscale", "4", "particles stop at this fraction of light radius (can be more than 1)"}; +cvar_t r_shadow_bouncegrid_dynamic_maxbounce = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_maxbounce", "2", "maximum number of bounces for a particle (minimum is 0)"}; +cvar_t r_shadow_bouncegrid_dynamic_maxphotons = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_maxphotons", "25000", "upper bound on photons to shoot per update, divided proportionately between lights - normally the number of photons is calculated by energyperphoton"}; +cvar_t r_shadow_bouncegrid_dynamic_spacing = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_spacing", "64", "unit size of bouncegrid pixel"}; +cvar_t r_shadow_bouncegrid_dynamic_stablerandom = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_stablerandom", "1", "make particle distribution consistent from frame to frame"}; +cvar_t r_shadow_bouncegrid_dynamic_updateinterval = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_updateinterval", "0", "update bouncegrid texture once per this many seconds, useful values are 0, 0.05, or 1000000"}; cvar_t r_shadow_bouncegrid_includedirectlighting = {CVAR_SAVE, "r_shadow_bouncegrid_includedirectlighting", "0", "allows direct lighting to be recorded, not just indirect (gives an effect somewhat like r_shadow_realtime_world_lightmaps)"}; cvar_t r_shadow_bouncegrid_intensity = {CVAR_SAVE, "r_shadow_bouncegrid_intensity", "4", "overall brightness of bouncegrid texture"}; -cvar_t r_shadow_bouncegrid_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_lightradiusscale", "4", "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", "2", "maximum number of bounces for a particle (minimum is 0)"}; cvar_t r_shadow_bouncegrid_particlebounceintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particlebounceintensity", "1", "amount of energy carried over after each bounce, this is a multiplier of texture color and the result is clamped to 1 or less, to prevent adding energy on each bounce"}; cvar_t r_shadow_bouncegrid_particleintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particleintensity", "1", "brightness of particles contributing to bouncegrid texture"}; -cvar_t r_shadow_bouncegrid_photons = {CVAR_SAVE, "r_shadow_bouncegrid_photons", "2000", "total photons to shoot per update, divided proportionately between lights"}; -cvar_t r_shadow_bouncegrid_spacing = {CVAR_SAVE, "r_shadow_bouncegrid_spacing", "64", "unit size of bouncegrid pixel"}; -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_sortlightpaths = {CVAR_SAVE, "r_shadow_bouncegrid_sortlightpaths", "1", "sort light paths before accumulating them into the bouncegrid texture, this reduces cpu cache misses"}; cvar_t r_shadow_bouncegrid_static = {CVAR_SAVE, "r_shadow_bouncegrid_static", "1", "use static radiosity solution (high quality) rather than dynamic (splotchy)"}; cvar_t r_shadow_bouncegrid_static_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_static_directionalshading", "1", "whether to use directionalshading when in static mode"}; +cvar_t r_shadow_bouncegrid_static_energyperphoton = {CVAR_SAVE, "r_shadow_bouncegrid_static_energyperphoton", "1000", "amount of light that one photon should represent in static mode"}; cvar_t r_shadow_bouncegrid_static_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_static_lightradiusscale", "10", "particles stop at this fraction of light radius (can be more than 1) when in static mode"}; cvar_t r_shadow_bouncegrid_static_maxbounce = {CVAR_SAVE, "r_shadow_bouncegrid_static_maxbounce", "5", "maximum number of bounces for a particle (minimum is 0) in static mode"}; -cvar_t r_shadow_bouncegrid_static_photons = {CVAR_SAVE, "r_shadow_bouncegrid_static_photons", "25000", "photons value to use when in static mode"}; -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_static_maxphotons = {CVAR_SAVE, "r_shadow_bouncegrid_static_maxphotons", "250000", "upper bound on photons in static mode"}; +cvar_t r_shadow_bouncegrid_static_spacing = {CVAR_SAVE, "r_shadow_bouncegrid_static_spacing", "32", "unit size of bouncegrid pixel when in static mode"}; 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"}; @@ -375,35 +382,7 @@ cvar_t r_editlights_current_specular = {0, "r_editlights_current_specular", "1", cvar_t r_editlights_current_normalmode = {0, "r_editlights_current_normalmode", "0", "normalmode flag of selected light"}; cvar_t r_editlights_current_realtimemode = {0, "r_editlights_current_realtimemode", "0", "realtimemode flag of selected light"}; - -typedef struct r_shadow_bouncegrid_settings_s -{ - qboolean staticmode; - qboolean bounceanglediffuse; - qboolean directionalshading; - qboolean includedirectlighting; - float dlightparticlemultiplier; - qboolean hitmodels; - float lightradiusscale; - int maxbounce; - float particlebounceintensity; - float particleintensity; - int photons; - float spacing[3]; - int stablerandom; -} -r_shadow_bouncegrid_settings_t; - -r_shadow_bouncegrid_settings_t r_shadow_bouncegridsettings; -rtexture_t *r_shadow_bouncegridtexture; -matrix4x4_t r_shadow_bouncegridmatrix; -vec_t r_shadow_bouncegridintensity; -qboolean r_shadow_bouncegriddirectional; -static double r_shadow_bouncegridtime; -static int r_shadow_bouncegridresolution[3]; -static int r_shadow_bouncegridnumpixels; -static unsigned char *r_shadow_bouncegridpixels; -static float *r_shadow_bouncegridhighpixels; +r_shadow_bouncegrid_state_t r_shadow_bouncegrid_state; // 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 @@ -555,11 +534,8 @@ static void R_Shadow_FreeShadowMaps(void) static 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_bouncegriddirectional = false; + memset(&r_shadow_bouncegrid_state, 0, sizeof(r_shadow_bouncegrid_state)); + r_shadow_bouncegrid_state.maxsplatpaths = 16384; r_shadow_attenuationgradienttexture = NULL; r_shadow_attenuation2dtexture = NULL; r_shadow_attenuation3dtexture = NULL; @@ -615,6 +591,29 @@ static void r_shadow_start(void) r_shadow_usingdeferredprepass = false; r_shadow_prepass_width = r_shadow_prepass_height = 0; + + // determine renderpath specific capabilities, we don't need to figure + // these out per frame... + switch(vid.renderpath) + { + case RENDERPATH_GL20: + r_shadow_bouncegrid_state.allowdirectionalshading = true; + r_shadow_bouncegrid_state.capable = vid.support.ext_texture_3d; + break; + case RENDERPATH_GLES2: + // for performance reasons, do not use directional shading on GLES devices + r_shadow_bouncegrid_state.capable = vid.support.ext_texture_3d; + break; + // these renderpaths do not currently have the code to display the bouncegrid, so disable it on them... + case RENDERPATH_GL11: + case RENDERPATH_GL13: + case RENDERPATH_GLES1: + case RENDERPATH_SOFT: + case RENDERPATH_D3D9: + case RENDERPATH_D3D10: + case RENDERPATH_D3D11: + break; + } } static void R_Shadow_FreeDeferred(void); @@ -631,11 +630,7 @@ static 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_bouncegriddirectional = false; + memset(&r_shadow_bouncegrid_state, 0, sizeof(r_shadow_bouncegrid_state)); r_shadow_attenuationgradienttexture = NULL; r_shadow_attenuation2dtexture = NULL; r_shadow_attenuation3dtexture = NULL; @@ -703,7 +698,7 @@ static void r_shadow_shutdown(void) static void r_shadow_newmap(void) { - if (r_shadow_bouncegridtexture) R_FreeTexture(r_shadow_bouncegridtexture);r_shadow_bouncegridtexture = NULL; + if (r_shadow_bouncegrid_state.texture) R_FreeTexture(r_shadow_bouncegrid_state.texture);r_shadow_bouncegrid_state.texture = 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); @@ -735,6 +730,7 @@ void R_Shadow_Init(void) Cvar_RegisterVariable(&r_shadow_lightradiusscale); Cvar_RegisterVariable(&r_shadow_projectdistance); Cvar_RegisterVariable(&r_shadow_frontsidecasting); + Cvar_RegisterVariable(&r_shadow_realtime_world_importlightentitiesfrommap); Cvar_RegisterVariable(&r_shadow_realtime_dlight); Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows); Cvar_RegisterVariable(&r_shadow_realtime_dlight_svbspculling); @@ -767,25 +763,31 @@ void R_Shadow_Init(void) Cvar_RegisterVariable(&r_shadow_polygonoffset); Cvar_RegisterVariable(&r_shadow_texture3d); Cvar_RegisterVariable(&r_shadow_bouncegrid); + Cvar_RegisterVariable(&r_shadow_bouncegrid_blur); Cvar_RegisterVariable(&r_shadow_bouncegrid_bounceanglediffuse); - Cvar_RegisterVariable(&r_shadow_bouncegrid_directionalshading); - Cvar_RegisterVariable(&r_shadow_bouncegrid_dlightparticlemultiplier); - Cvar_RegisterVariable(&r_shadow_bouncegrid_hitmodels); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_culllightpaths); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_directionalshading); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_dlightparticlemultiplier); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_hitmodels); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_energyperphoton); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_lightradiusscale); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_maxbounce); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_maxphotons); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_spacing); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_stablerandom); + Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_updateinterval); Cvar_RegisterVariable(&r_shadow_bouncegrid_includedirectlighting); 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_photons); - Cvar_RegisterVariable(&r_shadow_bouncegrid_spacing); - Cvar_RegisterVariable(&r_shadow_bouncegrid_stablerandom); + Cvar_RegisterVariable(&r_shadow_bouncegrid_sortlightpaths); Cvar_RegisterVariable(&r_shadow_bouncegrid_static); + Cvar_RegisterVariable(&r_shadow_bouncegrid_static_spacing); Cvar_RegisterVariable(&r_shadow_bouncegrid_static_directionalshading); Cvar_RegisterVariable(&r_shadow_bouncegrid_static_lightradiusscale); Cvar_RegisterVariable(&r_shadow_bouncegrid_static_maxbounce); - Cvar_RegisterVariable(&r_shadow_bouncegrid_static_photons); - Cvar_RegisterVariable(&r_shadow_bouncegrid_updateinterval); + Cvar_RegisterVariable(&r_shadow_bouncegrid_static_maxphotons); + Cvar_RegisterVariable(&r_shadow_bouncegrid_static_energyperphoton); Cvar_RegisterVariable(&r_shadow_bouncegrid_x); Cvar_RegisterVariable(&r_shadow_bouncegrid_y); Cvar_RegisterVariable(&r_shadow_bouncegrid_z); @@ -2328,94 +2330,113 @@ void R_Shadow_RenderMode_DrawDeferredLight(qboolean stenciltest, qboolean shadow R_Mesh_Draw(0, 8, 0, 12, NULL, NULL, 0, bboxelements, NULL, 0); } -void R_Shadow_UpdateBounceGridTexture(void) +// these are temporary data per-frame, sorted and performed in a more +// cache-friendly order than the original photons +typedef struct r_shadow_bouncegrid_splatpath_s { -#define MAXBOUNCEGRIDPARTICLESPERLIGHT 1048576 - dlight_t *light; - int flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE; - int bouncecount; - int hitsupercontentsmask; - int maxbounce; - int numpixels; - int resolution[3]; - int shootparticles; - int shotparticles; - int photoncount; - int tex[3]; - trace_t cliptrace; - //trace_t cliptrace2; - //trace_t cliptrace3; - unsigned char *pixel; - unsigned char *pixels; - float *highpixel; - float *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 surfcolor; - 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; - vec3_t steppos; - vec3_t stepdelta; - vec3_t cullmins, cullmaxs; - vec_t radius; - vec_t s; - vec_t lightintensity; - vec_t photonscaling; - vec_t photonresidual; - float m[16]; - float texlerp[2][3]; - float splatcolor[32]; - float pixelweight[8]; - float w; - int c[4]; - int pixelindex[8]; - int corner; - int pixelsperband; - int pixelband; - int pixelbands; - int numsteps; - int step; - int x, y, z; - rtlight_t *rtlight; - r_shadow_bouncegrid_settings_t settings; - qboolean enable = r_shadow_bouncegrid.integer != 0 && r_refdef.scene.worldmodel; - qboolean allowdirectionalshading = false; - switch(vid.renderpath) - { - case RENDERPATH_GL20: - allowdirectionalshading = true; - if (!vid.support.ext_texture_3d) - return; - break; - case RENDERPATH_GLES2: - // for performance reasons, do not use directional shading on GLES devices - if (!vid.support.ext_texture_3d) + vec3_t point; + vec3_t step; + vec3_t splatcolor; + vec3_t splatdir; + vec_t splatintensity; + int remainingsplats; +} +r_shadow_bouncegrid_splatpath_t; + +static void R_shadow_BounceGrid_AddSplatPath(vec3_t originalstart, vec3_t originalend, vec3_t color) +{ + int bestaxis; + int numsplats; + float len; + float ilen; + vec3_t start; + vec3_t end; + vec3_t diff; + vec3_t originaldir; + r_shadow_bouncegrid_splatpath_t *path; + + // cull paths that fail R_CullBox in dynamic mode + if (!r_shadow_bouncegrid_state.settings.staticmode + && r_shadow_bouncegrid_dynamic_culllightpaths.integer) + { + vec3_t cullmins, cullmaxs; + cullmins[0] = min(originalstart[0], originalend[0]) - r_shadow_bouncegrid_state.settings.spacing[0]; + cullmins[1] = min(originalstart[1], originalend[1]) - r_shadow_bouncegrid_state.settings.spacing[1]; + cullmins[2] = min(originalstart[2], originalend[2]) - r_shadow_bouncegrid_state.settings.spacing[2]; + cullmaxs[0] = max(originalstart[0], originalend[0]) + r_shadow_bouncegrid_state.settings.spacing[0]; + cullmaxs[1] = max(originalstart[1], originalend[1]) + r_shadow_bouncegrid_state.settings.spacing[1]; + cullmaxs[2] = max(originalstart[2], originalend[2]) + r_shadow_bouncegrid_state.settings.spacing[2]; + if (R_CullBox(cullmins, cullmaxs)) return; - break; - // these renderpaths do not currently have the code to display the bouncegrid, so disable it on them... - case RENDERPATH_GL11: - case RENDERPATH_GL13: - case RENDERPATH_GLES1: - case RENDERPATH_SOFT: - case RENDERPATH_D3D9: - case RENDERPATH_D3D10: - case RENDERPATH_D3D11: - return; } - r_shadow_bouncegridintensity = r_shadow_bouncegrid_intensity.value; + // if the light path is going upward, reverse it - we always draw down. + if (originalend[2] < originalstart[2]) + { + VectorCopy(originalend, start); + VectorCopy(originalstart, end); + } + else + { + VectorCopy(originalstart, start); + VectorCopy(originalend, end); + } + + // transform to texture pixels + start[0] = (start[0] - r_shadow_bouncegrid_state.mins[0]) * r_shadow_bouncegrid_state.ispacing[0]; + start[1] = (start[1] - r_shadow_bouncegrid_state.mins[1]) * r_shadow_bouncegrid_state.ispacing[1]; + start[2] = (start[2] - r_shadow_bouncegrid_state.mins[2]) * r_shadow_bouncegrid_state.ispacing[2]; + end[0] = (end[0] - r_shadow_bouncegrid_state.mins[0]) * r_shadow_bouncegrid_state.ispacing[0]; + end[1] = (end[1] - r_shadow_bouncegrid_state.mins[1]) * r_shadow_bouncegrid_state.ispacing[1]; + end[2] = (end[2] - r_shadow_bouncegrid_state.mins[2]) * r_shadow_bouncegrid_state.ispacing[2]; + + // check if we need to grow the splatpaths array + if (r_shadow_bouncegrid_state.maxsplatpaths <= r_shadow_bouncegrid_state.numsplatpaths) + { + // double the limit, this will persist from frame to frame so we don't + // make the same mistake each time + r_shadow_bouncegrid_splatpath_t *newpaths; + r_shadow_bouncegrid_state.maxsplatpaths *= 2; + newpaths = (r_shadow_bouncegrid_splatpath_t *)R_FrameData_Alloc(sizeof(r_shadow_bouncegrid_splatpath_t) * r_shadow_bouncegrid_state.maxsplatpaths); + if (r_shadow_bouncegrid_state.splatpaths) + memcpy(newpaths, r_shadow_bouncegrid_state.splatpaths, r_shadow_bouncegrid_state.numsplatpaths * sizeof(r_shadow_bouncegrid_splatpath_t)); + r_shadow_bouncegrid_state.splatpaths = newpaths; + } + + // divide a series of splats along the length using the maximum axis + VectorSubtract(end, start, diff); + // pick the best axis to trace along + bestaxis = 0; + if (diff[1]*diff[1] > diff[bestaxis]*diff[bestaxis]) + bestaxis = 1; + if (diff[2]*diff[2] > diff[bestaxis]*diff[bestaxis]) + bestaxis = 2; + len = fabs(diff[bestaxis]); + ilen = 1.0f / len; + numsplats = (int)(floor(len + 0.5f)); + // sanity limits + numsplats = bound(0, numsplats, 1024); + + VectorSubtract(originalstart, originalend, originaldir); + VectorNormalize(originaldir); + + path = r_shadow_bouncegrid_state.splatpaths + r_shadow_bouncegrid_state.numsplatpaths++; + VectorCopy(start, path->point); + VectorScale(diff, ilen, path->step); + VectorCopy(color, path->splatcolor); + VectorCopy(originaldir, path->splatdir); + path->splatintensity = VectorLength(color); + path->remainingsplats = numsplats; +} + +static qboolean R_Shadow_BounceGrid_CheckEnable(int flag) +{ + qboolean enable = r_shadow_bouncegrid_state.capable && r_shadow_bouncegrid.integer != 0 && r_refdef.scene.worldmodel; + int lightindex; + int range; + dlight_t *light; + rtlight_t *rtlight; + vec3_t lightcolor; // see if there are really any lights to render... if (enable && r_shadow_bouncegrid_static.integer) @@ -2439,61 +2460,76 @@ void R_Shadow_UpdateBounceGridTexture(void) } } - if (!enable) - { - 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; - r_shadow_bouncegriddirectional = false; - return; - } + return enable; +} + +static void R_Shadow_BounceGrid_GenerateSettings(r_shadow_bouncegrid_settings_t *settings) +{ + qboolean s = r_shadow_bouncegrid_static.integer != 0; + float spacing = s ? r_shadow_bouncegrid_static_spacing.value : r_shadow_bouncegrid_dynamic_spacing.value; + + // prevent any garbage in alignment padded areas as we'll be using memcmp + memset(settings, 0, sizeof(*settings)); // build up a complete collection of the desired settings, so that memcmp can be used to compare parameters - memset(&settings, 0, sizeof(settings)); - settings.staticmode = r_shadow_bouncegrid_static.integer != 0; - settings.bounceanglediffuse = r_shadow_bouncegrid_bounceanglediffuse.integer != 0; - settings.directionalshading = (r_shadow_bouncegrid_static.integer != 0 ? r_shadow_bouncegrid_static_directionalshading.integer != 0 : r_shadow_bouncegrid_directionalshading.integer != 0) && allowdirectionalshading; - settings.dlightparticlemultiplier = r_shadow_bouncegrid_dlightparticlemultiplier.value; - settings.hitmodels = r_shadow_bouncegrid_hitmodels.integer != 0; - settings.includedirectlighting = r_shadow_bouncegrid_includedirectlighting.integer != 0 || r_shadow_bouncegrid.integer == 2; - settings.lightradiusscale = (r_shadow_bouncegrid_static.integer != 0 ? r_shadow_bouncegrid_static_lightradiusscale.value : r_shadow_bouncegrid_lightradiusscale.value); - settings.maxbounce = (r_shadow_bouncegrid_static.integer != 0 ? r_shadow_bouncegrid_static_maxbounce.integer : r_shadow_bouncegrid_maxbounce.integer); - settings.particlebounceintensity = r_shadow_bouncegrid_particlebounceintensity.value; - settings.particleintensity = r_shadow_bouncegrid_particleintensity.value * 16384.0f * (settings.directionalshading ? 4.0f : 1.0f) / (r_shadow_bouncegrid_spacing.value * r_shadow_bouncegrid_spacing.value); - settings.photons = r_shadow_bouncegrid_static.integer ? r_shadow_bouncegrid_static_photons.integer : r_shadow_bouncegrid_photons.integer; - settings.spacing[0] = r_shadow_bouncegrid_spacing.value; - settings.spacing[1] = r_shadow_bouncegrid_spacing.value; - settings.spacing[2] = r_shadow_bouncegrid_spacing.value; - settings.stablerandom = r_shadow_bouncegrid_stablerandom.integer; + settings->staticmode = s; + settings->bounceanglediffuse = r_shadow_bouncegrid_bounceanglediffuse.integer != 0; + settings->directionalshading = (s ? r_shadow_bouncegrid_static_directionalshading.integer != 0 : r_shadow_bouncegrid_dynamic_directionalshading.integer != 0) && r_shadow_bouncegrid_state.allowdirectionalshading; + settings->dlightparticlemultiplier = s ? 0 : r_shadow_bouncegrid_dynamic_dlightparticlemultiplier.value; + settings->hitmodels = s ? false : r_shadow_bouncegrid_dynamic_hitmodels.integer != 0; + settings->includedirectlighting = r_shadow_bouncegrid_includedirectlighting.integer != 0 || r_shadow_bouncegrid.integer == 2; + settings->lightradiusscale = (s ? r_shadow_bouncegrid_static_lightradiusscale.value : r_shadow_bouncegrid_dynamic_lightradiusscale.value); + settings->maxbounce = (s ? r_shadow_bouncegrid_static_maxbounce.integer : r_shadow_bouncegrid_dynamic_maxbounce.integer); + settings->particlebounceintensity = r_shadow_bouncegrid_particlebounceintensity.value; + settings->particleintensity = r_shadow_bouncegrid_particleintensity.value * 16384.0f * (settings->directionalshading ? 4.0f : 1.0f) / (spacing * spacing); + settings->maxphotons = s ? r_shadow_bouncegrid_static_maxphotons.integer : r_shadow_bouncegrid_dynamic_maxphotons.integer; + settings->energyperphoton = s ? r_shadow_bouncegrid_static_energyperphoton.integer : r_shadow_bouncegrid_dynamic_energyperphoton.integer; + settings->spacing[0] = spacing; + settings->spacing[1] = spacing; + settings->spacing[2] = spacing; + settings->stablerandom = s ? 0 : r_shadow_bouncegrid_dynamic_stablerandom.integer; // bound the values for sanity - settings.photons = bound(1, settings.photons, 1048576); - settings.lightradiusscale = bound(0.0001f, settings.lightradiusscale, 1024.0f); - settings.maxbounce = bound(0, settings.maxbounce, 16); - settings.spacing[0] = bound(1, settings.spacing[0], 512); - settings.spacing[1] = bound(1, settings.spacing[1], 512); - settings.spacing[2] = bound(1, settings.spacing[2], 512); + settings->maxphotons = bound(1, settings->maxphotons, 25000000); + settings->lightradiusscale = bound(0.0001f, settings->lightradiusscale, 1024.0f); + settings->maxbounce = bound(0, settings->maxbounce, 16); + settings->spacing[0] = bound(1, settings->spacing[0], 512); + settings->spacing[1] = bound(1, settings->spacing[1], 512); + settings->spacing[2] = bound(1, settings->spacing[2], 512); +} + +static void R_Shadow_BounceGrid_UpdateSpacing(void) +{ + float m[16]; + int c[4]; + int resolution[3]; + int numpixels; + vec3_t ispacing; + vec3_t maxs; + vec3_t mins; + vec3_t size; + vec3_t spacing; + r_shadow_bouncegrid_settings_t *settings = &r_shadow_bouncegrid_state.settings; // get the spacing values - spacing[0] = settings.spacing[0]; - spacing[1] = settings.spacing[1]; - spacing[2] = settings.spacing[2]; + spacing[0] = settings->spacing[0]; + spacing[1] = settings->spacing[1]; + spacing[2] = settings->spacing[2]; ispacing[0] = 1.0f / spacing[0]; ispacing[1] = 1.0f / spacing[1]; ispacing[2] = 1.0f / spacing[2]; // calculate texture size enclosing entire world bounds at the spacing - VectorMA(r_refdef.scene.worldmodel->normalmins, -2.0f, spacing, mins); - VectorMA(r_refdef.scene.worldmodel->normalmaxs, 2.0f, spacing, maxs); + if (r_refdef.scene.worldmodel) + { + VectorMA(r_refdef.scene.worldmodel->normalmins, -2.0f, spacing, mins); + VectorMA(r_refdef.scene.worldmodel->normalmaxs, 2.0f, spacing, maxs); + } + else + { + VectorSet(mins, -1048576.0f, -1048576.0f, -1048576.0f); + VectorSet(maxs, 1048576.0f, 1048576.0f, 1048576.0f); + } VectorSubtract(maxs, mins, size); // now we can calculate the resolution we want c[0] = (int)floor(size[0] / spacing[0] + 0.5f); @@ -2521,7 +2557,7 @@ void R_Shadow_UpdateBounceGridTexture(void) // if dynamic we may or may not want to use the world bounds // if the dynamic size is smaller than the world bounds, use it instead - if (!settings.staticmode && (r_shadow_bouncegrid_x.integer * r_shadow_bouncegrid_y.integer * r_shadow_bouncegrid_z.integer < resolution[0] * resolution[1] * resolution[2])) + if (!settings->staticmode && (r_shadow_bouncegrid_x.integer * r_shadow_bouncegrid_y.integer * r_shadow_bouncegrid_z.integer < resolution[0] * resolution[1] * resolution[2])) { // we know the resolution we want c[0] = r_shadow_bouncegrid_x.integer; @@ -2555,63 +2591,66 @@ void R_Shadow_UpdateBounceGridTexture(void) // recalculate the maxs in case the resolution was not satisfactory VectorAdd(mins, size, maxs); - // if all the settings seem identical to the previous update, return - if (r_shadow_bouncegridtexture && (settings.staticmode || realtime < r_shadow_bouncegridtime + r_shadow_bouncegrid_updateinterval.value) && !memcmp(&r_shadow_bouncegridsettings, &settings, sizeof(settings))) - return; + // check if this changed the texture size + r_shadow_bouncegrid_state.createtexture = !(r_shadow_bouncegrid_state.texture && r_shadow_bouncegrid_state.resolution[0] == resolution[0] && r_shadow_bouncegrid_state.resolution[1] == resolution[1] && r_shadow_bouncegrid_state.resolution[2] == resolution[2] && r_shadow_bouncegrid_state.directional == r_shadow_bouncegrid_state.settings.directionalshading); + r_shadow_bouncegrid_state.directional = r_shadow_bouncegrid_state.settings.directionalshading; + VectorCopy(mins, r_shadow_bouncegrid_state.mins); + VectorCopy(maxs, r_shadow_bouncegrid_state.maxs); + VectorCopy(size, r_shadow_bouncegrid_state.size); + VectorCopy(spacing, r_shadow_bouncegrid_state.spacing); + VectorCopy(ispacing, r_shadow_bouncegrid_state.ispacing); + VectorCopy(resolution, r_shadow_bouncegrid_state.resolution); - // store the new settings - r_shadow_bouncegridsettings = settings; - - pixelbands = settings.directionalshading ? 8 : 1; - pixelsperband = resolution[0]*resolution[1]*resolution[2]; - numpixels = pixelsperband*pixelbands; - - // 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); // reallocate pixels for this update if needed... - if (r_shadow_bouncegridnumpixels != numpixels || !r_shadow_bouncegridpixels || !r_shadow_bouncegridhighpixels) + r_shadow_bouncegrid_state.pixelbands = settings->directionalshading ? 8 : 1; + r_shadow_bouncegrid_state.pixelsperband = resolution[0]*resolution[1]*resolution[2]; + r_shadow_bouncegrid_state.bytesperband = r_shadow_bouncegrid_state.pixelsperband*4; + numpixels = r_shadow_bouncegrid_state.pixelsperband*r_shadow_bouncegrid_state.pixelbands; + if (r_shadow_bouncegrid_state.numpixels != numpixels || !r_shadow_bouncegrid_state.pixels || !r_shadow_bouncegrid_state.highpixels) { - if (r_shadow_bouncegridtexture) + if (r_shadow_bouncegrid_state.texture) { - R_FreeTexture(r_shadow_bouncegridtexture); - r_shadow_bouncegridtexture = NULL; + R_FreeTexture(r_shadow_bouncegrid_state.texture); + r_shadow_bouncegrid_state.texture = NULL; } - r_shadow_bouncegridpixels = (unsigned char *)Mem_Realloc(r_main_mempool, r_shadow_bouncegridpixels, numpixels * sizeof(unsigned char[4])); - r_shadow_bouncegridhighpixels = (float *)Mem_Realloc(r_main_mempool, r_shadow_bouncegridhighpixels, numpixels * sizeof(float[4])); + r_shadow_bouncegrid_state.pixels = (unsigned char *)Mem_Realloc(r_main_mempool, r_shadow_bouncegrid_state.pixels, numpixels * sizeof(unsigned char[4])); + r_shadow_bouncegrid_state.highpixels = (float *)Mem_Realloc(r_main_mempool, r_shadow_bouncegrid_state.highpixels, numpixels * sizeof(float[4])); + r_shadow_bouncegrid_state.numpixels = numpixels; } - r_shadow_bouncegridnumpixels = numpixels; - pixels = r_shadow_bouncegridpixels; - highpixels = r_shadow_bouncegridhighpixels; - x = pixelsperband*4; - for (pixelband = 0;pixelband < pixelbands;pixelband++) - { - if (pixelband == 1) - memset(pixels + pixelband * x, 128, x); - else - memset(pixels + pixelband * x, 0, x); - } - memset(highpixels, 0, numpixels * sizeof(float[4])); - // figure out what we want to interact with - if (settings.hitmodels) - hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;// | SUPERCONTENTS_LIQUIDSMASK; - else - hitsupercontentsmask = SUPERCONTENTS_SOLID;// | SUPERCONTENTS_LIQUIDSMASK; - maxbounce = settings.maxbounce; - // clear variables that produce warnings otherwise - memset(splatcolor, 0, sizeof(splatcolor)); - // iterate world rtlights - range = (unsigned int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked - range1 = settings.staticmode ? 0 : r_refdef.scene.numlights; - range2 = range + range1; - photoncount = 0; + + // update the bouncegrid matrix to put it in the world properly + memset(m, 0, sizeof(m)); + m[0] = 1.0f / r_shadow_bouncegrid_state.size[0]; + m[3] = -r_shadow_bouncegrid_state.mins[0] * m[0]; + m[5] = 1.0f / r_shadow_bouncegrid_state.size[1]; + m[7] = -r_shadow_bouncegrid_state.mins[1] * m[5]; + m[10] = 1.0f / r_shadow_bouncegrid_state.size[2]; + m[11] = -r_shadow_bouncegrid_state.mins[2] * m[10]; + m[15] = 1.0f; + Matrix4x4_FromArrayFloatD3D(&r_shadow_bouncegrid_state.matrix, m); +} + +#define MAXBOUNCEGRIDPARTICLESPERLIGHT 1048576 + +// enumerate world rtlights and sum the overall amount of light in the world, +// from that we can calculate a scaling factor to fairly distribute photons +// to all the lights +// +// this modifies rtlight->photoncolor and rtlight->photons +static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *settings, unsigned int range, unsigned int range1, unsigned int range2, int flag, float *photonscaling) +{ + float normalphotonscaling; + float maxphotonscaling; + float photoncount = 0.0f; + float lightintensity; + float radius; + float s; + float w; + vec3_t cullmins; + vec3_t cullmaxs; + unsigned int lightindex; + dlight_t *light; + rtlight_t *rtlight; for (lightindex = 0;lightindex < range2;lightindex++) { if (lightindex < range) @@ -2624,7 +2663,7 @@ void R_Shadow_UpdateBounceGridTexture(void) rtlight->photons = 0; if (!(light->flags & flag)) continue; - if (settings.staticmode) + if (settings->staticmode) { // when static, we skip styled lights because they tend to change... if (rtlight->style > 0 && r_shadow_bouncegrid.integer != 2) @@ -2638,26 +2677,34 @@ void R_Shadow_UpdateBounceGridTexture(void) rtlight->photons = 0; } // draw only visible lights (major speedup) - radius = rtlight->radius * settings.lightradiusscale; + radius = rtlight->radius * settings->lightradiusscale; cullmins[0] = rtlight->shadoworigin[0] - radius; cullmins[1] = rtlight->shadoworigin[1] - radius; cullmins[2] = rtlight->shadoworigin[2] - radius; cullmaxs[0] = rtlight->shadoworigin[0] + radius; cullmaxs[1] = rtlight->shadoworigin[1] + radius; cullmaxs[2] = rtlight->shadoworigin[2] + radius; - if (R_CullBox(cullmins, cullmaxs)) - continue; - if (r_refdef.scene.worldmodel - && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs - && !r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, cullmins, cullmaxs)) - continue; w = r_shadow_lightintensityscale.value * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale); - if (w * VectorLength2(rtlight->color) == 0.0f) + if (!settings->staticmode) + { + if (R_CullBox(cullmins, cullmaxs)) + continue; + if (r_refdef.scene.worldmodel + && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs + && !r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, cullmins, cullmaxs)) + continue; + if (w * VectorLength2(rtlight->color) == 0.0f) + continue; + } + // a light that does not emit any light before style is applied, can be + // skipped entirely (it may just be a corona) + if (rtlight->radius == 0.0f || VectorLength2(rtlight->color) == 0.0f) continue; w *= ((rtlight->style >= 0 && rtlight->style < MAX_LIGHTSTYLES) ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1); VectorScale(rtlight->color, w, rtlight->photoncolor); - //if (!VectorLength2(rtlight->photoncolor)) - // continue; + // skip lights that will emit no photons + if (!VectorLength2(rtlight->photoncolor)) + 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 @@ -2666,12 +2713,350 @@ void R_Shadow_UpdateBounceGridTexture(void) s = rtlight->radius; lightintensity = VectorLength(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale); if (lightindex >= range) - lightintensity *= settings.dlightparticlemultiplier; - rtlight->photons = max(0.0f, lightintensity * s * s); + lightintensity *= settings->dlightparticlemultiplier; + rtlight->photons = bound(0.0f, lightintensity * s * s, MAXBOUNCEGRIDPARTICLESPERLIGHT); photoncount += rtlight->photons; + // if the lightstyle happens to be off right now, we can skip actually + // firing the photons, but we did have to count them in the total. + //if (VectorLength2(rtlight->photoncolor) == 0.0f) + // rtlight->photons = 0; + } + // the user provided an energyperphoton value which we try to use + // if that results in too many photons to shoot this frame, then we cap it + // which causes photons to appear/disappear from frame to frame, so we don't + // like doing that in the typical case + normalphotonscaling = 1.0f / max(0.0001f, r_shadow_bouncegrid_dynamic_energyperphoton.value); + maxphotonscaling = (float)settings->maxphotons / max(1, photoncount); + *photonscaling = min(normalphotonscaling, maxphotonscaling); +} + +static int R_Shadow_BounceGrid_SplatPathCompare(const void *pa, const void *pb) +{ + r_shadow_bouncegrid_splatpath_t *a = (r_shadow_bouncegrid_splatpath_t *)pa; + r_shadow_bouncegrid_splatpath_t *b = (r_shadow_bouncegrid_splatpath_t *)pb; + // we only really care about sorting by Z + if (a->point[2] < b->point[2]) + return -1; + if (a->point[2] > b->point[2]) + return 1; + return 0; +} + +static void R_Shadow_BounceGrid_ClearPixels(void) +{ + // clear the highpixels array we'll be accumulating into + memset(r_shadow_bouncegrid_state.highpixels, 0, r_shadow_bouncegrid_state.numpixels * sizeof(float[4])); +} + +static void R_Shadow_BounceGrid_PerformSplats(void) +{ + r_shadow_bouncegrid_splatpath_t *splatpaths = r_shadow_bouncegrid_state.splatpaths; + r_shadow_bouncegrid_splatpath_t *splatpath; + float *highpixel; + float *highpixels = r_shadow_bouncegrid_state.highpixels; + int numsplatpaths = r_shadow_bouncegrid_state.numsplatpaths; + int splatindex; + vec3_t steppos; + vec3_t stepdelta; + vec3_t dir; + float texlerp[2][3]; + float splatcolor[32]; + float pixelweight[8]; + float w; + int resolution[3]; + int tex[3]; + int pixelindex[8]; + int corner; + int pixelsperband = r_shadow_bouncegrid_state.pixelsperband; + int pixelband; + int pixelbands = r_shadow_bouncegrid_state.pixelbands; + int numsteps; + int step; + + // hush warnings about uninitialized data - pixelbands doesn't change but... + memset(splatcolor, 0, sizeof(splatcolor)); + + // we use this a lot, so get a local copy + VectorCopy(r_shadow_bouncegrid_state.resolution, resolution); + + // sort the splats before we execute them, to reduce cache misses + if (r_shadow_bouncegrid_sortlightpaths.integer) + qsort(splatpaths, numsplatpaths, sizeof(*splatpaths), R_Shadow_BounceGrid_SplatPathCompare); + + splatpath = splatpaths; + for (splatindex = 0;splatindex < numsplatpaths;splatindex++, splatpath++) + { + + // calculate second order spherical harmonics values (average, slopeX, slopeY, slopeZ) + // accumulate average shotcolor + VectorCopy(splatpath->splatdir, dir); + splatcolor[ 0] = splatpath->splatcolor[0]; + splatcolor[ 1] = splatpath->splatcolor[1]; + splatcolor[ 2] = splatpath->splatcolor[2]; + splatcolor[ 3] = 0.0f; + if (pixelbands > 1) + { + // store bentnormal in case the shader has a use for it, + // bentnormal is an intensity-weighted average of the directions, + // and will be normalized on conversion to texture pixels. + splatcolor[ 4] = dir[0] * splatpath->splatintensity; + splatcolor[ 5] = dir[1] * splatpath->splatintensity; + splatcolor[ 6] = dir[2] * splatpath->splatintensity; + splatcolor[ 7] = splatpath->splatintensity; + // for each color component (R, G, B) calculate the amount that a + // direction contributes + splatcolor[ 8] = splatcolor[0] * max(0.0f, dir[0]); + splatcolor[ 9] = splatcolor[0] * max(0.0f, dir[1]); + splatcolor[10] = splatcolor[0] * max(0.0f, dir[2]); + splatcolor[11] = 0.0f; + splatcolor[12] = splatcolor[1] * max(0.0f, dir[0]); + splatcolor[13] = splatcolor[1] * max(0.0f, dir[1]); + splatcolor[14] = splatcolor[1] * max(0.0f, dir[2]); + splatcolor[15] = 0.0f; + splatcolor[16] = splatcolor[2] * max(0.0f, dir[0]); + splatcolor[17] = splatcolor[2] * max(0.0f, dir[1]); + splatcolor[18] = splatcolor[2] * max(0.0f, dir[2]); + splatcolor[19] = 0.0f; + // and do the same for negative directions + splatcolor[20] = splatcolor[0] * max(0.0f, -dir[0]); + splatcolor[21] = splatcolor[0] * max(0.0f, -dir[1]); + splatcolor[22] = splatcolor[0] * max(0.0f, -dir[2]); + splatcolor[23] = 0.0f; + splatcolor[24] = splatcolor[1] * max(0.0f, -dir[0]); + splatcolor[25] = splatcolor[1] * max(0.0f, -dir[1]); + splatcolor[26] = splatcolor[1] * max(0.0f, -dir[2]); + splatcolor[27] = 0.0f; + splatcolor[28] = splatcolor[2] * max(0.0f, -dir[0]); + splatcolor[29] = splatcolor[2] * max(0.0f, -dir[1]); + splatcolor[30] = splatcolor[2] * max(0.0f, -dir[2]); + splatcolor[31] = 0.0f; + } + // calculate the number of steps we need to traverse this distance + VectorCopy(splatpath->point, steppos); + VectorCopy(splatpath->step, stepdelta); + numsteps = splatpath->remainingsplats; + for (step = 0;step < numsteps;step++) + { + r_refdef.stats[r_stat_bouncegrid_splats]++; + // figure out which texture pixels this is in + texlerp[1][0] = steppos[0] - 0.5f; + texlerp[1][1] = steppos[1] - 0.5f; + texlerp[1][2] = steppos[2] - 0.5f; + tex[0] = (int)floor(texlerp[1][0]); + tex[1] = (int)floor(texlerp[1][1]); + tex[2] = (int)floor(texlerp[1][2]); + if (tex[0] >= 1 + && tex[1] >= 1 + && tex[2] >= 1 + && tex[0] < resolution[0] - 2 + && tex[1] < resolution[1] - 2 + && tex[2] < resolution[2] - 2) + { + // it is within bounds... do the real work now + // calculate the lerp factors + texlerp[1][0] -= tex[0]; + texlerp[1][1] -= tex[1]; + texlerp[1][2] -= tex[2]; + texlerp[0][0] = 1.0f - texlerp[1][0]; + texlerp[0][1] = 1.0f - texlerp[1][1]; + texlerp[0][2] = 1.0f - texlerp[1][2]; + // calculate individual pixel indexes and weights + pixelindex[0] = (((tex[2] )*resolution[1]+tex[1] )*resolution[0]+tex[0] );pixelweight[0] = (texlerp[0][0]*texlerp[0][1]*texlerp[0][2]); + pixelindex[1] = (((tex[2] )*resolution[1]+tex[1] )*resolution[0]+tex[0]+1);pixelweight[1] = (texlerp[1][0]*texlerp[0][1]*texlerp[0][2]); + pixelindex[2] = (((tex[2] )*resolution[1]+tex[1]+1)*resolution[0]+tex[0] );pixelweight[2] = (texlerp[0][0]*texlerp[1][1]*texlerp[0][2]); + pixelindex[3] = (((tex[2] )*resolution[1]+tex[1]+1)*resolution[0]+tex[0]+1);pixelweight[3] = (texlerp[1][0]*texlerp[1][1]*texlerp[0][2]); + pixelindex[4] = (((tex[2]+1)*resolution[1]+tex[1] )*resolution[0]+tex[0] );pixelweight[4] = (texlerp[0][0]*texlerp[0][1]*texlerp[1][2]); + pixelindex[5] = (((tex[2]+1)*resolution[1]+tex[1] )*resolution[0]+tex[0]+1);pixelweight[5] = (texlerp[1][0]*texlerp[0][1]*texlerp[1][2]); + pixelindex[6] = (((tex[2]+1)*resolution[1]+tex[1]+1)*resolution[0]+tex[0] );pixelweight[6] = (texlerp[0][0]*texlerp[1][1]*texlerp[1][2]); + pixelindex[7] = (((tex[2]+1)*resolution[1]+tex[1]+1)*resolution[0]+tex[0]+1);pixelweight[7] = (texlerp[1][0]*texlerp[1][1]*texlerp[1][2]); + // update the 8 pixels... + for (pixelband = 0;pixelband < pixelbands;pixelband++) + { + for (corner = 0;corner < 8;corner++) + { + // calculate address for pixel + w = pixelweight[corner]; + highpixel = highpixels + 4 * pixelindex[corner] + pixelband * pixelsperband * 4; + // add to the high precision pixel color + highpixel[0] += (splatcolor[pixelband*4+0]*w); + highpixel[1] += (splatcolor[pixelband*4+1]*w); + highpixel[2] += (splatcolor[pixelband*4+2]*w); + highpixel[3] += (splatcolor[pixelband*4+3]*w); + } + } + } + VectorAdd(steppos, stepdelta, steppos); + } } - photonscaling = (float)settings.photons / max(1, photoncount); - photonresidual = 0.0f; +} + +static void R_Shadow_BounceGrid_BlurPixelsInDirection(const float *inpixels, float *outpixels, int off) +{ + const float *inpixel; + float *outpixel; + int pixelbands = r_shadow_bouncegrid_state.pixelbands; + int pixelband; + unsigned int index; + unsigned int x, y, z; + unsigned int resolution[3]; + VectorCopy(r_shadow_bouncegrid_state.resolution, resolution); + for (pixelband = 0;pixelband < pixelbands;pixelband++) + { + for (z = 1;z < resolution[2]-1;z++) + { + for (y = 1;y < resolution[1]-1;y++) + { + x = 1; + index = ((pixelband*resolution[2]+z)*resolution[1]+y)*resolution[0]+x; + inpixel = inpixels + 4*index; + outpixel = outpixels + 4*index; + for (;x < resolution[0]-1;x++, inpixel += 4, outpixel += 4) + { + outpixel[0] = (inpixel[0] + inpixel[ off] + inpixel[0-off]) * (1.0f / 3.0); + outpixel[1] = (inpixel[1] + inpixel[1+off] + inpixel[1-off]) * (1.0f / 3.0); + outpixel[2] = (inpixel[2] + inpixel[2+off] + inpixel[2-off]) * (1.0f / 3.0); + outpixel[3] = (inpixel[3] + inpixel[3+off] + inpixel[3-off]) * (1.0f / 3.0); + } + } + } + } +} + +static void R_Shadow_BounceGrid_BlurPixels(void) +{ + float *highpixels = r_shadow_bouncegrid_state.highpixels; + float *temppixels1 = (float *)R_FrameData_Alloc(r_shadow_bouncegrid_state.numpixels * sizeof(float[4])); + float *temppixels2 = (float *)R_FrameData_Alloc(r_shadow_bouncegrid_state.numpixels * sizeof(float[4])); + unsigned int resolution[3]; + + if (!r_shadow_bouncegrid_blur.integer) + return; + + VectorCopy(r_shadow_bouncegrid_state.resolution, resolution); + + // blur on X + R_Shadow_BounceGrid_BlurPixelsInDirection(highpixels, temppixels1, 4); + // blur on Y + R_Shadow_BounceGrid_BlurPixelsInDirection(temppixels1, temppixels2, resolution[0] * 4); + // blur on Z + R_Shadow_BounceGrid_BlurPixelsInDirection(temppixels2, highpixels, resolution[0] * resolution[1] * 4); +} + +static void R_Shadow_BounceGrid_ConvertPixelsAndUpload(void) +{ + unsigned char *pixels = r_shadow_bouncegrid_state.pixels; + unsigned char *pixel; + float *highpixels = r_shadow_bouncegrid_state.highpixels; + float *highpixel; + float *bandpixel; + unsigned int pixelsperband = r_shadow_bouncegrid_state.pixelsperband; + unsigned int pixelbands = r_shadow_bouncegrid_state.pixelbands; + unsigned int pixelband; + unsigned int x, y, z; + unsigned int index, bandindex; + unsigned int resolution[3]; + int c[4]; + VectorCopy(r_shadow_bouncegrid_state.resolution, resolution); + // start by clearing the pixels array - we won't be writing to all of it + for (pixelband = 0;pixelband < pixelbands;pixelband++) + { + // clear to neutral values before we bother converting + if (pixelband == 1) + memset(r_shadow_bouncegrid_state.pixels + pixelband * r_shadow_bouncegrid_state.bytesperband, 128, r_shadow_bouncegrid_state.bytesperband); + else + memset(r_shadow_bouncegrid_state.pixels + pixelband * r_shadow_bouncegrid_state.bytesperband, 0, r_shadow_bouncegrid_state.bytesperband); + } + // skip first and last columns, rows, and layers as these are always blank + // skip higher pixelbands on pixels that have no color + for (z = 1;z < resolution[2]-1;z++) + { + for (y = 1;y < resolution[1]-1;y++) + { + x = 1; + pixelband = 0; + index = ((pixelband*resolution[2]+z)*resolution[1]+y)*resolution[0]+x; + highpixel = highpixels + 4*index; + for (;x < resolution[0]-1;x++, index++, highpixel += 4) + { + // only convert pixels that were hit by photons + if (VectorLength2(highpixel)) + { + // process all of the pixelbands for this pixel + for (pixelband = 0, bandindex = index;pixelband < pixelbands;pixelband++, bandindex += pixelsperband) + { + pixel = pixels + 4*bandindex; + bandpixel = highpixels + 4*bandindex; + // normalize the bentnormal pixelband... + if (pixelband == 1) + { + VectorNormalize(bandpixel); + c[0] = (int)(bandpixel[0]*128.0f+128.0f); + c[1] = (int)(bandpixel[1]*128.0f+128.0f); + c[2] = (int)(bandpixel[2]*128.0f+128.0f); + c[3] = (int)(bandpixel[3]*128.0f+128.0f); + } + else + { + c[0] = (int)(bandpixel[0]*256.0f); + c[1] = (int)(bandpixel[1]*256.0f); + c[2] = (int)(bandpixel[2]*256.0f); + c[3] = (int)(bandpixel[3]*256.0f); + } + pixel[2] = (unsigned char)bound(0, c[0], 255); + pixel[1] = (unsigned char)bound(0, c[1], 255); + pixel[0] = (unsigned char)bound(0, c[2], 255); + pixel[3] = (unsigned char)bound(0, c[3], 255); + } + } + } + } + } + + if (!r_shadow_bouncegrid_state.createtexture) + R_UpdateTexture(r_shadow_bouncegrid_state.texture, pixels, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands); + else + { + if (r_shadow_bouncegrid_state.texture) + R_FreeTexture(r_shadow_bouncegrid_state.texture); + r_shadow_bouncegrid_state.texture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*pixelbands, pixels, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL); + } + r_shadow_bouncegrid_state.lastupdatetime = realtime; +} + +static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t settings, unsigned int range, unsigned int range1, unsigned int range2, float photonscaling, int flag) +{ + dlight_t *light; + int bouncecount; + int hitsupercontentsmask; + int maxbounce; + int shootparticles; + int shotparticles; + trace_t cliptrace; + //trace_t cliptrace2; + //trace_t cliptrace3; + unsigned int lightindex; + unsigned int seed = (unsigned int)(realtime * 1000.0f); + vec3_t shotcolor; + vec3_t baseshotcolor; + vec3_t surfcolor; + vec3_t clipend; + vec3_t clipstart; + vec3_t clipdiff; + vec_t radius; + vec_t s; + rtlight_t *rtlight; + + // we'll need somewhere to store these + r_shadow_bouncegrid_state.numsplatpaths = 0; + r_shadow_bouncegrid_state.splatpaths = (r_shadow_bouncegrid_splatpath_t *)R_FrameData_Alloc(sizeof(r_shadow_bouncegrid_splatpath_t) * r_shadow_bouncegrid_state.maxsplatpaths); + + // figure out what we want to interact with + if (settings.hitmodels) + hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;// | SUPERCONTENTS_LIQUIDSMASK; + else + hitsupercontentsmask = SUPERCONTENTS_SOLID;// | SUPERCONTENTS_LIQUIDSMASK; + maxbounce = settings.maxbounce; + for (lightindex = 0;lightindex < range2;lightindex++) { if (lightindex < range) @@ -2683,17 +3068,13 @@ void R_Shadow_UpdateBounceGridTexture(void) } else rtlight = r_refdef.scene.lights[lightindex - range]; - // skip a light with no photons - if (rtlight->photons == 0.0f) - continue; - // skip a light with no photon color) - if (VectorLength2(rtlight->photoncolor) == 0.0f) - continue; - photonresidual += rtlight->photons * photonscaling; - shootparticles = (int)bound(0, photonresidual, MAXBOUNCEGRIDPARTICLESPERLIGHT); + // note that this code used to keep track of residual photons and + // distribute them evenly to achieve exactly a desired photon count, + // but that caused unwanted flickering in dynamic mode + shootparticles = (int)floor(rtlight->photons * photonscaling); + // skip if we won't be shooting any photons if (!shootparticles) continue; - photonresidual -= shootparticles; radius = rtlight->radius * settings.lightradiusscale; s = settings.particleintensity / shootparticles; VectorScale(rtlight->photoncolor, s, baseshotcolor); @@ -2727,108 +3108,9 @@ void R_Shadow_UpdateBounceGridTexture(void) } if (bouncecount > 0 || settings.includedirectlighting) { - // calculate second order spherical harmonics values (average, slopeX, slopeY, slopeZ) - // accumulate average shotcolor - w = VectorLength(shotcolor); - splatcolor[ 0] = shotcolor[0]; - splatcolor[ 1] = shotcolor[1]; - splatcolor[ 2] = shotcolor[2]; - splatcolor[ 3] = 0.0f; - if (pixelbands > 1) - { - VectorSubtract(clipstart, cliptrace.endpos, clipdiff); - VectorNormalize(clipdiff); - // store bentnormal in case the shader has a use for it - splatcolor[ 4] = clipdiff[0] * w; - splatcolor[ 5] = clipdiff[1] * w; - splatcolor[ 6] = clipdiff[2] * w; - splatcolor[ 7] = w; - // accumulate directional contributions (+X, +Y, +Z, -X, -Y, -Z) - splatcolor[ 8] = shotcolor[0] * max(0.0f, clipdiff[0]); - splatcolor[ 9] = shotcolor[0] * max(0.0f, clipdiff[1]); - splatcolor[10] = shotcolor[0] * max(0.0f, clipdiff[2]); - splatcolor[11] = 0.0f; - splatcolor[12] = shotcolor[1] * max(0.0f, clipdiff[0]); - splatcolor[13] = shotcolor[1] * max(0.0f, clipdiff[1]); - splatcolor[14] = shotcolor[1] * max(0.0f, clipdiff[2]); - splatcolor[15] = 0.0f; - splatcolor[16] = shotcolor[2] * max(0.0f, clipdiff[0]); - splatcolor[17] = shotcolor[2] * max(0.0f, clipdiff[1]); - splatcolor[18] = shotcolor[2] * max(0.0f, clipdiff[2]); - splatcolor[19] = 0.0f; - splatcolor[20] = shotcolor[0] * max(0.0f, -clipdiff[0]); - splatcolor[21] = shotcolor[0] * max(0.0f, -clipdiff[1]); - splatcolor[22] = shotcolor[0] * max(0.0f, -clipdiff[2]); - splatcolor[23] = 0.0f; - splatcolor[24] = shotcolor[1] * max(0.0f, -clipdiff[0]); - splatcolor[25] = shotcolor[1] * max(0.0f, -clipdiff[1]); - splatcolor[26] = shotcolor[1] * max(0.0f, -clipdiff[2]); - splatcolor[27] = 0.0f; - splatcolor[28] = shotcolor[2] * max(0.0f, -clipdiff[0]); - splatcolor[29] = shotcolor[2] * max(0.0f, -clipdiff[1]); - splatcolor[30] = shotcolor[2] * max(0.0f, -clipdiff[2]); - splatcolor[31] = 0.0f; - } - // calculate the number of steps we need to traverse this distance - VectorSubtract(cliptrace.endpos, clipstart, stepdelta); - numsteps = (int)(VectorLength(stepdelta) * ispacing[0]); - numsteps = bound(1, numsteps, 1024); - w = 1.0f / numsteps; - VectorScale(stepdelta, w, stepdelta); - VectorMA(clipstart, 0.5f, stepdelta, steppos); - for (step = 0;step < numsteps;step++) - { - r_refdef.stats[r_stat_bouncegrid_splats]++; - // figure out which texture pixel this is in - texlerp[1][0] = ((steppos[0] - mins[0]) * ispacing[0]) - 0.5f; - texlerp[1][1] = ((steppos[1] - mins[1]) * ispacing[1]) - 0.5f; - texlerp[1][2] = ((steppos[2] - mins[2]) * ispacing[2]) - 0.5f; - tex[0] = (int)floor(texlerp[1][0]); - tex[1] = (int)floor(texlerp[1][1]); - tex[2] = (int)floor(texlerp[1][2]); - if (tex[0] >= 1 && tex[1] >= 1 && tex[2] >= 1 && tex[0] < resolution[0] - 2 && tex[1] < resolution[1] - 2 && tex[2] < resolution[2] - 2) - { - // it is within bounds... do the real work now - // calculate the lerp factors - texlerp[1][0] -= tex[0]; - texlerp[1][1] -= tex[1]; - texlerp[1][2] -= tex[2]; - texlerp[0][0] = 1.0f - texlerp[1][0]; - texlerp[0][1] = 1.0f - texlerp[1][1]; - texlerp[0][2] = 1.0f - texlerp[1][2]; - // calculate individual pixel indexes and weights - pixelindex[0] = (((tex[2] )*resolution[1]+tex[1] )*resolution[0]+tex[0] );pixelweight[0] = (texlerp[0][0]*texlerp[0][1]*texlerp[0][2]); - pixelindex[1] = (((tex[2] )*resolution[1]+tex[1] )*resolution[0]+tex[0]+1);pixelweight[1] = (texlerp[1][0]*texlerp[0][1]*texlerp[0][2]); - pixelindex[2] = (((tex[2] )*resolution[1]+tex[1]+1)*resolution[0]+tex[0] );pixelweight[2] = (texlerp[0][0]*texlerp[1][1]*texlerp[0][2]); - pixelindex[3] = (((tex[2] )*resolution[1]+tex[1]+1)*resolution[0]+tex[0]+1);pixelweight[3] = (texlerp[1][0]*texlerp[1][1]*texlerp[0][2]); - pixelindex[4] = (((tex[2]+1)*resolution[1]+tex[1] )*resolution[0]+tex[0] );pixelweight[4] = (texlerp[0][0]*texlerp[0][1]*texlerp[1][2]); - pixelindex[5] = (((tex[2]+1)*resolution[1]+tex[1] )*resolution[0]+tex[0]+1);pixelweight[5] = (texlerp[1][0]*texlerp[0][1]*texlerp[1][2]); - pixelindex[6] = (((tex[2]+1)*resolution[1]+tex[1]+1)*resolution[0]+tex[0] );pixelweight[6] = (texlerp[0][0]*texlerp[1][1]*texlerp[1][2]); - pixelindex[7] = (((tex[2]+1)*resolution[1]+tex[1]+1)*resolution[0]+tex[0]+1);pixelweight[7] = (texlerp[1][0]*texlerp[1][1]*texlerp[1][2]); - // update the 8 pixels... - for (pixelband = 0;pixelband < pixelbands;pixelband++) - { - for (corner = 0;corner < 8;corner++) - { - // calculate address for pixel - w = pixelweight[corner]; - pixel = pixels + 4 * pixelindex[corner] + pixelband * pixelsperband * 4; - highpixel = highpixels + 4 * pixelindex[corner] + pixelband * pixelsperband * 4; - // add to the high precision pixel color - highpixel[0] += (splatcolor[pixelband*4+0]*w); - highpixel[1] += (splatcolor[pixelband*4+1]*w); - highpixel[2] += (splatcolor[pixelband*4+2]*w); - highpixel[3] += (splatcolor[pixelband*4+3]*w); - // flag the low precision pixel as needing to be updated - pixel[3] = 255; - // advance to next band of coefficients - //pixel += pixelsperband*4; - //highpixel += pixelsperband*4; - } - } - } - VectorAdd(steppos, stepdelta, steppos); - } + vec3_t hitpos; + VectorCopy(cliptrace.endpos, hitpos); + R_shadow_BounceGrid_AddSplatPath(clipstart, hitpos, shotcolor); } if (cliptrace.fraction >= 1.0f) break; @@ -2873,56 +3155,84 @@ void R_Shadow_UpdateBounceGridTexture(void) } } } - // generate pixels array from highpixels array - // skip first and last columns, rows, and layers as these are blank - // the pixel[3] value was written above, so we can use it to detect only pixels that need to be calculated - for (pixelband = 0;pixelband < pixelbands;pixelband++) +} + +void R_Shadow_UpdateBounceGridTexture(void) +{ + int flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE; + r_shadow_bouncegrid_settings_t settings; + qboolean enable = false; + qboolean settingschanged; + unsigned int range; // number of world lights + unsigned int range1; // number of dynamic lights (or zero if disabled) + unsigned int range2; // range+range1 + float photonscaling; + + enable = R_Shadow_BounceGrid_CheckEnable(flag); + + R_Shadow_BounceGrid_GenerateSettings(&settings); + + // changing intensity does not require an update + r_shadow_bouncegrid_state.intensity = r_shadow_bouncegrid_intensity.value; + + settingschanged = memcmp(&r_shadow_bouncegrid_state.settings, &settings, sizeof(settings)) != 0; + + // when settings change, we free everything as it is just simpler that way. + if (settingschanged || !enable) { - for (z = 1;z < resolution[2]-1;z++) + // not enabled, make sure we free anything we don't need anymore. + if (r_shadow_bouncegrid_state.texture) { - for (y = 1;y < resolution[1]-1;y++) - { - for (x = 1, pixelindex[0] = ((pixelband*resolution[2]+z)*resolution[1]+y)*resolution[0]+x, pixel = pixels + 4*pixelindex[0], highpixel = highpixels + 4*pixelindex[0];x < resolution[0]-1;x++, pixel += 4, highpixel += 4) - { - // only convert pixels that were hit by photons - if (pixel[3] == 255) - { - // normalize the bentnormal... - if (pixelband == 1) - { - VectorNormalize(highpixel); - c[0] = (int)(highpixel[0]*128.0f+128.0f); - c[1] = (int)(highpixel[1]*128.0f+128.0f); - c[2] = (int)(highpixel[2]*128.0f+128.0f); - c[3] = (int)(highpixel[3]*128.0f+128.0f); - } - else - { - c[0] = (int)(highpixel[0]*256.0f); - c[1] = (int)(highpixel[1]*256.0f); - c[2] = (int)(highpixel[2]*256.0f); - c[3] = (int)(highpixel[3]*256.0f); - } - pixel[2] = (unsigned char)bound(0, c[0], 255); - pixel[1] = (unsigned char)bound(0, c[1], 255); - pixel[0] = (unsigned char)bound(0, c[2], 255); - pixel[3] = (unsigned char)bound(0, c[3], 255); - } - } - } + R_FreeTexture(r_shadow_bouncegrid_state.texture); + r_shadow_bouncegrid_state.texture = NULL; } + if (r_shadow_bouncegrid_state.pixels) + Mem_Free(r_shadow_bouncegrid_state.pixels); + r_shadow_bouncegrid_state.pixels = NULL; + if (r_shadow_bouncegrid_state.highpixels) + Mem_Free(r_shadow_bouncegrid_state.highpixels); + r_shadow_bouncegrid_state.highpixels = NULL; + r_shadow_bouncegrid_state.numpixels = 0; + r_shadow_bouncegrid_state.directional = false; + + if (!enable) + return; } - if (r_shadow_bouncegridtexture && r_shadow_bouncegridresolution[0] == resolution[0] && r_shadow_bouncegridresolution[1] == resolution[1] && r_shadow_bouncegridresolution[2] == resolution[2] && r_shadow_bouncegriddirectional == settings.directionalshading) - R_UpdateTexture(r_shadow_bouncegridtexture, pixels, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands); - else - { - VectorCopy(resolution, r_shadow_bouncegridresolution); - r_shadow_bouncegriddirectional = settings.directionalshading; - if (r_shadow_bouncegridtexture) - R_FreeTexture(r_shadow_bouncegridtexture); - r_shadow_bouncegridtexture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*pixelbands, pixels, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL); - } - r_shadow_bouncegridtime = realtime; + + // if all the settings seem identical to the previous update, return + if (r_shadow_bouncegrid_state.texture && (settings.staticmode || realtime < r_shadow_bouncegrid_state.lastupdatetime + r_shadow_bouncegrid_dynamic_updateinterval.value) && !settingschanged) + return; + + // store the new settings + r_shadow_bouncegrid_state.settings = settings; + + R_Shadow_BounceGrid_UpdateSpacing(); + + // get the range of light numbers we'll be looping over: + // range = static lights + // range1 = dynamic lights (optional) + // range2 = range + range1 + range = (unsigned int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked + range1 = settings.staticmode ? 0 : r_refdef.scene.numlights; + range2 = range + range1; + + // calculate weighting factors for distributing photons among the lights + R_Shadow_BounceGrid_AssignPhotons(&settings, range, range1, range2, flag, &photonscaling); + + // trace the photons from lights and accumulate illumination + R_Shadow_BounceGrid_TracePhotons(settings, range, range1, range2, photonscaling, flag); + + // clear the texture + R_Shadow_BounceGrid_ClearPixels(); + + // accumulate the light splatting into texture + R_Shadow_BounceGrid_PerformSplats(); + + // apply a mild blur filter to the texture + R_Shadow_BounceGrid_BlurPixels(); + + // convert the pixels to lower precision and upload the texture + R_Shadow_BounceGrid_ConvertPixelsAndUpload(); } void R_Shadow_RenderMode_VisibleShadowVolumes(void) @@ -3530,7 +3840,7 @@ void R_RTLight_Compile(rtlight_t *rtlight) lighttris++; shadowtris = 0; - if (rtlight->static_numlighttrispvsbytes) + if (rtlight->static_numshadowtrispvsbytes) for (i = 0;i < rtlight->static_numshadowtrispvsbytes*8;i++) if (CHECKPVSBIT(rtlight->static_shadowtrispvs, i)) shadowtris++; @@ -5167,7 +5477,7 @@ static void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale) CHECKGLERROR // See if we can use the GPU-side method to prevent implicit sync if (vid.support.arb_query_buffer_object) { -#define BUFFER_OFFSET(i) ((void*)NULL + (i)) +#define BUFFER_OFFSET(i) ((GLint *)((unsigned char*)NULL + (i))) if (!r_shadow_occlusion_buf) { qglGenBuffersARB(1, &r_shadow_occlusion_buf); qglBindBufferARB(GL_QUERY_BUFFER_ARB, r_shadow_occlusion_buf); @@ -6036,10 +6346,14 @@ void R_Shadow_EditLights_Reload_f(void) return; strlcpy(r_shadow_mapname, cl.worldname, sizeof(r_shadow_mapname)); R_Shadow_ClearWorldLights(); - R_Shadow_LoadWorldLights(); - if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray)) + if (r_shadow_realtime_world_importlightentitiesfrommap.integer <= 1) + { + R_Shadow_LoadWorldLights(); + if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray)) + R_Shadow_LoadLightsFile(); + } + if (r_shadow_realtime_world_importlightentitiesfrommap.integer >= 1) { - R_Shadow_LoadLightsFile(); if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray)) R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(); }