From ed75d3946d9e2341a22a3e0b9f72d6d8ac980e1c 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 | 48 ++++++++++++++++++++++++----- 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 | 43 ++++++++++++++++++-------- src/r_things.h | 4 +-- src/tables.h | 4 +++ 11 files changed, 167 insertions(+), 65 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 88963049e..800f0b188 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); @@ -5101,9 +5102,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. @@ -5134,19 +5132,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 @@ -6344,6 +6368,10 @@ void HWR_RenderSkyboxView(player_t *player) if (cv_glbatching.value) HWR_StartBatching(); + +#ifdef HWPRECIP + HWR_AddPrecipitationSprites(); +#endif HWR_RenderBSPNode((INT32)numnodes-1); @@ -6565,6 +6593,10 @@ void HWR_RenderPlayerView(void) if (cv_glbatching.value) HWR_StartBatching(); + +#ifdef HWPRECIP + HWR_AddPrecipitationSprites(); +#endif HWR_RenderBSPNode((INT32)numnodes-1); diff --git a/src/p_local.h b/src/p_local.h index 0e857e027..5c7de48fb 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -462,6 +462,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 // // P_INTER diff --git a/src/p_maputl.c b/src/p_maputl.c index 09e5cdd5d..b4ae2f3ed 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -971,15 +971,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 @@ -1036,24 +1066,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 @@ -1108,18 +1121,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 2a0591dd4..3173ac9f8 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -281,9 +281,10 @@ typedef struct mobj_s fixed_t old_x, old_y, old_z; // position interpolation fixed_t old_x2, old_y2, old_z2; - // More list: links in sector (if needed) - struct mobj_s *snext; - struct mobj_s **sprev; // killough 8/11/98: change to ptr-to-ptr + // Interaction info, by BLOCKMAP. + // Links in blocks (if needed). + struct mobj_s *bnext; + struct mobj_s **bprev; // killough 8/11/98: change to ptr-to-ptr // More drawing info: to determine current sprite. angle_t angle, pitch, roll; // orientation @@ -333,10 +334,9 @@ typedef struct mobj_s // 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). - struct mobj_s *bnext; - struct mobj_s **bprev; // killough 8/11/98: change to ptr-to-ptr + // More list: links in sector (if needed) + struct mobj_s *snext; + struct mobj_s **sprev; // killough 8/11/98: change to ptr-to-ptr // Additional pointers for NiGHTS hoops struct mobj_s *hnext; @@ -431,9 +431,10 @@ typedef struct precipmobj_s fixed_t old_x, old_y, old_z; // position interpolation fixed_t old_x2, old_y2, old_z2; - // More list: links in sector (if needed) - struct precipmobj_s *snext; - struct precipmobj_s **sprev; // killough 8/11/98: change to ptr-to-ptr + // Links in blocks (if needed). + // The blockmap is only used by precip to render. + struct precipmobj_s *bnext; + struct precipmobj_s **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 1eecffbbb..ba6e533e6 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -152,6 +152,7 @@ INT32 *blockmaplump; // Big blockmap fixed_t bmaporgx, bmaporgy; // for thing chains mobj_t **blocklinks; +precipmobj_t **precipblocklinks; // REJECT // For fast sight rejection. @@ -986,7 +987,6 @@ static void P_InitializeSector(sector_t *ss) ss->floorspeed = ss->ceilspeed = 0; - ss->preciplist = NULL; ss->touching_preciplist = NULL; ss->f_slope = NULL; @@ -2764,6 +2764,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; } @@ -3017,6 +3021,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 62085889b..9e50c4fdb 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -362,7 +362,6 @@ typedef struct sector_s fixed_t floorspeed, ceilspeed; // list of precipitation mobjs in sector - precipmobj_t *preciplist; struct mprecipsecnode_s *touching_preciplist; // Eternity engine slope diff --git a/src/r_main.c b/src/r_main.c index d6cad900e..bedd565b1 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -500,6 +500,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 // @@ -1564,8 +1591,8 @@ void R_RenderPlayerView(void) ps_numbspcalls = ps_numpolyobjects = ps_numdrawnodes = 0; ps_bsptime = I_GetPreciseTime(); R_RenderBSPNode((INT32)numnodes - 1); + R_AddPrecipitationSprites(); 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 e7f5b9d4c..6634c9b09 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -83,6 +83,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 6c6549124..8f8d28f47 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2501,7 +2501,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; @@ -2558,14 +2557,39 @@ 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)) + 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)) - R_ProjectPrecipitationSprite(precipthing); + for (th = precipblocklinks[(by * bmapwidth) + bx]; th; th = th->bnext) + { + if (R_PrecipThingVisible(th)) + { + R_ProjectPrecipitationSprite(th); + } + } } } } @@ -3448,17 +3472,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 1d8bde5bd..ba44acecd 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -63,6 +63,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); @@ -74,8 +75,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); diff --git a/src/tables.h b/src/tables.h index e122975e1..42e5ed899 100644 --- a/src/tables.h +++ b/src/tables.h @@ -24,6 +24,7 @@ #define FINEMASK (FINEANGLES - 1) #define ANGLETOFINESHIFT 19 // 0x100000000 to 0x2000 #define FINEANGLE_C(x) ((FixedAngle((x)*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK) // ((x*(ANGLE_45/45))>>ANGLETOFINESHIFT) & FINEMASK +#define ANGLETOFINE(x) (((x)>>ANGLETOFINESHIFT) & FINEMASK) // Effective size is 10240. extern fixed_t finesine[5*FINEANGLES/4]; @@ -132,4 +133,7 @@ void FM_Rotate(matrix_t *dest, angle_t angle, fixed_t x, fixed_t y, fixed_t z); #define FINECOSINE(n) (finecosine[n]>>(FINE_FRACBITS-FRACBITS)) #define FINETANGENT(n) (finetangent[n]>>(FINE_FRACBITS-FRACBITS)) +#define FSIN(n) FINESINE(ANGLETOFINE(n)) +#define FCOS(n) FINECOSINE(ANGLETOFINE(n)) + #endif