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
parent 8955f2ead0
commit bced5ecbc5
10 changed files with 165 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);
@ -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];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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