X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=r_shadow.c;h=a5e9922585124fce072e32ddbcc5e01a709237a8;hb=9467a5d4d63a1d3587eff10afae41a853dea68f5;hp=49034d008a86eb6b2fcc5c8eb9cc31e5f287426c;hpb=0e6f1ee37a279e6224129b8eb4346592cc6b9885;p=xonotic%2Fdarkplaces.git diff --git a/r_shadow.c b/r_shadow.c index 49034d00..a5e99225 100644 --- a/r_shadow.c +++ b/r_shadow.c @@ -170,8 +170,8 @@ r_shadow_rendermode_t; typedef enum r_shadow_shadowmode_e { - R_SHADOW_SHADOWMODE_STENCIL, - R_SHADOW_SHADOWMODE_SHADOWMAP2D + R_SHADOW_SHADOWMODE_STENCIL, + R_SHADOW_SHADOWMODE_SHADOWMAP2D } r_shadow_shadowmode_t; @@ -196,12 +196,13 @@ int r_shadow_shadowmapdepthbits; int r_shadow_shadowmapmaxsize; qboolean r_shadow_shadowmapvsdct; qboolean r_shadow_shadowmapsampler; +qboolean r_shadow_shadowmapshadowsampler; int r_shadow_shadowmappcf; int r_shadow_shadowmapborder; matrix4x4_t r_shadow_shadowmapmatrix; int r_shadow_lightscissor[4]; qboolean r_shadow_usingdeferredprepass; - +qboolean r_shadow_shadowmapdepthtexture; int maxshadowtriangles; int *shadowelements; @@ -268,6 +269,9 @@ static rtexture_t *r_shadow_fb_colortexture; // lights are reloaded when this changes char r_shadow_mapname[MAX_QPATH]; +// buffer for doing corona fading +unsigned int r_shadow_occlusion_buf = 0; + // used only for light filters (cubemaps) rtexturepool_t *r_shadow_filters_texturepool; @@ -294,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"}; @@ -303,6 +308,7 @@ cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_wor cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"}; cvar_t r_shadow_shadowmapping = {CVAR_SAVE, "r_shadow_shadowmapping", "1", "enables use of shadowmapping (depth texture sampling) instead of stencil shadow volumes, requires gl_fbo 1"}; cvar_t r_shadow_shadowmapping_filterquality = {CVAR_SAVE, "r_shadow_shadowmapping_filterquality", "-1", "shadowmap filter modes: -1 = auto-select, 0 = no filtering, 1 = bilinear, 2 = bilinear 2x2 blur (fast), 3 = 3x3 blur (moderate), 4 = 4x4 blur (slow)"}; +cvar_t r_shadow_shadowmapping_useshadowsampler = {CVAR_SAVE, "r_shadow_shadowmapping_useshadowsampler", "1", "whether to use sampler2DShadow if available"}; cvar_t r_shadow_shadowmapping_depthbits = {CVAR_SAVE, "r_shadow_shadowmapping_depthbits", "24", "requested minimum shadowmap texture depth bits"}; cvar_t r_shadow_shadowmapping_vsdct = {CVAR_SAVE, "r_shadow_shadowmapping_vsdct", "1", "enables use of virtual shadow depth cube texture"}; cvar_t r_shadow_shadowmapping_minsize = {CVAR_SAVE, "r_shadow_shadowmapping_minsize", "32", "shadowmap size limit"}; @@ -320,31 +326,37 @@ 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"}; -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 = {CVAR_SAVE, "r_coronas", "0", "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)"}; +cvar_t r_coronas_occlusionquery = {CVAR_SAVE, "r_coronas_occlusionquery", "0", "use GL_ARB_occlusion_query extension if supported (fades coronas according to visibility) - bad performance (synchronous rendering) - worse on multi-gpu!"}; cvar_t gl_flashblend = {CVAR_SAVE, "gl_flashblend", "0", "render bright coronas for dynamic lights instead of actual lighting, fast but ugly"}; cvar_t gl_ext_separatestencil = {0, "gl_ext_separatestencil", "1", "make use of OpenGL 2.0 glStencilOpSeparate or GL_ATI_separate_stencil extension"}; cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"}; @@ -354,35 +366,23 @@ cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "ho cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"}; 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"}; - -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; +cvar_t r_editlights_drawproperties = {0, "r_editlights_drawproperties", "1", "draw properties of currently selected light"}; +cvar_t r_editlights_current_origin = {0, "r_editlights_current_origin", "0 0 0", "origin of selected light"}; +cvar_t r_editlights_current_angles = {0, "r_editlights_current_angles", "0 0 0", "angles of selected light"}; +cvar_t r_editlights_current_color = {0, "r_editlights_current_color", "1 1 1", "color of selected light"}; +cvar_t r_editlights_current_radius = {0, "r_editlights_current_radius", "0", "radius of selected light"}; +cvar_t r_editlights_current_corona = {0, "r_editlights_current_corona", "0", "corona intensity of selected light"}; +cvar_t r_editlights_current_coronasize = {0, "r_editlights_current_coronasize", "0", "corona size of selected light"}; +cvar_t r_editlights_current_style = {0, "r_editlights_current_style", "0", "style of selected light"}; +cvar_t r_editlights_current_shadows = {0, "r_editlights_current_shadows", "0", "shadows flag of selected light"}; +cvar_t r_editlights_current_cubemap = {0, "r_editlights_current_cubemap", "0", "cubemap of selected light"}; +cvar_t r_editlights_current_ambient = {0, "r_editlights_current_ambient", "0", "ambient intensity of selected light"}; +cvar_t r_editlights_current_diffuse = {0, "r_editlights_current_diffuse", "1", "diffuse intensity of selected light"}; +cvar_t r_editlights_current_specular = {0, "r_editlights_current_specular", "1", "specular intensity of selected light"}; +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"}; + +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 @@ -427,12 +427,14 @@ static void R_Shadow_SetShadowMode(void) r_shadow_shadowmapmaxsize = bound(1, r_shadow_shadowmapping_maxsize.integer, (int)vid.maxtexturesize_2d / 4); r_shadow_shadowmapvsdct = r_shadow_shadowmapping_vsdct.integer != 0 && vid.renderpath == RENDERPATH_GL20; r_shadow_shadowmapfilterquality = r_shadow_shadowmapping_filterquality.integer; + r_shadow_shadowmapshadowsampler = r_shadow_shadowmapping_useshadowsampler.integer != 0; r_shadow_shadowmapdepthbits = r_shadow_shadowmapping_depthbits.integer; r_shadow_shadowmapborder = bound(0, r_shadow_shadowmapping_bordersize.integer, 16); r_shadow_shadowmaplod = -1; r_shadow_shadowmapsize = 0; r_shadow_shadowmapsampler = false; r_shadow_shadowmappcf = 0; + r_shadow_shadowmapdepthtexture = r_fb.usedepthtextures; r_shadow_shadowmode = R_SHADOW_SHADOWMODE_STENCIL; if ((r_shadow_shadowmapping.integer || r_shadow_deferred.integer) && vid.support.ext_framebuffer_object) { @@ -443,27 +445,26 @@ static void R_Shadow_SetShadowMode(void) { if (!r_fb.usedepthtextures) r_shadow_shadowmappcf = 1; - else if(vid.support.amd_texture_texture4 || vid.support.arb_texture_gather) - r_shadow_shadowmappcf = 1; - else if(strstr(gl_vendor, "NVIDIA") || strstr(gl_renderer, "Radeon HD")) + else if((strstr(gl_vendor, "NVIDIA") || strstr(gl_renderer, "Radeon HD")) && vid.support.arb_shadow && r_shadow_shadowmapshadowsampler) { - r_shadow_shadowmapsampler = vid.support.arb_shadow; + r_shadow_shadowmapsampler = true; r_shadow_shadowmappcf = 1; } - else if(strstr(gl_vendor, "ATI")) + else if(vid.support.amd_texture_texture4 || vid.support.arb_texture_gather) + r_shadow_shadowmappcf = 1; + else if((strstr(gl_vendor, "ATI") || strstr(gl_vendor, "Advanced Micro Devices")) && !strstr(gl_renderer, "Mesa") && !strstr(gl_version, "Mesa")) r_shadow_shadowmappcf = 1; else - r_shadow_shadowmapsampler = vid.support.arb_shadow; + r_shadow_shadowmapsampler = vid.support.arb_shadow && r_shadow_shadowmapshadowsampler; } else { + r_shadow_shadowmapsampler = vid.support.arb_shadow && r_shadow_shadowmapshadowsampler; switch (r_shadow_shadowmapfilterquality) { case 1: - r_shadow_shadowmapsampler = vid.support.arb_shadow; break; case 2: - r_shadow_shadowmapsampler = vid.support.arb_shadow; r_shadow_shadowmappcf = 1; break; case 3: @@ -493,6 +494,9 @@ static void R_Shadow_SetShadowMode(void) break; } } + + if(R_CompileShader_CheckStaticParms()) + R_GLSL_Restart_f(); } qboolean R_Shadow_ShadowMappingEnabled(void) @@ -530,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; @@ -590,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); @@ -606,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; @@ -678,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); @@ -710,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); @@ -725,6 +746,7 @@ void R_Shadow_Init(void) Cvar_RegisterVariable(&r_shadow_shadowmapping); Cvar_RegisterVariable(&r_shadow_shadowmapping_vsdct); Cvar_RegisterVariable(&r_shadow_shadowmapping_filterquality); + Cvar_RegisterVariable(&r_shadow_shadowmapping_useshadowsampler); Cvar_RegisterVariable(&r_shadow_shadowmapping_depthbits); Cvar_RegisterVariable(&r_shadow_shadowmapping_precision); Cvar_RegisterVariable(&r_shadow_shadowmapping_maxsize); @@ -741,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); @@ -917,10 +945,10 @@ void R_Shadow_PrepareShadowMark(int numtris) void R_Shadow_PrepareShadowSides(int numtris) { - if (maxshadowsides < numtris) - { - maxshadowsides = numtris; - if (shadowsides) + if (maxshadowsides < numtris) + { + maxshadowsides = numtris; + if (shadowsides) Mem_Free(shadowsides); if (shadowsideslist) Mem_Free(shadowsideslist); @@ -1277,7 +1305,7 @@ void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *inv v[2] = invertex3f + e[2] * 3; TriangleNormal(v[0], v[1], v[2], normal); if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0) - && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs)) + && TriangleBBoxOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs)) shadowmarklist[numshadowmark++] = t; } } @@ -1289,7 +1317,7 @@ void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *inv v[1] = invertex3f + e[1] * 3; v[2] = invertex3f + e[2] * 3; if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2]) - && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs)) + && TriangleBBoxOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs)) shadowmarklist[numshadowmark++] = t; } } @@ -1361,7 +1389,7 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_VISIBLEVOLUMES) { tris = R_Shadow_ConstructShadowVolume_ZFail(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris); - R_Mesh_PrepareVertices_Vertex3f(outverts, shadowvertex3f, NULL); + R_Mesh_PrepareVertices_Vertex3f(outverts, shadowvertex3f, NULL, 0); R_Mesh_Draw(0, outverts, 0, tris, shadowelements, NULL, 0, NULL, NULL, 0); } else @@ -1373,8 +1401,8 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, tris = R_Shadow_ConstructShadowVolume_ZPass(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris); else tris = R_Shadow_ConstructShadowVolume_ZFail(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris); - r_refdef.stats.lights_dynamicshadowtriangles += tris; - r_refdef.stats.lights_shadowtriangles += tris; + r_refdef.stats[r_stat_lights_dynamicshadowtriangles] += tris; + r_refdef.stats[r_stat_lights_shadowtriangles] += tris; if (r_shadow_rendermode == R_SHADOW_RENDERMODE_ZPASS_STENCIL) { // increment stencil if frontface is infront of depthbuffer @@ -1395,58 +1423,58 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, GL_CullFace(r_refdef.view.cullface_back); R_SetStencil(true, 255, GL_KEEP, GL_INCR, GL_KEEP, GL_ALWAYS, 128, 255); } - R_Mesh_PrepareVertices_Vertex3f(outverts, shadowvertex3f, NULL); + R_Mesh_PrepareVertices_Vertex3f(outverts, shadowvertex3f, NULL, 0); R_Mesh_Draw(0, outverts, 0, tris, shadowelements, NULL, 0, NULL, NULL, 0); } } int R_Shadow_CalcTriangleSideMask(const vec3_t p1, const vec3_t p2, const vec3_t p3, float bias) { - // p1, p2, p3 are in the cubemap's local coordinate system - // bias = border/(size - border) + // p1, p2, p3 are in the cubemap's local coordinate system + // bias = border/(size - border) int mask = 0x3F; - float dp1 = p1[0] + p1[1], dn1 = p1[0] - p1[1], ap1 = fabs(dp1), an1 = fabs(dn1), - dp2 = p2[0] + p2[1], dn2 = p2[0] - p2[1], ap2 = fabs(dp2), an2 = fabs(dn2), - dp3 = p3[0] + p3[1], dn3 = p3[0] - p3[1], ap3 = fabs(dp3), an3 = fabs(dn3); + float dp1 = p1[0] + p1[1], dn1 = p1[0] - p1[1], ap1 = fabs(dp1), an1 = fabs(dn1), + dp2 = p2[0] + p2[1], dn2 = p2[0] - p2[1], ap2 = fabs(dp2), an2 = fabs(dn2), + dp3 = p3[0] + p3[1], dn3 = p3[0] - p3[1], ap3 = fabs(dp3), an3 = fabs(dn3); if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3) - mask &= (3<<4) + mask &= (3<<4) | (dp1 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2)) | (dp2 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2)) | (dp3 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2)); - if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3) - mask &= (3<<4) - | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)) - | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)) - | (dn3 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)); - - dp1 = p1[1] + p1[2], dn1 = p1[1] - p1[2], ap1 = fabs(dp1), an1 = fabs(dn1), - dp2 = p2[1] + p2[2], dn2 = p2[1] - p2[2], ap2 = fabs(dp2), an2 = fabs(dn2), - dp3 = p3[1] + p3[2], dn3 = p3[1] - p3[2], ap3 = fabs(dp3), an3 = fabs(dn3); - if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3) - mask &= (3<<0) - | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)) - | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)) - | (dp3 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)); - if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3) - mask &= (3<<0) - | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)) - | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)) - | (dn3 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)); - - dp1 = p1[2] + p1[0], dn1 = p1[2] - p1[0], ap1 = fabs(dp1), an1 = fabs(dn1), - dp2 = p2[2] + p2[0], dn2 = p2[2] - p2[0], ap2 = fabs(dp2), an2 = fabs(dn2), - dp3 = p3[2] + p3[0], dn3 = p3[2] - p3[0], ap3 = fabs(dp3), an3 = fabs(dn3); - if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3) - mask &= (3<<2) - | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)) - | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)) - | (dp3 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)); - if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3) - mask &= (3<<2) - | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)) - | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)) - | (dn3 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)); + if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3) + mask &= (3<<4) + | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)) + | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)) + | (dn3 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)); + + dp1 = p1[1] + p1[2], dn1 = p1[1] - p1[2], ap1 = fabs(dp1), an1 = fabs(dn1), + dp2 = p2[1] + p2[2], dn2 = p2[1] - p2[2], ap2 = fabs(dp2), an2 = fabs(dn2), + dp3 = p3[1] + p3[2], dn3 = p3[1] - p3[2], ap3 = fabs(dp3), an3 = fabs(dn3); + if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3) + mask &= (3<<0) + | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)) + | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)) + | (dp3 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)); + if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3) + mask &= (3<<0) + | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)) + | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)) + | (dn3 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)); + + dp1 = p1[2] + p1[0], dn1 = p1[2] - p1[0], ap1 = fabs(dp1), an1 = fabs(dn1), + dp2 = p2[2] + p2[0], dn2 = p2[2] - p2[0], ap2 = fabs(dp2), an2 = fabs(dn2), + dp3 = p3[2] + p3[0], dn3 = p3[2] - p3[0], ap3 = fabs(dp3), an3 = fabs(dn3); + if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3) + mask &= (3<<2) + | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)) + | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)) + | (dp3 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)); + if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3) + mask &= (3<<2) + | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)) + | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)) + | (dn3 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)); return mask; } @@ -1458,80 +1486,81 @@ static int R_Shadow_CalcBBoxSideMask(const vec3_t mins, const vec3_t maxs, const int mask = 0x3F; VectorSubtract(maxs, mins, radius); - VectorScale(radius, 0.5f, radius); - VectorAdd(mins, radius, center); - Matrix4x4_Transform(worldtolight, center, lightcenter); + VectorScale(radius, 0.5f, radius); + VectorAdd(mins, radius, center); + Matrix4x4_Transform(worldtolight, center, lightcenter); Matrix4x4_Transform3x3(radiustolight, radius, lightradius); VectorSubtract(lightcenter, lightradius, pmin); VectorAdd(lightcenter, lightradius, pmax); - dp1 = pmax[0] + pmax[1], dn1 = pmax[0] - pmin[1], ap1 = fabs(dp1), an1 = fabs(dn1), - dp2 = pmin[0] + pmin[1], dn2 = pmin[0] - pmax[1], ap2 = fabs(dp2), an2 = fabs(dn2); - if(ap1 > bias*an1 && ap2 > bias*an2) - mask &= (3<<4) - | (dp1 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2)) - | (dp2 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2)); - if(an1 > bias*ap1 && an2 > bias*ap2) - mask &= (3<<4) - | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)) - | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)); - - dp1 = pmax[1] + pmax[2], dn1 = pmax[1] - pmin[2], ap1 = fabs(dp1), an1 = fabs(dn1), - dp2 = pmin[1] + pmin[2], dn2 = pmin[1] - pmax[2], ap2 = fabs(dp2), an2 = fabs(dn2); - if(ap1 > bias*an1 && ap2 > bias*an2) - mask &= (3<<0) - | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)) - | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)); - if(an1 > bias*ap1 && an2 > bias*ap2) - mask &= (3<<0) - | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)) - | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)); - - dp1 = pmax[2] + pmax[0], dn1 = pmax[2] - pmin[0], ap1 = fabs(dp1), an1 = fabs(dn1), - dp2 = pmin[2] + pmin[0], dn2 = pmin[2] - pmax[0], ap2 = fabs(dp2), an2 = fabs(dn2); - if(ap1 > bias*an1 && ap2 > bias*an2) - mask &= (3<<2) - | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)) - | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)); - if(an1 > bias*ap1 && an2 > bias*ap2) - mask &= (3<<2) - | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)) - | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)); - - return mask; + dp1 = pmax[0] + pmax[1], dn1 = pmax[0] - pmin[1], ap1 = fabs(dp1), an1 = fabs(dn1), + dp2 = pmin[0] + pmin[1], dn2 = pmin[0] - pmax[1], ap2 = fabs(dp2), an2 = fabs(dn2); + if(ap1 > bias*an1 && ap2 > bias*an2) + mask &= (3<<4) + | (dp1 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2)) + | (dp2 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2)); + if(an1 > bias*ap1 && an2 > bias*ap2) + mask &= (3<<4) + | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)) + | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)); + + dp1 = pmax[1] + pmax[2], dn1 = pmax[1] - pmin[2], ap1 = fabs(dp1), an1 = fabs(dn1), + dp2 = pmin[1] + pmin[2], dn2 = pmin[1] - pmax[2], ap2 = fabs(dp2), an2 = fabs(dn2); + if(ap1 > bias*an1 && ap2 > bias*an2) + mask &= (3<<0) + | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)) + | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)); + if(an1 > bias*ap1 && an2 > bias*ap2) + mask &= (3<<0) + | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)) + | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)); + + dp1 = pmax[2] + pmax[0], dn1 = pmax[2] - pmin[0], ap1 = fabs(dp1), an1 = fabs(dn1), + dp2 = pmin[2] + pmin[0], dn2 = pmin[2] - pmax[0], ap2 = fabs(dp2), an2 = fabs(dn2); + if(ap1 > bias*an1 && ap2 > bias*an2) + mask &= (3<<2) + | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)) + | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)); + if(an1 > bias*ap1 && an2 > bias*ap2) + mask &= (3<<2) + | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)) + | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)); + + return mask; } #define R_Shadow_CalcEntitySideMask(ent, worldtolight, radiustolight, bias) R_Shadow_CalcBBoxSideMask((ent)->mins, (ent)->maxs, worldtolight, radiustolight, bias) int R_Shadow_CalcSphereSideMask(const vec3_t p, float radius, float bias) { - // p is in the cubemap's local coordinate system - // bias = border/(size - border) - float dxyp = p[0] + p[1], dxyn = p[0] - p[1], axyp = fabs(dxyp), axyn = fabs(dxyn); - float dyzp = p[1] + p[2], dyzn = p[1] - p[2], ayzp = fabs(dyzp), ayzn = fabs(dyzn); - float dzxp = p[2] + p[0], dzxn = p[2] - p[0], azxp = fabs(dzxp), azxn = fabs(dzxn); - int mask = 0x3F; - if(axyp > bias*axyn + radius) mask &= dxyp < 0 ? ~((1<<0)|(1<<2)) : ~((2<<0)|(2<<2)); - if(axyn > bias*axyp + radius) mask &= dxyn < 0 ? ~((1<<0)|(2<<2)) : ~((2<<0)|(1<<2)); - if(ayzp > bias*ayzn + radius) mask &= dyzp < 0 ? ~((1<<2)|(1<<4)) : ~((2<<2)|(2<<4)); - if(ayzn > bias*ayzp + radius) mask &= dyzn < 0 ? ~((1<<2)|(2<<4)) : ~((2<<2)|(1<<4)); - if(azxp > bias*azxn + radius) mask &= dzxp < 0 ? ~((1<<4)|(1<<0)) : ~((2<<4)|(2<<0)); - if(azxn > bias*azxp + radius) mask &= dzxn < 0 ? ~((1<<4)|(2<<0)) : ~((2<<4)|(1<<0)); - return mask; + // p is in the cubemap's local coordinate system + // bias = border/(size - border) + float dxyp = p[0] + p[1], dxyn = p[0] - p[1], axyp = fabs(dxyp), axyn = fabs(dxyn); + float dyzp = p[1] + p[2], dyzn = p[1] - p[2], ayzp = fabs(dyzp), ayzn = fabs(dyzn); + float dzxp = p[2] + p[0], dzxn = p[2] - p[0], azxp = fabs(dzxp), azxn = fabs(dzxn); + int mask = 0x3F; + if(axyp > bias*axyn + radius) mask &= dxyp < 0 ? ~((1<<0)|(1<<2)) : ~((2<<0)|(2<<2)); + if(axyn > bias*axyp + radius) mask &= dxyn < 0 ? ~((1<<0)|(2<<2)) : ~((2<<0)|(1<<2)); + if(ayzp > bias*ayzn + radius) mask &= dyzp < 0 ? ~((1<<2)|(1<<4)) : ~((2<<2)|(2<<4)); + if(ayzn > bias*ayzp + radius) mask &= dyzn < 0 ? ~((1<<2)|(2<<4)) : ~((2<<2)|(1<<4)); + if(azxp > bias*azxn + radius) mask &= dzxp < 0 ? ~((1<<4)|(1<<0)) : ~((2<<4)|(2<<0)); + if(azxn > bias*azxp + radius) mask &= dzxn < 0 ? ~((1<<4)|(2<<0)) : ~((2<<4)|(1<<0)); + return mask; } static int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float border) { int i; - vec3_t p, n; + vec3_t o, p, n; int sides = 0x3F, masks[6] = { 3<<4, 3<<4, 3<<0, 3<<0, 3<<2, 3<<2 }; float scale = (size - 2*border)/size, len; float bias = border / (float)(size - border), dp, dn, ap, an; // check if cone enclosing side would cross frustum plane scale = 2 / (scale*scale + 2); + Matrix4x4_OriginFromMatrix(&rtlight->matrix_lighttoworld, o); for (i = 0;i < 5;i++) { - if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[i]) > -0.03125) + if (PlaneDiff(o, &r_refdef.view.frustum[i]) > -0.03125) continue; Matrix4x4_Transform3x3(&rtlight->matrix_worldtolight, r_refdef.view.frustum[i].normal, n); len = scale*VectorLength2(n); @@ -1539,10 +1568,10 @@ static int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float borde if(n[1]*n[1] > len) sides &= n[1] < 0 ? ~(1<<2) : ~(2 << 2); if(n[2]*n[2] > len) sides &= n[2] < 0 ? ~(1<<4) : ~(2 << 4); } - if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[4]) >= r_refdef.farclip - r_refdef.nearclip + 0.03125) + if (PlaneDiff(o, &r_refdef.view.frustum[4]) >= r_refdef.farclip - r_refdef.nearclip + 0.03125) { - Matrix4x4_Transform3x3(&rtlight->matrix_worldtolight, r_refdef.view.frustum[4].normal, n); - len = scale*VectorLength(n); + Matrix4x4_Transform3x3(&rtlight->matrix_worldtolight, r_refdef.view.frustum[4].normal, n); + len = scale*VectorLength2(n); if(n[0]*n[0] > len) sides &= n[0] >= 0 ? ~(1<<0) : ~(2 << 0); if(n[1]*n[1] > len) sides &= n[1] >= 0 ? ~(1<<2) : ~(2 << 2); if(n[2]*n[2] > len) sides &= n[2] >= 0 ? ~(1<<4) : ~(2 << 4); @@ -1550,33 +1579,33 @@ static int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float borde // this next test usually clips off more sides than the former, but occasionally clips fewer/different ones, so do both and combine results // check if frustum corners/origin cross plane sides #if 1 - // infinite version, assumes frustum corners merely give direction and extend to infinite distance - Matrix4x4_Transform(&rtlight->matrix_worldtolight, r_refdef.view.origin, p); - dp = p[0] + p[1], dn = p[0] - p[1], ap = fabs(dp), an = fabs(dn); - masks[0] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2)); - masks[1] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)); - dp = p[1] + p[2], dn = p[1] - p[2], ap = fabs(dp), an = fabs(dn); - masks[2] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)); - masks[3] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)); - dp = p[2] + p[0], dn = p[2] - p[0], ap = fabs(dp), an = fabs(dn); - masks[4] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)); - masks[5] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)); - for (i = 0;i < 4;i++) - { - Matrix4x4_Transform(&rtlight->matrix_worldtolight, r_refdef.view.frustumcorner[i], n); - VectorSubtract(n, p, n); - dp = n[0] + n[1], dn = n[0] - n[1], ap = fabs(dp), an = fabs(dn); - if(ap > 0) masks[0] |= dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2); - if(an > 0) masks[1] |= dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2); - dp = n[1] + n[2], dn = n[1] - n[2], ap = fabs(dp), an = fabs(dn); - if(ap > 0) masks[2] |= dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4); - if(an > 0) masks[3] |= dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4); - dp = n[2] + n[0], dn = n[2] - n[0], ap = fabs(dp), an = fabs(dn); - if(ap > 0) masks[4] |= dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0); - if(an > 0) masks[5] |= dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0); - } + // infinite version, assumes frustum corners merely give direction and extend to infinite distance + Matrix4x4_Transform(&rtlight->matrix_worldtolight, r_refdef.view.origin, p); + dp = p[0] + p[1], dn = p[0] - p[1], ap = fabs(dp), an = fabs(dn); + masks[0] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2)); + masks[1] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2)); + dp = p[1] + p[2], dn = p[1] - p[2], ap = fabs(dp), an = fabs(dn); + masks[2] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4)); + masks[3] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4)); + dp = p[2] + p[0], dn = p[2] - p[0], ap = fabs(dp), an = fabs(dn); + masks[4] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0)); + masks[5] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0)); + for (i = 0;i < 4;i++) + { + Matrix4x4_Transform(&rtlight->matrix_worldtolight, r_refdef.view.frustumcorner[i], n); + VectorSubtract(n, p, n); + dp = n[0] + n[1], dn = n[0] - n[1], ap = fabs(dp), an = fabs(dn); + if(ap > 0) masks[0] |= dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2); + if(an > 0) masks[1] |= dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2); + dp = n[1] + n[2], dn = n[1] - n[2], ap = fabs(dp), an = fabs(dn); + if(ap > 0) masks[2] |= dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4); + if(an > 0) masks[3] |= dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4); + dp = n[2] + n[0], dn = n[2] - n[0], ap = fabs(dp), an = fabs(dn); + if(ap > 0) masks[4] |= dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0); + if(an > 0) masks[5] |= dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0); + } #else - // finite version, assumes corners are a finite distance from origin dependent on far plane + // finite version, assumes corners are a finite distance from origin dependent on far plane for (i = 0;i < 5;i++) { Matrix4x4_Transform(&rtlight->matrix_worldtolight, !i ? r_refdef.view.origin : r_refdef.view.frustumcorner[i-1], p); @@ -1660,7 +1689,7 @@ int R_Shadow_ChooseSidesFromBox(int firsttriangle, int numtris, const float *inv v[0] = invertex3f + e[0] * 3, v[1] = invertex3f + e[1] * 3, v[2] = invertex3f + e[2] * 3; TriangleNormal(v[0], v[1], v[2], normal); if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0) - && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs)) + && TriangleBBoxOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs)) { Matrix4x4_Transform(worldtolight, v[0], p[0]), Matrix4x4_Transform(worldtolight, v[1], p[1]), Matrix4x4_Transform(worldtolight, v[2], p[2]); mask = R_Shadow_CalcTriangleSideMask(p[0], p[1], p[2], bias); @@ -1680,7 +1709,7 @@ int R_Shadow_ChooseSidesFromBox(int firsttriangle, int numtris, const float *inv { v[0] = invertex3f + e[0] * 3, v[1] = invertex3f + e[1] * 3, v[2] = invertex3f + e[2] * 3; if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2]) - && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs)) + && TriangleBBoxOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs)) { Matrix4x4_Transform(worldtolight, v[0], p[0]), Matrix4x4_Transform(worldtolight, v[1], p[1]), Matrix4x4_Transform(worldtolight, v[2], p[2]); mask = R_Shadow_CalcTriangleSideMask(p[0], p[1], p[2], bias); @@ -2039,7 +2068,7 @@ void R_Shadow_RenderMode_Reset(void) void R_Shadow_ClearStencil(void) { GL_Clear(GL_STENCIL_BUFFER_BIT, NULL, 1.0f, 128); - r_refdef.stats.lights_clears++; + r_refdef.stats[r_stat_lights_clears]++; } void R_Shadow_RenderMode_StencilShadowVolumes(qboolean zpass) @@ -2052,7 +2081,7 @@ void R_Shadow_RenderMode_StencilShadowVolumes(qboolean zpass) GL_ColorMask(0, 0, 0, 0); GL_PolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR GL_CullFace(GL_NONE); - R_SetupShader_DepthOrShadow(false, false); + R_SetupShader_DepthOrShadow(false, false, false); // FIXME test if we have a skeletal model? r_shadow_rendermode = mode; switch(mode) { @@ -2097,7 +2126,7 @@ static void R_Shadow_MakeShadowMap(int side, int size) if (r_shadow_shadowmap2ddepthtexture) return; if (r_fb.usedepthtextures) { - r_shadow_shadowmap2ddepthtexture = R_LoadTextureShadowMap2D(r_shadow_texturepool, "shadowmap", size*2, size*(vid.support.arb_texture_non_power_of_two ? 3 : 4), r_shadow_shadowmapdepthbits >= 24 ? (r_shadow_shadowmapsampler ? TEXTYPE_SHADOWMAP24_COMP : TEXTYPE_SHADOWMAP24_RAW) : (r_shadow_shadowmapsampler ? TEXTYPE_SHADOWMAP16_COMP : TEXTYPE_SHADOWMAP16_RAW), false); + r_shadow_shadowmap2ddepthtexture = R_LoadTextureShadowMap2D(r_shadow_texturepool, "shadowmap", size*2, size*(vid.support.arb_texture_non_power_of_two ? 3 : 4), r_shadow_shadowmapdepthbits >= 24 ? (r_shadow_shadowmapsampler ? TEXTYPE_SHADOWMAP24_COMP : TEXTYPE_SHADOWMAP24_RAW) : (r_shadow_shadowmapsampler ? TEXTYPE_SHADOWMAP16_COMP : TEXTYPE_SHADOWMAP16_RAW), r_shadow_shadowmapsampler); r_shadow_shadowmap2ddepthbuffer = NULL; r_shadow_fbo2d = R_Mesh_CreateFramebufferObject(r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL, NULL); } @@ -2149,7 +2178,7 @@ static void R_Shadow_RenderMode_ShadowMap(int side, int clear, int size) R_Mesh_SetRenderTargets(fbo2d, r_shadow_shadowmap2ddepthbuffer, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL); else R_Mesh_SetRenderTargets(fbo2d, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL, NULL); - R_SetupShader_DepthOrShadow(true, r_shadow_shadowmap2ddepthbuffer != NULL); + R_SetupShader_DepthOrShadow(true, r_shadow_shadowmap2ddepthbuffer != NULL, false); // FIXME test if we have a skeletal model? GL_PolygonOffset(r_shadow_shadowmapping_polygonfactor.value, r_shadow_shadowmapping_polygonoffset.value); GL_DepthMask(true); GL_DepthTest(true); @@ -2297,104 +2326,123 @@ void R_Shadow_RenderMode_DrawDeferredLight(qboolean stenciltest, qboolean shadow GL_DepthTest(true); GL_DepthFunc(GL_GREATER); GL_CullFace(r_refdef.view.cullface_back); - R_Mesh_PrepareVertices_Vertex3f(8, vertex3f, NULL); + R_Mesh_PrepareVertices_Vertex3f(8, vertex3f, NULL, 0); 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) { enable = false; - range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked + range = (unsigned int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked for (lightindex = 0;lightindex < range;lightindex++) { light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); @@ -2412,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); @@ -2494,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; @@ -2528,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; - - // store the new settings - r_shadow_bouncegridsettings = settings; - - pixelbands = settings.directionalshading ? 8 : 1; - pixelsperband = resolution[0]*resolution[1]*resolution[2]; - numpixels = pixelsperband*pixelbands; + // 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); - // 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 = 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) @@ -2597,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) @@ -2611,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 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1); + 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 @@ -2639,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) @@ -2656,22 +3068,18 @@ 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); - r_refdef.stats.bouncegrid_lights++; - r_refdef.stats.bouncegrid_particles += shootparticles; + r_refdef.stats[r_stat_bouncegrid_lights]++; + r_refdef.stats[r_stat_bouncegrid_particles] += shootparticles; for (shotparticles = 0;shotparticles < shootparticles;shotparticles++) { if (settings.stablerandom > 0) @@ -2685,13 +3093,13 @@ void R_Shadow_UpdateBounceGridTexture(void) VectorMA(clipstart, radius, clipend, clipend); for (bouncecount = 0;;bouncecount++) { - r_refdef.stats.bouncegrid_traces++; + r_refdef.stats[r_stat_bouncegrid_traces]++; //r_refdef.scene.worldmodel->TraceLineAgainstSurfaces(r_refdef.scene.worldmodel, NULL, NULL, &cliptrace, clipstart, clipend, hitsupercontentsmask); //r_refdef.scene.worldmodel->TraceLine(r_refdef.scene.worldmodel, NULL, NULL, &cliptrace2, clipstart, clipend, hitsupercontentsmask); if (settings.staticmode) { // static mode fires a LOT of rays but none of them are identical, so they are not cached - cliptrace = CL_TraceLine(clipstart, clipend, settings.staticmode ? MOVE_WORLDONLY : (settings.hitmodels ? MOVE_HITMODEL : MOVE_NOMONSTERS), NULL, hitsupercontentsmask, true, false, NULL, true, true); + cliptrace = CL_TraceLine(clipstart, clipend, settings.staticmode ? MOVE_WORLDONLY : (settings.hitmodels ? MOVE_HITMODEL : MOVE_NOMONSTERS), NULL, hitsupercontentsmask, collision_extendmovelength.value, true, false, NULL, true, true); } else { @@ -2700,112 +3108,13 @@ 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.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; - r_refdef.stats.bouncegrid_hits++; + r_refdef.stats[r_stat_bouncegrid_hits]++; if (bouncecount >= maxbounce) break; // scale down shot color by bounce intensity and texture color (or 50% if no texture reported) @@ -2821,7 +3130,7 @@ void R_Shadow_UpdateBounceGridTexture(void) VectorMultiply(shotcolor, surfcolor, shotcolor); if (VectorLength2(baseshotcolor) == 0.0f) break; - r_refdef.stats.bouncegrid_bounces++; + r_refdef.stats[r_stat_bouncegrid_bounces]++; if (settings.bounceanglediffuse) { // random direction, primarily along plane normal @@ -2846,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) @@ -2967,7 +3304,7 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs) || r_shadow_lightscissor[1] != r_refdef.view.viewport.y || r_shadow_lightscissor[2] != r_refdef.view.viewport.width || r_shadow_lightscissor[3] != r_refdef.view.viewport.height) - r_refdef.stats.lights_scissored++; + r_refdef.stats[r_stat_lights_scissored]++; return false; } @@ -3503,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++; @@ -3740,11 +4077,8 @@ static void R_Shadow_DrawWorldShadow_ShadowMap(int numsurfaces, int *surfacelist { if (!mesh->sidetotals[r_shadow_shadowmapside]) continue; - r_refdef.stats.lights_shadowtriangles += mesh->sidetotals[r_shadow_shadowmapside]; - if (mesh->vertex3fbuffer) - R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vertex3fbuffer); - else - R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vbo_vertexbuffer); + r_refdef.stats[r_stat_lights_shadowtriangles] += mesh->sidetotals[r_shadow_shadowmapside]; + R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vbo_vertexbuffer, mesh->vbooffset_vertex3f); R_Mesh_Draw(0, mesh->numverts, mesh->sideoffsets[r_shadow_shadowmapside], mesh->sidetotals[r_shadow_shadowmapside], mesh->element3i, mesh->element3i_indexbuffer, mesh->element3i_bufferoffset, mesh->element3s, mesh->element3s_indexbuffer, mesh->element3s_bufferoffset); } CHECKGLERROR @@ -3780,11 +4114,8 @@ static void R_Shadow_DrawWorldShadow_ShadowVolume(int numsurfaces, int *surfacel mesh = zpass ? rsurface.rtlight->static_meshchain_shadow_zpass : rsurface.rtlight->static_meshchain_shadow_zfail; for (;mesh;mesh = mesh->next) { - r_refdef.stats.lights_shadowtriangles += mesh->numtriangles; - if (mesh->vertex3fbuffer) - R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vertex3fbuffer); - else - R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vbo_vertexbuffer); + r_refdef.stats[r_stat_lights_shadowtriangles] += mesh->numtriangles; + R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vbo_vertexbuffer, mesh->vbooffset_vertex3f); if (r_shadow_rendermode == R_SHADOW_RENDERMODE_ZPASS_STENCIL) { // increment stencil if frontface is infront of depthbuffer @@ -3915,8 +4246,21 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight) static entity_render_t *shadowentities[MAX_EDICTS]; static entity_render_t *shadowentities_noselfshadow[MAX_EDICTS]; qboolean nolight; + qboolean castshadows; rtlight->draw = false; + rtlight->cached_numlightentities = 0; + rtlight->cached_numlightentities_noselfshadow = 0; + rtlight->cached_numshadowentities = 0; + rtlight->cached_numshadowentities_noselfshadow = 0; + rtlight->cached_numsurfaces = 0; + rtlight->cached_lightentities = NULL; + rtlight->cached_lightentities_noselfshadow = NULL; + rtlight->cached_shadowentities = NULL; + rtlight->cached_shadowentities_noselfshadow = NULL; + rtlight->cached_shadowtrispvs = NULL; + rtlight->cached_lighttrispvs = NULL; + rtlight->cached_surfacelist = NULL; // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights) // skip lights that are basically invisible (color 0 0 0) @@ -3937,7 +4281,7 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight) rtlight->currentcubemap = rtlight->cubemapname[0] ? R_GetCubemap(rtlight->cubemapname) : r_texture_whitecube; // look up the light style value at this time - f = (rtlight->style >= 0 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value; + f = ((rtlight->style >= 0 && rtlight->style < MAX_LIGHTSTYLES) ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value; VectorScale(rtlight->color, f, rtlight->currentcolor); /* if (rtlight->selected) @@ -3964,6 +4308,10 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight) R_Shadow_ComputeShadowCasterCullingPlanes(rtlight); + // don't allow lights to be drawn if using r_shadow_bouncegrid 2, except if we're using static bouncegrid where dynamic lights still need to draw + if (r_shadow_bouncegrid.integer == 2 && (rtlight->isstatic || !r_shadow_bouncegrid_static.integer)) + return; + if (rtlight->compiled && r_shadow_realtime_world_compile.integer) { // compiled light, world available and can receive realtime lighting @@ -4089,18 +4437,21 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight) return; // count this light in the r_speeds - r_refdef.stats.lights++; + r_refdef.stats[r_stat_lights]++; // flag it as worth drawing later rtlight->draw = true; + // if we have shadows disabled, don't count the shadow entities, this way we don't do the R_AnimCache_GetEntity on each one + castshadows = numsurfaces + numshadowentities + numshadowentities_noselfshadow > 0 && rtlight->shadow && (rtlight->isstatic ? r_refdef.scene.rtworldshadows : r_refdef.scene.rtdlightshadows); + if (!castshadows) + numshadowentities = numshadowentities_noselfshadow = 0; + // cache all the animated entities that cast a shadow but are not visible for (i = 0;i < numshadowentities;i++) - if (!shadowentities[i]->animcache_vertex3f) - R_AnimCache_GetEntity(shadowentities[i], false, false); + R_AnimCache_GetEntity(shadowentities[i], false, false); for (i = 0;i < numshadowentities_noselfshadow;i++) - if (!shadowentities_noselfshadow[i]->animcache_vertex3f) - R_AnimCache_GetEntity(shadowentities_noselfshadow[i], false, false); + R_AnimCache_GetEntity(shadowentities_noselfshadow[i], false, false); // allocate some temporary memory for rendering this light later in the frame // reusable buffers need to be copied, static data can be used as-is @@ -4491,8 +4842,10 @@ void R_Shadow_PrepareLights(int fbo, rtexture_t *depthtexture, rtexture_t *color (r_shadow_shadowmode != R_SHADOW_SHADOWMODE_STENCIL) != (r_shadow_shadowmapping.integer || r_shadow_deferred.integer) || r_shadow_shadowmapvsdct != (r_shadow_shadowmapping_vsdct.integer != 0 && vid.renderpath == RENDERPATH_GL20) || r_shadow_shadowmapfilterquality != r_shadow_shadowmapping_filterquality.integer || + r_shadow_shadowmapshadowsampler != (vid.support.arb_shadow && r_shadow_shadowmapping_useshadowsampler.integer) || r_shadow_shadowmapdepthbits != r_shadow_shadowmapping_depthbits.integer || - r_shadow_shadowmapborder != bound(0, r_shadow_shadowmapping_bordersize.integer, 16)) + r_shadow_shadowmapborder != bound(0, r_shadow_shadowmapping_bordersize.integer, 16) || + r_shadow_shadowmapdepthtexture != r_fb.usedepthtextures) R_Shadow_FreeShadowMaps(); r_shadow_fb_fbo = fbo; @@ -4526,9 +4879,9 @@ void R_Shadow_PrepareLights(int fbo, rtexture_t *depthtexture, rtexture_t *color r_shadow_prepass_width = vid.width; r_shadow_prepass_height = vid.height; r_shadow_prepassgeometrydepthbuffer = R_LoadTextureRenderBuffer(r_shadow_texturepool, "prepassgeometrydepthbuffer", vid.width, vid.height, TEXTYPE_DEPTHBUFFER24); - r_shadow_prepassgeometrynormalmaptexture = R_LoadTexture2D(r_shadow_texturepool, "prepassgeometrynormalmap", vid.width, vid.height, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL); - r_shadow_prepasslightingdiffusetexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingdiffuse", vid.width, vid.height, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL); - r_shadow_prepasslightingspeculartexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingspecular", vid.width, vid.height, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL); + r_shadow_prepassgeometrynormalmaptexture = R_LoadTexture2D(r_shadow_texturepool, "prepassgeometrynormalmap", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER32F, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL); + r_shadow_prepasslightingdiffusetexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingdiffuse", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER16F, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL); + r_shadow_prepasslightingspeculartexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingspecular", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER16F, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL); // set up the geometry pass fbo (depth + normalmap) r_shadow_prepassgeometryfbo = R_Mesh_CreateFramebufferObject(r_shadow_prepassgeometrydepthbuffer, r_shadow_prepassgeometrynormalmaptexture, NULL, NULL, NULL); @@ -4562,25 +4915,22 @@ void R_Shadow_PrepareLights(int fbo, rtexture_t *depthtexture, rtexture_t *color R_Shadow_EnlargeLeafSurfaceTrisBuffer(r_refdef.scene.worldmodel->brush.num_leafs, r_refdef.scene.worldmodel->num_surfaces, r_refdef.scene.worldmodel->brush.shadowmesh ? r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles : r_refdef.scene.worldmodel->surfmesh.num_triangles, r_refdef.scene.worldmodel->surfmesh.num_triangles); flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE; - if (r_shadow_bouncegrid.integer != 2) + if (r_shadow_debuglight.integer >= 0) + { + lightindex = r_shadow_debuglight.integer; + light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); + if (light) + R_Shadow_PrepareLight(&light->rtlight); + } + else { - if (r_shadow_debuglight.integer >= 0) + range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked + for (lightindex = 0;lightindex < range;lightindex++) { - lightindex = r_shadow_debuglight.integer; light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); - if (light) + if (light && (light->flags & flag)) R_Shadow_PrepareLight(&light->rtlight); } - else - { - range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked - for (lightindex = 0;lightindex < range;lightindex++) - { - light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); - if (light && (light->flags & flag)) - R_Shadow_PrepareLight(&light->rtlight); - } - } } if (r_refdef.scene.rtdlight) { @@ -4592,7 +4942,7 @@ void R_Shadow_PrepareLights(int fbo, rtexture_t *depthtexture, rtexture_t *color for (lnum = 0;lnum < r_refdef.scene.numlights;lnum++) { rtlight_t *rtlight = r_refdef.scene.lights[lnum]; - f = (rtlight->style >= 0 ? r_refdef.scene.lightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value; + f = ((rtlight->style >= 0 && rtlight->style < MAX_LIGHTSTYLES) ? r_refdef.scene.lightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value; VectorScale(rtlight->color, f, rtlight->currentcolor); } } @@ -4611,26 +4961,23 @@ void R_Shadow_DrawLights(void) R_Shadow_RenderMode_Begin(); - if (r_shadow_bouncegrid.integer != 2) + flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE; + if (r_shadow_debuglight.integer >= 0) { - flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE; - if (r_shadow_debuglight.integer >= 0) + lightindex = r_shadow_debuglight.integer; + light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); + if (light) + R_Shadow_DrawLight(&light->rtlight); + } + else + { + range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked + for (lightindex = 0;lightindex < range;lightindex++) { - lightindex = r_shadow_debuglight.integer; light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); - if (light) + if (light && (light->flags & flag)) R_Shadow_DrawLight(&light->rtlight); } - else - { - range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked - for (lightindex = 0;lightindex < range;lightindex++) - { - light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); - if (light && (light->flags & flag)) - R_Shadow_DrawLight(&light->rtlight); - } - } } if (r_refdef.scene.rtdlight) for (lnum = 0;lnum < r_refdef.scene.numlights;lnum++) @@ -4639,13 +4986,19 @@ void R_Shadow_DrawLights(void) R_Shadow_RenderMode_End(); } +#define MAX_MODELSHADOWS 1024 +static int r_shadow_nummodelshadows; +static entity_render_t *r_shadow_modelshadows[MAX_MODELSHADOWS]; + void R_Shadow_PrepareModelShadows(void) { int i; float scale, size, radius, dot1, dot2; + prvm_vec3_t prvmshadowdir, prvmshadowfocus; vec3_t shadowdir, shadowforward, shadowright, shadoworigin, shadowfocus, shadowmins, shadowmaxs; entity_render_t *ent; + r_shadow_nummodelshadows = 0; if (!r_refdef.scene.numentities) return; @@ -4656,11 +5009,18 @@ void R_Shadow_PrepareModelShadows(void) break; // fall through case R_SHADOW_SHADOWMODE_STENCIL: + if (!vid.stencil) + return; for (i = 0;i < r_refdef.scene.numentities;i++) { ent = r_refdef.scene.entities[i]; - if (!ent->animcache_vertex3f && ent->model && ent->model->DrawShadowVolume != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW)) + if (ent->model && ent->model->DrawShadowVolume != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW)) + { + if (r_shadow_nummodelshadows >= MAX_MODELSHADOWS) + break; + r_shadow_modelshadows[r_shadow_nummodelshadows++] = ent; R_AnimCache_GetEntity(ent, false, false); + } } return; default: @@ -4671,7 +5031,8 @@ void R_Shadow_PrepareModelShadows(void) scale = r_shadow_shadowmapping_precision.value * r_shadows_shadowmapscale.value; radius = 0.5f * size / scale; - Math_atov(r_shadows_throwdirection.string, shadowdir); + Math_atov(r_shadows_throwdirection.string, prvmshadowdir); + VectorCopy(prvmshadowdir, shadowdir); VectorNormalize(shadowdir); dot1 = DotProduct(r_refdef.view.forward, shadowdir); dot2 = DotProduct(r_refdef.view.up, shadowdir); @@ -4681,7 +5042,8 @@ void R_Shadow_PrepareModelShadows(void) VectorMA(r_refdef.view.up, -dot2, shadowdir, shadowforward); VectorNormalize(shadowforward); CrossProduct(shadowdir, shadowforward, shadowright); - Math_atov(r_shadows_focus.string, shadowfocus); + Math_atov(r_shadows_focus.string, prvmshadowfocus); + VectorCopy(prvmshadowfocus, shadowfocus); VectorM(shadowfocus[0], r_refdef.view.right, shadoworigin); VectorMA(shadoworigin, shadowfocus[1], r_refdef.view.up, shadoworigin); VectorMA(shadoworigin, -shadowfocus[2], r_refdef.view.forward, shadoworigin); @@ -4703,8 +5065,13 @@ void R_Shadow_PrepareModelShadows(void) if (!BoxesOverlap(ent->mins, ent->maxs, shadowmins, shadowmaxs)) continue; // cast shadows from anything of the map (submodels are optional) - if (!ent->animcache_vertex3f && ent->model && ent->model->DrawShadowMap != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW)) + if (ent->model && ent->model->DrawShadowMap != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW)) + { + if (r_shadow_nummodelshadows >= MAX_MODELSHADOWS) + break; + r_shadow_modelshadows[r_shadow_nummodelshadows++] = ent; R_AnimCache_GetEntity(ent, false, false); + } } } @@ -4717,13 +5084,14 @@ void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colort vec3_t relativelightdirection, relativeforward, relativeright; vec3_t relativeshadowmins, relativeshadowmaxs; vec3_t shadowdir, shadowforward, shadowright, shadoworigin, shadowfocus; + prvm_vec3_t prvmshadowdir, prvmshadowfocus; float m[12]; matrix4x4_t shadowmatrix, cameramatrix, mvpmatrix, invmvpmatrix, scalematrix, texmatrix; r_viewport_t viewport; GLuint shadowfbo = 0; float clearcolor[4]; - if (!r_refdef.scene.numentities) + if (!r_shadow_nummodelshadows) return; switch (r_shadow_shadowmode) @@ -4761,16 +5129,18 @@ void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colort radius = 0.5f / scale; nearclip = -r_shadows_throwdistance.value; farclip = r_shadows_throwdistance.value; - bias = r_shadow_shadowmapping_bias.value * r_shadow_shadowmapping_nearclip.value / (2 * r_shadows_throwdistance.value) * (1024.0f / size); + bias = (r_shadows_shadowmapbias.value < 0) ? r_shadow_shadowmapping_bias.value : r_shadows_shadowmapbias.value * r_shadow_shadowmapping_nearclip.value / (2 * r_shadows_throwdistance.value) * (1024.0f / size); r_shadow_shadowmap_parameters[0] = size; r_shadow_shadowmap_parameters[1] = size; r_shadow_shadowmap_parameters[2] = 1.0; r_shadow_shadowmap_parameters[3] = bound(0.0f, 1.0f - r_shadows_darken.value, 1.0f); - Math_atov(r_shadows_throwdirection.string, shadowdir); + Math_atov(r_shadows_throwdirection.string, prvmshadowdir); + VectorCopy(prvmshadowdir, shadowdir); VectorNormalize(shadowdir); - Math_atov(r_shadows_focus.string, shadowfocus); + Math_atov(r_shadows_focus.string, prvmshadowfocus); + VectorCopy(prvmshadowfocus, shadowfocus); VectorM(shadowfocus[0], r_refdef.view.right, shadoworigin); VectorMA(shadoworigin, shadowfocus[1], r_refdef.view.up, shadoworigin); VectorMA(shadoworigin, -shadowfocus[2], r_refdef.view.forward, shadoworigin); @@ -4801,7 +5171,7 @@ void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colort R_Mesh_SetRenderTargets(shadowfbo, r_shadow_shadowmap2ddepthbuffer, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL); else R_Mesh_SetRenderTargets(shadowfbo, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL, NULL); - R_SetupShader_DepthOrShadow(true, r_shadow_shadowmap2ddepthbuffer != NULL); + R_SetupShader_DepthOrShadow(true, r_shadow_shadowmap2ddepthbuffer != NULL, false); // FIXME test if we have a skeletal model? GL_PolygonOffset(r_shadow_shadowmapping_polygonfactor.value, r_shadow_shadowmapping_polygonoffset.value); GL_DepthMask(true); GL_DepthTest(true); @@ -4819,36 +5189,23 @@ void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colort // outside the usable area GL_Scissor(viewport.x + r_shadow_shadowmapborder, viewport.y + r_shadow_shadowmapborder, viewport.width - 2*r_shadow_shadowmapborder, viewport.height - 2*r_shadow_shadowmapborder); -#if 0 - // debugging - R_Mesh_SetRenderTargets(r_shadow_fb_fbo, r_shadow_fb_depthtexture, r_shadow_fb_colortexture, NULL, NULL, NULL); - R_SetupShader_ShowDepth(true); - GL_ColorMask(1,1,1,1); - GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0); -#endif - - for (i = 0;i < r_refdef.scene.numentities;i++) - { - ent = r_refdef.scene.entities[i]; - - // cast shadows from anything of the map (submodels are optional) - if (ent->model && ent->model->DrawShadowMap != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW)) - { - relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix); - Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativelightorigin); - Matrix4x4_Transform3x3(&ent->inversematrix, shadowdir, relativelightdirection); - Matrix4x4_Transform3x3(&ent->inversematrix, shadowforward, relativeforward); - Matrix4x4_Transform3x3(&ent->inversematrix, shadowright, relativeright); - relativeshadowmins[0] = relativelightorigin[0] - r_shadows_throwdistance.value * fabs(relativelightdirection[0]) - radius * (fabs(relativeforward[0]) + fabs(relativeright[0])); - relativeshadowmins[1] = relativelightorigin[1] - r_shadows_throwdistance.value * fabs(relativelightdirection[1]) - radius * (fabs(relativeforward[1]) + fabs(relativeright[1])); - relativeshadowmins[2] = relativelightorigin[2] - r_shadows_throwdistance.value * fabs(relativelightdirection[2]) - radius * (fabs(relativeforward[2]) + fabs(relativeright[2])); - relativeshadowmaxs[0] = relativelightorigin[0] + r_shadows_throwdistance.value * fabs(relativelightdirection[0]) + radius * (fabs(relativeforward[0]) + fabs(relativeright[0])); - relativeshadowmaxs[1] = relativelightorigin[1] + r_shadows_throwdistance.value * fabs(relativelightdirection[1]) + radius * (fabs(relativeforward[1]) + fabs(relativeright[1])); - relativeshadowmaxs[2] = relativelightorigin[2] + r_shadows_throwdistance.value * fabs(relativelightdirection[2]) + radius * (fabs(relativeforward[2]) + fabs(relativeright[2])); - RSurf_ActiveModelEntity(ent, false, false, false); - ent->model->DrawShadowMap(0, ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, NULL, relativeshadowmins, relativeshadowmaxs); - rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity - } + for (i = 0;i < r_shadow_nummodelshadows;i++) + { + ent = r_shadow_modelshadows[i]; + relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix); + Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativelightorigin); + Matrix4x4_Transform3x3(&ent->inversematrix, shadowdir, relativelightdirection); + Matrix4x4_Transform3x3(&ent->inversematrix, shadowforward, relativeforward); + Matrix4x4_Transform3x3(&ent->inversematrix, shadowright, relativeright); + relativeshadowmins[0] = relativelightorigin[0] - r_shadows_throwdistance.value * fabs(relativelightdirection[0]) - radius * (fabs(relativeforward[0]) + fabs(relativeright[0])); + relativeshadowmins[1] = relativelightorigin[1] - r_shadows_throwdistance.value * fabs(relativelightdirection[1]) - radius * (fabs(relativeforward[1]) + fabs(relativeright[1])); + relativeshadowmins[2] = relativelightorigin[2] - r_shadows_throwdistance.value * fabs(relativelightdirection[2]) - radius * (fabs(relativeforward[2]) + fabs(relativeright[2])); + relativeshadowmaxs[0] = relativelightorigin[0] + r_shadows_throwdistance.value * fabs(relativelightdirection[0]) + radius * (fabs(relativeforward[0]) + fabs(relativeright[0])); + relativeshadowmaxs[1] = relativelightorigin[1] + r_shadows_throwdistance.value * fabs(relativelightdirection[1]) + radius * (fabs(relativeforward[1]) + fabs(relativeright[1])); + relativeshadowmaxs[2] = relativelightorigin[2] + r_shadows_throwdistance.value * fabs(relativelightdirection[2]) + radius * (fabs(relativeforward[2]) + fabs(relativeright[2])); + RSurf_ActiveModelEntity(ent, false, false, false); + ent->model->DrawShadowMap(0, ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, NULL, relativeshadowmins, relativeshadowmaxs); + rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity } #if 0 @@ -4885,7 +5242,7 @@ void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colort case RENDERPATH_D3D9: case RENDERPATH_D3D10: case RENDERPATH_D3D11: -#ifdef OPENGL_ORIENTATION +#ifdef MATRIX4x4_OPENGLORIENTATION r_shadow_shadowmapmatrix.m[0][0] *= -1.0f; r_shadow_shadowmapmatrix.m[0][1] *= -1.0f; r_shadow_shadowmapmatrix.m[0][2] *= -1.0f; @@ -4919,8 +5276,9 @@ void R_DrawModelShadows(int fbo, rtexture_t *depthtexture, rtexture_t *colortext vec3_t relativelightdirection; vec3_t relativeshadowmins, relativeshadowmaxs; vec3_t tmp, shadowdir; + prvm_vec3_t prvmshadowdir; - if (!r_refdef.scene.numentities || !vid.stencil || (r_shadow_shadowmode != R_SHADOW_SHADOWMODE_STENCIL && r_shadows.integer != 1)) + if (!r_shadow_nummodelshadows || (r_shadow_shadowmode != R_SHADOW_SHADOWMODE_STENCIL && r_shadows.integer != 1)) return; r_shadow_fb_fbo = fbo; @@ -4941,66 +5299,64 @@ void R_DrawModelShadows(int fbo, rtexture_t *depthtexture, rtexture_t *colortext // get shadow dir if (r_shadows.integer == 2) { - Math_atov(r_shadows_throwdirection.string, shadowdir); + Math_atov(r_shadows_throwdirection.string, prvmshadowdir); + VectorCopy(prvmshadowdir, shadowdir); VectorNormalize(shadowdir); } R_Shadow_ClearStencil(); - for (i = 0;i < r_refdef.scene.numentities;i++) + for (i = 0;i < r_shadow_nummodelshadows;i++) { - ent = r_refdef.scene.entities[i]; + ent = r_shadow_modelshadows[i]; // cast shadows from anything of the map (submodels are optional) - if (ent->model && ent->model->DrawShadowVolume != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW)) + relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix); + VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance); + VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance); + if (r_shadows.integer == 2) // 2: simpler mode, throw shadows always in same direction + Matrix4x4_Transform3x3(&ent->inversematrix, shadowdir, relativelightdirection); + else { - relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix); - VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance); - VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance); - if (r_shadows.integer == 2) // 2: simpler mode, throw shadows always in same direction - Matrix4x4_Transform3x3(&ent->inversematrix, shadowdir, relativelightdirection); - else + if(ent->entitynumber != 0) { - if(ent->entitynumber != 0) + if(ent->entitynumber >= MAX_EDICTS) // csqc entity + { + // FIXME handle this + VectorNegate(ent->modellight_lightdir, relativelightdirection); + } + else { - if(ent->entitynumber >= MAX_EDICTS) // csqc entity + // networked entity - might be attached in some way (then we should use the parent's light direction, to not tear apart attached entities) + int entnum, entnum2, recursion; + entnum = entnum2 = ent->entitynumber; + for(recursion = 32; recursion > 0; --recursion) { - // FIXME handle this - VectorNegate(ent->modellight_lightdir, relativelightdirection); + entnum2 = cl.entities[entnum].state_current.tagentity; + if(entnum2 >= 1 && entnum2 < cl.num_entities && cl.entities_active[entnum2]) + entnum = entnum2; + else + break; } - else + if(recursion && recursion != 32) // if we followed a valid non-empty attachment chain { - // networked entity - might be attached in some way (then we should use the parent's light direction, to not tear apart attached entities) - int entnum, entnum2, recursion; - entnum = entnum2 = ent->entitynumber; - for(recursion = 32; recursion > 0; --recursion) - { - entnum2 = cl.entities[entnum].state_current.tagentity; - if(entnum2 >= 1 && entnum2 < cl.num_entities && cl.entities_active[entnum2]) - entnum = entnum2; - else - break; - } - if(recursion && recursion != 32) // if we followed a valid non-empty attachment chain - { - VectorNegate(cl.entities[entnum].render.modellight_lightdir, relativelightdirection); - // transform into modelspace of OUR entity - Matrix4x4_Transform3x3(&cl.entities[entnum].render.matrix, relativelightdirection, tmp); - Matrix4x4_Transform3x3(&ent->inversematrix, tmp, relativelightdirection); - } - else - VectorNegate(ent->modellight_lightdir, relativelightdirection); + VectorNegate(cl.entities[entnum].render.modellight_lightdir, relativelightdirection); + // transform into modelspace of OUR entity + Matrix4x4_Transform3x3(&cl.entities[entnum].render.matrix, relativelightdirection, tmp); + Matrix4x4_Transform3x3(&ent->inversematrix, tmp, relativelightdirection); } + else + VectorNegate(ent->modellight_lightdir, relativelightdirection); } - else - VectorNegate(ent->modellight_lightdir, relativelightdirection); } - - VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin); - RSurf_ActiveModelEntity(ent, false, false, false); - ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, relativeshadowmins, relativeshadowmaxs); - rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity + else + VectorNegate(ent->modellight_lightdir, relativelightdirection); } + + VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin); + RSurf_ActiveModelEntity(ent, false, false, false); + ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, relativeshadowmins, relativeshadowmaxs); + rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity } // not really the right mode, but this will disable any silly stencil features @@ -5041,7 +5397,9 @@ static void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequer { float zdist; vec3_t centerorigin; +#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2) float vertex3f[12]; +#endif // if it's too close, skip it if (VectorLength(rtlight->currentcolor) < (1.0f / 256.0f)) return; @@ -5062,19 +5420,19 @@ static void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequer case RENDERPATH_GL20: case RENDERPATH_GLES1: case RENDERPATH_GLES2: -#ifdef GL_SAMPLES_PASSED_ARB +#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2) CHECKGLERROR // NOTE: GL_DEPTH_TEST must be enabled or ATI won't count samples, so use GL_DepthFunc instead qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, rtlight->corona_queryindex_allpixels); GL_DepthFunc(GL_ALWAYS); R_CalcSprite_Vertex3f(vertex3f, centerorigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale); - R_Mesh_PrepareVertices_Vertex3f(4, vertex3f, NULL); + R_Mesh_PrepareVertices_Vertex3f(4, vertex3f, NULL, 0); R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); qglEndQueryARB(GL_SAMPLES_PASSED_ARB); GL_DepthFunc(GL_LEQUAL); qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, rtlight->corona_queryindex_visiblepixels); R_CalcSprite_Vertex3f(vertex3f, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale); - R_Mesh_PrepareVertices_Vertex3f(4, vertex3f, NULL); + R_Mesh_PrepareVertices_Vertex3f(4, vertex3f, NULL, 0); R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); qglEndQueryARB(GL_SAMPLES_PASSED_ARB); CHECKGLERROR @@ -5102,7 +5460,9 @@ static float spritetexcoord2f[4*2] = {0, 1, 0, 0, 1, 0, 1, 1}; static void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale) { vec3_t color; + unsigned int occlude = 0; GLint allpixels = 0, visiblepixels = 0; + // now we have to check the query result if (rtlight->corona_queryindex_visiblepixels) { @@ -5113,36 +5473,55 @@ static void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale) case RENDERPATH_GL20: case RENDERPATH_GLES1: case RENDERPATH_GLES2: -#ifdef GL_SAMPLES_PASSED_ARB +#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2) CHECKGLERROR - qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, &visiblepixels); - qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, &allpixels); + // See if we can use the GPU-side method to prevent implicit sync + if (vid.support.arb_query_buffer_object) { +#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); + qglBufferDataARB(GL_QUERY_BUFFER_ARB, 8, NULL, GL_DYNAMIC_COPY); + } else { + qglBindBufferARB(GL_QUERY_BUFFER_ARB, r_shadow_occlusion_buf); + } + qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, BUFFER_OFFSET(0)); + qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, BUFFER_OFFSET(4)); + qglBindBufferBase(GL_UNIFORM_BUFFER, 0, r_shadow_occlusion_buf); + occlude = MATERIALFLAG_OCCLUDE; + } else { + qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, &visiblepixels); + qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, &allpixels); + if (visiblepixels < 1 || allpixels < 1) + return; + rtlight->corona_visibility *= bound(0, (float)visiblepixels / (float)allpixels, 1); + } + cscale *= rtlight->corona_visibility; CHECKGLERROR -#endif break; +#else + return; +#endif case RENDERPATH_D3D9: Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); - break; + return; case RENDERPATH_D3D10: Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); - break; + return; case RENDERPATH_D3D11: Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); - break; + return; case RENDERPATH_SOFT: //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); - break; - } - //Con_Printf("%i of %i pixels\n", (int)visiblepixels, (int)allpixels); - if (visiblepixels < 1 || allpixels < 1) return; - rtlight->corona_visibility *= bound(0, (float)visiblepixels / (float)allpixels, 1); - cscale *= rtlight->corona_visibility; + default: + return; + } } else { // FIXME: these traces should scan all render entities instead of cl.world - if (CL_TraceLine(r_refdef.view.origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction < 1) + if (CL_TraceLine(r_refdef.view.origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction < 1) return; } VectorScale(rtlight->currentcolor, cscale, color); @@ -5157,7 +5536,7 @@ static void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale) } R_CalcSprite_Vertex3f(vertex3f, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale); RSurf_ActiveCustomEntity(&identitymatrix, &identitymatrix, RENDER_NODEPTHTEST, 0, color[0], color[1], color[2], 1, 4, vertex3f, spritetexcoord2f, NULL, NULL, NULL, NULL, 2, polygonelement3i, polygonelement3s, false, false); - R_DrawCustomSurface(r_shadow_lightcorona, &identitymatrix, MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false, false); + R_DrawCustomSurface(r_shadow_lightcorona, &identitymatrix, MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE | MATERIALFLAG_NODEPTHTEST | occlude, 0, 4, 0, 2, false, false); if(negated) GL_BlendEquationSubtract(false); } @@ -5192,15 +5571,15 @@ void R_Shadow_DrawCoronas(void) case RENDERPATH_GLES1: case RENDERPATH_GLES2: usequery = vid.support.arb_occlusion_query && r_coronas_occlusionquery.integer; -#ifdef GL_SAMPLES_PASSED_ARB +#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2) if (usequery) { GL_ColorMask(0,0,0,0); - if (r_maxqueries < (range + r_refdef.scene.numlights) * 2) + if (r_maxqueries < ((unsigned int)range + r_refdef.scene.numlights) * 2) if (r_maxqueries < MAX_OCCLUSION_QUERIES) { i = r_maxqueries; - r_maxqueries = (range + r_refdef.scene.numlights) * 4; + r_maxqueries = ((unsigned int)range + r_refdef.scene.numlights) * 4; r_maxqueries = min(r_maxqueries, MAX_OCCLUSION_QUERIES); CHECKGLERROR qglGenQueriesARB(r_maxqueries - i, r_queries + i); @@ -5298,12 +5677,10 @@ static dlight_t *R_Shadow_NewWorldLight(void) static void R_Shadow_UpdateWorldLight(dlight_t *light, vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags) { matrix4x4_t matrix; + + // note that style is no longer validated here, -1 is used for unstyled lights and >= MAX_LIGHTSTYLES is accepted for sake of editing rtlights files that might be out of bounds but perfectly formatted + // validate parameters - if (style < 0 || style >= MAX_LIGHTSTYLES) - { - Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES); - style = 0; - } if (!cubemapname) cubemapname = ""; @@ -5429,10 +5806,10 @@ void R_Shadow_DrawLightSprites(void) { light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); if (light) - R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 5, &light->rtlight); + R_MeshQueue_AddTransparent(TRANSPARENTSORT_DISTANCE, light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 5, &light->rtlight); } if (!r_editlights_lockcursor) - R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL); + R_MeshQueue_AddTransparent(TRANSPARENTSORT_DISTANCE, r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL); } int R_Shadow_GetRTLightInfo(unsigned int lightindex, float *origin, float *radius, float *color) @@ -5440,7 +5817,7 @@ int R_Shadow_GetRTLightInfo(unsigned int lightindex, float *origin, float *radiu unsigned int range; dlight_t *light; rtlight_t *rtlight; - range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); + range = (unsigned int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); if (lightindex >= range) return -1; light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); @@ -5477,7 +5854,7 @@ static void R_Shadow_SelectLightInView(void) if (rating >= 0.95) { rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp))); - if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.view.origin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction == 1.0f) + if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.view.origin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction == 1.0f) { bestrating = rating; best = light; @@ -5505,8 +5882,8 @@ void R_Shadow_LoadWorldLights(void) n = 0; while (*s) { - t = s; /* + t = s; shadow = true; for (;COM_Parse(t, true) && strcmp( if (COM_Parse(t, true)) @@ -5927,7 +6304,7 @@ static void R_Shadow_SetCursorLocationForView(void) vec3_t dest, endpos; trace_t trace; VectorMA(r_refdef.view.origin, r_editlights_cursordistance.value, r_refdef.view.forward, dest); - trace = CL_TraceLine(r_refdef.view.origin, dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true); + trace = CL_TraceLine(r_refdef.view.origin, dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true); if (trace.fraction < 1) { dist = trace.fraction * r_editlights_cursordistance.value; @@ -5969,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(); } @@ -6372,10 +6753,37 @@ void R_Shadow_EditLights_DrawSelectedLightProperties(void) int lightnumber, lightcount; size_t lightindex, range; dlight_t *light; - float x, y; char temp[256]; + float x, y; + if (!r_editlights.integer) return; + + // update cvars so QC can query them + if (r_shadow_selectedlight) + { + dpsnprintf(temp, sizeof(temp), "%f %f %f", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]); + Cvar_SetQuick(&r_editlights_current_origin, temp); + dpsnprintf(temp, sizeof(temp), "%f %f %f", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]); + Cvar_SetQuick(&r_editlights_current_angles, temp); + dpsnprintf(temp, sizeof(temp), "%f %f %f", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]); + Cvar_SetQuick(&r_editlights_current_color, temp); + Cvar_SetValueQuick(&r_editlights_current_radius, r_shadow_selectedlight->radius); + Cvar_SetValueQuick(&r_editlights_current_corona, r_shadow_selectedlight->corona); + Cvar_SetValueQuick(&r_editlights_current_coronasize, r_shadow_selectedlight->coronasizescale); + Cvar_SetValueQuick(&r_editlights_current_style, r_shadow_selectedlight->style); + Cvar_SetValueQuick(&r_editlights_current_shadows, r_shadow_selectedlight->shadow); + Cvar_SetQuick(&r_editlights_current_cubemap, r_shadow_selectedlight->cubemapname); + Cvar_SetValueQuick(&r_editlights_current_ambient, r_shadow_selectedlight->ambientscale); + Cvar_SetValueQuick(&r_editlights_current_diffuse, r_shadow_selectedlight->diffusescale); + Cvar_SetValueQuick(&r_editlights_current_specular, r_shadow_selectedlight->specularscale); + Cvar_SetValueQuick(&r_editlights_current_normalmode, (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? 1 : 0); + Cvar_SetValueQuick(&r_editlights_current_realtimemode, (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? 1 : 0); + } + + // draw properties on screen + if (!r_editlights_drawproperties.integer) + return; x = vid_conwidth.value - 240; y = 5; DrawQ_Pic(x-5, y-5, NULL, 250, 155, 0, 0, 0, 0.75, 0); @@ -6388,7 +6796,7 @@ void R_Shadow_EditLights_DrawSelectedLightProperties(void) if (!light) continue; if (light == r_shadow_selectedlight) - lightnumber = lightindex; + lightnumber = (int)lightindex; lightcount++; } dpsnprintf(temp, sizeof(temp), "Cursor origin: %.0f %.0f %.0f", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2]); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, false, FONT_DEFAULT);y += 8; @@ -6503,7 +6911,7 @@ static void R_Shadow_EditLights_Help_f(void) "sizescale scale : multiply radius (size) of light (1 does nothing)\n" "originscale x y z : multiply origin of light (1 1 1 does nothing)\n" "style style : set lightstyle of light (flickering patterns, switches, etc)\n" -"cubemap basename : set filter cubemap of light (not yet supported)\n" +"cubemap basename : set filter cubemap of light\n" "shadows 1/0 : turn on/off shadows\n" "corona n : set corona intensity\n" "coronasize n : set corona size (0-1)\n" @@ -6588,6 +6996,21 @@ static void R_Shadow_EditLights_Init(void) Cvar_RegisterVariable(&r_editlights_cursorpushoff); Cvar_RegisterVariable(&r_editlights_cursorgrid); Cvar_RegisterVariable(&r_editlights_quakelightsizescale); + Cvar_RegisterVariable(&r_editlights_drawproperties); + Cvar_RegisterVariable(&r_editlights_current_origin); + Cvar_RegisterVariable(&r_editlights_current_angles); + Cvar_RegisterVariable(&r_editlights_current_color); + Cvar_RegisterVariable(&r_editlights_current_radius); + Cvar_RegisterVariable(&r_editlights_current_corona); + Cvar_RegisterVariable(&r_editlights_current_coronasize); + Cvar_RegisterVariable(&r_editlights_current_style); + Cvar_RegisterVariable(&r_editlights_current_shadows); + Cvar_RegisterVariable(&r_editlights_current_cubemap); + Cvar_RegisterVariable(&r_editlights_current_ambient); + Cvar_RegisterVariable(&r_editlights_current_diffuse); + Cvar_RegisterVariable(&r_editlights_current_specular); + Cvar_RegisterVariable(&r_editlights_current_normalmode); + Cvar_RegisterVariable(&r_editlights_current_realtimemode); Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system"); Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)"); Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f, "reloads rtlights file (or imports from .lights file or .ent file or the map itself)"); @@ -6615,7 +7038,7 @@ LIGHT SAMPLING ============================================================================= */ -void R_LightPoint(vec3_t color, const vec3_t p, const int flags) +void R_LightPoint(float *color, const vec3_t p, const int flags) { int i, numlights, flag; float f, relativepoint[3], dist, dist2, lightradius2; @@ -6649,7 +7072,7 @@ void R_LightPoint(vec3_t color, const vec3_t p, const int flags) if (flags & LP_RTWORLD) { flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE; - numlights = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); + numlights = (int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); for (i = 0; i < numlights; i++) { dlight = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, i); @@ -6669,7 +7092,7 @@ void R_LightPoint(vec3_t color, const vec3_t p, const int flags) if (f <= 0) continue; // todo: add to both ambient and diffuse - if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction == 1) + if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction == 1) VectorMA(color, f, light->currentcolor, color); } } @@ -6690,7 +7113,7 @@ void R_LightPoint(vec3_t color, const vec3_t p, const int flags) if (f <= 0) continue; // todo: add to both ambient and diffuse - if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction == 1) + if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction == 1) VectorMA(color, f, light->color, color); } } @@ -6755,7 +7178,7 @@ void R_CompleteLightPoint(vec3_t ambient, vec3_t diffuse, vec3_t lightdir, const if (flags & LP_RTWORLD) { flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE; - numlights = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); + numlights = (int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); for (i = 0; i < numlights; i++) { dlight = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, i); @@ -6774,7 +7197,7 @@ void R_CompleteLightPoint(vec3_t ambient, vec3_t diffuse, vec3_t lightdir, const intensity = min(1.0f, (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) * r_shadow_lightintensityscale.value; if (intensity <= 0.0f) continue; - if (light->shadow && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction < 1) + if (light->shadow && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction < 1) continue; // scale down intensity to add to both ambient and diffuse //intensity *= 0.5f; @@ -6807,7 +7230,7 @@ void R_CompleteLightPoint(vec3_t ambient, vec3_t diffuse, vec3_t lightdir, const intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist) * r_shadow_lightintensityscale.value; if (intensity <= 0.0f) continue; - if (light->shadow && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction < 1) + if (light->shadow && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction < 1) continue; // scale down intensity to add to both ambient and diffuse //intensity *= 0.5f;