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
This commit is contained in:
James R 2023-04-05 22:11:23 -07:00 committed by NepDisk
parent a047144c4d
commit ed75d3946d
11 changed files with 167 additions and 65 deletions

View file

@ -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++)
{
if (R_PrecipThingVisible(precipthing, limit_dist))
HWR_ProjectPrecipitationSprite(precipthing);
for (by = yl; by <= yh; by++)
{
for (th = precipblocklinks[(by * bmapwidth) + bx]; th; th = th->bnext)
{
if (R_PrecipThingVisible(th))
{
HWR_ProjectPrecipitationSprite(th);
}
}
}
}
}
#endif
}
// --------------------------------------------------------------------------
// HWR_ProjectSprite
@ -6345,6 +6369,10 @@ void HWR_RenderSkyboxView(player_t *player)
if (cv_glbatching.value)
HWR_StartBatching();
#ifdef HWPRECIP
HWR_AddPrecipitationSprites();
#endif
HWR_RenderBSPNode((INT32)numnodes-1);
#ifndef NEWCLIP
@ -6566,6 +6594,10 @@ void HWR_RenderPlayerView(void)
if (cv_glbatching.value)
HWR_StartBatching();
#ifdef HWPRECIP
HWR_AddPrecipitationSprites();
#endif
HWR_RenderBSPNode((INT32)numnodes-1);
#ifndef NEWCLIP

View file

@ -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

View file

@ -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);
}
//

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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++)
{
if (R_PrecipThingVisible(precipthing, limit_dist))
R_ProjectPrecipitationSprite(precipthing);
for (by = yl; by <= yh; by++)
{
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)

View file

@ -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);

View file

@ -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