fix rendering glitches (#73)

This commit introduces:

* Vertex coordinates only used for rendering. These get moved in P_RemoveSlimeTrails(), the actual vertex coordinates are moved only if not in compatibility mode, and the rendering coordinates are moved back to their original location if a Linguortal is detected.
* Seg lengths and angles only used for rendering. These get recalculated after the vertices were moved.
* A fix for the "Long Line Wobble" based on the seg lengths introduced above.
* An overflow-safe variant of SlopeDiv(). This is only used in R_PointToAngleCrispy(), which in turn is an overflow-safe variant of R_PointToAngle(), which in turn is only used for rendering in R_CheckBBox(), R_AddLine() and P_SegLengthsAngles().
* Fixes for the Automap to cope with huge level dimensions which span the entire INT range.

All of the above fix the rendering glitches found in extreme maps such as e.g. planisf2. They were taken from Crispy Doom (and thus mostly from PrBoom+), where they have been introduced years ago and have proven to work. Fixes #70
This commit is contained in:
Fabian Greffrath 2020-03-05 12:10:51 +01:00 committed by GitHub
parent 1892141334
commit 719c988433
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 139 additions and 49 deletions

View File

@ -100,8 +100,9 @@ extern int joybautomap;
#define M_ZOOMOUT ((int) (FRACUNIT/1.02))
// translates between frame-buffer and map distances
#define FTOM(x) FixedMul(((x)<<16),scale_ftom)
#define MTOF(x) (FixedMul((x),scale_mtof)>>16)
// [FG] fix int overflow that causes map and grid lines to disappear
#define FTOM(x) ((((int64_t)(x)<<16)*scale_ftom)>>16)
#define MTOF(x) ((((int64_t)(x)*scale_mtof)>>16)>>16)
// translates between frame-buffer and map coordinates
#define CXMTOF(x) (f_x + MTOF((x)-m_x))
#define CYMTOF(y) (f_y + (f_h - MTOF((y)-m_y)))
@ -235,14 +236,14 @@ static mpoint_t m_paninc; // how far the window pans each tic (map coords)
static fixed_t mtof_zoommul; // how far the window zooms each tic (map coords)
static fixed_t ftom_zoommul; // how far the window zooms each tic (fb coords)
static fixed_t m_x, m_y; // LL x,y window location on the map (map coords)
static fixed_t m_x2, m_y2; // UR x,y window location on the map (map coords)
static int64_t m_x, m_y; // LL x,y window location on the map (map coords)
static int64_t m_x2, m_y2; // UR x,y window location on the map (map coords)
//
// width/height of window on map (map coords)
//
static fixed_t m_w;
static fixed_t m_h;
static int64_t m_w;
static int64_t m_h;
// based on level size
static fixed_t min_x;
@ -262,8 +263,8 @@ static fixed_t min_scale_mtof; // used to tell when to stop zooming out
static fixed_t max_scale_mtof; // used to tell when to stop zooming in
// old stuff for recovery later
static fixed_t old_m_w, old_m_h;
static fixed_t old_m_x, old_m_y;
static int64_t old_m_w, old_m_h;
static int64_t old_m_x, old_m_y;
// old location used by the Follower routine
static mpoint_t f_oldloc;
@ -433,8 +434,9 @@ void AM_findMinMaxBoundaries(void)
max_y = vertexes[i].y;
}
max_w = max_x - min_x;
max_h = max_y - min_y;
// [FG] cope with huge level dimensions which span the entire INT range
max_w = max_x/2 - min_x/2;
max_h = max_y/2 - min_y/2;
min_w = 2*PLAYERRADIUS; // const? never changed?
min_h = 2*PLAYERRADIUS;
@ -442,7 +444,7 @@ void AM_findMinMaxBoundaries(void)
a = FixedDiv(f_w<<FRACBITS, max_w);
b = FixedDiv(f_h<<FRACBITS, max_h);
min_scale_mtof = a < b ? a : b;
min_scale_mtof = a < b ? a/2 : b/2;
max_scale_mtof = FixedDiv(f_h<<FRACBITS, 2*PLAYERRADIUS);
}
@ -1181,8 +1183,8 @@ void AM_drawMline
//
void AM_drawGrid(int color)
{
fixed_t x, y;
fixed_t start, end;
int64_t x, y;
int64_t start, end;
mline_t ml;
// Figure out start of vertical gridlines
@ -1477,11 +1479,11 @@ void AM_drawWalls(void)
// Returns the coordinates rotated by the angle
//
void AM_rotate
( fixed_t* x,
fixed_t* y,
( int64_t* x,
int64_t* y,
angle_t a )
{
fixed_t tmpx;
int64_t tmpx;
tmpx =
FixedMul(*x,finecosine[a>>ANGLETOFINESHIFT])

View File

@ -61,7 +61,7 @@ extern void AM_clearMarks(void);
typedef struct
{
fixed_t x,y;
int64_t x,y;
} mpoint_t;
extern mpoint_t *markpoints;

View File

@ -339,9 +339,11 @@ void P_LoadNodes_ZDBSP (int lump, boolean compressed)
for (i = 0; i < newVerts; i++)
{
newvertarray[i + orgVerts].r_x =
newvertarray[i + orgVerts].x = *((unsigned int*)data);
data += sizeof(newvertarray[0].x);
newvertarray[i + orgVerts].r_y =
newvertarray[i + orgVerts].y = *((unsigned int*)data);
data += sizeof(newvertarray[0].y);
}

View File

@ -138,6 +138,10 @@ void P_LoadVertexes (int lump)
{
vertexes[i].x = SHORT(((mapvertex_t *) data)[i].x)<<FRACBITS;
vertexes[i].y = SHORT(((mapvertex_t *) data)[i].y)<<FRACBITS;
// [FG] vertex coordinates used for rendering
vertexes[i].r_x = vertexes[i].x;
vertexes[i].r_y = vertexes[i].y;
}
// Free buffer memory.
@ -917,8 +921,24 @@ void P_RemoveSlimeTrails(void) // killough 10/98
Long64 dxy = (l->dx >> FRACBITS) * (l->dy >> FRACBITS);
Long64 s = dx2 + dy2;
int x0 = v->x, y0 = v->y, x1 = l->v1->x, y1 = l->v1->y;
v->x = (fixed_t)((dx2 * x0 + dy2 * x1 + dxy * (y0 - y1)) / s);
v->y = (fixed_t)((dy2 * y0 + dx2 * y1 + dxy * (x0 - x1)) / s);
// [FG] move vertex coordinates used for rendering
v->r_x = (fixed_t)((dx2 * x0 + dy2 * x1 + dxy * (y0 - y1)) / s);
v->r_y = (fixed_t)((dy2 * y0 + dx2 * y1 + dxy * (x0 - x1)) / s);
// [FG] override actual vertex coordinates except in compatibility mode
if (!demo_compatibility)
{
v->x = v->r_x;
v->y = v->r_y;
}
// [FG] wait a minute... moved more than 8 map units?
// maybe that's a Linguortal then, back to the original coordinates
if (abs(v->r_x - x0) > 8*FRACUNIT || abs(v->r_y - y0) > 8*FRACUNIT)
{
v->r_x = x0;
v->r_y = y0;
}
}
} // Obfuscated C contest entry: :)
while ((v != segs[i].v2) && (v = segs[i].v2));
@ -927,6 +947,27 @@ void P_RemoveSlimeTrails(void) // killough 10/98
free(hit);
}
// [FG] re-calculated seg lengths and angles used for rendering
static void P_SegLengthsAngles (void)
{
int i;
for (i = 0; i < numsegs; i++)
{
seg_t *li = segs+i;
int64_t dx, dy;
dx = li->v2->r_x - li->v1->r_x;
dy = li->v2->r_y - li->v1->r_y;
li->r_length = (uint32_t)(sqrt((double)dx*dx + (double)dy*dy)/2);
viewx = li->v1->r_x;
viewy = li->v1->r_y;
li->r_angle = R_PointToAngleCrispy(li->v2->r_x, li->v2->r_y);
}
}
// [FG] pad the REJECT table when the lump is too small
static void P_LoadReject(int lumpnum)
@ -1048,6 +1089,9 @@ void P_SetupLevel(int episode, int map, int playermask, skill_t skill)
P_RemoveSlimeTrails(); // killough 10/98: remove slime trails from wad
// [FG] seg lengths and angles used for rendering
P_SegLengthsAngles();
// Note: you don't need to clear player queue slots --
// a much simpler fix is in g_game.c -- killough 10/98

