Classic renderer: high-precision sprite drawing, preventing stray lines on top.

Face- and wall-aligned sprites are drawn using the wall routines in BUILD.
However, the per-x-screen-coordinate distance (swall[]) is calculated in a way
that potentially incurs great precision loss (for example 5 bits for
xdimen=1280, yxaspect=65536). This leads to the starting (top) vertical texture
coordinate possibly wrapping to large values, leaving an unsightly "stray line"
on top of the sprite from certain viewing angles/horiz values.

The approach to fix it has two parts: first, the distance is calculated using
float values, preventing the precision loss. Because this doesn't fully prevent
the unwanted lines, the texture coords are clamped to the mininum and maximum
(0 and UINT32_MAX respectively) when calculating them for sprites.

Note that stray lines may still appear at the *bottom* of sprites under certain
circumstances, for example when viewing at a y-flipped sprite from above.
These should be less noticable in real-world usage though.

The feature is guarded by a macro HIGH_PRECISION_SPRITE in case using floating
point or 64-bit integers is undesirable/impossible on some platforms.

git-svn-id: https://svn.eduke32.com/eduke32@3483 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2013-02-14 16:02:12 +00:00
parent 32b3c074d2
commit 40874820f3

View file

@ -47,6 +47,7 @@
//#define CLASSIC_NONPOW2_YSIZE_WALLS
#define CLASSIC_NONPOW2_YSIZE_SPRITES
#define HIGH_PRECISION_SPRITE
#define MULTI_COLUMN_VLINE
//#define DEBUG_TILESIZY_512
@ -2223,6 +2224,9 @@ static int16_t uplc[MAXXDIM], dplc[MAXXDIM];
static int16_t uwall[MAXXDIM], dwall[MAXXDIM];
static int32_t swplc[MAXXDIM], lplc[MAXXDIM];
static int32_t swall[MAXXDIM], lwall[MAXXDIM+4];
#ifdef HIGH_PRECISION_SPRITE
static float swallf[MAXXDIM];
#endif
int32_t xdimen = -1, xdimenrecip, halfxdimen, xdimenscale, xdimscale;
int32_t ydimen;
static int32_t wx1, wy1, wx2, wy2;
@ -2252,7 +2256,12 @@ static int32_t globalxpanning, globalypanning;
int32_t globalshade, globalorientation;
int16_t globalpicnum;
static int16_t globalshiftval;
static int32_t globalzd, globalyscale;
#ifdef HIGH_PRECISION_SPRITE
static int64_t globalzd;
#else
static int32_t globalzd;
#endif
static int32_t globalyscale;
static int32_t globalxspan, globalyspan, globalispow2=1; // true if texture has power-of-two x and y size
static intptr_t globalbufplc;
@ -2679,12 +2688,38 @@ static WSHELPER_DECL void calc_bufplc(intptr_t *bufplc, int32_t lw, int32_t tsiz
*bufplc = waloff[globalpicnum] + i;
}
static WSHELPER_DECL void calc_vplcinc(uint32_t *vplc, int32_t *vinc, int64_t sw, int32_t y1v)
static WSHELPER_DECL void calc_vplcinc_wall(uint32_t *vplc, int32_t *vinc, int64_t sw, int32_t y1v)
{
*vinc = sw*globalyscale;
*vplc = globalzd + (uint32_t)(*vinc)*(y1v-globalhoriz+1);
}
#ifdef HIGH_PRECISION_SPRITE
static WSHELPER_DECL void calc_vplcinc_sprite(uint32_t *vplc, int32_t *vinc, int32_t x, int32_t y1v)
{
int64_t tmpvinc = swallf[x];
int64_t tmpvplc = globalzd + tmpvinc*(y1v-globalhoriz+1);
*vinc = tmpvinc;
// Clamp the vertical texture coordinate!
*vplc = min(max(0, tmpvplc), UINT32_MAX);
}
#endif
static int32_t drawing_sprite = 0;
static WSHELPER_DECL void calc_vplcinc(uint32_t *vplc, int32_t *vinc, const int32_t *swal, int32_t x, int32_t y1v)
{
#if !defined HIGH_PRECISION_SPRITE
(void)drawing_sprite;
#else
if (drawing_sprite)
calc_vplcinc_sprite(vplc, vinc, x, y1v);
else
#endif
calc_vplcinc_wall(vplc, vinc, swal[x], y1v);
}
#undef NONPOW2_YSIZE_ASM
#if !defined ENGINE_USING_A_C
# if defined CLASSIC_NONPOW2_YSIZE_WALLS || defined CLASSIC_NONPOW2_YSIZE_SPRITES
@ -2748,7 +2783,7 @@ static void maskwallscan(int32_t x1, int32_t x2)
palookupoffse[0] = fpalookup + getpalookupsh(mulscale16(swall[x],globvis));
calc_bufplc(&bufplce[0], lwall[x], tsizx, tsizy);
calc_vplcinc(&vplce[0], &vince[0], swall[x], y1ve[0]);
calc_vplcinc(&vplce[0], &vince[0], swall, x, y1ve[0]);
mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0],p+ylookup[y1ve[0]]);
}
@ -2764,7 +2799,7 @@ static void maskwallscan(int32_t x1, int32_t x2)
if (y2ve[z] < y1ve[z]) { bad += pow2char[z]; continue; }
calc_bufplc(&bufplce[z], lwall[dax], tsizx, tsizy);
calc_vplcinc(&vplce[z], &vince[z], swall[dax], y1ve[z]);
calc_vplcinc(&vplce[z], &vince[z], swall, dax, y1ve[z]);
}
if (bad == 15) continue;
@ -2821,7 +2856,7 @@ do_mvlineasm1:
palookupoffse[0] = fpalookup + getpalookupsh(mulscale16(swall[x],globvis));
calc_bufplc(&bufplce[0], lwall[x], tsizx, tsizy);
calc_vplcinc(&vplce[0], &vince[0], swall[x], y1ve[0]);
calc_vplcinc(&vplce[0], &vince[0], swall, x, y1ve[0]);
#ifdef NONPOW2_YSIZE_ASM
if (globalshiftval==0)
@ -3740,7 +3775,7 @@ static void wallscan(int32_t x1, int32_t x2,
palookupoffse[0] = fpalookup + getpalookupsh(mulscale16(swal[x],globvis));
calc_bufplc(&bufplce[0], lwal[x], tsizx, tsizy);
calc_vplcinc(&vplce[0], &vince[0], swal[x], y1ve[0]);
calc_vplcinc(&vplce[0], &vince[0], swal, x, y1ve[0]);
vlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0],x+frameoffset+ylookup[y1ve[0]]);
}
@ -3754,7 +3789,7 @@ static void wallscan(int32_t x1, int32_t x2,
if (y2ve[z] < y1ve[z]) { bad += pow2char[z]; continue; }
calc_bufplc(&bufplce[z], lwal[x+z], tsizx, tsizy);
calc_vplcinc(&vplce[z], &vince[z], swal[x+z], y1ve[z]);
calc_vplcinc(&vplce[z], &vince[z], swal, x+z, y1ve[z]);
}
if (bad == 15) continue;
@ -3811,7 +3846,7 @@ do_vlineasm1:
palookupoffse[0] = fpalookup + getpalookupsh(mulscale16(swal[x],globvis));
calc_bufplc(&bufplce[0], lwal[x], tsizx, tsizy);
calc_vplcinc(&vplce[0], &vince[0], swal[x], y1ve[0]);
calc_vplcinc(&vplce[0], &vince[0], swal, x, y1ve[0]);
#ifdef NONPOW2_YSIZE_ASM
if (globalshiftval==0)
@ -3845,7 +3880,7 @@ static void transmaskvline(int32_t x)
palookupoffs = FP_OFF(palookup[globalpal]) + getpalookupsh(mulscale16(swall[x],globvis));
calc_bufplc(&bufplc, lwall[x], -tilesizx[globalpicnum], -tilesizy[globalpicnum]);
calc_vplcinc(&vplc, &vinc, swall[x], y1v);
calc_vplcinc(&vplc, &vinc, swall, x, y1v);
p = ylookup[y1v]+x+frameoffset;
@ -3888,8 +3923,8 @@ static void transmaskvline2(int32_t x)
calc_bufplc(&bufplce[0], lwall[x], -tilesizx[globalpicnum], -tilesizy[globalpicnum]);
calc_bufplc(&bufplce[1], lwall[x2], -tilesizx[globalpicnum], -tilesizy[globalpicnum]);
calc_vplcinc(&vplce[0], &vince[0], swall[x], y1ve[0]);
calc_vplcinc(&vplce[1], &vince[1], swall[x2], y1ve[1]);
calc_vplcinc(&vplce[0], &vince[0], swall, x, y1ve[0]);
calc_vplcinc(&vplce[1], &vince[1], swall, x2, y1ve[1]);
y1 = max(y1ve[0],y1ve[1]);
y2 = min(y2ve[0],y2ve[1]);
@ -4070,18 +4105,14 @@ static inline void ceilspritehline(int32_t x2, int32_t y)
if ((globalorientation&2) == 0)
mhline(globalbufplc,bx,(x2-x1)<<16,0L,by,ylookup[y]+x1+frameoffset);
else
{
thline(globalbufplc,bx,(x2-x1)<<16,0L,by,ylookup[y]+x1+frameoffset);
}
}
else
{
if ((globalorientation&2) == 0)
nonpow2_mhline(globalbufplc,bx,(x2-x1)<<16,0L,by,(char *)(ylookup[y]+x1+frameoffset));
else
{
nonpow2_thline(globalbufplc,bx,(x2-x1)<<16,0L,by,(char *)(ylookup[y]+x1+frameoffset));
}
}
}
@ -5681,16 +5712,32 @@ draw_as_face_sprite:
qinterpolatedown16((intptr_t)&lwall[lx],rx-lx+1,linum,linuminc);
clearbuf(&swall[lx],rx-lx+1,mulscale19(yp,xdimscale));
{
#ifdef HIGH_PRECISION_SPRITE
union { float f; int32_t i; } sw = {
// initialize the float of the union
((cstat&8) ? -1 : 1)
* (float)yp * xdimscale
* (1<<(22-19)) / (yspan*tspr->yrepeat)
};
clearbuf(&swallf[lx], rx-lx+1, sw.i);
#endif
}
drawing_sprite = 1;
if ((cstat&2) == 0)
maskwallscan(lx,rx);
else
transmaskwallscan(lx,rx);
drawing_sprite = 0;
}
else if ((cstat&48) == 16)
{
int32_t swapped, top, bot, topinc, botinc;
int32_t xv, yv, hplc, hinc;
int32_t sx1, sx2, sy1, sy2;
int32_t xv, yv, sx1, sx2, sy1, sy2;
if ((cstat&4) > 0) xoff = -xoff;
if ((cstat&8) > 0) yoff = -yoff;
@ -5796,10 +5843,6 @@ draw_as_face_sprite:
rx1[MAXWALLSB-1] = xp1; ry1[MAXWALLSB-1] = yp1;
rx2[MAXWALLSB-1] = xp2; ry2[MAXWALLSB-1] = yp2;
hplc = divscale19(xdimenscale,sy1);
hinc = divscale19(xdimenscale,sy2);
hinc = (hinc-hplc)/(sx2-sx1+1);
setup_globals_sprite1(tspr, sec, yspan, yoff, tilenum, cstat, &z1, &z2);
if (((sec->ceilingstat&1) == 0) && (z1 < sec->ceilingz))
@ -5813,8 +5856,29 @@ draw_as_face_sprite:
yb2[MAXWALLSB-1] = sy2;
owallmost(uwall, MAXWALLSB-1, z1-globalposz);
owallmost(dwall, MAXWALLSB-1, z2-globalposz);
for (i=sx1; i<=sx2; i++)
{ swall[i] = (krecipasm(hplc)<<2); hplc += hinc; }
{
int32_t hplc = divscale19(xdimenscale,sy1);
const int32_t hplc2 = divscale19(xdimenscale,sy2);
int32_t hinc = sx2-sx1 ? (hplc2-hplc)/(sx2-sx1) : 0;
#ifdef HIGH_PRECISION_SPRITE
const float cc = ((1<<19)*(float)xdimen*yxaspect)/320.f;
float hplcf = cc/sy1;
const float hincf = sx2-sx1 ? (cc/sy2 - hplcf)/(sx2-sx1) : 0;
const float loopcc = ((cstat&8) ? -1 : 1)*((float)(1<<30)*(1<<24))
/ (yspan*tspr->yrepeat);
#endif
for (i=sx1; i<=sx2; i++)
{
swall[i] = (krecipasm(hplc)<<2);
hplc += hinc;
#ifdef HIGH_PRECISION_SPRITE
swallf[i] = loopcc/hplcf;
hplcf += hincf;
#endif
}
}
for (i=smostwallcnt-1; i>=0; i--)
{
@ -5923,14 +5987,14 @@ draw_as_face_sprite:
searchstat = 3; searchit = 1;
}
drawing_sprite = 1;
if ((cstat&2) == 0)
{
maskwallscan(sx1,sx2);
}
else
{
transmaskwallscan(sx1,sx2);
}
drawing_sprite = 0;
}
else if ((cstat&48) == 32)
{
@ -6271,8 +6335,8 @@ draw_as_face_sprite:
lx = 0; rx = xdim-1;
for (x=lx; x<=rx; x++)
{
lwall[x] = (int32_t)startumost[x+windowx1]-windowy1;
swall[x] = (int32_t)startdmost[x+windowx1]-windowy1;
lwall[x] = startumost[x+windowx1]-windowy1;
swall[x] = startdmost[x+windowx1]-windowy1;
}
for (i=smostwallcnt-1; i>=0; i--)
{