From: havoc Date: Wed, 23 Feb 2011 16:13:24 +0000 (+0000) Subject: added r_shadow_bouncegrid cvar, this feature does realtime global X-Git-Tag: xonotic-v0.5.0~425^2~46 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=d751baf5a54e65ccd087a62ca7d7623d2e0c3453;p=xonotic%2Fdarkplaces.git added r_shadow_bouncegrid cvar, this feature does realtime global illumination by particle tracing and uploading a new 3D texture every frame containing the lighting git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@10856 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/cl_gecko.c b/cl_gecko.c index 015b9bef..d92ed0a4 100644 --- a/cl_gecko.c +++ b/cl_gecko.c @@ -435,7 +435,7 @@ static void cl_gecko_updatecallback( rtexture_t *texture, void* callbackData ) { if( instance->browser ) { // TODO: OSGK only supports BGRA right now TIMING_TIMESTATEMENT(data = osgk_browser_lock_data( instance->browser, NULL )); - R_UpdateTexture( texture, data, 0, 0, instance->width, instance->height ); + R_UpdateTexture( texture, data, 0, 0, 0, instance->width, instance->height, 1 ); osgk_browser_unlock_data( instance->browser, data ); } } diff --git a/cl_video.c b/cl_video.c index aca8249e..2db4756e 100644 --- a/cl_video.c +++ b/cl_video.c @@ -61,7 +61,7 @@ static qboolean OpenStream( clvideo_t * video ) static void VideoUpdateCallback(rtexture_t *rt, void *data) { clvideo_t *video = (clvideo_t *) data; - R_UpdateTexture( video->cpif.tex, (unsigned char *)video->imagedata, 0, 0, video->cpif.width, video->cpif.height ); + R_UpdateTexture( video->cpif.tex, (unsigned char *)video->imagedata, 0, 0, 0, video->cpif.width, video->cpif.height, 1 ); } static void LinkVideoTexture( clvideo_t *video ) diff --git a/dpsoftrast.h b/dpsoftrast.h index a3e01039..016d2111 100644 --- a/dpsoftrast.h +++ b/dpsoftrast.h @@ -186,13 +186,14 @@ typedef enum shaderpermutation_e SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<19, ///< (lightsource) use higher quality percentage closer filtering on shadowmap test results SHADERPERMUTATION_SHADOWSAMPLER = 1<<20, ///< (lightsource) use hardware shadowmap test SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<21, ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing - SHADERPERMUTATION_SHADOWMAPORTHO = 1<<22, //< (lightsource) use orthographic shadowmap projection + SHADERPERMUTATION_SHADOWMAPORTHO = 1<<22, ///< (lightsource) use orthographic shadowmap projection SHADERPERMUTATION_DEFERREDLIGHTMAP = 1<<23, ///< (lightmap) read Texture_ScreenDiffuse/Specular textures and add them on top of lightmapping SHADERPERMUTATION_ALPHAKILL = 1<<24, ///< (deferredgeometry) discard pixel if diffuse texture alpha below 0.5 SHADERPERMUTATION_REFLECTCUBE = 1<<25, ///< fake reflections using global cubemap (not HDRI light probe) - SHADERPERMUTATION_NORMALMAPSCROLLBLEND = 1<<26, // (water) counter-direction normalmaps scrolling - SHADERPERMUTATION_LIMIT = 1<<27, ///< size of permutations array - SHADERPERMUTATION_COUNT = 27 ///< size of shaderpermutationinfo array + SHADERPERMUTATION_NORMALMAPSCROLLBLEND = 1<<26, ///< (water) counter-direction normalmaps scrolling + SHADERPERMUTATION_BOUNCEGRID = 1<<27, ///< (lightmap) use Texture_BounceGrid as an additional source of ambient light + SHADERPERMUTATION_LIMIT = 1<<28, ///< size of permutations array + SHADERPERMUTATION_COUNT = 28 ///< size of shaderpermutationinfo array } shaderpermutation_t; diff --git a/gl_draw.c b/gl_draw.c index 1654cd4b..38e1c7ac 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -516,7 +516,7 @@ cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, u { if (pic->tex && pic->width == width && pic->height == height) { - R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height); + R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1); return pic; } } diff --git a/gl_rmain.c b/gl_rmain.c index 90e49a7c..67ebbd2e 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -512,8 +512,8 @@ static void R_BuildFogTexture(void) } if (r_texture_fogattenuation) { - R_UpdateTexture(r_texture_fogattenuation, &data1[0][0], 0, 0, FOGWIDTH, 1); - //R_UpdateTexture(r_texture_fogattenuation, &data2[0][0], 0, 0, FOGWIDTH, 1); + R_UpdateTexture(r_texture_fogattenuation, &data1[0][0], 0, 0, 0, FOGWIDTH, 1, 1); + //R_UpdateTexture(r_texture_fogattenuation, &data2[0][0], 0, 0, 0, FOGWIDTH, 1, 1); } else { @@ -3438,6 +3438,7 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] = {"#define USEALPHAKILL\n", " alphakill"}, {"#define USEREFLECTCUBE\n", " reflectcube"}, {"#define USENORMALMAPSCROLLBLEND\n", " normalmapscrollblend"}, + {"#define USEBOUNCEGRID\n", " bouncegrid"}, }; // NOTE: MUST MATCH ORDER OF SHADERMODE_* ENUMS! @@ -3525,6 +3526,7 @@ typedef struct r_glsl_permutation_s int tex_Texture_ScreenSpecular; int tex_Texture_ReflectMask; int tex_Texture_ReflectCube; + int tex_Texture_BounceGrid; /// locations of detected uniforms in program object, or -1 if not found int loc_Texture_First; int loc_Texture_Second; @@ -3555,6 +3557,7 @@ typedef struct r_glsl_permutation_s int loc_Texture_ScreenSpecular; int loc_Texture_ReflectMask; int loc_Texture_ReflectCube; + int loc_Texture_BounceGrid; int loc_Alpha; int loc_BloomBlur_Parameters; int loc_ClientTime; @@ -3608,6 +3611,8 @@ typedef struct r_glsl_permutation_s int loc_ShadowMapMatrix; int loc_BloomColorSubtract; int loc_NormalmapScrollBlend; + int loc_BounceGridMatrix; + int loc_BounceGridIntensity; } r_glsl_permutation_t; @@ -3854,6 +3859,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->loc_Texture_ScreenSpecular = qglGetUniformLocation(p->program, "Texture_ScreenSpecular"); p->loc_Texture_ReflectMask = qglGetUniformLocation(p->program, "Texture_ReflectMask"); p->loc_Texture_ReflectCube = qglGetUniformLocation(p->program, "Texture_ReflectCube"); + p->loc_Texture_BounceGrid = qglGetUniformLocation(p->program, "Texture_BounceGrid"); p->loc_Alpha = qglGetUniformLocation(p->program, "Alpha"); p->loc_BloomBlur_Parameters = qglGetUniformLocation(p->program, "BloomBlur_Parameters"); p->loc_ClientTime = qglGetUniformLocation(p->program, "ClientTime"); @@ -3907,6 +3913,8 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->loc_ShadowMapMatrix = qglGetUniformLocation(p->program, "ShadowMapMatrix"); p->loc_BloomColorSubtract = qglGetUniformLocation(p->program, "BloomColorSubtract"); p->loc_NormalmapScrollBlend = qglGetUniformLocation(p->program, "NormalmapScrollBlend"); + p->loc_BounceGridMatrix = qglGetUniformLocation(p->program, "BounceGridMatrix"); + p->loc_BounceGridIntensity = qglGetUniformLocation(p->program, "BounceGridIntensity"); // initialize the samplers to refer to the texture units we use p->tex_Texture_First = -1; p->tex_Texture_Second = -1; @@ -3937,6 +3945,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->tex_Texture_ScreenSpecular = -1; p->tex_Texture_ReflectMask = -1; p->tex_Texture_ReflectCube = -1; + p->tex_Texture_BounceGrid = -1; sampler = 0; if (p->loc_Texture_First >= 0) {p->tex_Texture_First = sampler;qglUniform1i(p->loc_Texture_First , sampler);sampler++;} if (p->loc_Texture_Second >= 0) {p->tex_Texture_Second = sampler;qglUniform1i(p->loc_Texture_Second , sampler);sampler++;} @@ -3967,6 +3976,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode if (p->loc_Texture_ScreenSpecular >= 0) {p->tex_Texture_ScreenSpecular = sampler;qglUniform1i(p->loc_Texture_ScreenSpecular , sampler);sampler++;} if (p->loc_Texture_ReflectMask >= 0) {p->tex_Texture_ReflectMask = sampler;qglUniform1i(p->loc_Texture_ReflectMask , sampler);sampler++;} if (p->loc_Texture_ReflectCube >= 0) {p->tex_Texture_ReflectCube = sampler;qglUniform1i(p->loc_Texture_ReflectCube , sampler);sampler++;} + if (p->loc_Texture_BounceGrid >= 0) {p->tex_Texture_BounceGrid = sampler;qglUniform1i(p->loc_Texture_BounceGrid , sampler);sampler++;} CHECKGLERROR Con_DPrintf("^5GLSL shader %s compiled (%i textures).\n", permutationname, sampler); } @@ -4872,6 +4882,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, static float dummy_colormod[3] = {1, 1, 1}; float *colormod = rsurface.colormod; float m16f[16]; + matrix4x4_t tempmatrix; r_waterstate_waterplane_t *waterplane = (r_waterstate_waterplane_t *)surfacewaterplane; if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) permutation |= SHADERPERMUTATION_ALPHAKILL; @@ -5025,6 +5036,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, permutation |= SHADERPERMUTATION_REFLECTION; if (rsurface.texture->reflectmasktexture) permutation |= SHADERPERMUTATION_REFLECTCUBE; + if (r_shadow_bouncegridtexture) + permutation |= SHADERPERMUTATION_BOUNCEGRID; GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); allow_colormod = R_BlendFuncAllowsColormod(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); } @@ -5074,6 +5087,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP; if (rsurface.texture->reflectmasktexture) permutation |= SHADERPERMUTATION_REFLECTCUBE; + if (r_shadow_bouncegridtexture) + permutation |= SHADERPERMUTATION_BOUNCEGRID; GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); allow_colormod = R_BlendFuncAllowsColormod(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); } @@ -5120,6 +5135,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP; if (rsurface.texture->reflectmasktexture) permutation |= SHADERPERMUTATION_REFLECTCUBE; + if (r_shadow_bouncegridtexture) + permutation |= SHADERPERMUTATION_BOUNCEGRID; GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); allow_colormod = R_BlendFuncAllowsColormod(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); } @@ -5202,6 +5219,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, // ordinary vertex coloring (q3bsp) mode = SHADERMODE_VERTEXCOLOR; } + if (r_shadow_bouncegridtexture) + permutation |= SHADERPERMUTATION_BOUNCEGRID; GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); allow_colormod = R_BlendFuncAllowsColormod(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2); } @@ -5460,6 +5479,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (r_glsl_permutation->loc_OffsetMapping_Scale >= 0) qglUniform1f(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value*rsurface.texture->offsetscale); if (r_glsl_permutation->loc_ScreenToDepth >= 0) qglUniform2f(r_glsl_permutation->loc_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]); if (r_glsl_permutation->loc_PixelToScreenTexCoord >= 0) qglUniform2f(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height); + if (r_glsl_permutation->loc_BounceGridMatrix >= 0) {Matrix4x4_Concat(&tempmatrix, &r_shadow_bouncegridmatrix, &rsurface.matrix);Matrix4x4_ToArrayFloatGL(&tempmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_BounceGridMatrix, 1, false, m16f);} + if (r_glsl_permutation->loc_BounceGridIntensity >= 0) qglUniform1f(r_glsl_permutation->loc_BounceGridIntensity, r_shadow_bouncegridintensity); if (r_glsl_permutation->tex_Texture_First >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , r_texture_white ); if (r_glsl_permutation->tex_Texture_Second >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second , r_texture_white ); @@ -5504,6 +5525,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, if (r_glsl_permutation->tex_Texture_CubeProjection >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_CubeProjection , r_shadow_shadowmapvsdcttexture ); } } + if (r_glsl_permutation->tex_Texture_BounceGrid >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_BounceGrid, r_shadow_bouncegridtexture); CHECKGLERROR break; case RENDERPATH_GL13: @@ -9139,7 +9161,7 @@ void R_UpdateVariables(void) } if (r_texture_gammaramps) { - R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, RAMPWIDTH, 1); + R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, 0, RAMPWIDTH, 1, 1); } else { diff --git a/gl_rsurf.c b/gl_rsurf.c index 4a058661..f04bb2b6 100644 --- a/gl_rsurf.c +++ b/gl_rsurf.c @@ -119,7 +119,7 @@ void R_BuildLightMap (const entity_render_t *ent, msurface_t *surface) } } - R_UpdateTexture(surface->lightmaptexture, templight, surface->lightmapinfo->lightmaporigin[0], surface->lightmapinfo->lightmaporigin[1], smax, tmax); + R_UpdateTexture(surface->lightmaptexture, templight, surface->lightmapinfo->lightmaporigin[0], surface->lightmapinfo->lightmaporigin[1], 0, smax, tmax, 1); // update the surface's deluxemap if it has one if (surface->deluxemaptexture != r_texture_blanknormalmap) @@ -157,7 +157,7 @@ void R_BuildLightMap (const entity_render_t *ent, msurface_t *surface) l = (int)(n[2] * 128 + 128);out[0] = bound(0, l, 255); out[3] = 255; } - R_UpdateTexture(surface->deluxemaptexture, templight, surface->lightmapinfo->lightmaporigin[0], surface->lightmapinfo->lightmaporigin[1], smax, tmax); + R_UpdateTexture(surface->deluxemaptexture, templight, surface->lightmapinfo->lightmaporigin[0], surface->lightmapinfo->lightmaporigin[1], 0, smax, tmax, 1); } } diff --git a/gl_textures.c b/gl_textures.c index 62947bbd..fc027b2f 100644 --- a/gl_textures.c +++ b/gl_textures.c @@ -2425,7 +2425,7 @@ int R_TextureHeight(rtexture_t *rt) return rt ? ((gltexture_t *)rt)->inputheight : 0; } -void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height) +void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int z, int width, int height, int depth) { gltexture_t *glt = (gltexture_t *)rt; if (data == NULL) @@ -2446,6 +2446,8 @@ void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, in int outputskip = glt->tilewidth*bpp; const unsigned char *input = data; unsigned char *output = glt->bufferpixels; + if (glt->inputdepth != 1 || glt->sides != 1) + Sys_Error("R_UpdateTexture on buffered texture that is not 2D\n"); if (x < 0) { width += x; @@ -2470,8 +2472,8 @@ void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, in for (j = 0;j < height;j++, output += outputskip, input += inputskip) memcpy(output, input, width*bpp); } - else if (x || y || width != glt->inputwidth || height != glt->inputheight) - R_UploadPartialTexture(glt, data, x, y, 0, width, height, 1); + else if (x || y || z || width != glt->inputwidth || height != glt->inputheight || depth != glt->inputdepth) + R_UploadPartialTexture(glt, data, x, y, z, width, height, depth); else R_UploadFullTexture(glt, data); } diff --git a/r_shadow.c b/r_shadow.c index f7549cf6..d4c4570a 100644 --- a/r_shadow.c +++ b/r_shadow.c @@ -327,6 +327,23 @@ cvar_t r_shadow_particletrace_maxbounce = {CVAR_SAVE, "r_shadow_particletrace_ma 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_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", "16", "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", "16", "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", "128", "maximum texture size of bouncegrid on X axis"}; +cvar_t r_shadow_bouncegrid_y = {CVAR_SAVE, "r_shadow_bouncegrid_y", "128", "maximum texture size of bouncegrid on Y axis"}; +cvar_t r_shadow_bouncegrid_z = {CVAR_SAVE, "r_shadow_bouncegrid_z", "64", "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)"}; @@ -340,6 +357,12 @@ 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]; + // 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 @@ -483,6 +506,7 @@ void R_Shadow_FreeShadowMaps(void) void r_shadow_start(void) { // allocate vertex processing arrays + r_shadow_bouncegridtexture = NULL; r_shadow_attenuationgradienttexture = NULL; r_shadow_attenuation2dtexture = NULL; r_shadow_attenuation3dtexture = NULL; @@ -554,6 +578,7 @@ void r_shadow_shutdown(void) r_shadow_prepass_width = r_shadow_prepass_height = 0; CHECKGLERROR + r_shadow_bouncegridtexture = NULL; r_shadow_attenuationgradienttexture = NULL; r_shadow_attenuation2dtexture = NULL; r_shadow_attenuation3dtexture = NULL; @@ -621,6 +646,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); @@ -692,6 +718,23 @@ void R_Shadow_Init(void) 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_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); @@ -2244,6 +2287,232 @@ 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 bouncelimit; + int c[3]; + int hitsupercontentsmask; + int maxbounce; + int numpixels; + int resolution[3]; + int shootparticles; + int shotparticles; + int tex[3]; + trace_t cliptrace; + unsigned char *pixel; + unsigned char *pixels; + 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 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; + } + return; + } + if (r_refdef.scene.worldmodel) + { + 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); + } + 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); + 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; + numpixels = resolution[0]*resolution[1]*resolution[2]; + // allocate pixels for this update... + pixels = Mem_Alloc(r_main_mempool, numpixels * sizeof(unsigned char[4])); + // 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; + // 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 = 255.0f * r_shadow_bouncegrid_particleintensity.value / shootparticles; + VectorScale(lightcolor, s, baseshotcolor); + maxbounce = bound(1, r_shadow_bouncegrid_maxbounce.integer, 16); + 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); + bouncelimit = 1 + (rtlight->particlecache_updateparticle % maxbounce); + for (bouncecount = 0;;bouncecount++) + { + 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; + if (VectorLength2(shotcolor) < 1.0f) + break; + if (bouncecount > 0) + { + // 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... + pixel = pixels + 4 * ((tex[2]*resolution[1]+tex[1])*resolution[0]+tex[0]); + // add to the pixel color + c[0] = pixel[0] + (int)shotcolor[2]; + c[1] = pixel[1] + (int)shotcolor[1]; + c[2] = pixel[2] + (int)shotcolor[0]; + pixel[0] = (unsigned char)min(c[0], 255); + pixel[1] = (unsigned char)min(c[1], 255); + pixel[2] = (unsigned char)min(c[2], 255); + pixel[3] = 255; + } + } + if (bouncecount >= bouncelimit) + 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); + // 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); + 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); + // 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); + } + Mem_Free(pixels); + r_shadow_bouncegridtime = realtime; +} + #define MAXPARTICLESPERLIGHT 262144 #define MAXLIGHTSPERDRAW 1024 @@ -4261,6 +4530,8 @@ void R_Shadow_PrepareLights(void) if (r_editlights.integer) R_Shadow_DrawLightSprites(); + + R_Shadow_UpdateBounceGridTexture(); } void R_Shadow_DrawLights(void) diff --git a/r_shadow.h b/r_shadow.h index b08f00c9..003f4b28 100644 --- a/r_shadow.h +++ b/r_shadow.h @@ -37,6 +37,11 @@ extern cvar_t r_shadow_texture3d; extern cvar_t gl_ext_separatestencil; extern cvar_t gl_ext_stenciltwoside; +// used by shader for bouncegrid feature +extern rtexture_t *r_shadow_bouncegridtexture; +extern matrix4x4_t r_shadow_bouncegridmatrix; +extern vec_t r_shadow_bouncegridintensity; + void R_Shadow_Init(void); qboolean R_Shadow_ShadowMappingEnabled(void); void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, const vec3_t projectdirection, float projectdistance, int nummarktris, const int *marktris, vec3_t trismins, vec3_t trismaxs); diff --git a/r_textures.h b/r_textures.h index 8732ca2e..c89b27e8 100644 --- a/r_textures.h +++ b/r_textures.h @@ -142,7 +142,7 @@ void R_FreeTexture(rtexture_t *rt); // update a portion of the image data of a texture, used by lightmap updates // and procedural textures such as video playback, actual uploads may be // delayed by gl_nopartialtextureupdates cvar until R_Mesh_TexBind uses it -void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height); +void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int z, int width, int height, int depth); // returns the renderer dependent texture slot number (call this before each // use, as a texture might not have been precached)