View File

@ -378,8 +378,8 @@ static void R_AddLine (seg_t *line)
curline = line;
angle1 = R_PointToAngle (line->v1->x, line->v1->y);
angle2 = R_PointToAngle (line->v2->x, line->v2->y);
angle1 = R_PointToAngleCrispy (line->v1->r_x, line->v1->r_y);
angle2 = R_PointToAngleCrispy (line->v2->r_x, line->v2->r_y);
// Clip to view edges.
span = angle1 - angle2;
@ -532,8 +532,8 @@ static boolean R_CheckBBox(fixed_t *bspcoord) // killough 1/28/98: static
y2 = bspcoord[checkcoord[boxpos][3]];
// check clip list for an open space
angle1 = R_PointToAngle (x1, y1) - viewangle;
angle2 = R_PointToAngle (x2, y2) - viewangle;
angle1 = R_PointToAngleCrispy (x1, y1) - viewangle;
angle2 = R_PointToAngleCrispy (x2, y2) - viewangle;
span = angle1 - angle2;

View File

@ -68,6 +68,9 @@
typedef struct
{
fixed_t x, y;
// [FG] vertex coordinates used for rendering
fixed_t r_x, r_y;
} vertex_t;
// Each sector has a degenmobj_t in its center for sound origin purposes.
@ -255,6 +258,10 @@ typedef struct
// backsector is NULL for one sided lines
sector_t *frontsector, *backsector;
// [FG] seg lengths and angles used for rendering
uint32_t r_length;
angle_t r_angle;
} seg_t;
//

