X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=r_shadow.c;h=654cfa91b2e9c279aa67060cac0ac95f1966ed06;hb=9a55902a4fb9136da728839cd5b08897e7fe098b;hp=ce14e749ba73e50ab5a784e5c379b25442ffa91c;hpb=231968698ef7d74a4e2b78148a37c1dc9066f7af;p=xonotic%2Fdarkplaces.git diff --git a/r_shadow.c b/r_shadow.c index ce14e749..654cfa91 100644 --- a/r_shadow.c +++ b/r_shadow.c @@ -349,6 +349,7 @@ static memexpandablearray_t r_shadow_worldlightsarray; dlight_t *r_shadow_selectedlight; dlight_t r_shadow_bufferlight; vec3_t r_editlights_cursorlocation; +qboolean r_editlights_lockcursor; extern int con_vislines; @@ -442,6 +443,12 @@ void R_Shadow_SetShadowMode(void) r_shadow_shadowmode = R_SHADOW_SHADOWMODE_SHADOWMAP2D; break; } + // Cg has very little choice in depth texture sampling + if (vid.cgcontext) + { + r_shadow_shadowmapsampler = false; + r_shadow_shadowmode = R_SHADOW_SHADOWMODE_SHADOWMAP2D; + } } break; case RENDERPATH_GL13: @@ -659,7 +666,7 @@ void r_shadow_newmap(void) if (r_editlights_sprcubemaplight) R_SkinFrame_MarkUsed(r_editlights_sprcubemaplight); if (r_editlights_sprcubemapnoshadowlight) R_SkinFrame_MarkUsed(r_editlights_sprcubemapnoshadowlight); if (r_editlights_sprselection) R_SkinFrame_MarkUsed(r_editlights_sprselection); - if (cl.worldmodel && strncmp(cl.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname))) + if (strncmp(cl.worldname, r_shadow_mapname, sizeof(r_shadow_mapname))) R_Shadow_EditLights_Reload_f(); } @@ -754,7 +761,7 @@ void R_Shadow_Init(void) r_shadow_buffer_surfacesides = NULL; r_shadow_buffer_shadowtrispvs = NULL; r_shadow_buffer_lighttrispvs = NULL; - R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap); + R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap, NULL, NULL); } matrix4x4_t matrix_attenuationxyz = @@ -1507,19 +1514,48 @@ int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float border) } // 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); + } +#else + // 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); - dp = p[0] + p[1], dn = p[0] - p[1], ap = fabs(dp), an = fabs(dn), + 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), + 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), + 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)); } +#endif return sides & masks[0] & masks[1] & masks[2] & masks[3] & masks[4] & masks[5]; } @@ -1718,12 +1754,12 @@ static void R_Shadow_MakeTextures(void) // 1D gradient texture for (x = 0;x < ATTEN1DSIZE;x++) data[x] = R_Shadow_MakeTextures_SamplePoint((x + 0.5f) * (1.0f / ATTEN1DSIZE) * (1.0f / 0.9375), 0, 0); - r_shadow_attenuationgradienttexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation1d", ATTEN1DSIZE, 1, (unsigned char *)data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, NULL); + r_shadow_attenuationgradienttexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation1d", ATTEN1DSIZE, 1, (unsigned char *)data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, -1, NULL); // 2D circle texture for (y = 0;y < ATTEN2DSIZE;y++) for (x = 0;x < ATTEN2DSIZE;x++) data[y*ATTEN2DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), 0); - r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, NULL); + r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, -1, NULL); // 3D sphere texture if (r_shadow_texture3d.integer && vid.support.ext_texture_3d) { @@ -1731,7 +1767,7 @@ static void R_Shadow_MakeTextures(void) for (y = 0;y < ATTEN3DSIZE;y++) for (x = 0;x < ATTEN3DSIZE;x++) data[(z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375)); - r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, NULL); + r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, -1, NULL); } else r_shadow_attenuation3dtexture = NULL; @@ -2054,7 +2090,7 @@ static void R_Shadow_MakeVSDCT(void) 0, 0, 0x33, 0xFF, // +Z: <0, 0>, <0.5, 2.5> 0, 0, 0x99, 0xFF, // -Z: <0, 0>, <1.5, 2.5> }; - r_shadow_shadowmapvsdcttexture = R_LoadTextureCubeMap(r_shadow_texturepool, "shadowmapvsdct", 1, data, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALPHA, NULL); + r_shadow_shadowmapvsdcttexture = R_LoadTextureCubeMap(r_shadow_texturepool, "shadowmapvsdct", 1, data, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALPHA, -1, NULL); } static void R_Shadow_MakeShadowMap(int side, int size) @@ -2108,15 +2144,15 @@ void R_Shadow_RenderMode_ShadowMap(int side, int clear, int size) nearclip = r_shadow_shadowmapping_nearclip.value / rsurface.rtlight->radius; farclip = 1.0f; bias = r_shadow_shadowmapping_bias.value * nearclip * (1024.0f / size);// * rsurface.rtlight->radius; - r_shadow_shadowmap_parameters[2] = 0.5f + 0.5f * (farclip + nearclip) / (farclip - nearclip); - r_shadow_shadowmap_parameters[3] = -nearclip * farclip / (farclip - nearclip) - 0.5f * bias; + r_shadow_shadowmap_parameters[1] = -nearclip * farclip / (farclip - nearclip) - 0.5f * bias; + r_shadow_shadowmap_parameters[3] = 0.5f + 0.5f * (farclip + nearclip) / (farclip - nearclip); r_shadow_shadowmapside = side; r_shadow_shadowmapsize = size; switch (r_shadow_shadowmode) { case R_SHADOW_SHADOWMODE_SHADOWMAP2D: r_shadow_shadowmap_parameters[0] = 0.5f * (size - r_shadow_shadowmapborder); - r_shadow_shadowmap_parameters[1] = r_shadow_shadowmapvsdct ? 2.5f*size : size; + r_shadow_shadowmap_parameters[2] = r_shadow_shadowmapvsdct ? 2.5f*size : size; R_Viewport_InitRectSideView(&viewport, &rsurface.rtlight->matrix_lighttoworld, side, size, r_shadow_shadowmapborder, nearclip, farclip, NULL); if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SHADOWMAP2D) goto init_done; @@ -2133,7 +2169,7 @@ void R_Shadow_RenderMode_ShadowMap(int side, int clear, int size) break; case R_SHADOW_SHADOWMODE_SHADOWMAPRECTANGLE: r_shadow_shadowmap_parameters[0] = 0.5f * (size - r_shadow_shadowmapborder); - r_shadow_shadowmap_parameters[1] = r_shadow_shadowmapvsdct ? 2.5f*size : size; + r_shadow_shadowmap_parameters[2] = r_shadow_shadowmapvsdct ? 2.5f*size : size; R_Viewport_InitRectSideView(&viewport, &rsurface.rtlight->matrix_lighttoworld, side, size, r_shadow_shadowmapborder, nearclip, farclip, NULL); if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SHADOWMAPRECTANGLE) goto init_done; @@ -2150,7 +2186,7 @@ void R_Shadow_RenderMode_ShadowMap(int side, int clear, int size) break; case R_SHADOW_SHADOWMODE_SHADOWMAPCUBESIDE: r_shadow_shadowmap_parameters[0] = 1.0f; - r_shadow_shadowmap_parameters[1] = 1.0f; + r_shadow_shadowmap_parameters[2] = 1.0f; R_Viewport_InitCubeSideView(&viewport, &rsurface.rtlight->matrix_lighttoworld, side, size, nearclip, farclip, NULL); if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SHADOWMAPCUBESIDE) goto init_done; @@ -2412,116 +2448,21 @@ int bboxedges[12][2] = qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs) { - int i, ix1, iy1, ix2, iy2; - float x1, y1, x2, y2; - vec4_t v, v2; - float vertex[20][3]; - int j, k; - vec4_t plane4f; - int numvertices; - float corner[8][4]; - float dist[8]; - int sign[8]; - float f; - - r_shadow_lightscissor[0] = r_refdef.view.viewport.x; - 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; - if (!r_shadow_scissor.integer) - return false; - - // if view is inside the light box, just say yes it's visible - if (BoxesOverlap(r_refdef.view.origin, r_refdef.view.origin, mins, maxs)) - return false; - - x1 = y1 = x2 = y2 = 0; - - // transform all corners that are infront of the nearclip plane - VectorNegate(r_refdef.view.frustum[4].normal, plane4f); - plane4f[3] = r_refdef.view.frustum[4].dist; - numvertices = 0; - for (i = 0;i < 8;i++) - { - Vector4Set(corner[i], (i & 1) ? maxs[0] : mins[0], (i & 2) ? maxs[1] : mins[1], (i & 4) ? maxs[2] : mins[2], 1); - dist[i] = DotProduct4(corner[i], plane4f); - sign[i] = dist[i] > 0; - if (!sign[i]) - { - VectorCopy(corner[i], vertex[numvertices]); - numvertices++; - } - } - // if some points are behind the nearclip, add clipped edge points to make - // sure that the scissor boundary is complete - if (numvertices > 0 && numvertices < 8) - { - // add clipped edge points - for (i = 0;i < 12;i++) - { - j = bboxedges[i][0]; - k = bboxedges[i][1]; - if (sign[j] != sign[k]) - { - f = dist[j] / (dist[j] - dist[k]); - VectorLerp(corner[j], f, corner[k], vertex[numvertices]); - numvertices++; - } - } - } - - // if we have no points to check, the light is behind the view plane - if (!numvertices) - return true; - - // if we have some points to transform, check what screen area is covered - x1 = y1 = x2 = y2 = 0; - v[3] = 1.0f; - //Con_Printf("%i vertices to transform...\n", numvertices); - for (i = 0;i < numvertices;i++) { - VectorCopy(vertex[i], v); - R_Viewport_TransformToScreen(&r_refdef.view.viewport, v, v2); - //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]); - if (i) - { - if (x1 > v2[0]) x1 = v2[0]; - if (x2 < v2[0]) x2 = v2[0]; - if (y1 > v2[1]) y1 = v2[1]; - if (y2 < v2[1]) y2 = v2[1]; - } - else - { - x1 = x2 = v2[0]; - y1 = y2 = v2[1]; - } + r_shadow_lightscissor[0] = r_refdef.view.viewport.x; + 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; + return false; } - - // now convert the scissor rectangle to integer screen coordinates - ix1 = (int)(x1 - 1.0f); - iy1 = vid.height - (int)(y2 - 1.0f); - ix2 = (int)(x2 + 1.0f); - iy2 = vid.height - (int)(y1 + 1.0f); - //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2); - - // clamp it to the screen - if (ix1 < r_refdef.view.viewport.x) ix1 = r_refdef.view.viewport.x; - if (iy1 < r_refdef.view.viewport.y) iy1 = r_refdef.view.viewport.y; - if (ix2 > r_refdef.view.viewport.x + r_refdef.view.viewport.width) ix2 = r_refdef.view.viewport.x + r_refdef.view.viewport.width; - if (iy2 > r_refdef.view.viewport.y + r_refdef.view.viewport.height) iy2 = r_refdef.view.viewport.y + r_refdef.view.viewport.height; - - // if it is inside out, it's not visible - if (ix2 <= ix1 || iy2 <= iy1) - return true; - - // the light area is visible, set up the scissor rectangle - r_shadow_lightscissor[0] = ix1; - r_shadow_lightscissor[1] = iy1; - r_shadow_lightscissor[2] = ix2 - ix1; - r_shadow_lightscissor[3] = iy2 - iy1; - - r_refdef.stats.lights_scissored++; + if(R_ScissorForBBox(mins, maxs, r_shadow_lightscissor)) + return true; // invisible + if(r_shadow_lightscissor[0] != r_refdef.view.viewport.x + || 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++; return false; } @@ -3456,19 +3397,19 @@ void R_Shadow_PrepareLight(rtlight_t *rtlight) static entity_render_t *lightentities_noselfshadow[MAX_EDICTS]; static entity_render_t *shadowentities[MAX_EDICTS]; static entity_render_t *shadowentities_noselfshadow[MAX_EDICTS]; + qboolean nolight; rtlight->draw = false; // 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) - if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f)) - return; + nolight = VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f); // loading is done before visibility checks because loading should happen // all at once at the start of a level, not when it stalls gameplay. // (especially important to benchmarks) // compile light - if (rtlight->isstatic && (!rtlight->compiled || (rtlight->shadow && rtlight->shadowmode != (int)r_shadow_shadowmode)) && r_shadow_realtime_world_compile.integer) + if (rtlight->isstatic && !nolight && (!rtlight->compiled || (rtlight->shadow && rtlight->shadowmode != (int)r_shadow_shadowmode)) && r_shadow_realtime_world_compile.integer) { if (rtlight->compiled) R_RTLight_Uncompile(rtlight); @@ -3493,6 +3434,10 @@ void R_Shadow_PrepareLight(rtlight_t *rtlight) if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f)) return; + // skip processing on corona-only lights + if (nolight) + return; + // if the light box is offscreen, skip it if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs)) return; @@ -4066,9 +4011,9 @@ void R_Shadow_PrepareLights(void) r_shadow_prepass_width = vid.width; r_shadow_prepass_height = vid.height; r_shadow_prepassgeometrydepthtexture = R_LoadTextureShadowMap2D(r_shadow_texturepool, "prepassgeometrydepthmap", vid.width, vid.height, 24, false); - r_shadow_prepassgeometrynormalmaptexture = R_LoadTexture2D(r_shadow_texturepool, "prepassgeometrynormalmap", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, NULL); - r_shadow_prepasslightingdiffusetexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingdiffuse", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, NULL); - r_shadow_prepasslightingspeculartexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingspecular", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, NULL); + r_shadow_prepassgeometrynormalmaptexture = R_LoadTexture2D(r_shadow_texturepool, "prepassgeometrynormalmap", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL); + r_shadow_prepasslightingdiffusetexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingdiffuse", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL); + r_shadow_prepasslightingspeculartexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingspecular", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL); // set up the geometry pass fbo (depth + normalmap) qglGenFramebuffersEXT(1, &r_shadow_prepassgeometryfbo);CHECKGLERROR @@ -4203,12 +4148,13 @@ extern cvar_t r_shadows_castfrombmodels; extern cvar_t r_shadows_throwdistance; extern cvar_t r_shadows_throwdirection; extern cvar_t r_shadows_focus; +extern cvar_t r_shadows_shadowmapscale; void R_Shadow_PrepareModelShadows(void) { int i; float scale, size, radius, dot1, dot2; - vec3_t shadowdir, shadowforward, shadowright, shadoworigin, shadowmins, shadowmaxs; + vec3_t shadowdir, shadowforward, shadowright, shadoworigin, shadowfocus, shadowmins, shadowmaxs; entity_render_t *ent; if (!r_refdef.scene.numentities) @@ -4218,7 +4164,9 @@ void R_Shadow_PrepareModelShadows(void) { case R_SHADOW_SHADOWMODE_SHADOWMAP2D: case R_SHADOW_SHADOWMODE_SHADOWMAPRECTANGLE: - break; + if (r_shadows.integer >= 2) + break; + // fall through case R_SHADOW_SHADOWMODE_STENCIL: for (i = 0;i < r_refdef.scene.numentities;i++) { @@ -4232,8 +4180,9 @@ void R_Shadow_PrepareModelShadows(void) } size = 2*r_shadow_shadowmapmaxsize; - scale = r_shadow_shadowmapping_precision.value; + scale = r_shadow_shadowmapping_precision.value * r_shadows_shadowmapscale.value; radius = 0.5f * size / scale; + Math_atov(r_shadows_throwdirection.string, shadowdir); VectorNormalize(shadowdir); dot1 = DotProduct(r_refdef.view.forward, shadowdir); @@ -4244,8 +4193,13 @@ 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, shadoworigin); + Math_atov(r_shadows_focus.string, 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); VectorAdd(shadoworigin, r_refdef.view.origin, shadoworigin); + if (shadowfocus[0] || shadowfocus[1] || shadowfocus[2]) + dot1 = 1; VectorMA(shadoworigin, (1.0f - fabs(dot1)) * radius, shadowforward, shadoworigin); shadowmins[0] = shadoworigin[0] - r_shadows_throwdistance.value * fabs(shadowdir[0]) - radius * (fabs(shadowforward[0]) + fabs(shadowright[0])); @@ -4269,12 +4223,12 @@ void R_Shadow_PrepareModelShadows(void) void R_DrawModelShadowMaps(void) { int i; - float relativethrowdistance, scale, size, radius, nearclip, farclip, dot1, dot2; + float relativethrowdistance, scale, size, radius, nearclip, farclip, bias, dot1, dot2; entity_render_t *ent; vec3_t relativelightorigin; vec3_t relativelightdirection, relativeforward, relativeright; vec3_t relativeshadowmins, relativeshadowmaxs; - vec3_t shadowdir, shadowforward, shadowright, shadoworigin; + vec3_t shadowdir, shadowforward, shadowright, shadoworigin, shadowfocus; float m[12]; matrix4x4_t shadowmatrix, cameramatrix, mvpmatrix, invmvpmatrix, scalematrix, texmatrix; r_viewport_t viewport; @@ -4320,19 +4274,23 @@ void R_DrawModelShadowMaps(void) } size = 2*r_shadow_shadowmapmaxsize; - - r_shadow_shadowmap_parameters[0] = bound(0.0f, 1.0f - r_shadows_darken.value, 1.0f); - r_shadow_shadowmap_parameters[1] = 1.0f; - r_shadow_shadowmap_parameters[2] = size; - r_shadow_shadowmap_parameters[3] = size; - - scale = r_shadow_shadowmapping_precision.value / size; + scale = (r_shadow_shadowmapping_precision.value * r_shadows_shadowmapscale.value) / size; 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); + + 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); VectorNormalize(shadowdir); - Math_atov(r_shadows_focus.string, shadoworigin); + Math_atov(r_shadows_focus.string, 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); VectorAdd(shadoworigin, r_refdef.view.origin, shadoworigin); dot1 = DotProduct(r_refdef.view.forward, shadowdir); dot2 = DotProduct(r_refdef.view.up, shadowdir); @@ -4342,6 +4300,8 @@ void R_DrawModelShadowMaps(void) VectorMA(r_refdef.view.up, -dot2, shadowdir, shadowforward); VectorNormalize(shadowforward); VectorM(scale, shadowforward, &m[0]); + if (shadowfocus[0] || shadowfocus[1] || shadowfocus[2]) + dot1 = 1; m[3] = fabs(dot1) * 0.5f - DotProduct(shadoworigin, &m[0]); CrossProduct(shadowdir, shadowforward, shadowright); VectorM(scale, shadowright, &m[4]); @@ -4406,7 +4366,7 @@ void R_DrawModelShadowMaps(void) Matrix4x4_Concat(&mvpmatrix, &r_refdef.view.viewport.projectmatrix, &r_refdef.view.viewport.viewmatrix); Matrix4x4_Invert_Full(&invmvpmatrix, &mvpmatrix); Matrix4x4_CreateScale3(&scalematrix, size, -size, 1); - Matrix4x4_AdjustOrigin(&scalematrix, 0, size, 0); + Matrix4x4_AdjustOrigin(&scalematrix, 0, size, -0.5f * bias); Matrix4x4_Concat(&texmatrix, &scalematrix, &shadowmatrix); Matrix4x4_Concat(&r_shadow_shadowmapmatrix, &texmatrix, &invmvpmatrix); @@ -4434,7 +4394,7 @@ void R_DrawModelShadows(void) vec3_t relativeshadowmins, relativeshadowmaxs; vec3_t tmp, shadowdir; - if (!r_refdef.scene.numentities || !vid.stencil || r_shadow_shadowmode != R_SHADOW_SHADOWMODE_STENCIL) + if (!r_refdef.scene.numentities || !vid.stencil || (r_shadow_shadowmode != R_SHADOW_SHADOWMODE_STENCIL && r_shadows.integer != 1)) return; CHECKGLERROR @@ -4474,26 +4434,34 @@ void R_DrawModelShadows(void) { if(ent->entitynumber != 0) { - // 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) + if(ent->entitynumber >= MAX_EDICTS) // csqc entity { - entnum2 = cl.entities[entnum].state_current.tagentity; - if(entnum2 >= 1 && entnum2 < cl.num_entities && cl.entities_active[entnum2]) - entnum = entnum2; - else - break; + // FIXME handle this + VectorNegate(ent->modellight_lightdir, relativelightdirection); } - if(recursion && recursion != 32) // if we followed a valid non-empty attachment chain + else { - 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); + // 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); } - else - VectorNegate(ent->modellight_lightdir, relativelightdirection); } else VectorNegate(ent->modellight_lightdir, relativelightdirection); @@ -4868,7 +4836,8 @@ void R_Shadow_DrawLightSprites(void) if (light) R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 5, &light->rtlight); } - R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL); + if (!r_editlights_lockcursor) + R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL); } int R_Shadow_GetRTLightInfo(unsigned int lightindex, float *origin, float *radius, float *color) @@ -4900,6 +4869,9 @@ void R_Shadow_SelectLightInView(void) size_t range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked best = NULL; bestrating = 0; + + if (r_editlights_lockcursor) + return; for (lightindex = 0;lightindex < range;lightindex++) { light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); @@ -4930,8 +4902,7 @@ void R_Shadow_LoadWorldLights(void) Con_Print("No map loaded.\n"); return; } - FS_StripExtension (cl.worldmodel->name, name, sizeof (name)); - strlcat (name, ".rtlights", sizeof (name)); + dpsnprintf(name, sizeof(name), "%s.rtlights", cl.worldnamenoextension); lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL); if (lightsstring) { @@ -5041,8 +5012,7 @@ void R_Shadow_SaveWorldLights(void) Con_Print("No map loaded.\n"); return; } - FS_StripExtension (cl.worldmodel->name, name, sizeof (name)); - strlcat (name, ".rtlights", sizeof (name)); + dpsnprintf(name, sizeof(name), "%s.rtlights", cl.worldnamenoextension); bufchars = bufmaxchars = 0; buf = NULL; for (lightindex = 0;lightindex < range;lightindex++) @@ -5090,8 +5060,7 @@ void R_Shadow_LoadLightsFile(void) Con_Print("No map loaded.\n"); return; } - FS_StripExtension (cl.worldmodel->name, name, sizeof (name)); - strlcat (name, ".lights", sizeof (name)); + dpsnprintf(name, sizeof(name), "%s.lights", cl.worldnamenoextension); lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL); if (lightsstring) { @@ -5153,8 +5122,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void) return; } // try to load a .ent file first - FS_StripExtension (cl.worldmodel->name, key, sizeof (key)); - strlcat (key, ".ent", sizeof (key)); + dpsnprintf(key, sizeof(key), "%s.ent", cl.worldnamenoextension); data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true, NULL); // and if that is not found, fall back to the bsp file entity string if (!data) @@ -5403,7 +5371,7 @@ void R_Shadow_EditLights_Reload_f(void) { if (!cl.worldmodel) return; - strlcpy(r_shadow_mapname, cl.worldmodel->name, sizeof(r_shadow_mapname)); + strlcpy(r_shadow_mapname, cl.worldname, sizeof(r_shadow_mapname)); R_Shadow_ClearWorldLights(); R_Shadow_LoadWorldLights(); if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray)) @@ -5768,7 +5736,7 @@ void R_Shadow_EditLights_Edit_f(void) void R_Shadow_EditLights_EditAll_f(void) { size_t lightindex; - dlight_t *light; + dlight_t *light, *oldselected; size_t range; if (!r_editlights.integer) @@ -5777,6 +5745,7 @@ void R_Shadow_EditLights_EditAll_f(void) return; } + oldselected = r_shadow_selectedlight; // EditLights doesn't seem to have a "remove" command or something so: range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked for (lightindex = 0;lightindex < range;lightindex++) @@ -5787,6 +5756,8 @@ void R_Shadow_EditLights_EditAll_f(void) R_Shadow_SelectLight(light); R_Shadow_EditLights_Edit_f(); } + // return to old selected (to not mess editing once selection is locked) + R_Shadow_SelectLight(oldselected); } void R_Shadow_EditLights_DrawSelectedLightProperties(void) @@ -5896,6 +5867,7 @@ void R_Shadow_EditLights_Help_f(void) "r_editlights_help : this help\n" "r_editlights_clear : remove all lights\n" "r_editlights_reload : reload .rtlights, .lights file, or entities\n" +"r_editlights_lock : lock selection to current light, if already locked - unlock\n" "r_editlights_save : save to .rtlights file\n" "r_editlights_spawn : create a light with default settings\n" "r_editlights_edit command : edit selected light - more documentation below\n" @@ -5980,6 +5952,26 @@ void R_Shadow_EditLights_PasteInfo_f(void) R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_bufferlight.angles, r_shadow_bufferlight.color, r_shadow_bufferlight.radius, r_shadow_bufferlight.corona, r_shadow_bufferlight.style, r_shadow_bufferlight.shadow, r_shadow_bufferlight.cubemapname, r_shadow_bufferlight.coronasizescale, r_shadow_bufferlight.ambientscale, r_shadow_bufferlight.diffusescale, r_shadow_bufferlight.specularscale, r_shadow_bufferlight.flags); } +void R_Shadow_EditLights_Lock_f(void) +{ + if (!r_editlights.integer) + { + Con_Print("Cannot lock on light when not in editing mode. Set r_editlights to 1.\n"); + return; + } + if (r_editlights_lockcursor) + { + r_editlights_lockcursor = false; + return; + } + if (!r_shadow_selectedlight) + { + Con_Print("No selected light to lock on.\n"); + return; + } + r_editlights_lockcursor = true; +} + void R_Shadow_EditLights_Init(void) { Cvar_RegisterVariable(&r_editlights); @@ -6002,6 +5994,7 @@ void R_Shadow_EditLights_Init(void) Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f, "load lights from .lights file (ignoring .rtlights or .ent files and map entities)"); Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f, "store a copy of all properties (except origin) of the selected light"); Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f, "apply the stored properties onto the selected light (making it exactly identical except for origin)"); + Cmd_AddCommand("r_editlights_lock", R_Shadow_EditLights_Lock_f, "lock selection to current light, if already locked - unlock"); } @@ -6014,7 +6007,7 @@ LIGHT SAMPLING ============================================================================= */ -void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const vec3_t p, int dynamic) +void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const vec3_t p, qboolean dynamic, qboolean rtworld) { VectorClear(diffusecolor); VectorClear(diffusenormal); @@ -6029,16 +6022,57 @@ void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffu if (dynamic) { - int i; - float f, v[3]; + int i, numlights, flag; + float f, relativepoint[3], dist, dist2, lightradius2; rtlight_t *light; + dlight_t *dlight; + + // sample rtlights + if (rtworld) + { + flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE; + numlights = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); + for (i = 0; i < numlights; i++) + { + dlight = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, i); + if (!dlight) + continue; + light = &dlight->rtlight; + if (!(light->flags & flag)) + continue; + // sample + lightradius2 = light->radius * light->radius; + VectorSubtract(light->shadoworigin, p, relativepoint); + dist2 = VectorLength2(relativepoint); + if (dist2 >= lightradius2) + continue; + dist = sqrt(dist2) / light->radius; + f = dist < 1 ? (r_shadow_lightintensityscale.value * ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist))) : 0; + if (f <= 0) + continue; + // todo: add to both ambient and diffuse + if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1) + VectorMA(ambientcolor, f, light->currentcolor, ambientcolor); + } + } + + // sample dlights for (i = 0;i < r_refdef.scene.numlights;i++) { light = r_refdef.scene.lights[i]; - Matrix4x4_Transform(&light->matrix_worldtolight, p, v); - f = 1 - VectorLength2(v); - if (f > 0 && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1) - VectorMA(ambientcolor, f, light->currentcolor, ambientcolor); + // sample + lightradius2 = light->radius * light->radius; + VectorSubtract(light->shadoworigin, p, relativepoint); + dist2 = VectorLength2(relativepoint); + if (dist2 >= lightradius2) + continue; + dist = sqrt(dist2) / light->radius; + f = dist < 1 ? (r_shadow_lightintensityscale.value * ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist))) : 0; + if (f <= 0) + continue; + // todo: add to both ambient and diffuse + if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1) + VectorMA(ambientcolor, f, light->color, ambientcolor); } } }