From f5c01d29320e5e45965d995a164291c2cc071578 Mon Sep 17 00:00:00 2001 From: havoc Date: Thu, 12 Feb 2009 05:35:34 +0000 Subject: [PATCH 1/1] implemented occlusion query support on corona rendering, this enables coronas to fade according to the percentage of occluded pixels around the light origin, giving a more realistic corona behavior some minor cleanup on shadow volume construction code git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8702 d7cf8633-e32d-0410-b094-e92efae38249 --- client.h | 4 + gl_rmain.c | 21 +++- gl_rsurf.c | 6 +- glquake.h | 20 +++- r_shadow.c | 264 ++++++++++++++++++++++++++++++++++----------------- r_shadow.h | 5 +- render.h | 5 + vid_shared.c | 28 ++++++ 8 files changed, 257 insertions(+), 96 deletions(-) diff --git a/client.h b/client.h index 14eacf09..d75c5e2f 100644 --- a/client.h +++ b/client.h @@ -109,6 +109,10 @@ typedef struct rtlight_s // rendering properties, updated each time a light is rendered // this is rtlight->color * d_lightstylevalue vec3_t currentcolor; + // used by corona updates, due to occlusion query + float corona_visibility; + unsigned int corona_queryindex_visiblepixels; + unsigned int corona_queryindex_allpixels; // this is R_Shadow_Cubemap(rtlight->cubemapname) rtexture_t *currentcubemap; diff --git a/gl_rmain.c b/gl_rmain.c index e0e7ab43..9d83c1f1 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -168,6 +168,10 @@ rtexture_t *r_texture_gammaramps; unsigned int r_texture_gammaramps_serial; //rtexture_t *r_texture_fogintensity; +unsigned int r_queries[R_MAX_OCCLUSION_QUERIES]; +unsigned int r_numqueries; +unsigned int r_maxqueries; + char r_qwskincache[MAX_SCOREBOARD][MAX_QPATH]; skinframe_t *r_qwskincache_skinframe[MAX_SCOREBOARD]; @@ -1734,7 +1738,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f if (r_glsl_permutation->loc_SpecularScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, r_refdef.lightmapintensity * specularscale); } if (r_glsl_permutation->loc_TintColor >= 0) qglUniform4fARB(r_glsl_permutation->loc_TintColor, rsurface.texture->lightmapcolor[0], rsurface.texture->lightmapcolor[1], rsurface.texture->lightmapcolor[2], rsurface.texture->lightmapcolor[3]); - if (r_glsl_permutation->loc_GlowScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_GlowScale, r_hdr_glowintensity.value); + if (r_glsl_permutation->loc_GlowScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_GlowScale, r_hdr_glowintensity.value); // additive passes are only darkened by fog, not tinted if (r_glsl_permutation->loc_FogColor >= 0) { @@ -2246,6 +2250,10 @@ skinframe_t *R_SkinFrame_LoadMissing(void) void gl_main_start(void) { + r_numqueries = 0; + r_maxqueries = 0; + memset(r_queries, 0, sizeof(r_queries)); + memset(r_qwskincache, 0, sizeof(r_qwskincache)); memset(r_qwskincache_skinframe, 0, sizeof(r_qwskincache_skinframe)); @@ -2275,6 +2283,13 @@ void gl_main_start(void) void gl_main_shutdown(void) { + if (r_maxqueries) + qglDeleteQueriesARB(r_maxqueries, r_queries); + + r_numqueries = 0; + r_maxqueries = 0; + memset(r_queries, 0, sizeof(r_queries)); + memset(r_qwskincache, 0, sizeof(r_qwskincache)); memset(r_qwskincache_skinframe, 0, sizeof(r_qwskincache_skinframe)); @@ -2473,8 +2488,8 @@ void GL_Init (void) Con_Printf("GL_VENDOR: %s\n", gl_vendor); Con_Printf("GL_RENDERER: %s\n", gl_renderer); Con_Printf("GL_VERSION: %s\n", gl_version); - Con_Printf("GL_EXTENSIONS: %s\n", gl_extensions); - Con_Printf("%s_EXTENSIONS: %s\n", gl_platform, gl_platformextensions); + Con_DPrintf("GL_EXTENSIONS: %s\n", gl_extensions); + Con_DPrintf("%s_EXTENSIONS: %s\n", gl_platform, gl_platformextensions); VID_CheckExtensions(); diff --git a/gl_rsurf.c b/gl_rsurf.c index c6fe69e8..e8426310 100644 --- a/gl_rsurf.c +++ b/gl_rsurf.c @@ -974,7 +974,7 @@ void R_Q1BSP_CompileShadowVolume(entity_render_t *ent, vec3_t relativelightorigi continue; R_Shadow_MarkVolumeFromBox(surface->num_firstshadowmeshtriangle, surface->num_triangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, relativelightorigin, relativelightdirection, r_shadow_compilingrtlight->cullmins, r_shadow_compilingrtlight->cullmaxs, surface->mins, surface->maxs); } - R_Shadow_VolumeFromList(model->brush.shadowmesh->numverts, model->brush.shadowmesh->numtriangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, model->brush.shadowmesh->neighbor3i, relativelightorigin, relativelightdirection, projectdistance, numshadowmark, shadowmarklist); + R_Shadow_VolumeFromList(model->brush.shadowmesh->numverts, model->brush.shadowmesh->numtriangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, model->brush.shadowmesh->neighbor3i, relativelightorigin, relativelightdirection, projectdistance, numshadowmark, shadowmarklist, ent->mins, ent->maxs); r_shadow_compilingrtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, false, false, true); } @@ -1002,7 +1002,7 @@ void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, const vec3_t relativelightor continue; R_Shadow_MarkVolumeFromBox(surface->num_firstshadowmeshtriangle, surface->num_triangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, relativelightorigin, relativelightdirection, lightmins, lightmaxs, surface->mins, surface->maxs); } - R_Shadow_VolumeFromList(model->brush.shadowmesh->numverts, model->brush.shadowmesh->numtriangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, model->brush.shadowmesh->neighbor3i, relativelightorigin, relativelightdirection, projectdistance, numshadowmark, shadowmarklist); + R_Shadow_VolumeFromList(model->brush.shadowmesh->numverts, model->brush.shadowmesh->numtriangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, model->brush.shadowmesh->neighbor3i, relativelightorigin, relativelightdirection, projectdistance, numshadowmark, shadowmarklist, ent->mins, ent->maxs); } else { @@ -1018,7 +1018,7 @@ void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, const vec3_t relativelightor RSurf_PrepareVerticesForBatch(false, false, 1, &surface); R_Shadow_MarkVolumeFromBox(surface->num_firsttriangle, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i, relativelightorigin, relativelightdirection, lightmins, lightmaxs, surface->mins, surface->maxs); } - R_Shadow_VolumeFromList(model->surfmesh.num_vertices, model->surfmesh.num_triangles, rsurface.vertex3f, model->surfmesh.data_element3i, model->surfmesh.data_neighbor3i, relativelightorigin, relativelightdirection, projectdistance, numshadowmark, shadowmarklist); + R_Shadow_VolumeFromList(model->surfmesh.num_vertices, model->surfmesh.num_triangles, rsurface.vertex3f, model->surfmesh.data_element3i, model->surfmesh.data_neighbor3i, relativelightorigin, relativelightdirection, projectdistance, numshadowmark, shadowmarklist, ent->mins, ent->maxs); } if (ent->model->brush.submodel) GL_PolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset); diff --git a/glquake.h b/glquake.h index 5acbc446..4ff40cd3 100644 --- a/glquake.h +++ b/glquake.h @@ -807,7 +807,25 @@ extern void (GLAPIENTRY *qglGetCompressedTexImageARB)(GLenum target, GLint lod, #define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 #define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 #endif - + +// GL_ARB_occlusion_query +extern int gl_support_arb_occlusion_query; +extern void (GLAPIENTRY *qglGenQueriesARB)(GLsizei n, GLuint *ids); +extern void (GLAPIENTRY *qglDeleteQueriesARB)(GLsizei n, const GLuint *ids); +extern GLboolean (GLAPIENTRY *qglIsQueryARB)(GLuint qid); +extern void (GLAPIENTRY *qglBeginQueryARB)(GLenum target, GLuint qid); +extern void (GLAPIENTRY *qglEndQueryARB)(GLenum target); +extern void (GLAPIENTRY *qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params); +extern void (GLAPIENTRY *qglGetQueryObjectivARB)(GLuint qid, GLenum pname, GLint *params); +extern void (GLAPIENTRY *qglGetQueryObjectuivARB)(GLuint qid, GLenum pname, GLuint *params); +#ifndef GL_SAMPLES_PASSED_ARB +#define GL_SAMPLES_PASSED_ARB 0x8914 +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#endif + // GL_EXT_bgr #define GL_BGR 0x80E0 diff --git a/r_shadow.c b/r_shadow.c index 284f569e..cfb1d58a 100644 --- a/r_shadow.c +++ b/r_shadow.c @@ -236,6 +236,8 @@ 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 r_glsl lighting)"}; cvar_t r_coronas = {CVAR_SAVE, "r_coronas", "1", "brightness of corona flare effects around certain lights, 0 disables corona effects"}; +cvar_t r_coronas_occlusionsizescale = {CVAR_SAVE, "r_coronas_occlusionsizescale", "0.1", "size of light source for corona occlusion checksm 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 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)"}; @@ -466,6 +468,8 @@ void R_Shadow_Init(void) Cvar_RegisterVariable(&r_shadow_polygonoffset); Cvar_RegisterVariable(&r_shadow_texture3d); Cvar_RegisterVariable(&r_coronas); + Cvar_RegisterVariable(&r_coronas_occlusionsizescale); + Cvar_RegisterVariable(&r_coronas_occlusionquery); Cvar_RegisterVariable(&gl_flashblend); Cvar_RegisterVariable(&gl_ext_separatestencil); Cvar_RegisterVariable(&gl_ext_stenciltwoside); @@ -624,28 +628,6 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int * else VectorClear(projectvector); - if (maxvertexupdate < innumvertices) - { - maxvertexupdate = innumvertices; - if (vertexupdate) - Mem_Free(vertexupdate); - if (vertexremap) - Mem_Free(vertexremap); - vertexupdate = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int)); - vertexremap = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int)); - vertexupdatenum = 0; - } - vertexupdatenum++; - if (vertexupdatenum == 0) - { - vertexupdatenum = 1; - memset(vertexupdate, 0, maxvertexupdate * sizeof(int)); - memset(vertexremap, 0, maxvertexupdate * sizeof(int)); - } - - for (i = 0;i < numshadowmarktris;i++) - shadowmark[shadowmarktris[i]] = shadowmarkcount; - // create the vertices if (projectdirection) { @@ -889,36 +871,9 @@ void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *inv } } -static void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i) -{ - if (r_shadow_compilingrtlight) - { - // if we're compiling an rtlight, capture the mesh - Mod_ShadowMesh_AddMesh(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i); - return; - } - r_refdef.stats.lights_shadowtriangles += numtriangles; - CHECKGLERROR - R_Mesh_VertexPointer(vertex3f, 0, 0); - GL_LockArrays(0, numvertices); - if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL) - { - // decrement stencil if backface is behind depthbuffer - GL_CullFace(r_refdef.view.cullface_front); - qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR - R_Mesh_Draw(0, numvertices, 0, numtriangles, element3i, NULL, 0, 0); - // increment stencil if frontface is behind depthbuffer - GL_CullFace(r_refdef.view.cullface_back); - qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR - } - R_Mesh_Draw(0, numvertices, 0, numtriangles, element3i, NULL, 0, 0); - GL_LockArrays(0, 0); - CHECKGLERROR -} - -void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, const vec3_t projectdirection, float projectdistance, int nummarktris, const int *marktris) +void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, const vec3_t projectdirection, float projectdistance, int nummarktris, const int *marktris, vec3_t trismins, vec3_t trismaxs) { - int tris, outverts; + int i, tris, outverts; if (projectdistance < 0.1) { Con_Printf("R_Shadow_Volume: projectdistance %f\n", projectdistance); @@ -929,9 +884,57 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, // make sure shadowelements is big enough for this volume if (maxshadowtriangles < nummarktris || maxshadowvertices < numverts) R_Shadow_ResizeShadowArrays((numverts + 255) & ~255, (nummarktris + 255) & ~255); - tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris); - r_refdef.stats.lights_dynamicshadowtriangles += tris; - R_Shadow_RenderVolume(outverts, tris, shadowvertex3f, shadowelements); + + if (maxvertexupdate < numverts) + { + maxvertexupdate = numverts; + if (vertexupdate) + Mem_Free(vertexupdate); + if (vertexremap) + Mem_Free(vertexremap); + vertexupdate = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int)); + vertexremap = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int)); + vertexupdatenum = 0; + } + vertexupdatenum++; + if (vertexupdatenum == 0) + { + vertexupdatenum = 1; + memset(vertexupdate, 0, maxvertexupdate * sizeof(int)); + memset(vertexremap, 0, maxvertexupdate * sizeof(int)); + } + + for (i = 0;i < nummarktris;i++) + shadowmark[marktris[i]] = shadowmarkcount; + + if (r_shadow_compilingrtlight) + { + // if we're compiling an rtlight, capture the mesh + tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris); + Mod_ShadowMesh_AddMesh(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, shadowvertex3f, NULL, NULL, NULL, NULL, tris, shadowelements); + } + else + { + tris = R_Shadow_ConstructShadowVolume(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; + CHECKGLERROR + R_Mesh_VertexPointer(shadowvertex3f, 0, 0); + GL_LockArrays(0, outverts); + if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL) + { + // decrement stencil if backface is behind depthbuffer + GL_CullFace(r_refdef.view.cullface_front); + qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR + R_Mesh_Draw(0, outverts, 0, tris, shadowelements, NULL, 0, 0); + // increment stencil if frontface is behind depthbuffer + GL_CullFace(r_refdef.view.cullface_back); + qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR + } + R_Mesh_Draw(0, outverts, 0, tris, shadowelements, NULL, 0, 0); + GL_LockArrays(0, 0); + CHECKGLERROR + } } static void R_Shadow_MakeTextures_MakeCorona(void) @@ -1092,7 +1095,14 @@ void R_Shadow_RenderMode_Reset(void) R_SetupGenericShader(false); } -void R_Shadow_RenderMode_StencilShadowVolumes(qboolean clearstencil) +void R_Shadow_ClearStencil(void) +{ + CHECKGLERROR + GL_Clear(GL_STENCIL_BUFFER_BIT); + r_refdef.stats.lights_clears++; +} + +void R_Shadow_RenderMode_StencilShadowVolumes(void) { CHECKGLERROR R_Shadow_RenderMode_Reset(); @@ -1119,9 +1129,6 @@ void R_Shadow_RenderMode_StencilShadowVolumes(qboolean clearstencil) qglStencilMask(~0);CHECKGLERROR qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR } - if (clearstencil) - GL_Clear(GL_STENCIL_BUFFER_BIT); - r_refdef.stats.lights_clears++; } void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent) @@ -2787,7 +2794,7 @@ void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned if (CHECKPVSBIT(trispvs, t)) shadowmarklist[numshadowmark++] = t; } - R_Shadow_VolumeFromList(r_refdef.scene.worldmodel->brush.shadowmesh->numverts, r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles, r_refdef.scene.worldmodel->brush.shadowmesh->vertex3f, r_refdef.scene.worldmodel->brush.shadowmesh->element3i, r_refdef.scene.worldmodel->brush.shadowmesh->neighbor3i, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius + r_refdef.scene.worldmodel->radius*2 + r_shadow_projectdistance.value, numshadowmark, shadowmarklist); + R_Shadow_VolumeFromList(r_refdef.scene.worldmodel->brush.shadowmesh->numverts, r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles, r_refdef.scene.worldmodel->brush.shadowmesh->vertex3f, r_refdef.scene.worldmodel->brush.shadowmesh->element3i, r_refdef.scene.worldmodel->brush.shadowmesh->neighbor3i, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius + r_refdef.scene.worldmodel->radius*2 + r_shadow_projectdistance.value, numshadowmark, shadowmarklist, r_refdef.scene.worldmodel->normalmins, r_refdef.scene.worldmodel->normalmaxs); } else if (numsurfaces) r_refdef.scene.worldmodel->DrawShadowVolume(r_refdef.scene.worldentity, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius, numsurfaces, surfacelist, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs); @@ -3057,7 +3064,8 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible) { // draw stencil shadow volumes to mask off pixels that are in shadow // so that they won't receive lighting - R_Shadow_RenderMode_StencilShadowVolumes(true); + R_Shadow_ClearStencil(); + R_Shadow_RenderMode_StencilShadowVolumes(); if (numsurfaces) R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs); for (i = 0;i < numshadowentities;i++) @@ -3078,7 +3086,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible) R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]); } - R_Shadow_RenderMode_StencilShadowVolumes(false); + R_Shadow_RenderMode_StencilShadowVolumes(); } for (i = 0;i < numshadowentities_noselfshadow;i++) R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]); @@ -3201,7 +3209,8 @@ void R_DrawModelShadows(void) else r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL; - R_Shadow_RenderMode_StencilShadowVolumes(true); + R_Shadow_ClearStencil(); + R_Shadow_RenderMode_StencilShadowVolumes(); for (i = 0;i < r_refdef.scene.numentities;i++) { @@ -3297,64 +3306,145 @@ void R_DrawModelShadows(void) R_Shadow_RenderMode_End(); } +void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequery) +{ + // if it's too close, skip it + if (VectorLength(rtlight->color) < (1.0f / 256.0f)) + return; + if (VectorDistance2(rtlight->shadoworigin, r_refdef.view.origin) < 32.0f * 32.0f) + return; + if (usequery && r_numqueries + 2 <= r_maxqueries) + { + rtlight->corona_queryindex_allpixels = r_queries[r_numqueries++]; + rtlight->corona_queryindex_visiblepixels = r_queries[r_numqueries++]; + CHECKGLERROR + qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, rtlight->corona_queryindex_allpixels); + R_DrawSprite(GL_ONE, GL_ZERO, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, 1, 1, 1, 1); + qglEndQueryARB(GL_SAMPLES_PASSED_ARB); + qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, rtlight->corona_queryindex_visiblepixels); + R_DrawSprite(GL_ONE, GL_ZERO, r_shadow_lightcorona, NULL, false, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, 1, 1, 1, 1); + qglEndQueryARB(GL_SAMPLES_PASSED_ARB); + CHECKGLERROR + } + rtlight->corona_visibility = 1; +} + +void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale) +{ + vec3_t color; + GLint allpixels = 0, visiblepixels = 0; + // now we have to check the query result + if (rtlight->corona_queryindex_visiblepixels) + { + CHECKGLERROR + qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, &visiblepixels); + qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, &allpixels); + CHECKGLERROR + //Con_Printf("%i of %i pixels\n", (int)visiblepixels, (int)allpixels); + if (visiblepixels < 1 || allpixels < 1) + return; + rtlight->corona_visibility *= (float)visiblepixels / (float)allpixels; + cscale *= rtlight->corona_visibility; + } + else + { + // FIXME: these traces should scan all render entities instead of cl.world + if (CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1) + return; + } + VectorScale(rtlight->color, cscale, color); + if (VectorLength(color) > (1.0f / 256.0f)) + R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, color[0], color[1], color[2], 1); +} + void R_DrawCoronas(void) { int i, flag; - float cscale, scale; + qboolean usequery; size_t lightindex; dlight_t *light; rtlight_t *rtlight; size_t range; if (r_coronas.value < (1.0f / 256.0f) && !gl_flashblend.integer) return; - R_Mesh_Matrix(&identitymatrix); + if (r_waterstate.renderingscene) + return; flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE; - // FIXME: these traces should scan all render entities instead of cl.world + R_Mesh_Matrix(&identitymatrix); + range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked + + // check occlusion of coronas + // use GL_ARB_occlusion_query if available + // otherwise use raytraces + r_numqueries = 0; + usequery = gl_support_arb_occlusion_query && r_coronas_occlusionquery.integer; + if (usequery) + { + GL_ColorMask(0,0,0,0); + if (r_maxqueries < range + r_refdef.scene.numlights) + if (r_maxqueries < R_MAX_OCCLUSION_QUERIES) + { + i = r_maxqueries; + r_maxqueries = (range + r_refdef.scene.numlights) * 2; + r_maxqueries = min(r_maxqueries, R_MAX_OCCLUSION_QUERIES); + CHECKGLERROR + qglGenQueriesARB(r_maxqueries - i, r_queries + i); + CHECKGLERROR + } + } for (lightindex = 0;lightindex < range;lightindex++) { light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); if (!light) continue; rtlight = &light->rtlight; + rtlight->corona_visibility = 0; + rtlight->corona_queryindex_visiblepixels = 0; + rtlight->corona_queryindex_allpixels = 0; if (!(rtlight->flags & flag)) continue; - if (rtlight->corona * r_coronas.value <= 0) + if (rtlight->corona <= 0) continue; if (r_shadow_debuglight.integer >= 0 && r_shadow_debuglight.integer != (int)lightindex) continue; - cscale = rtlight->corona * r_coronas.value* 0.25f; - scale = rtlight->radius * rtlight->coronasizescale; - if (VectorDistance2(rtlight->shadoworigin, r_refdef.view.origin) < 16.0f * 16.0f) - continue; - if (CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1) - continue; - R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, rtlight->color[0] * cscale, rtlight->color[1] * cscale, rtlight->color[2] * cscale, 1); + R_BeginCoronaQuery(rtlight, rtlight->radius * rtlight->coronasizescale * r_coronas_occlusionsizescale.value, usequery); } for (i = 0;i < r_refdef.scene.numlights;i++) { rtlight = &r_refdef.scene.lights[i]; + rtlight->corona_visibility = 0; + rtlight->corona_queryindex_visiblepixels = 0; + rtlight->corona_queryindex_allpixels = 0; if (!(rtlight->flags & flag)) continue; if (rtlight->corona <= 0) continue; - if (VectorDistance2(rtlight->shadoworigin, r_refdef.view.origin) < 32.0f * 32.0f) + R_BeginCoronaQuery(rtlight, rtlight->radius * rtlight->coronasizescale * r_coronas_occlusionsizescale.value, usequery); + } + if (usequery) + GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1); + + // now draw the coronas using the query data for intensity info + for (lightindex = 0;lightindex < range;lightindex++) + { + light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex); + if (!light) continue; - if (gl_flashblend.integer) - { - cscale = rtlight->corona * 1.0f; - scale = rtlight->radius * rtlight->coronasizescale * 2.0f; - } - else - { - cscale = rtlight->corona * r_coronas.value* 0.25f; - scale = rtlight->radius * rtlight->coronasizescale; - } - if (VectorLength(rtlight->color) * cscale < (1.0f / 256.0f)) + rtlight = &light->rtlight; + if (rtlight->corona_visibility <= 0) continue; - if (CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1) + R_DrawCorona(rtlight, rtlight->corona * r_coronas.value * 0.25f, rtlight->radius * rtlight->coronasizescale); + } + for (i = 0;i < r_refdef.scene.numlights;i++) + { + rtlight = &r_refdef.scene.lights[i]; + if (rtlight->corona_visibility <= 0) continue; - R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, rtlight->color[0] * cscale, rtlight->color[1] * cscale, rtlight->color[2] * cscale, 1); + if (gl_flashblend.integer) + R_DrawCorona(rtlight, rtlight->corona, rtlight->radius * rtlight->coronasizescale * 2.0f); + else + R_DrawCorona(rtlight, rtlight->corona * r_coronas.value * 0.25f, rtlight->radius * rtlight->coronasizescale); } } diff --git a/r_shadow.h b/r_shadow.h index 33fc453e..706f75aa 100644 --- a/r_shadow.h +++ b/r_shadow.h @@ -38,17 +38,18 @@ extern cvar_t gl_ext_separatestencil; extern cvar_t gl_ext_stenciltwoside; void R_Shadow_Init(void); -void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, const vec3_t projectdirection, float projectdistance, int nummarktris, const int *marktris); +void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, const vec3_t projectdirection, float projectdistance, int nummarktris, const int *marktris, vec3_t trismins, vec3_t trismaxs); void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, const vec3_t projectdirection, const vec3_t lightmins, const vec3_t lightmaxs, const vec3_t surfacemins, const vec3_t surfacemaxs); void R_Shadow_RenderLighting(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const unsigned short *element3s, int element3i_bufferobject, int element3s_bufferobject); void R_Shadow_RenderMode_Begin(void); void R_Shadow_RenderMode_ActiveLight(const rtlight_t *rtlight); void R_Shadow_RenderMode_Reset(void); -void R_Shadow_RenderMode_StencilShadowVolumes(qboolean clearstencil); +void R_Shadow_RenderMode_StencilShadowVolumes(void); void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent); void R_Shadow_RenderMode_VisibleShadowVolumes(void); void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent); void R_Shadow_RenderMode_End(void); +void R_Shadow_ClearStencil(void); void R_Shadow_SetupEntityLight(const entity_render_t *ent); qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs); diff --git a/render.h b/render.h index c39b4032..f2b617b9 100644 --- a/render.h +++ b/render.h @@ -191,6 +191,11 @@ extern rtexture_t *r_texture_normalizationcube; extern rtexture_t *r_texture_fogattenuation; //extern rtexture_t *r_texture_fogintensity; +#define R_MAX_OCCLUSION_QUERIES 4096 +extern unsigned int r_queries[R_MAX_OCCLUSION_QUERIES]; +extern unsigned int r_numqueries; +extern unsigned int r_maxqueries; + void R_TimeReport(char *name); // r_stain diff --git a/vid_shared.c b/vid_shared.c index 5edba670..3848c5f9 100644 --- a/vid_shared.c +++ b/vid_shared.c @@ -63,6 +63,8 @@ int gl_support_fragment_shader = false; int gl_support_arb_vertex_buffer_object = false; //GL_ARB_texture_compression int gl_support_texture_compression = false; +//GL_ARB_occlusion_query +int gl_support_arb_occlusion_query = false; // LordHavoc: if window is hidden, don't update screen qboolean vid_hidden = true; @@ -382,6 +384,15 @@ void (GLAPIENTRY *qglCompressedTexSubImage2DARB)(GLenum target, GLint level, GLi void (GLAPIENTRY *qglCompressedTexSubImage1DARB)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); void (GLAPIENTRY *qglGetCompressedTexImageARB)(GLenum target, GLint lod, void *img); +void (GLAPIENTRY *qglGenQueriesARB)(GLsizei n, GLuint *ids); +void (GLAPIENTRY *qglDeleteQueriesARB)(GLsizei n, const GLuint *ids); +GLboolean (GLAPIENTRY *qglIsQueryARB)(GLuint qid); +void (GLAPIENTRY *qglBeginQueryARB)(GLenum target, GLuint qid); +void (GLAPIENTRY *qglEndQueryARB)(GLenum target); +void (GLAPIENTRY *qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params); +void (GLAPIENTRY *qglGetQueryObjectivARB)(GLuint qid, GLenum pname, GLint *params); +void (GLAPIENTRY *qglGetQueryObjectuivARB)(GLuint qid, GLenum pname, GLuint *params); + int GL_CheckExtension(const char *name, const dllfunction_t *funcs, const char *disableparm, int silent) { int failed = false; @@ -705,6 +716,19 @@ static dllfunction_t texturecompressionfuncs[] = {NULL, NULL} }; +static dllfunction_t occlusionqueryfuncs[] = +{ + {"glGenQueriesARB", (void **) &qglGenQueriesARB}, + {"glDeleteQueriesARB", (void **) &qglDeleteQueriesARB}, + {"glIsQueryARB", (void **) &qglIsQueryARB}, + {"glBeginQueryARB", (void **) &qglBeginQueryARB}, + {"glEndQueryARB", (void **) &qglEndQueryARB}, + {"glGetQueryivARB", (void **) &qglGetQueryivARB}, + {"glGetQueryObjectivARB", (void **) &qglGetQueryObjectivARB}, + {"glGetQueryObjectuivARB", (void **) &qglGetQueryObjectuivARB}, + {NULL, NULL} +}; + void VID_CheckExtensions(void) { gl_stencil = vid_bitsperpixel.integer == 32; @@ -734,6 +758,7 @@ void VID_CheckExtensions(void) gl_support_fragment_shader = false; gl_support_arb_vertex_buffer_object = false; gl_support_texture_compression = false; + gl_support_arb_occlusion_query = false; if (!GL_CheckExtension("OpenGL 1.1.0", opengl110funcs, NULL, false)) Sys_Error("OpenGL 1.1.0 functions not found"); @@ -813,6 +838,9 @@ void VID_CheckExtensions(void) if ((gl_support_vertex_shader = GL_CheckExtension("GL_ARB_vertex_shader", vertexshaderfuncs, "-novertexshader", false))) gl_support_fragment_shader = GL_CheckExtension("GL_ARB_fragment_shader", NULL, "-nofragmentshader", false); CHECKGLERROR + +// COMMANDLINEOPTION: GL: -noocclusionquery disables GL_ARB_occlusion_query (which allows coronas to fade according to visibility, and potentially used for rendering optimizations) + gl_support_arb_occlusion_query = GL_CheckExtension("GL_ARB_occlusion_query", occlusionqueryfuncs, "-noocclusionquery", false); } void Force_CenterView_f (void) -- 2.39.2