View File

@ -188,6 +188,37 @@ angle_t R_PointToAngle2(fixed_t viewx, fixed_t viewy, fixed_t x, fixed_t y)
0;
}
// [FG] overflow-safe R_PointToAngle() flavor,
// only used in R_CheckBBox(), R_AddLine() and P_SegLengthsAngles()
angle_t R_PointToAngleCrispy(fixed_t x, fixed_t y)
{
// [FG] fix overflows for very long distances
int64_t y_viewy = (int64_t)y - viewy;
int64_t x_viewx = (int64_t)x - viewx;
// [FG] the worst that could happen is e.g. INT_MIN-INT_MAX = 2*INT_MIN
if (x_viewx < INT_MIN || x_viewx > INT_MAX || y_viewy < INT_MIN || y_viewy > INT_MAX)
{
// [FG] preserving the angle by halfing the distance in both directions
x = (int)(x_viewx / 2 + viewx);
y = (int)(y_viewy / 2 + viewy);
}
return (y -= viewy, (x -= viewx) || y) ?
x >= 0 ?
y >= 0 ?
(x > y) ? tantoangle[SlopeDivCrispy(y,x)] : // octant 0
ANG90-1-tantoangle[SlopeDivCrispy(x,y)] : // octant 1
x > (y = -y) ? 0-tantoangle[SlopeDivCrispy(y,x)] : // octant 8
ANG270+tantoangle[SlopeDivCrispy(x,y)] : // octant 7
y >= 0 ? (x = -x) > y ? ANG180-1-tantoangle[SlopeDivCrispy(y,x)] : // octant 3
ANG90 + tantoangle[SlopeDivCrispy(x,y)] : // octant 2
(x = -x) > (y = -y) ? ANG180+tantoangle[ SlopeDivCrispy(y,x)] : // octant 4
ANG270-1-tantoangle[SlopeDivCrispy(x,y)] : // octant 5
0;
}
//
// R_ScaleFromGlobalAngle
// Returns the texture mapping scale

View File

@ -92,6 +92,7 @@ int R_PointOnSide(fixed_t x, fixed_t y, node_t *node);
int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t *line);
angle_t R_PointToAngle(fixed_t x, fixed_t y);
angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2);
angle_t R_PointToAngleCrispy(fixed_t x, fixed_t y);
fixed_t R_ScaleFromGlobalAngle(angle_t visangle);
subsector_t *R_PointInSubsector(fixed_t x, fixed_t y);

View File

