From fabf8055763f9a2d36eab5f0413873f1720683de Mon Sep 17 00:00:00 2001 From: havoc Date: Sun, 30 Sep 2007 14:50:52 +0000 Subject: [PATCH] implemented pvs support in water reflection rendering, greatly improving performance with r_glsl_water 1 implemented some checks to shut off r_glsl_water if texture creation fails git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@7596 d7cf8633-e32d-0410-b094-e92efae38249 --- client.h | 7 +-- gl_rmain.c | 121 ++++++++++++++++++++++++++++++++++++------------- gl_rsurf.c | 25 +++++++++- model_brush.c | 5 +- model_shared.h | 2 +- 5 files changed, 121 insertions(+), 39 deletions(-) diff --git a/client.h b/client.h index cd92bc8d..b358749d 100644 --- a/client.h +++ b/client.h @@ -1406,6 +1406,7 @@ typedef struct r_view_s int numfrustumplanes; mplane_t frustum[6]; qboolean useclipplane; + qboolean usecustompvs; // uses r_viewcache.pvsbits as-is rather than computing it mplane_t clipplane; float frustum_x, frustum_y; vec3_t frustumcorner[4]; @@ -1448,11 +1449,11 @@ typedef struct r_viewcache_s // flag arrays used for visibility checking on world model // (all other entities have no per-surface/per-leaf visibility checks) // TODO: dynamic resize according to r_refdef.worldmodel->brush.num_clusters - unsigned char world_pvsbits[(32768+7)>>3]; + unsigned char world_pvsbits[(32768+7)>>3]; // FIXME: buffer overflow on huge maps // TODO: dynamic resize according to r_refdef.worldmodel->brush.num_leafs - unsigned char world_leafvisible[32768]; + unsigned char world_leafvisible[32768]; // FIXME: buffer overflow on huge maps // TODO: dynamic resize according to r_refdef.worldmodel->num_surfaces - unsigned char world_surfacevisible[262144]; + unsigned char world_surfacevisible[262144]; // FIXME: buffer overflow on huge maps // if true, the view is currently in a leaf without pvs data qboolean world_novis; } diff --git a/gl_rmain.c b/gl_rmain.c index f0b636c3..af984312 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -155,6 +155,8 @@ typedef struct r_waterstate_waterplane_s rtexture_t *texture_reflection; mplane_t plane; int materialflags; // combined flags of all water surfaces on this plane + unsigned char pvsbits[(32768+7)>>3]; // FIXME: buffer overflow on huge maps + qboolean pvsvalid; } r_waterstate_waterplane_t; @@ -2480,8 +2482,10 @@ static void R_Water_AddWaterPlane(msurface_t *surface) { int triangleindex, planeindex; const int *e; + vec_t f; vec3_t vert[3]; vec3_t normal; + vec3_t center; r_waterstate_waterplane_t *p; // just use the first triangle with a valid normal for any decisions VectorClear(normal); @@ -2494,16 +2498,24 @@ static void R_Water_AddWaterPlane(msurface_t *surface) if (VectorLength2(normal) >= 0.001) break; } - for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++) + // now find the center of this surface + for (triangleindex = 0, e = rsurface.modelelement3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles*3;triangleindex++, e++) { + Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[0]*3, vert[0]); + VectorAdd(center, vert[0], center); + } + f = 1.0 / surface->num_triangles*3; + VectorScale(center, f, center); + + // find a matching plane if there is one + for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++) if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1) - { - p->materialflags |= surface->texture->currentframe->currentmaterialflags; break; - } - } - // if this triangle does not fit any known plane rendered this frame, render textures for it - if (planeindex >= r_waterstate.numwaterplanes && planeindex < r_waterstate.maxwaterplanes) + if (planeindex >= r_waterstate.maxwaterplanes) + return; // nothing we can do, out of planes + + // if this triangle does not fit any known plane rendered this frame, add one + if (planeindex >= r_waterstate.numwaterplanes) { // store the new plane r_waterstate.numwaterplanes++; @@ -2518,7 +2530,17 @@ static void R_Water_AddWaterPlane(msurface_t *surface) p->plane.dist *= -1; PlaneClassify(&p->plane); } - p->materialflags = surface->texture->currentframe->currentmaterialflags; + // clear materialflags and pvs + p->materialflags = 0; + p->pvsvalid = false; + } + // merge this surface's materialflags into the waterplane + p->materialflags |= surface->texture->currentframe->currentmaterialflags; + // merge this surface's PVS into the waterplane + if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION) && r_refdef.worldmodel && r_refdef.worldmodel->brush.FatPVS) + { + r_refdef.worldmodel->brush.FatPVS(r_refdef.worldmodel, r_view.origin, 2, p->pvsbits, sizeof(p->pvsbits), p->pvsvalid); + p->pvsvalid = true; } } @@ -2528,28 +2550,45 @@ static void R_Water_ProcessPlanes(void) int planeindex; r_waterstate_waterplane_t *p; + // make sure enough textures are allocated for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++) { - if (!p->texture_refraction && (p->materialflags & MATERIALFLAG_WATERSHADER)) - p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_refraction", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL); - if (!p->texture_reflection && (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))) - p->texture_reflection = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_reflection", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL); + if (p->materialflags & MATERIALFLAG_WATERSHADER) + { + if (!p->texture_refraction) + p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_refraction", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL); + if (!p->texture_refraction) + goto error; + } + + if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)) + { + if (!p->texture_reflection) + p->texture_reflection = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_reflection", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL); + if (!p->texture_reflection) + goto error; + } + } + // render views + for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++) + { originalview = r_view; r_view.showdebug = false; r_view.width = r_waterstate.waterwidth; r_view.height = r_waterstate.waterheight; r_view.useclipplane = true; - - r_view.clipplane = p->plane; - VectorNegate(r_view.clipplane.normal, r_view.clipplane.normal); - r_view.clipplane.dist = -r_view.clipplane.dist; - PlaneClassify(&r_view.clipplane); r_waterstate.renderingscene = true; + // render the normal view scene and copy into texture // (except that a clipping plane should be used to hide everything on one side of the water, and the viewer's weapon model should be omitted) if (p->materialflags & MATERIALFLAG_WATERSHADER) { + r_view.clipplane = p->plane; + VectorNegate(r_view.clipplane.normal, r_view.clipplane.normal); + r_view.clipplane.dist = -r_view.clipplane.dist; + PlaneClassify(&r_view.clipplane); + R_RenderScene(false); // copy view into the screen texture @@ -2559,29 +2598,47 @@ static void R_Water_ProcessPlanes(void) qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR } - // render reflected scene and copy into texture - Matrix4x4_Reflect(&r_view.matrix, p->plane.normal[0], p->plane.normal[1], p->plane.normal[2], p->plane.dist, -2); - r_view.clipplane = p->plane; - // reverse the cullface settings for this render - r_view.cullface_front = GL_FRONT; - r_view.cullface_back = GL_BACK; + if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)) + { + // render reflected scene and copy into texture + Matrix4x4_Reflect(&r_view.matrix, p->plane.normal[0], p->plane.normal[1], p->plane.normal[2], p->plane.dist, -2); + r_view.clipplane = p->plane; + // reverse the cullface settings for this render + r_view.cullface_front = GL_FRONT; + r_view.cullface_back = GL_BACK; + if (r_refdef.worldmodel && r_refdef.worldmodel->brush.num_pvsclusterbytes) + { + r_view.usecustompvs = true; + if (p->pvsvalid) + memcpy(r_viewcache.world_pvsbits, p->pvsbits, r_refdef.worldmodel->brush.num_pvsclusterbytes); + else + memset(r_viewcache.world_pvsbits, 0xFF, r_refdef.worldmodel->brush.num_pvsclusterbytes); + } - R_ResetViewRendering3D(); - R_ClearScreen(); + R_ResetViewRendering3D(); + R_ClearScreen(); - R_RenderScene(false); + R_RenderScene(false); - R_Mesh_TexBind(0, R_GetTexture(p->texture_reflection)); - GL_ActiveTexture(0); - CHECKGLERROR - qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR + R_Mesh_TexBind(0, R_GetTexture(p->texture_reflection)); + GL_ActiveTexture(0); + CHECKGLERROR + qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR - R_ResetViewRendering3D(); - R_ClearScreen(); + R_ResetViewRendering3D(); + R_ClearScreen(); + } r_view = originalview; r_waterstate.renderingscene = false; } + return; +error: + r_view = originalview; + r_waterstate.renderingscene = false; + Cvar_SetValueQuick(&r_glsl_water, 0); + Con_Printf("R_Water_ProcessPlanes: Error: texture creation failed! Turned off r_glsl_water.\n"); + return; } void R_Bloom_StartFrame(void) diff --git a/gl_rsurf.c b/gl_rsurf.c index cdd43926..d18e540a 100644 --- a/gl_rsurf.c +++ b/gl_rsurf.c @@ -414,11 +414,34 @@ void R_View_WorldVisibility(qboolean forcenovis) if (!model) return; + if (r_view.usecustompvs) + { + // clear the visible surface and leaf flags arrays + memset(r_viewcache.world_surfacevisible, 0, model->num_surfaces); + memset(r_viewcache.world_leafvisible, 0, model->brush.num_leafs); + r_viewcache.world_novis = false; + + // simply cull each marked leaf to the frustum (view pyramid) + for (j = 0, leaf = model->brush.data_leafs;j < model->brush.num_leafs;j++, leaf++) + { + // if leaf is in current pvs and on the screen, mark its surfaces + if (CHECKPVSBIT(r_viewcache.world_pvsbits, leaf->clusterindex) && !R_CullBox(leaf->mins, leaf->maxs)) + { + r_refdef.stats.world_leafs++; + r_viewcache.world_leafvisible[j] = true; + if (leaf->numleafsurfaces) + for (i = 0, mark = leaf->firstleafsurface;i < leaf->numleafsurfaces;i++, mark++) + r_viewcache.world_surfacevisible[*mark] = true; + } + } + return; + } + // if possible find the leaf the view origin is in viewleaf = model->brush.PointInLeaf ? model->brush.PointInLeaf(model, r_view.origin) : NULL; // if possible fetch the visible cluster bits if (!r_lockpvs.integer && model->brush.FatPVS) - model->brush.FatPVS(model, r_view.origin, 2, r_viewcache.world_pvsbits, sizeof(r_viewcache.world_pvsbits)); + model->brush.FatPVS(model, r_view.origin, 2, r_viewcache.world_pvsbits, sizeof(r_viewcache.world_pvsbits), false); if (!r_lockvisibility.integer) { diff --git a/model_brush.c b/model_brush.c index 2e6f45e6..833787e1 100644 --- a/model_brush.c +++ b/model_brush.c @@ -3309,7 +3309,7 @@ static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, //Calculates a PVS that is the inclusive or of all leafs within radius pixels //of the given point. -static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbufferlength) +static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbufferlength, qboolean merge) { int bytes = model->brush.num_pvsclusterbytes; bytes = min(bytes, pvsbufferlength); @@ -3318,7 +3318,8 @@ static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, unsi memset(pvsbuffer, 0xFF, bytes); return bytes; } - memset(pvsbuffer, 0, bytes); + if (!merge) + memset(pvsbuffer, 0, bytes); Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brush.data_nodes); return bytes; } diff --git a/model_shared.h b/model_shared.h index f179f724..eaf147ac 100644 --- a/model_shared.h +++ b/model_shared.h @@ -619,7 +619,7 @@ typedef struct model_brush_s int (*SuperContentsFromNativeContents)(struct model_s *model, int nativecontents); int (*NativeContentsFromSuperContents)(struct model_s *model, int supercontents); unsigned char *(*GetPVS)(struct model_s *model, const vec3_t p); - int (*FatPVS)(struct model_s *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbufferlength); + int (*FatPVS)(struct model_s *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbufferlength, qboolean merge); int (*BoxTouchingPVS)(struct model_s *model, const unsigned char *pvs, const vec3_t mins, const vec3_t maxs); int (*BoxTouchingLeafPVS)(struct model_s *model, const unsigned char *pvs, const vec3_t mins, const vec3_t maxs); int (*BoxTouchingVisibleLeafs)(struct model_s *model, const unsigned char *visibleleafs, const vec3_t mins, const vec3_t maxs); -- 2.39.2