From: havoc Date: Mon, 28 Oct 2002 09:17:44 +0000 (+0000) Subject: implemented scissor rect clipping of lights in realtime lighting mode X-Git-Tag: RELEASE_0_2_0_RC1~108 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=commitdiff_plain;h=105b92bbfd2a623a7bc6f59c4f7110c5b259c9b2 implemented scissor rect clipping of lights in realtime lighting mode DP is no longer fillrate limited on my GF4 at 640x480 :) git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@2571 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/gl_backend.c b/gl_backend.c index d735341a..8f681d23 100644 --- a/gl_backend.c +++ b/gl_backend.c @@ -80,6 +80,7 @@ static matrix4x4_t backend_viewmatrix; static matrix4x4_t backend_modelmatrix; static matrix4x4_t backend_modelviewmatrix; static matrix4x4_t backend_glmodelviewmatrix; +static matrix4x4_t backend_projectmatrix; static int backendunits, backendactive; static qbyte *varray_bcolor; @@ -289,6 +290,22 @@ void GL_SetupView_Mode_PerspectiveInfiniteFarClip (double fovx, double fovy, dou qglLoadMatrixd(m); qglMatrixMode(GL_MODELVIEW);CHECKGLERROR GL_SetupView_Orientation_Identity(); + backend_projectmatrix.m[0][0] = m[0]; + backend_projectmatrix.m[1][0] = m[1]; + backend_projectmatrix.m[2][0] = m[2]; + backend_projectmatrix.m[3][0] = m[3]; + backend_projectmatrix.m[0][1] = m[4]; + backend_projectmatrix.m[1][1] = m[5]; + backend_projectmatrix.m[2][1] = m[6]; + backend_projectmatrix.m[3][1] = m[7]; + backend_projectmatrix.m[0][2] = m[8]; + backend_projectmatrix.m[1][2] = m[9]; + backend_projectmatrix.m[2][2] = m[10]; + backend_projectmatrix.m[3][2] = m[11]; + backend_projectmatrix.m[0][3] = m[12]; + backend_projectmatrix.m[1][3] = m[13]; + backend_projectmatrix.m[2][3] = m[14]; + backend_projectmatrix.m[3][3] = m[15]; } void GL_SetupView_Mode_Ortho (double x1, double y1, double x2, double y2, double zNear, double zFar) @@ -447,6 +464,18 @@ void GL_Color(float cr, float cg, float cb, float ca) qglColor4f(cr, cg, cb, ca); } +void GL_TransformToScreen(const vec4_t in, vec4_t out) +{ + vec4_t temp; + float iw; + Matrix4x4_Transform4 (&backend_viewmatrix, in, temp); + Matrix4x4_Transform4 (&backend_projectmatrix, temp, out); + iw = 1.0f / out[3]; + out[0] = r_refdef.x + (out[0] * iw + 1.0f) * r_refdef.width * 0.5f; + out[1] = r_refdef.y + (out[1] * iw + 1.0f) * r_refdef.height * 0.5f; + out[2] = out[2] * iw; +} + // called at beginning of frame void R_Mesh_Start(void) { diff --git a/gl_backend.h b/gl_backend.h index 40bb6cee..f26fa654 100644 --- a/gl_backend.h +++ b/gl_backend.h @@ -17,6 +17,7 @@ void GL_SetupView_Mode_PerspectiveInfiniteFarClip (double fovx, double fovy, dou void GL_SetupView_Mode_Ortho (double x1, double y1, double x2, double y2, double zNear, double zFar); void GL_UseColorArray(void); void GL_Color(float cr, float cg, float cb, float ca); +void GL_TransformToScreen(const vec4_t in, vec4_t out); extern cvar_t gl_lockarrays; diff --git a/gl_rmain.c b/gl_rmain.c index 7ba84023..24c9baa0 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -865,6 +865,16 @@ void R_ShadowVolumeLighting (int visiblevolumes) if (clipmaxs[2] < leaf->maxs[2]) clipmaxs[2] = leaf->maxs[2]; } } + if (clipmins[0] < wl->mins[0]) clipmins[0] = wl->mins[0]; + if (clipmins[1] < wl->mins[1]) clipmins[1] = wl->mins[1]; + if (clipmins[2] < wl->mins[2]) clipmins[2] = wl->mins[2]; + if (clipmaxs[0] > wl->maxs[0]) clipmaxs[0] = wl->maxs[0]; + if (clipmaxs[1] > wl->maxs[1]) clipmaxs[1] = wl->maxs[1]; + if (clipmaxs[2] > wl->maxs[2]) clipmaxs[2] = wl->maxs[2]; + + if (R_Shadow_ScissorForBBoxAndSphere(clipmins, clipmaxs, wl->origin, wl->cullradius)) + continue; + // mark the leafs we care about so only things in those leafs will matter for (i = 0;i < wl->numleafs;i++) wl->leafs[i]->worldnodeframe = shadowframecount; @@ -1046,6 +1056,9 @@ void R_ShadowVolumeLighting (int visiblevolumes) clipmaxs[1] = rd->origin[1] + cullradius; clipmaxs[2] = rd->origin[2] + cullradius; + if (R_Shadow_ScissorForBBoxAndSphere(clipmins, clipmaxs, rd->origin, rd->cullradius)) + continue; + if (!visiblevolumes) R_Shadow_Stage_ShadowVolumes(); R_TestAndDrawShadowVolume(&cl_entities[0].render, rd->origin, cullradius, lightradius, clipmins, clipmaxs); @@ -1102,6 +1115,7 @@ void R_ShadowVolumeLighting (int visiblevolumes) if (!visiblevolumes) R_Shadow_Stage_End(); + qglDisable(GL_SCISSOR_TEST); } static void R_SetFrustum (void) diff --git a/glquake.h b/glquake.h index f68e815f..ef65f0d1 100644 --- a/glquake.h +++ b/glquake.h @@ -337,6 +337,12 @@ extern int gl_dot3ext; #endif */ +#ifndef GL_SCISSOR_TEST +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SCISSOR_BOX 0x0C10 +#endif + +extern void (GLAPIENTRY *qglScissor)(GLint x, GLint y, GLsizei width, GLsizei height); extern void (GLAPIENTRY *qglClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); diff --git a/r_shadow.c b/r_shadow.c index 4ee1b62c..aa664707 100644 --- a/r_shadow.c +++ b/r_shadow.c @@ -37,6 +37,7 @@ cvar_t r_shadow_erasebydrawing = {0, "r_shadow_erasebydrawing", "0"}; cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "0"}; cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"}; cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"}; +cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"}; void R_Shadow_ClearWorldLights(void); void r_shadow_start(void) @@ -92,6 +93,7 @@ void R_Shadow_Init(void) Cvar_RegisterVariable(&r_shadow_gloss); Cvar_RegisterVariable(&r_shadow_debuglight); Cvar_RegisterVariable(&r_shadow_erasebydrawing); + Cvar_RegisterVariable(&r_shadow_scissor); R_Shadow_EditLights_Init(); R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap); } @@ -532,6 +534,8 @@ void R_Shadow_Stage_ShadowVolumes(void) qglEnable(GL_CULL_FACE); qglEnable(GL_DEPTH_TEST); r_shadowstage = SHADOWSTAGE_STENCIL; + if (!r_shadow_erasebydrawing.integer) + qglClear(GL_STENCIL_BUFFER_BIT); } void R_Shadow_Stage_Light(void) @@ -578,10 +582,7 @@ int R_Shadow_Stage_EraseShadowVolumes(void) return true; } else - { - qglClear(GL_STENCIL_BUFFER_BIT); return false; - } } void R_Shadow_Stage_End(void) @@ -594,6 +595,7 @@ void R_Shadow_Stage_End(void) // now restore the rest of the state to normal GL_Color(1, 1, 1, 1); qglColorMask(1, 1, 1, 1); + qglDisable(GL_SCISSOR_TEST); qglDepthFunc(GL_LEQUAL); qglDisable(GL_STENCIL_TEST); qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); @@ -611,6 +613,142 @@ void R_Shadow_Stage_End(void) r_shadowstage = SHADOWSTAGE_NONE; } +int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius) +{ + int i, ix1, iy1, ix2, iy2; + float x1, y1, x2, y2, x, y; + vec3_t smins, smaxs; + vec4_t v, v2; + if (!r_shadow_scissor.integer) + return false; + // if view is inside the box, just say yes it's visible + if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0] + && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1] + && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2]) + { + qglDisable(GL_SCISSOR_TEST); + return false; + } + VectorSubtract(r_origin, origin, v); + if (DotProduct(v, v) < radius * radius) + { + qglDisable(GL_SCISSOR_TEST); + return false; + } + // create viewspace bbox + for (i = 0;i < 8;i++) + { + v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0]; + v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1]; + v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2]; + v2[0] = DotProduct(v, vright); + v2[1] = DotProduct(v, vup); + v2[2] = DotProduct(v, vpn); + if (i) + { + if (smins[0] > v2[0]) smins[0] = v2[0]; + if (smaxs[0] < v2[0]) smaxs[0] = v2[0]; + if (smins[1] > v2[1]) smins[1] = v2[1]; + if (smaxs[1] < v2[1]) smaxs[1] = v2[1]; + if (smins[2] > v2[2]) smins[2] = v2[2]; + if (smaxs[2] < v2[2]) smaxs[2] = v2[2]; + } + else + { + smins[0] = smaxs[0] = v2[0]; + smins[1] = smaxs[1] = v2[1]; + smins[2] = smaxs[2] = v2[2]; + } + } + // now we have a bbox in viewspace + // clip it to the viewspace version of the sphere + v[0] = origin[0] - r_origin[0]; + v[1] = origin[1] - r_origin[1]; + v[2] = origin[2] - r_origin[2]; + v2[0] = DotProduct(v, vright); + v2[1] = DotProduct(v, vup); + v2[2] = DotProduct(v, vpn); + if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius; + if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius; + if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius; + if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius; + if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius; + if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius; + // clip it to the view plane + if (smins[2] < 1) + smins[2] = 1; + // return true if that culled the box + if (smins[2] >= smaxs[2]) + return true; + // ok some of it is infront of the view, transform each corner back to + // worldspace and then to screenspace and make screen rect + for (i = 0;i < 8;i++) + { + v2[0] = (i & 1) ? smins[0] : smaxs[0]; + v2[1] = (i & 2) ? smins[1] : smaxs[1]; + v2[2] = (i & 4) ? smins[2] : smaxs[2]; + v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0]; + v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1]; + v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2]; + v[3] = 1.0f; + GL_TransformToScreen(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]); + x = v2[0]; + y = v2[1]; + if (i) + { + if (x1 > x) x1 = x; + if (x2 < x) x2 = x; + if (y1 > y) y1 = y; + if (y2 < y) y2 = y; + } + else + { + x1 = x2 = x; + y1 = y2 = y; + } + } + /* + // this code doesn't handle boxes with any points behind view properly + x1 = 1000;x2 = -1000; + y1 = 1000;y2 = -1000; + for (i = 0;i < 8;i++) + { + v[0] = (i & 1) ? mins[0] : maxs[0]; + v[1] = (i & 2) ? mins[1] : maxs[1]; + v[2] = (i & 4) ? mins[2] : maxs[2]; + v[3] = 1.0f; + GL_TransformToScreen(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 (v2[2] > 0) + { + x = v2[0]; + y = v2[1]; + + if (x1 > x) x1 = x; + if (x2 < x) x2 = x; + if (y1 > y) y1 = y; + if (y2 < y) y2 = y; + } + } + */ + ix1 = x1 - 1.0f; + iy1 = y1 - 1.0f; + ix2 = x2 + 1.0f; + iy2 = y2 + 1.0f; + //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2); + if (ix1 < r_refdef.x) ix1 = r_refdef.x; + if (iy1 < r_refdef.y) iy1 = r_refdef.y; + if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width; + if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height; + if (ix2 <= ix1 || iy2 <= iy1) + return true; + // set up the scissor rectangle + qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1); + qglEnable(GL_SCISSOR_TEST); + return false; +} + void R_Shadow_GenTexCoords_Attenuation2D1D(float *out2d, float *out1d, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, float lightradius) { int i; diff --git a/r_shadow.h b/r_shadow.h index 1aa85320..9b119357 100644 --- a/r_shadow.h +++ b/r_shadow.h @@ -24,6 +24,7 @@ void R_Shadow_Stage_Light(void); // otherwise clears stencil int R_Shadow_Stage_EraseShadowVolumes(void); void R_Shadow_Stage_End(void); +int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius); typedef struct worldlight_s { diff --git a/vid_shared.c b/vid_shared.c index a65d5a72..88f2dbb8 100644 --- a/vid_shared.c +++ b/vid_shared.c @@ -179,6 +179,7 @@ void (GLAPIENTRY *qglTexImage3D)(GLenum target, GLint level, GLenum internalForm void (GLAPIENTRY *qglTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); void (GLAPIENTRY *qglCopyTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +void (GLAPIENTRY *qglScissor)(GLint x, GLint y, GLsizei width, GLsizei height); int GL_CheckExtension(const char *name, const gl_extensionfunctionlist_t *funcs, const char *disableparm, int silent) { @@ -303,6 +304,7 @@ static gl_extensionfunctionlist_t opengl110funcs[] = {"glCopyTexImage2D", (void **) &qglCopyTexImage2D}, {"glCopyTexSubImage1D", (void **) &qglCopyTexSubImage1D}, {"glCopyTexSubImage2D", (void **) &qglCopyTexSubImage2D}, + {"glScissor", (void **) &qglScissor}, {NULL, NULL} };