@ -365,6 +365,7 @@ static void R_RenderSegLoop (void)
}
}
#if 0
// killough 5/2/98: move from r_main.c, made static, simplified
static fixed_t R_PointToDist(fixed_t x, fixed_t y)
@ -380,6 +381,7 @@ static fixed_t R_PointToDist(fixed_t x, fixed_t y)
return dx ? FixedDiv(dx, finesine[(tantoangle[FixedDiv(dy,dx) >> DBITS]
+ ANG90) >> ANGLETOFINESHIFT]) : 0;
}
#endif
//
// R_StoreWallRange
@ -388,9 +390,9 @@ static fixed_t R_PointToDist(fixed_t x, fixed_t y)
//
void R_StoreWallRange(const int start, const int stop)
{
fixed_t hyp;
fixed_t sineval;
angle_t distangle, offsetangle;
// [FG] fix long wall wobble
int64_t dx, dy, dx1, dy1, dist;
const uint32_t len = curline->r_length; // [FG] use re-calculated seg lengths
if (ds_p == drawsegs+maxdrawsegs) // killough 1/98 -- fix 2s line HOM
{
@ -412,16 +414,16 @@ void R_StoreWallRange(const int start, const int stop)
linedef->flags |= ML_MAPPED;
// calculate rw_distance for scale calculation
rw_normalangle = curline->angle + ANG90;
offsetangle = abs(rw_normalangle-rw_angle1);
rw_normalangle = curline->r_angle + ANG90; // [FG] use re-calculated seg angles
if (offsetangle > ANG90)
offsetangle = ANG90;
distangle = ANG90 - offsetangle;
hyp = R_PointToDist (curline->v1->x, curline->v1->y);
sineval = finesine[distangle>>ANGLETOFINESHIFT];
rw_distance = FixedMul(hyp, sineval);
// [FG] fix long wall wobble
// shift right to avoid possibility of int64 overflow in rw_distance calculation
dx = ((int64_t)curline->v2->r_x - curline->v1->r_x) >> 1;
dy = ((int64_t)curline->v2->r_y - curline->v1->r_y) >> 1;
dx1 = ((int64_t)viewx - curline->v1->r_x) >> 1;
dy1 = ((int64_t)viewy - curline->v1->r_y) >> 1;
dist = ((dy * dx1 - dx * dy1) / len) << 1;
rw_distance = (fixed_t)(dist < INT_MIN ? INT_MIN : dist > INT_MAX ? INT_MAX : dist);
ds_p->x1 = rw_x = start;
ds_p->x2 = stop;
@ -627,20 +629,8 @@ void R_StoreWallRange(const int start, const int stop)
if (segtextured)
{
offsetangle = rw_normalangle-rw_angle1;
if (offsetangle > ANG180)
offsetangle = 0 - offsetangle;
if (offsetangle > ANG90)
offsetangle = ANG90;
sineval = finesine[offsetangle >>ANGLETOFINESHIFT];
rw_offset = FixedMul (hyp, sineval);
if (rw_normalangle-rw_angle1 < ANG180)
rw_offset = -rw_offset;
// [FG] fix long wall wobble
rw_offset = (fixed_t)(((dx * dx1 + dy * dy1) / len) << 1);
rw_offset += sidedef->textureoffset + curline->offset;
rw_centerangle = ANG90 + viewangle - rw_normalangle;

View File

@ -54,6 +54,18 @@ int SlopeDiv(unsigned num, unsigned den)
return ans <= SLOPERANGE ? ans : SLOPERANGE;
}
// [FG] overflow-safe SlopeDiv() flavor, only used in R_PointToAngleCrispy()
int SlopeDivCrispy(unsigned num, unsigned den)
{
uint64_t ans;
if (den < 512)
return SLOPERANGE;
ans = ((uint64_t)num<<3)/(den>>8);
return ans <= SLOPERANGE ? (int)ans : SLOPERANGE;
}
const int finetangent[4096] = { -170910304,
-56965752,-34178904,-24413316,-18988036,-15535599,-13145455,-11392683,
-10052327,-8994149,-8137527,-7429880,-6835455,-6329090,-5892567,-5512368,

View File

@ -78,6 +78,7 @@ extern const angle_t tantoangle[SLOPERANGE+1];
// Utility function, called by R_PointToAngle.
int SlopeDiv(unsigned num, unsigned den);
int SlopeDivCrispy(unsigned num, unsigned den);
#endif