From bced5ecbc5701699b3494605380b74dd151e6740 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 5 Apr 2023 22:11:23 -0700 Subject: [PATCH] Render precip through blockmap instead of during BSP There can be very many precipmobjs in a sector. During BSP rendering, for each sector reached by BSP (the entire visible level at least), all the precipmobjs would be iterated and their distance from the camera calculated, to determine whether they are within draw distance. The issue with the old approach is that there really are too many precipmobjs in each sector. By iterating the blockmap within a certain range instead, precipmobjs out of view are not iterated at all, saving some time. Caveat: drawdist_precip is no longer respected as an exact value, since it must round to the precision of the blockmap. List of potentially scary changes: - Removes snext/sprev for precipmobj_t - Removes preciplist for sector_t - Adds bnext/bprev for precipmobj_t - mobj_t and precipmobj_t field offsets are symmetrical - Modified P_SetPrecipitationThingPosition and P_SetThingPosition - The syntax in these functions is kind of wacky --- src/hardware/hw_main.c | 44 +++++++++++++++++++++----- src/p_local.h | 1 + src/p_maputl.c | 70 ++++++++++++++++++++++++------------------ src/p_mobj.h | 21 +++++++------ src/p_setup.c | 9 +++++- src/r_defs.h | 1 - src/r_main.c | 29 ++++++++++++++++- src/r_main.h | 2 ++ src/r_things.c | 49 +++++++++++++++++++++-------- src/r_things.h | 4 +-- 10 files changed, 165 insertions(+), 65 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index d8535dc0a..ca53980cf 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -69,6 +69,7 @@ struct hwdriver_s hwdriver; static void HWR_AddSprites(sector_t *sec); static void HWR_ProjectSprite(mobj_t *thing); #ifdef HWPRECIP +static void HWR_AddPrecipitationSprites(void); static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing); #endif static void HWR_ProjectBoundingBox(mobj_t *thing); @@ -5052,9 +5053,6 @@ static UINT8 sectorlight; static void HWR_AddSprites(sector_t *sec) { mobj_t *thing; -#ifdef HWPRECIP - precipmobj_t *precipthing; -#endif fixed_t limit_dist; // BSP is traversed by subsector. @@ -5085,19 +5083,45 @@ static void HWR_AddSprites(sector_t *sec) HWR_ProjectBoundingBox(thing); } } +} #ifdef HWPRECIP +// -------------------------------------------------------------------------- +// HWR_AddPrecipitationSprites +// This renders through the blockmap instead of BSP to avoid +// iterating a huge amount of precipitation sprites in sectors +// that are beyond drawdist. +// -------------------------------------------------------------------------- +static void HWR_AddPrecipitationSprites(void) +{ + const fixed_t drawdist = cv_drawdist_precip.value * mapobjectscale; + + INT32 xl, xh, yl, yh, bx, by; + precipmobj_t *th; + // no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off - if ((limit_dist = (fixed_t)cv_drawdist_precip.value * mapobjectscale)) + if (drawdist == 0) { - for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext) + return; + } + + R_GetRenderBlockMapDimensions(drawdist, &xl, &xh, &yl, &yh); + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) { - if (R_PrecipThingVisible(precipthing, limit_dist)) - HWR_ProjectPrecipitationSprite(precipthing); + for (th = precipblocklinks[(by * bmapwidth) + bx]; th; th = th->bnext) + { + if (R_PrecipThingVisible(th)) + { + HWR_ProjectPrecipitationSprite(th); + } + } } } -#endif } +#endif // -------------------------------------------------------------------------- // HWR_ProjectSprite @@ -6315,6 +6339,10 @@ static void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolea HWR_RenderBSPNode((INT32)numnodes-1); +#ifdef HWPRECIP + HWR_AddPrecipitationSprites(); +#endif + #ifndef NEWCLIP // Make a viewangle int so we can render things based on mouselook viewangle = localaiming[viewssnum]; diff --git a/src/p_local.h b/src/p_local.h index eb4633ff4..51f946572 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -510,6 +510,7 @@ extern INT32 bmapheight; // in mapblocks extern fixed_t bmaporgx; extern fixed_t bmaporgy; // origin of block map extern mobj_t **blocklinks; // for thing chains +extern precipmobj_t **precipblocklinks; // special blockmap for precip rendering extern struct minimapinfo { diff --git a/src/p_maputl.c b/src/p_maputl.c index e1ceeb0fe..75282c9ef 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -1006,15 +1006,45 @@ void P_UnsetThingPosition(mobj_t *thing) void P_UnsetPrecipThingPosition(precipmobj_t *thing) { - precipmobj_t **sprev = thing->sprev; - precipmobj_t *snext = thing->snext; - if ((*sprev = snext) != NULL) // unlink from sector list - snext->sprev = sprev; + precipmobj_t **bprev = thing->bprev; + precipmobj_t *bnext = thing->bnext; + + if (bprev && (*bprev = bnext) != NULL) // unlink from block map + bnext->bprev = bprev; precipsector_list = thing->touching_sectorlist; thing->touching_sectorlist = NULL; //to be restored by P_SetPrecipThingPosition } +static void P_LinkToBlockMap(mobj_t *thing, mobj_t **bmap) +{ + const INT32 blockx = (unsigned)(thing->x - bmaporgx) >> MAPBLOCKSHIFT; + const INT32 blocky = (unsigned)(thing->y - bmaporgy) >> MAPBLOCKSHIFT; + + if (blockx >= 0 && blockx < bmapwidth + && blocky >= 0 && blocky < bmapheight) + { + // killough 8/11/98: simpler scheme using + // pointer-to-pointer prev pointers -- + // allows head nodes to be treated like everything else + + mobj_t **link = &bmap[(blocky * bmapwidth) + blockx]; + mobj_t *bnext = *link; + + thing->bnext = bnext; + + if (bnext != NULL) + bnext->bprev = &thing->bnext; + + thing->bprev = link; + *link = thing; + } + else // thing is off the map + { + thing->bnext = NULL, thing->bprev = NULL; + } +} + // // P_SetThingPosition // Links a thing into both a block and a subsector @@ -1071,24 +1101,7 @@ void P_SetThingPosition(mobj_t *thing) if (!(thing->flags & MF_NOBLOCKMAP)) { // inert things don't need to be in blockmap - const INT32 blockx = (unsigned)(thing->x - bmaporgx)>>MAPBLOCKSHIFT; - const INT32 blocky = (unsigned)(thing->y - bmaporgy)>>MAPBLOCKSHIFT; - if (blockx >= 0 && blockx < bmapwidth - && blocky >= 0 && blocky < bmapheight) - { - // killough 8/11/98: simpler scheme using - // pointer-to-pointer prev pointers -- - // allows head nodes to be treated like everything else - - mobj_t **link = &blocklinks[blocky*bmapwidth + blockx]; - mobj_t *bnext = *link; - if ((thing->bnext = bnext) != NULL) - bnext->bprev = &thing->bnext; - thing->bprev = link; - *link = thing; - } - else // thing is off the map - thing->bnext = NULL, thing->bprev = NULL; + P_LinkToBlockMap(thing, blocklinks); } // Allows you to 'step' on a new linedef exec when the previous @@ -1143,18 +1156,15 @@ void P_SetUnderlayPosition(mobj_t *thing) void P_SetPrecipitationThingPosition(precipmobj_t *thing) { - subsector_t *ss = thing->subsector = R_PointInSubsector(thing->x, thing->y); - - precipmobj_t **link = &ss->sector->preciplist; - precipmobj_t *snext = *link; - if ((thing->snext = snext) != NULL) - snext->sprev = &thing->snext; - thing->sprev = link; - *link = thing; + thing->subsector = R_PointInSubsector(thing->x, thing->y); P_CreatePrecipSecNodeList(thing, thing->x, thing->y); thing->touching_sectorlist = precipsector_list; // Attach to Thing's precipmobj_t precipsector_list = NULL; // clear for next time + + // NOTE: this works because bnext/bprev are at the same + // offsets in precipmobj_t and mobj_t + P_LinkToBlockMap((mobj_t*)thing, (mobj_t**)precipblocklinks); } // diff --git a/src/p_mobj.h b/src/p_mobj.h index 56c95a7c3..c084b245c 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -291,9 +291,10 @@ struct mobj_t mobjtype_t type; const mobjinfo_t *info; // &mobjinfo[mobj->type] - // More list: links in sector (if needed) - mobj_t *snext; - mobj_t **sprev; // killough 8/11/98: change to ptr-to-ptr + // Interaction info, by BLOCKMAP. + // Links in blocks (if needed). + mobj_t *bnext; + mobj_t **bprev; // killough 8/11/98: change to ptr-to-ptr // More drawing info: to determine current sprite. angle_t angle, pitch, roll; // orientation @@ -347,10 +348,9 @@ struct mobj_t // using an internal color lookup table for re-indexing. UINT16 color; // This replaces MF_TRANSLATION. Use 0 for default (no translation). - // Interaction info, by BLOCKMAP. - // Links in blocks (if needed). - mobj_t *bnext; - mobj_t **bprev; // killough 8/11/98: change to ptr-to-ptr + // More list: links in sector (if needed) + mobj_t *snext; + mobj_t **sprev; // killough 8/11/98: change to ptr-to-ptr // Additional pointers for NiGHTS hoops mobj_t *hnext; @@ -448,9 +448,10 @@ struct precipmobj_t mobjtype_t type; const mobjinfo_t *info; // &mobjinfo[mobj->type] - // More list: links in sector (if needed) - precipmobj_t *snext; - precipmobj_t **sprev; // killough 8/11/98: change to ptr-to-ptr + // Links in blocks (if needed). + // The blockmap is only used by precip to render. + precipmobj_t *bnext; + precipmobj_t **bprev; // killough 8/11/98: change to ptr-to-ptr // More drawing info: to determine current sprite. angle_t angle, pitch, roll; // orientation diff --git a/src/p_setup.c b/src/p_setup.c index 9082c2796..ba006d39d 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -160,6 +160,7 @@ INT32 *blockmaplump; // Big blockmap fixed_t bmaporgx, bmaporgy; // for thing chains mobj_t **blocklinks; +precipmobj_t **precipblocklinks; // REJECT // For fast sight rejection. @@ -881,7 +882,6 @@ static void P_InitializeSector(sector_t *ss) ss->floorspeed = ss->ceilspeed = 0; - ss->preciplist = NULL; ss->touching_preciplist = NULL; ss->f_slope = NULL; @@ -3546,6 +3546,10 @@ static boolean P_LoadBlockMap(UINT8 *data, size_t count) // haleyjd 2/22/06: setup polyobject blockmap count = sizeof(*polyblocklinks) * bmapwidth * bmapheight; polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL); + + count = sizeof (*precipblocklinks)* bmapwidth*bmapheight; + precipblocklinks = Z_Calloc(count, PU_LEVEL, NULL); + return true; } @@ -3799,6 +3803,9 @@ static void P_CreateBlockMap(void) // haleyjd 2/22/06: setup polyobject blockmap count = sizeof(*polyblocklinks) * bmapwidth * bmapheight; polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL); + + count = sizeof (*precipblocklinks)* bmapwidth*bmapheight; + precipblocklinks = Z_Calloc(count, PU_LEVEL, NULL); } } diff --git a/src/r_defs.h b/src/r_defs.h index 4e947616b..c550fc92f 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -524,7 +524,6 @@ struct sector_t fixed_t floorspeed, ceilspeed; // list of precipitation mobjs in sector - precipmobj_t *preciplist; mprecipsecnode_t *touching_preciplist; // Eternity engine slope diff --git a/src/r_main.c b/src/r_main.c index 06f8113aa..e227a0dd9 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -493,6 +493,33 @@ boolean R_DoCulling(line_t *cullheight, line_t *viewcullheight, fixed_t vz, fixe return false; } +// Returns search dimensions within a blockmap, in the direction of viewangle and out to a certain distance. +void R_GetRenderBlockMapDimensions(fixed_t drawdist, INT32 *xl, INT32 *xh, INT32 *yl, INT32 *yh) +{ + const angle_t left = viewangle - clipangle[viewssnum]; + const angle_t right = viewangle + clipangle[viewssnum]; + + const fixed_t vxleft = viewx + FixedMul(drawdist, FCOS(left)); + const fixed_t vyleft = viewy + FixedMul(drawdist, FSIN(left)); + + const fixed_t vxright = viewx + FixedMul(drawdist, FCOS(right)); + const fixed_t vyright = viewy + FixedMul(drawdist, FSIN(right)); + + // Try to narrow the search to within only the field of view + *xl = (unsigned)(min(viewx, min(vxleft, vxright)) - bmaporgx)>>MAPBLOCKSHIFT; + *xh = (unsigned)(max(viewx, max(vxleft, vxright)) - bmaporgx)>>MAPBLOCKSHIFT; + *yl = (unsigned)(min(viewy, min(vyleft, vyright)) - bmaporgy)>>MAPBLOCKSHIFT; + *yh = (unsigned)(max(viewy, max(vyleft, vyright)) - bmaporgy)>>MAPBLOCKSHIFT; + + if (*xh >= bmapwidth) + *xh = bmapwidth - 1; + + if (*yh >= bmapheight) + *yh = bmapheight - 1; + + BMBOUNDFIX(*xl, *xh, *yl, *yh); +} + // // R_InitTextureMapping // @@ -1485,6 +1512,7 @@ static void R_RenderViewpoint(maskcount_t* mask) curdrawsegs = ds_p; R_RenderBSPNode((INT32)numnodes - 1); + R_AddPrecipitationSprites(); Mask_Post(mask); } @@ -1556,7 +1584,6 @@ void R_RenderPlayerView(void) ps_bsptime = I_GetPreciseTime(); R_RenderViewpoint(&masks[nummasks - 1]); ps_bsptime = I_GetPreciseTime() - ps_bsptime; - ps_numsprites = visspritecount; #ifdef TIMING RDMSR(0x10, &mycount); mytotal += mycount; // 64bit add diff --git a/src/r_main.h b/src/r_main.h index 786b1eaf6..36d53f1b3 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -87,6 +87,8 @@ subsector_t *R_PointInSubsectorOrNull(fixed_t x, fixed_t y); boolean R_DoCulling(line_t *cullheight, line_t *viewcullheight, fixed_t vz, fixed_t bottomh, fixed_t toph); +void R_GetRenderBlockMapDimensions(fixed_t drawdist, INT32 *xl, INT32 *xh, INT32 *yl, INT32 *yh); + // Render stats extern precise_t ps_prevframetime;// time when previous frame was rendered diff --git a/src/r_things.c b/src/r_things.c index 86de5352e..4ca7b1736 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2639,7 +2639,6 @@ weatherthink: void R_AddSprites(sector_t *sec, INT32 lightlevel) { mobj_t *thing; - precipmobj_t *precipthing; // Tails 08-25-2002 INT32 lightnum; fixed_t limit_dist; @@ -2696,14 +2695,45 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) } } } +} + +// R_AddPrecipitationSprites +// This renders through the blockmap instead of BSP to avoid +// iterating a huge amount of precipitation sprites in sectors +// that are beyond drawdist. +// +void R_AddPrecipitationSprites(void) +{ + const fixed_t drawdist = cv_drawdist_precip.value * mapobjectscale; + + INT32 xl, xh, yl, yh, bx, by; + precipmobj_t *th; // no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off - if ((limit_dist = (fixed_t)cv_drawdist_precip.value * mapobjectscale) && !portalskipprecipmobjs) + if (drawdist == 0) { - for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext) + return; + } + + // do not render in skybox + if (portalskipprecipmobjs) + { + return; + } + + R_GetRenderBlockMapDimensions(drawdist, &xl, &xh, &yl, &yh); + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) { - if (R_PrecipThingVisible(precipthing, limit_dist)) - R_ProjectPrecipitationSprite(precipthing); + for (th = precipblocklinks[(by * bmapwidth) + bx]; th; th = th->bnext) + { + if (R_PrecipThingVisible(th)) + { + R_ProjectPrecipitationSprite(th); + } + } } } } @@ -3641,17 +3671,12 @@ boolean R_ThingWithinDist (mobj_t *thing, fixed_t limit_dist) } /* Check if precipitation may be drawn from our current view. */ -boolean R_PrecipThingVisible (precipmobj_t *precipthing, - fixed_t limit_dist) +boolean R_PrecipThingVisible (precipmobj_t *precipthing) { - fixed_t approx_dist; - if (( precipthing->precipflags & PCF_INVISIBLE )) return false; - approx_dist = P_AproxDistance(viewx-precipthing->x, viewy-precipthing->y); - - return ( approx_dist <= limit_dist ); + return true; } boolean R_ThingHorizontallyFlipped(mobj_t *thing) diff --git a/src/r_things.h b/src/r_things.h index 2d82b4381..b40eb4ed4 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -67,6 +67,7 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope); //SoM: 6/5/2000: Light sprites correctly! void R_AddSprites(sector_t *sec, INT32 lightlevel); +void R_AddPrecipitationSprites(void); void R_InitSprites(void); void R_ClearSprites(void); @@ -78,8 +79,7 @@ boolean R_ThingVisible (mobj_t *thing); boolean R_ThingWithinDist (mobj_t *thing, fixed_t draw_dist); -boolean R_PrecipThingVisible (precipmobj_t *precipthing, - fixed_t precip_draw_dist); +boolean R_PrecipThingVisible (precipmobj_t *precipthing); boolean R_ThingHorizontallyFlipped (mobj_t *thing); boolean R_ThingVerticallyFlipped (mobj_t *thing);