Merge branch 'linkdraw_reliability' into 'master'

MF2_LINKDRAW reliability improvements

Seriously improves the reliability of the linkdraw system (in Software, the only rendering engine which actually does anything with this information), reducing the number of visual errors it produces significantly. Deliberately engineered to only affect objects with MF2_LINKDRAW applied.

Of particular note: Standing in shallow pools of water as Smiles will now reliably associate the bottom half of the tails following mobj with the underwater portion of Smiles' body, and the flicking ends with his un-submerged head.

Test with ```<root>/!LatestSRB2Files/srb2win_branch_linkdraw.exe``` and ```<root>/toaster/smiles.wad```.

See merge request !101
This commit is contained in:
Monster Iestyn 2017-08-04 17:26:31 -04:00
commit c6be44d0da
2 changed files with 138 additions and 73 deletions

View file

@ -817,7 +817,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
colfunc = basecolfunc; // hack: this isn't resetting properly somewhere. colfunc = basecolfunc; // hack: this isn't resetting properly somewhere.
dc_colormap = vis->colormap; dc_colormap = vis->colormap;
if ((vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash" if (!(vis->cut & SC_PRECIP) && (vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash"
{ {
// translate certain pixels to white // translate certain pixels to white
colfunc = transcolfunc; colfunc = transcolfunc;
@ -832,7 +832,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
{ {
colfunc = transtransfunc; colfunc = transtransfunc;
dc_transmap = vis->transmap; dc_transmap = vis->transmap;
if (vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // MT_GHOST LOOKS LIKE A PLAYER SO USE THE PLAYER TRANSLATION TABLES. >_> if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // MT_GHOST LOOKS LIKE A PLAYER SO USE THE PLAYER TRANSLATION TABLES. >_>
{ {
size_t skinnum = (skin_t*)vis->mobj->skin-skins; size_t skinnum = (skin_t*)vis->mobj->skin-skins;
dc_translation = R_GetTranslationColormap((INT32)skinnum, vis->mobj->color, GTC_CACHE); dc_translation = R_GetTranslationColormap((INT32)skinnum, vis->mobj->color, GTC_CACHE);
@ -851,7 +851,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
colfunc = transcolfunc; colfunc = transcolfunc;
// New colormap stuff for skins Tails 06-07-2002 // New colormap stuff for skins Tails 06-07-2002
if (vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // This thing is a player! if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // This thing is a player!
{ {
size_t skinnum = (skin_t*)vis->mobj->skin-skins; size_t skinnum = (skin_t*)vis->mobj->skin-skins;
dc_translation = R_GetTranslationColormap((INT32)skinnum, vis->mobj->color, GTC_CACHE); dc_translation = R_GetTranslationColormap((INT32)skinnum, vis->mobj->color, GTC_CACHE);
@ -881,18 +881,18 @@ static void R_DrawVisSprite(vissprite_t *vis)
frac = vis->startfrac; frac = vis->startfrac;
windowtop = windowbottom = sprbotscreen = INT32_MAX; windowtop = windowbottom = sprbotscreen = INT32_MAX;
if (vis->mobj->skin && ((skin_t *)vis->mobj->skin)->flags & SF_HIRES) if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && ((skin_t *)vis->mobj->skin)->flags & SF_HIRES)
this_scale = FixedMul(this_scale, ((skin_t *)vis->mobj->skin)->highresscale); this_scale = FixedMul(this_scale, ((skin_t *)vis->mobj->skin)->highresscale);
if (this_scale <= 0) if (this_scale <= 0)
this_scale = 1; this_scale = 1;
if (this_scale != FRACUNIT) if (this_scale != FRACUNIT)
{ {
if (!vis->isScaled) if (!(vis->cut & SC_ISSCALED))
{ {
vis->scale = FixedMul(vis->scale, this_scale); vis->scale = FixedMul(vis->scale, this_scale);
vis->scalestep = FixedMul(vis->scalestep, this_scale); vis->scalestep = FixedMul(vis->scalestep, this_scale);
vis->xiscale = FixedDiv(vis->xiscale,this_scale); vis->xiscale = FixedDiv(vis->xiscale,this_scale);
vis->isScaled = true; vis->cut |= SC_ISSCALED;
} }
dc_texturemid = FixedDiv(dc_texturemid,this_scale); dc_texturemid = FixedDiv(dc_texturemid,this_scale);
} }
@ -934,7 +934,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale)); sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
dc_iscale = (0xffffffffu / (unsigned)spryscale); dc_iscale = (0xffffffffu / (unsigned)spryscale);
} }
if (vis->vflip) if (vis->cut & SC_VFLIP)
R_DrawFlippedMaskedColumn(column, patch->height); R_DrawFlippedMaskedColumn(column, patch->height);
else else
R_DrawMaskedColumn(column); R_DrawMaskedColumn(column);
@ -1013,7 +1013,7 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis)
// //
// R_SplitSprite // R_SplitSprite
// runs through a sector's lightlist and // runs through a sector's lightlist and
static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing) static void R_SplitSprite(vissprite_t *sprite)
{ {
INT32 i, lightnum, lindex; INT32 i, lightnum, lindex;
INT16 cutfrac; INT16 cutfrac;
@ -1049,6 +1049,8 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing)
// adjust the heights. // adjust the heights.
newsprite = M_Memcpy(R_NewVisSprite(), sprite, sizeof (vissprite_t)); newsprite = M_Memcpy(R_NewVisSprite(), sprite, sizeof (vissprite_t));
newsprite->cut |= (sprite->cut & SC_FLAGMASK);
sprite->cut |= SC_BOTTOM; sprite->cut |= SC_BOTTOM;
sprite->gz = testheight; sprite->gz = testheight;
@ -1081,15 +1083,7 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing)
newsprite->extra_colormap = sector->lightlist[i].extra_colormap; newsprite->extra_colormap = sector->lightlist[i].extra_colormap;
/* if (!((newsprite->cut & SC_FULLBRIGHT) && (!newsprite->extra_colormap || !newsprite->extra_colormap->fog)))
if (thing->frame & FF_TRANSMASK)
;
else if (thing->flags2 & MF2_SHADOW)
;
else
*/
if (!((thing->frame & (FF_FULLBRIGHT|FF_TRANSMASK) || thing->flags2 & MF2_SHADOW)
&& (!newsprite->extra_colormap || !newsprite->extra_colormap->fog)))
{ {
lindex = FixedMul(sprite->xscale, FixedDiv(640, vid.width))>>(LIGHTSCALESHIFT); lindex = FixedMul(sprite->xscale, FixedDiv(640, vid.width))>>(LIGHTSCALESHIFT);
@ -1109,6 +1103,7 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing)
// //
static void R_ProjectSprite(mobj_t *thing) static void R_ProjectSprite(mobj_t *thing)
{ {
mobj_t *oldthing = thing;
fixed_t tr_x, tr_y; fixed_t tr_x, tr_y;
fixed_t gxt, gyt; fixed_t gxt, gyt;
fixed_t tx, tz; fixed_t tx, tz;
@ -1128,6 +1123,8 @@ static void R_ProjectSprite(mobj_t *thing)
vissprite_t *vis; vissprite_t *vis;
spritecut_e cut = SC_NONE;
angle_t ang = 0; // compiler complaints angle_t ang = 0; // compiler complaints
fixed_t iscale; fixed_t iscale;
fixed_t scalestep; fixed_t scalestep;
@ -1326,24 +1323,14 @@ static void R_ProjectSprite(mobj_t *thing)
if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY) if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY)
{ {
fixed_t linkscale; fixed_t linkscale;
#if 0 // support for chains of linkdraw - probably not network safe to modify mobjs during rendering
mobj_t *link, *link2;
for (link = thing->tracer; (link->tracer && (link->flags2 & MF2_LINKDRAW)); link = link->tracer) thing = thing->tracer;
link->flags2 &= ~MF2_LINKDRAW; // to prevent infinite loops, otherwise would just be a ;
for (link2 = thing->tracer; (link2->tracer && (link2 != link)); link2 = link2->tracer) if (thing->sprite == SPR_NULL || thing->flags2 & MF2_DONTDRAW)
link->flags2 |= MF2_LINKDRAW; // only needed for correction of the above return;
if (link->flags2 & MF2_LINKDRAW) tr_x = thing->x - viewx;
link->flags2 &= ~MF2_LINKDRAW; // let's try and make sure this doesn't happen again... tr_y = thing->y - viewy;
tr_x = link->x - viewx;
tr_y = link->y - viewy;
#else
tr_x = thing->tracer->x - viewx;
tr_y = thing->tracer->y - viewy;
#endif
gxt = FixedMul(tr_x, viewcos); gxt = FixedMul(tr_x, viewcos);
gyt = -FixedMul(tr_y, viewsin); gyt = -FixedMul(tr_y, viewsin);
tz = gxt-gyt; tz = gxt-gyt;
@ -1356,6 +1343,7 @@ static void R_ProjectSprite(mobj_t *thing)
dispoffset *= -1; // if it's physically behind, make sure it's ordered behind (if dispoffset > 0) dispoffset *= -1; // if it's physically behind, make sure it's ordered behind (if dispoffset > 0)
sortscale = linkscale; // now make sure it's linked sortscale = linkscale; // now make sure it's linked
cut = SC_LINKDRAW;
} }
// PORTAL SPRITE CLIPPING // PORTAL SPRITE CLIPPING
@ -1374,12 +1362,12 @@ static void R_ProjectSprite(mobj_t *thing)
// When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned. // When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned.
// sprite height - sprite topoffset is the proper inverse of the vertical offset, of course. // sprite height - sprite topoffset is the proper inverse of the vertical offset, of course.
// remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes! // remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes!
gz = thing->z + thing->height - FixedMul(spritecachedinfo[lump].topoffset, this_scale); gz = oldthing->z + oldthing->height - FixedMul(spritecachedinfo[lump].topoffset, this_scale);
gzt = gz + FixedMul(spritecachedinfo[lump].height, this_scale); gzt = gz + FixedMul(spritecachedinfo[lump].height, this_scale);
} }
else else
{ {
gzt = thing->z + FixedMul(spritecachedinfo[lump].topoffset, this_scale); gzt = oldthing->z + FixedMul(spritecachedinfo[lump].topoffset, this_scale);
gz = gzt - FixedMul(spritecachedinfo[lump].height, this_scale); gz = gzt - FixedMul(spritecachedinfo[lump].height, this_scale);
} }
@ -1469,7 +1457,7 @@ static void R_ProjectSprite(mobj_t *thing)
vis->sector = thing->subsector->sector; vis->sector = thing->subsector->sector;
vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, sortscale))>>FRACBITS); vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, sortscale))>>FRACBITS);
vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS); vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS);
vis->cut = SC_NONE; vis->cut = cut;
if (thing->subsector->sector->numlights) if (thing->subsector->sector->numlights)
vis->extra_colormap = thing->subsector->sector->lightlist[light].extra_colormap; vis->extra_colormap = thing->subsector->sector->lightlist[light].extra_colormap;
else else
@ -1506,12 +1494,15 @@ static void R_ProjectSprite(mobj_t *thing)
// specific translucency // specific translucency
if (!cv_translucency.value) if (!cv_translucency.value)
; // no translucency ; // no translucency
else if (thing->flags2 & MF2_SHADOW) // actually only the player should use this (temporary invisibility) else if (oldthing->flags2 & MF2_SHADOW) // actually only the player should use this (temporary invisibility)
vis->transmap = transtables + ((tr_trans80-1)<<FF_TRANSSHIFT); // because now the translucency is set through FF_TRANSMASK vis->transmap = transtables + ((tr_trans80-1)<<FF_TRANSSHIFT); // because now the translucency is set through FF_TRANSMASK
else if (thing->frame & FF_TRANSMASK) else if (oldthing->frame & FF_TRANSMASK)
vis->transmap = transtables + (thing->frame & FF_TRANSMASK) - 0x10000; vis->transmap = transtables + (oldthing->frame & FF_TRANSMASK) - 0x10000;
if (((thing->frame & FF_FULLBRIGHT) || (thing->flags2 & MF2_SHADOW)) if ((oldthing->frame & FF_FULLBRIGHT) || (oldthing->flags2 & MF2_SHADOW))
vis->cut |= SC_FULLBRIGHT;
if (vis->cut & SC_FULLBRIGHT
&& (!vis->extra_colormap || !vis->extra_colormap->fog)) && (!vis->extra_colormap || !vis->extra_colormap->fog))
{ {
// full bright: goggles // full bright: goggles
@ -1528,14 +1519,11 @@ static void R_ProjectSprite(mobj_t *thing)
vis->colormap = spritelights[lindex]; vis->colormap = spritelights[lindex];
} }
vis->precip = false; if (vflip)
vis->cut |= SC_VFLIP;
vis->vflip = vflip;
vis->isScaled = false;
if (thing->subsector->sector->numlights) if (thing->subsector->sector->numlights)
R_SplitSprite(vis, thing); R_SplitSprite(vis);
// Debug // Debug
++objectsdrawn; ++objectsdrawn;
@ -1559,7 +1547,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
fixed_t iscale; fixed_t iscale;
//SoM: 3/17/2000 //SoM: 3/17/2000
fixed_t gz ,gzt; fixed_t gz, gzt;
// transform the origin point // transform the origin point
tr_x = thing->x - viewx; tr_x = thing->x - viewx;
@ -1695,16 +1683,14 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
else else
vis->transmap = NULL; vis->transmap = NULL;
vis->mobj = (mobj_t *)thing;
vis->mobjflags = 0; vis->mobjflags = 0;
vis->cut = SC_NONE; vis->cut = SC_PRECIP;
vis->extra_colormap = thing->subsector->sector->extra_colormap; vis->extra_colormap = thing->subsector->sector->extra_colormap;
vis->heightsec = thing->subsector->sector->heightsec; vis->heightsec = thing->subsector->sector->heightsec;
// Fullbright // Fullbright
vis->colormap = colormaps; vis->colormap = colormaps;
vis->precip = true;
vis->vflip = false;
vis->isScaled = false;
} }
// R_AddSprites // R_AddSprites
@ -1797,7 +1783,7 @@ static vissprite_t vsprsortedhead;
void R_SortVisSprites(void) void R_SortVisSprites(void)
{ {
UINT32 i; UINT32 i, linkedvissprites = 0;
vissprite_t *ds, *dsprev, *dsnext, *dsfirst; vissprite_t *ds, *dsprev, *dsnext, *dsfirst;
vissprite_t *best = NULL; vissprite_t *best = NULL;
vissprite_t unsorted; vissprite_t unsorted;
@ -1821,22 +1807,91 @@ void R_SortVisSprites(void)
ds->next = dsnext; ds->next = dsnext;
ds->prev = dsprev; ds->prev = dsprev;
ds->linkdraw = NULL;
} }
// Fix first and last. ds still points to the last one after the loop // Fix first and last. ds still points to the last one after the loop
dsfirst->prev = &unsorted; dsfirst->prev = &unsorted;
unsorted.next = dsfirst; unsorted.next = dsfirst;
if (ds) if (ds)
{
ds->next = &unsorted; ds->next = &unsorted;
ds->linkdraw = NULL;
}
unsorted.prev = ds; unsorted.prev = ds;
// bundle linkdraw
for (ds = unsorted.prev; ds != &unsorted; ds = ds->prev)
{
if (!(ds->cut & SC_LINKDRAW))
continue;
// reuse dsfirst...
for (dsfirst = unsorted.prev; dsfirst != &unsorted; dsfirst = dsfirst->prev)
{
// don't connect if it's also a link
if (dsfirst->cut & SC_LINKDRAW)
continue;
// don't connect if it's not the tracer
if (dsfirst->mobj != ds->mobj)
continue;
// don't connect if the tracer's top is cut off, but lower than the link's top
if ((dsfirst->cut & SC_TOP)
&& dsfirst->szt > ds->szt)
continue;
// don't connect if the tracer's bottom is cut off, but higher than the link's bottom
if ((dsfirst->cut & SC_BOTTOM)
&& dsfirst->sz < ds->sz)
continue;
break;
}
// remove from chain
ds->next->prev = ds->prev;
ds->prev->next = ds->next;
linkedvissprites++;
if (dsfirst != &unsorted)
{
if (!(ds->cut & SC_FULLBRIGHT))
ds->colormap = dsfirst->colormap;
ds->extra_colormap = dsfirst->extra_colormap;
// reusing dsnext...
dsnext = dsfirst->linkdraw;
if (!dsnext || ds->dispoffset < dsnext->dispoffset)
{
ds->next = dsnext;
dsfirst->linkdraw = ds;
}
else
{
for (; dsnext->next != NULL; dsnext = dsnext->next)
if (ds->dispoffset < dsnext->next->dispoffset)
break;
ds->next = dsnext->next;
dsnext->next = ds;
}
}
}
// pull the vissprites out by scale // pull the vissprites out by scale
vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead;
for (i = 0; i < visspritecount; i++) for (i = 0; i < visspritecount-linkedvissprites; i++)
{ {
bestscale = bestdispoffset = INT32_MAX; bestscale = bestdispoffset = INT32_MAX;
for (ds = unsorted.next; ds != &unsorted; ds = ds->next) for (ds = unsorted.next; ds != &unsorted; ds = ds->next)
{ {
#ifdef PARANOIA
if (ds->cut & SC_LINKDRAW)
I_Error("R_SortVisSprites: no link or discardal made for linkdraw!");
#endif
if (ds->sortscale < bestscale) if (ds->sortscale < bestscale)
{ {
bestscale = ds->sortscale; bestscale = ds->sortscale;
@ -2090,20 +2145,6 @@ static void R_CreateDrawNodes(void)
} }
else if (r2->seg) else if (r2->seg)
{ {
#if 0 //#ifdef POLYOBJECTS_PLANES
if (r2->seg->curline->polyseg && rover->mobj && P_MobjInsidePolyobj(r2->seg->curline->polyseg, rover->mobj)) {
// Determine if we need to sort in front of the polyobj, based on the planes. This fixes the issue where
// polyobject planes render above the object standing on them. (A bit hacky... but it works.) -Red
mobj_t *mo = rover->mobj;
sector_t *po = r2->seg->curline->backsector;
if (po->ceilingheight < viewz && mo->z+mo->height > po->ceilingheight)
continue;
if (po->floorheight > viewz && mo->z < po->floorheight)
continue;
}
#endif
if (rover->x1 > r2->seg->x2 || rover->x2 < r2->seg->x1) if (rover->x1 > r2->seg->x2 || rover->x2 < r2->seg->x1)
continue; continue;
@ -2230,7 +2271,7 @@ static void R_DrawPrecipitationSprite(vissprite_t *spr)
void R_ClipSprites(void) void R_ClipSprites(void)
{ {
vissprite_t *spr; vissprite_t *spr;
for (;clippedvissprites < visspritecount; clippedvissprites++) for (; clippedvissprites < visspritecount; clippedvissprites++)
{ {
drawseg_t *ds; drawseg_t *ds;
INT32 x; INT32 x;
@ -2451,10 +2492,24 @@ void R_DrawMasked(void)
next = r2->prev; next = r2->prev;
// Tails 08-18-2002 // Tails 08-18-2002
if (r2->sprite->precip == true) if (r2->sprite->cut & SC_PRECIP)
R_DrawPrecipitationSprite(r2->sprite); R_DrawPrecipitationSprite(r2->sprite);
else else if (!r2->sprite->linkdraw)
R_DrawSprite(r2->sprite); R_DrawSprite(r2->sprite);
else // unbundle linkdraw
{
vissprite_t *ds = r2->sprite->linkdraw;
for (;
(ds != NULL && r2->sprite->dispoffset > ds->dispoffset);
ds = ds->next)
R_DrawSprite(ds);
R_DrawSprite(r2->sprite);
for (; ds != NULL; ds = ds->next)
R_DrawSprite(ds);
}
R_DoneWithNode(r2); R_DoneWithNode(r2);
r2 = next; r2 = next;

View file

@ -127,9 +127,19 @@ typedef struct
// ----------- // -----------
typedef enum typedef enum
{ {
// actual cuts
SC_NONE = 0, SC_NONE = 0,
SC_TOP = 1, SC_TOP = 1,
SC_BOTTOM = 2 SC_BOTTOM = 1<<1,
// other flags
SC_PRECIP = 1<<2,
SC_LINKDRAW = 1<<3,
SC_FULLBRIGHT = 1<<4,
SC_VFLIP = 1<<5,
SC_ISSCALED = 1>>6,
// masks
SC_CUTMASK = SC_TOP|SC_BOTTOM,
SC_FLAGMASK = ~SC_CUTMASK
} spritecut_e; } spritecut_e;
// A vissprite_t is a thing that will be drawn during a refresh, // A vissprite_t is a thing that will be drawn during a refresh,
@ -140,6 +150,9 @@ typedef struct vissprite_s
struct vissprite_s *prev; struct vissprite_s *prev;
struct vissprite_s *next; struct vissprite_s *next;
// Bonus linkdraw pointer.
struct vissprite_s *linkdraw;
mobj_t *mobj; // for easy access mobj_t *mobj; // for easy access
INT32 x1, x2; INT32 x1, x2;
@ -178,9 +191,6 @@ typedef struct vissprite_s
INT16 clipbot[MAXVIDWIDTH], cliptop[MAXVIDWIDTH]; INT16 clipbot[MAXVIDWIDTH], cliptop[MAXVIDWIDTH];
boolean precip;
boolean vflip; // Flip vertically
boolean isScaled;
INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing
} vissprite_t; } vissprite_t;