Port Antimony's render grid traversal algorithm

This commit is contained in:
Gustaf Alhäll 2025-08-17 20:17:57 +02:00 committed by NepDisk
parent 34f7d76aeb
commit cab86f9ad3

View file

@ -1736,10 +1736,6 @@ static boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac)
boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2,
INT32 flags, traverser_t trav)
{
fixed_t xt1, yt1, xt2, yt2;
fixed_t xstep, ystep, partialx, partialy, xintercept, yintercept;
INT32 mapx, mapy, mapxstep, mapystep, count;
validcount++;
intercept_p = intercepts;
@ -1756,170 +1752,94 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2,
px1 -= bmaporgx;
py1 -= bmaporgy;
xt1 = (unsigned)px1>>MAPBLOCKSHIFT;
yt1 = (unsigned)py1>>MAPBLOCKSHIFT;
px2 -= bmaporgx;
py2 -= bmaporgy;
xt2 = (unsigned)px2>>MAPBLOCKSHIFT;
yt2 = (unsigned)py2>>MAPBLOCKSHIFT;
if (xt2 > xt1)
// blockmap traversal algorithm ported from antimony
int gridpos_x = px1 >> MAPBLOCKSHIFT;
int gridpos_y = py1 >> MAPBLOCKSHIFT;
if (flags & PT_ADDLINES)
if (!P_BlockLinesIterator(gridpos_x, gridpos_y, PIT_AddLineIntercepts))
return false; // early out
if (flags & PT_ADDTHINGS)
if (!P_BlockThingsIterator(gridpos_x, gridpos_y, PIT_AddThingIntercepts))
return false; // early out
int endpos_x = px2 >> MAPBLOCKSHIFT;
int endpos_y = py2 >> MAPBLOCKSHIFT;
if (gridpos_x == endpos_x && gridpos_y == endpos_y)
return P_TraverseIntercepts(trav, FRACUNIT);
fixed_t pos_x = px1;
fixed_t pos_y = py1;
fixed_t dist_x = px2 - px1;
fixed_t dist_y = py2 - py1;
int dir_x = dist_x < 0 ? -1 : 1;
int dir_y = dist_y < 0 ? -1 : 1;
fixed_t delta_x;
fixed_t delta_y;
// special cases for 0 to avoid crashes
if (dist_y == 0)
{
mapxstep = 1;
partialx = FRACUNIT - ((px1>>MAPBTOFRAC) & FRACMASK);
ystep = FixedDiv(py2 - py1, abs(px2 - px1));
delta_x = INT32_MAX;
delta_y = 0;
}
else if (xt2 < xt1)
else if (dist_x == 0)
{
mapxstep = -1;
partialx = (px1>>MAPBTOFRAC) & FRACMASK;
ystep = FixedDiv(py2 - py1, abs(px2 - px1));
delta_x = 0;
delta_y = INT32_MAX;
}
else
{
mapxstep = 0;
partialx = FRACUNIT;
ystep = 256*FRACUNIT;
delta_x = FixedDiv2(dist_x, abs(dist_y));
delta_y = FixedDiv2(dist_y, abs(dist_x));
}
yintercept = (py1>>MAPBTOFRAC) + FixedMul(partialx, ystep);
while (gridpos_x >= 0 && gridpos_y >= 0 && gridpos_x < bmapwidth && gridpos_y < bmapheight)
{
fixed_t hitx_x;
if (dir_x > 0)
hitx_x = (gridpos_x+1) << MAPBLOCKSHIFT;
else
hitx_x = gridpos_x << MAPBLOCKSHIFT;
if (yt2 > yt1)
{
mapystep = 1;
partialy = FRACUNIT - ((py1>>MAPBTOFRAC) & FRACMASK);
xstep = FixedDiv(px2 - px1, abs(py2 - py1));
}
else if (yt2 < yt1)
{
mapystep = -1;
partialy = (py1>>MAPBTOFRAC) & FRACMASK;
xstep = FixedDiv(px2 - px1, abs(py2 - py1));
}
else
{
mapystep = 0;
partialy = FRACUNIT;
xstep = 256*FRACUNIT;
}
xintercept = (px1>>MAPBTOFRAC) + FixedMul(partialy, xstep);
fixed_t hity_y;
if (dir_y > 0)
hity_y = (gridpos_y+1) << MAPBLOCKSHIFT;
else
hity_y = gridpos_y << MAPBLOCKSHIFT;
// [RH] Fix for traces that pass only through blockmap corners. In that case,
// xintercept and yintercept can both be set ahead of mapx and mapy, so the
// for loop would never advance anywhere.
if (abs(xstep) == FRACUNIT && abs(ystep) == FRACUNIT)
{
if (ystep < 0)
hitx_x -= pos_x;
hity_y -= pos_y;
fixed_t hitx_y = FixedMul(delta_y, abs(hitx_x));
fixed_t hity_x = FixedMul(delta_x, abs(hity_y));
if (FixedMul(hitx_x, hitx_x) + FixedMul(hitx_y, hitx_y) > FixedMul(hity_x, hity_x) + FixedMul(hity_y, hity_y))
{
partialx = FRACUNIT - partialx;
gridpos_y += dir_y;
pos_x += hity_x;
pos_y += hity_y;
}
if (xstep < 0)
else
{
partialy = FRACUNIT - partialy;
gridpos_x += dir_x;
pos_x += hitx_x;
pos_y += hitx_y;
}
if (partialx == partialy)
{
xintercept = xt1 << FRACBITS;
yintercept = yt1 << FRACBITS;
}
}
// Step through map blocks.
// Count is present to prevent a round off error
// from skipping the break.
mapx = xt1;
mapy = yt1;
for (count = 0; count < 100; count++)
{
if (flags & PT_ADDLINES)
{
P_BlockLinesIterator(mapx, mapy, PIT_AddLineIntercepts);
}
if (!P_BlockLinesIterator(gridpos_x, gridpos_y, PIT_AddLineIntercepts))
return false; // early out
if (flags & PT_ADDTHINGS)
{
P_BlockThingsIterator(mapx, mapy, PIT_AddThingIntercepts);
}
// both coordinates reached the end, so end the traversing.
if ((mapxstep | mapystep) == 0)
{
if (!P_BlockThingsIterator(gridpos_x, gridpos_y, PIT_AddThingIntercepts))
return false; // early out
if (gridpos_x == endpos_x && gridpos_y == endpos_y)
break;
}
// [RH] Handle corner cases properly instead of pretending they don't exist.
switch ( (((yintercept >> FRACBITS) == mapy) << 1) | ((xintercept >> FRACBITS) == mapx) )
{
case 0: // neither xintercept nor yintercept match!
{
count = 100; // Stop traversing, because somebody screwed up.
break;
}
case 1: // xintercept matches
{
xintercept += xstep;
mapy += mapystep;
if (mapy == yt2)
{
mapystep = 0;
}
break;
}
case 2: // yintercept matches
{
yintercept += ystep;
mapx += mapxstep;
if (mapx == xt2)
{
mapxstep = 0;
}
break;
}
case 3: // xintercept and yintercept both match
{
// The trace is exiting a block through its corner. Not only does the block
// being entered need to be checked (which will happen when this loop
// continues), but the other two blocks adjacent to the corner also need to
// be checked.
if (flags & PT_ADDLINES)
{
P_BlockLinesIterator(mapx + mapxstep, mapy, PIT_AddLineIntercepts);
P_BlockLinesIterator(mapx, mapy + mapystep, PIT_AddLineIntercepts);
}
if (flags & PT_ADDTHINGS)
{
P_BlockThingsIterator(mapx + mapxstep, mapy, PIT_AddThingIntercepts);
P_BlockThingsIterator(mapx, mapy + mapystep, PIT_AddThingIntercepts);
}
xintercept += xstep;
yintercept += ystep;
mapx += mapxstep;
mapy += mapystep;
if (mapx == xt2)
{
mapxstep = 0;
}
if (mapy == yt2)
{
mapystep = 0;
}
break;
}
}
}
// Go through the sorted list
return P_TraverseIntercepts(trav, FRACUNIT);
}
// =========================================================================
// BLOCKMAP ITERATORS
// =========================================================================