- better wall sprite handling.

When they are directly on a wall, project their coordinates onto the wall to eliminate the imprecisions from Build's ad-hoc setup.
This commit is contained in:
Christoph Oelckers 2021-12-29 18:16:50 +01:00
parent 1a2b9d43f7
commit d46a7abb0e
4 changed files with 53 additions and 76 deletions

View file

@ -343,7 +343,24 @@ inline double SquareDist(double lx1, double ly1, double lx2, double ly2)
return dx * dx + dy * dy;
}
inline double SquareDistToWall(double px, double py, const walltype* wal)
inline DVector2 NearestPointLine(double px, double py, const walltype* wal)
{
double lx1 = wal->pos.X;
double ly1 = wal->pos.Y;
double lx2 = wal->point2Wall()->pos.X;
double ly2 = wal->point2Wall()->pos.Y;
double wall_length = SquareDist(lx1, ly1, lx2, ly2);
if (wall_length == 0) return { lx1, ly1 };
double t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1)) / wall_length;
double xx = lx1 + t * (lx2 - lx1);
double yy = ly1 + t * (ly2 - ly1);
return { xx, yy };
}
inline double SquareDistToWall(double px, double py, const walltype* wal, DVector2* point = nullptr)
{
double lx1 = wal->pos.X;
double ly1 = wal->pos.Y;
@ -356,7 +373,10 @@ inline double SquareDistToWall(double px, double py, const walltype* wal)
double t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1)) / wall_length;
t = clamp(t, 0., 1.);
return SquareDist(px, py, lx1 + t * (lx2 - lx1), ly1 + t * (ly2 - ly1));
double xx = lx1 + t * (lx2 - lx1);
double yy = ly1 + t * (ly2 - ly1);
if (point) *point = { xx, yy };
return SquareDist(px, py, xx, yy);
}
inline void alignceilslope(sectortype* sect, int x, int y, int z)

View file

@ -163,7 +163,8 @@ public:
float ViewDistance;
float visibility;
int walldist;
walltype* walldist;
FVector2 wallpoint;
short shade, palette;
PalEntry fade;

View file

@ -48,89 +48,34 @@ DCoreActor* wall_to_sprite_actors[8]; // gets updated each frame. Todo: Encapsul
//
//==========================================================================
static int GetClosestPointOnWall(tspritetype* spr, walltype* wal, vec2_t* const n)
static walltype* IsOnWall(tspritetype* tspr, int height, DVector2& outpos)
{
auto w = wal->pos;
auto d = wal->point2Wall()->pos - w;
auto pos = spr->pos;
const double maxdistsq = (tspr->ang & 0x1ff)? 3 * 3 : 1; // lower tolerance for perfectly orthogonal sprites
// avoid the math below for orthogonal walls. Here we allow only sprites that exactly match the line's coordinate and orientation
if (d.X == 0 && d.Y == 0)
{
// line has no length.
// In Blood's E1M1 this gets triggered for wall 522.
return 1;
}
else if (d.X == 0)
{
// line is vertical.
if (abs(pos.X - w.X) <= 1 && (spr->ang & 0x3ff) == 0)
{
*n = pos.vec2;
return 0;
}
return 1;
}
else if (d.Y == 0)
{
// line is horizontal.
if (abs(pos.Y - w.Y) <= 1 && (spr->ang & 0x3ff) == 0x200)
{
*n = pos.vec2;
return 0;
}
return 1;
}
int64_t i = d.X * ((int64_t)pos.X - w.X) + d.Y * ((int64_t)pos.Y - w.Y);
if (i < 0)
return 1;
int64_t j = (int64_t)d.X * d.X + (int64_t)d.Y * d.Y;
if (i > j)
return 1;
i = ((i << 15) / j) << 15;
n->X = w.X + ((d.X * i) >> 30);
n->Y = w.Y + ((d.Y * i) >> 30);
return 0;
}
//==========================================================================
//
//
//
//==========================================================================
static int IsOnWall(tspritetype* tspr, int height)
{
int dist = 3;
auto sect = tspr->sector();
vec2_t n;
walltype* closest = nullptr;
int topz = (tspr->pos.Z - ((height * tspr->yrepeat) << 2));
for(auto& wal : wallsofsector(sect))
{
if ((wal.nextsector == -1 || (wal.cstat & CSTAT_WALL_1WAY) || ((wal.nextSector()->ceilingz > topz) ||
wal.nextSector()->floorz < tspr->pos.Z)) && !GetClosestPointOnWall(tspr, &wal, &n))
{
int const dst = abs(tspr->pos.X - n.X) + abs(tspr->pos.Y - n.Y);
// Intentionally include two sided walls. Even on them the sprite should be projected onto the wall for better results.
auto d = wal.delta();
int walang = getangle(d.X, d.Y);
int deltaang = abs(walang - tspr->ang) & 2047;
const int maxangdelta = 1;
if (dst <= dist)
// angle of the sprite must either be the wall's normal or the negative wall's normal to be aligned.
if (deltaang >= 512 - maxangdelta && deltaang <= 512 + maxangdelta)
{
double wdist = SquareDistToWall(tspr->pos.X, tspr->pos.Y, &wal, &outpos);
if (wdist <= maxdistsq)
{
dist = dst;
closest = &wal;
}
}
}
return closest == nullptr? -1 : dist;
// todo: cache this in the sprite to avoid recalculation.
return closest;
}
//==========================================================================
@ -238,7 +183,7 @@ void HWWall::RenderTexturedWall(HWDrawInfo *di, FRenderState &state, int rflags)
else if (!(rflags & RWF_TRANS))
{
auto oldbias = state.GetDepthBias();
if (walldist >= 0) state.SetDepthBias(-1, glseg.x1 == glseg.x2 || glseg.y1 == glseg.y2? -129 : -192);
if (walldist) state.SetDepthBias(-1, -129);
else state.ClearDepthBias();
RenderWall(di, state, rflags);
state.SetDepthBias(oldbias);
@ -1150,7 +1095,20 @@ void HWWall::ProcessWallSprite(HWDrawInfo* di, tspritetype* spr, sectortype* sec
topofs = ((int)tex->GetDisplayTopOffset() + spr->yoffset);
}
walldist = IsOnWall(spr, height);
DVector2 vec;
walldist = IsOnWall(spr, height, vec);
wallpoint = { float(vec.X * (1 / 16.f)), float(vec.Y * (-1 / 16.f)) };
if (walldist)
{
// project the sprite right onto the wall.
auto v1 = NearestPointLine(pos[0].X, pos[0].Y, walldist);
auto v2 = NearestPointLine(pos[1].X, pos[1].Y, walldist);
glseg.x1 = v1.X * (1 / 16.f);
glseg.y1 = v1.Y * (1 / -16.f);
glseg.x2 = v2.X * (1 / 16.f);
glseg.y2 = v2.Y * (1 / -16.f);
}
if (spr->cstat & CSTAT_SPRITE_YFLIP)
topofs = -topofs;

View file

@ -1,2 +0,0 @@
// Hickston Swamp E1L3 (RR mod) wall sprite in wrong sector flickers with new renderer
sprite 225 sector 565