- rewrote getzrange

Not using any old Build code anymore. Aside from the trivial stuff this uses code from Doom and SW instead.
This commit is contained in:
Christoph Oelckers 2022-09-29 16:39:24 +02:00
parent 13d643deb5
commit 8e9ddf370c
6 changed files with 277 additions and 252 deletions

View file

@ -111,14 +111,6 @@ void setVideoMode();
class F2DDrawer;
void getzrange(const DVector3& pos, sectortype* sect, double* ceilz, CollisionBase& ceilhit, double* florz, CollisionBase& florhit, int32_t walldist, uint32_t cliptype);
inline
void getzrange(const DVector3& pos, sectortype* sect, double* ceilz, CollisionBase& ceilhit, double* florz, CollisionBase& florhit, double walldist, uint32_t cliptype)
{
getzrange(pos, sect, ceilz, ceilhit, florz, florhit, int(walldist * worldtoint), cliptype);
}
struct HitInfoBase;
inline int32_t krand(void)

View file

@ -256,34 +256,6 @@ static inline void keepaway(int32_t *x, int32_t *y, int32_t w)
while (1);
}
static int get_floorspr_clipyou(vec2_t const v1, vec2_t const v2, vec2_t const v3, vec2_t const v4)
{
int clipyou = 0;
if ((v1.Y^v2.Y) < 0)
{
if ((v1.X^v2.X) < 0) clipyou ^= (v1.X*v2.Y < v2.X*v1.Y)^(v1.Y<v2.Y);
else if (v1.X >= 0) clipyou ^= 1;
}
if ((v2.Y^v3.Y) < 0)
{
if ((v2.X^v3.X) < 0) clipyou ^= (v2.X*v3.Y < v3.X*v2.Y)^(v2.Y<v3.Y);
else if (v2.X >= 0) clipyou ^= 1;
}
if ((v3.Y^v4.Y) < 0)
{
if ((v3.X^v4.X) < 0) clipyou ^= (v3.X*v4.Y < v4.X*v3.Y)^(v3.Y<v4.Y);
else if (v3.X >= 0) clipyou ^= 1;
}
if ((v4.Y^v1.Y) < 0)
{
if ((v4.X^v1.X) < 0) clipyou ^= (v4.X*v1.Y < v1.X*v4.Y)^(v4.Y<v1.Y);
else if (v4.X >= 0) clipyou ^= 1;
}
return clipyou;
}
static int32_t getwalldist(vec2_t const in, int const wallnum)
{
auto dvec = NearestPointOnWall(in.X * maptoworld, in.Y * maptoworld, &wall[wallnum]);
@ -882,212 +854,3 @@ int pushmove_(vec3_t *const vect, int *const sectnum,
return bad;
}
//
// getzrange
//
void getzrange(const DVector3& pos_, sectortype* sect, double* ceilz_, CollisionBase& ceilhit, double* florz_, CollisionBase& florhit, int32_t walldist, uint32_t cliptype)
{
vec3_t pos(int(pos_.X * worldtoint), int(pos_.Y * worldtoint), int(pos_.Z * zworldtoint) );
if (sect == nullptr)
{
*ceilz_ = -FLT_MAX; ceilhit.setVoid();
*florz_ = FLT_MAX; florhit.setVoid();
return;
}
int32_t clipsectcnt = 0;
int32_t clipspritecnt = 0;
//Extra walldist for sprites on sector lines
const int32_t extradist = walldist+MAXCLIPDIST+1;
const int32_t xmin = pos.X-extradist, ymin = pos.Y-extradist;
const int32_t xmax = pos.X+extradist, ymax = pos.Y+extradist;
const int32_t dawalclipmask = (cliptype&65535);
const int32_t dasprclipmask = (cliptype >> 16);
vec2_t closest = pos.vec2;
int sectnum = ::sectnum(sect);
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
{
DVector2 v;
SquareDistToSector(closest.X * inttoworld, closest.Y * inttoworld, &sector[sectnum], &v);
closest = { int(v.X * worldtoint), int(v.Y * worldtoint) };
}
getzsofslopeptr(sect, closest.X * inttoworld, closest.Y * inttoworld, ceilz_, florz_);
ceilhit.setSector(sect);
florhit.setSector(sect);
clipsectorlist[0] = sectnum;
clipsectnum = 1;
clipspritenum = 0;
clipsectormap.Zero();
clipsectormap.Set(sectnum);
do //Collect sectors inside your square first
{
////////// Walls //////////
auto const startsec = &sector[clipsectorlist[clipsectcnt]];
for(auto&wal : wallsofsector(startsec))
{
if (wal.twoSided())
{
auto nextsect = wal.nextSector();
vec2_t const v1 = wal.wall_int_pos();
vec2_t const v2 = wal.point2Wall()->wall_int_pos();
if ((v1.X < xmin && (v2.X < xmin)) || (v1.X > xmax && v2.X > xmax) ||
(v1.Y < ymin && (v2.Y < ymin)) || (v1.Y > ymax && v2.Y > ymax))
continue;
vec2_t const d = { v2.X-v1.X, v2.Y-v1.Y };
if (d.X*(pos.Y-v1.Y) < (pos.X-v1.X)*d.Y) continue; //back
vec2_t da = { (d.X > 0) ? d.X*(ymin-v1.Y) : d.X*(ymax-v1.Y),
(d.Y > 0) ? d.Y*(xmax-v1.X) : d.Y*(xmin-v1.X) };
if (da.X >= da.Y)
continue;
if (wal.cstat & EWallFlags::FromInt(dawalclipmask)) continue; // XXX?
if (((nextsect->ceilingstat & CSTAT_SECTOR_SKY) == 0) && (pos.Z <= int(nextsect->ceilingz * zworldtoint)+(3<<8))) continue;
if (((nextsect->floorstat & CSTAT_SECTOR_SKY) == 0) && (pos.Z >= int(nextsect->floorz * zworldtoint)-(3<<8))) continue;
int nextsectno = ::sectnum(nextsect);
if (!clipsectormap[nextsectno])
addclipsect(nextsectno);
if (((v1.X < xmin + MAXCLIPDIST) && (v2.X < xmin + MAXCLIPDIST)) ||
((v1.X > xmax - MAXCLIPDIST) && (v2.X > xmax - MAXCLIPDIST)) ||
((v1.Y < ymin + MAXCLIPDIST) && (v2.Y < ymin + MAXCLIPDIST)) ||
((v1.Y > ymax - MAXCLIPDIST) && (v2.Y > ymax - MAXCLIPDIST)))
continue;
if (d.X > 0) da.X += d.X*MAXCLIPDIST; else da.X -= d.X*MAXCLIPDIST;
if (d.Y > 0) da.Y -= d.Y*MAXCLIPDIST; else da.Y += d.Y*MAXCLIPDIST;
if (da.X >= da.Y)
continue;
//It actually got here, through all the continue's!!!
double daz = 0, daz2 = 0;
closest = pos.vec2;
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
{
DVector2 v;
SquareDistToSector(closest.X * inttoworld, closest.Y * inttoworld, &sector[nextsectno], &v);
closest = { int(v.X * worldtoint), int(v.Y * worldtoint) };
}
getzsofslopeptr(nextsect, closest.X * inttoworld,closest.Y * inttoworld, &daz,&daz2);
{
if (daz > *ceilz_)
*ceilz_ = daz, ceilhit.setSector(nextsect);
if (daz2 < *florz_)
*florz_ = daz2, florhit.setSector(nextsect);
}
}
}
clipsectcnt++;
}
while (clipsectcnt < clipsectnum || clipspritecnt < clipspritenum);
////////// Sprites //////////
if (dasprclipmask)
for (int i=0; i<clipsectnum; i++)
{
if (!validSectorIndex(clipsectorlist[i])) continue; // we got a deleted sprite in here somewhere. Skip this entry.
TSectIterator<DCoreActor> it(clipsectorlist[i]);
while (auto actor = it.Next())
{
const int32_t cstat = actor->spr.cstat;
int32_t daz = 0, daz2 = 0;
if (actor->spr.cstat2 & CSTAT2_SPRITE_NOFIND) continue;
if (cstat&dasprclipmask)
{
int32_t clipyou = 0;
vec2_t v1 = actor->int_pos().vec2;
switch (cstat & CSTAT_SPRITE_ALIGNMENT_MASK)
{
case CSTAT_SPRITE_ALIGNMENT_FACING:
{
int32_t k = walldist+(actor->int_clipdist())+1;
if ((abs(v1.X-pos.X) <= k) && (abs(v1.Y-pos.Y) <= k))
{
daz = actor->int_pos().Z + actor->GetOffsetAndHeight(k);
daz2 = daz - k;
clipyou = 1;
}
break;
}
case CSTAT_SPRITE_ALIGNMENT_WALL:
{
vec2_t v2;
get_wallspr_points(actor, &v1.X, &v2.X, &v1.Y, &v2.Y);
if (clipinsideboxline(pos.X,pos.Y,v1.X,v1.Y,v2.X,v2.Y,walldist+1) != 0)
{
int32_t k;
daz = actor->int_pos().Z + actor->GetOffsetAndHeight(k);
daz2 = daz-k;
clipyou = 1;
}
break;
}
case CSTAT_SPRITE_ALIGNMENT_FLOOR:
case CSTAT_SPRITE_ALIGNMENT_SLOPE:
{
daz = spriteGetZOfSlope(&actor->spr, pos.X, pos.Y, spriteGetSlope(actor));
daz2 = daz;
if ((cstat & CSTAT_SPRITE_ONE_SIDE) != 0 && (pos.Z > daz) == ((cstat & CSTAT_SPRITE_YFLIP)==0))
continue;
vec2_t v2, v3, v4;
get_floorspr_points(actor, pos.X, pos.Y, &v1.X, &v2.X, &v3.X, &v4.X,
&v1.Y, &v2.Y, &v3.Y, &v4.Y);
vec2_t const da = { MulScale(bcos(actor->int_ang() - 256), walldist + 4, 14),
MulScale(bsin(actor->int_ang() - 256), walldist + 4, 14) };
v1.X += da.X; v2.X -= da.Y; v3.X -= da.X; v4.X += da.Y;
v1.Y += da.Y; v2.Y += da.X; v3.Y -= da.Y; v4.Y -= da.X;
clipyou = get_floorspr_clipyou(v1, v2, v3, v4);
break;
}
}
if (clipyou != 0)
{
if ((pos.Z > daz) && (daz * zinttoworld > *ceilz_))
{
*ceilz_ = daz * zinttoworld;
ceilhit.setSprite(actor);
}
if ((pos.Z < daz2) && (daz2 * zinttoworld < *florz_))
{
*florz_ = daz2 * zinttoworld;
florhit.setSprite(actor);
}
}
}
}
}
}

View file

@ -51,12 +51,6 @@ inline double PointOnLineSide(double x, double y, double linex, double liney, do
return (x - linex) * deltay - (y - liney) * deltax;
}
template<class T>
inline double PointOnLineSide(const TVector2<T>& pos, const TVector2<T>& linestart, const TVector2<T>& lineend)
{
return (pos.X - linestart.X) * (lineend.Y - linestart.Y) - (pos.Y - linestart.Y) * (lineend.X - linestart.X);
}
//==========================================================================
//
//
@ -175,3 +169,63 @@ inline double LinePlaneIntersect(const DVector3& start, const DVector3& trace, c
return (dist - dotStart) / dotTrace; // we are only interested in the factor
}
//==========================================================================
//
// BoxOnLineSide
//
// Based on Doom's, but rewritten to be standalone
//
//==========================================================================
inline int BoxOnLineSide(const DVector2& boxtl, const DVector2& boxbr, const DVector2& start, const DVector2& delta)
{
int p1;
int p2;
if (delta.X == 0)
{
p1 = boxbr.X < start.X;
p2 = boxtl.X < start.X;
if (delta.Y < 0)
{
p1 ^= 1;
p2 ^= 1;
}
}
else if (delta.Y == 0)
{
p1 = boxtl.Y > start.Y;
p2 = boxbr.Y > start.Y;
if (delta.X < 0)
{
p1 ^= 1;
p2 ^= 1;
}
}
else if (delta.X * delta.Y <= 0)
{
p1 = PointOnLineSide(boxtl.X, boxtl.Y, start.X, start.Y, delta.X, delta.Y) > 0;
p2 = PointOnLineSide(boxbr.X, boxbr.Y, start.X, start.Y, delta.X, delta.Y) > 0;
}
else
{
p1 = PointOnLineSide(boxbr.X, boxtl.Y, start.X, start.Y, delta.X, delta.Y) > 0;
p2 = PointOnLineSide(boxtl.X, boxbr.Y, start.X, start.Y, delta.X, delta.Y) > 0;
}
return (p1 == p2) ? p1 : -1;
}
//==========================================================================
//
// BoxInRange
//
//==========================================================================
inline bool BoxInRange(const DVector2& boxtl, const DVector2& boxbr, const DVector2& start, const DVector2& end)
{
return boxtl.X < max(start.X, end.X) &&
boxbr.X > min(start.X, end.X) &&
boxtl.Y < max(start.Y, end.Y) &&
boxbr.Y > min(start.Y, end.Y);
}

View file

@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "hw_voxels.h"
IntRect viewport3d;
constexpr double MAXCLIPDISTF = 64;
//---------------------------------------------------------------------------
//
@ -869,6 +870,215 @@ int hitscan(const DVector3& start, const sectortype* startsect, const DVector3&
return 0;
}
//==========================================================================
//
//
//
//==========================================================================
bool checkRangeOfWall(walltype* wal, EWallFlags flagmask, const DVector3& pos, double maxdist, double* theZs)
{
if (!wal->twoSided()) return false;
if (wal->cstat & flagmask) return false;
if (PointOnLineSide(pos.XY(), wal) > 0) return false;
auto nextsect = wal->nextSector();
// Rather pointless sky check that needs to be kept...
if (((nextsect->ceilingstat & CSTAT_SECTOR_SKY) == 0) && (pos.Z <= nextsect->ceilingz + 3)) return false;
if (((nextsect->floorstat & CSTAT_SECTOR_SKY) == 0) && (pos.Z >= nextsect->floorz - 3)) return false;
auto pos1 = wal->pos;
auto pos2 = wal->point2Wall()->pos;
// Checks borrowed from GZDoom.
DVector2 boxtl = pos - DVector2(maxdist, maxdist);
DVector2 boxbr = pos + DVector2(maxdist, maxdist);
if (!BoxInRange(boxtl, boxbr, pos1, pos2)) return false;
if (BoxOnLineSide(boxtl, boxbr, pos1, pos2 - pos1) != -1) return false;
auto closest = pos.XY();
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE) // todo: need to check if it makes sense to have this always on.
SquareDistToSector(closest.X, closest.Y, nextsect, &closest);
getzsofslopeptr(nextsect, closest.X, closest.Y, &theZs[0], &theZs[1]);
return true;
}
//==========================================================================
//
//
//
//==========================================================================
bool checkRangeOfFaceSprite(DCoreActor* itActor, const DVector3& pos, double maxdist, double* theZs)
{
double dist = maxdist + itActor->fClipdist();
if (abs(pos.X - itActor->spr.pos.X) > dist || abs(pos.Y - itActor->spr.pos.Y) > dist) return false; // Just like Doom: actors are square...
double h;
theZs[0] = itActor->spr.pos.Z + itActor->GetOffsetAndHeight(h);
theZs[1] = theZs[0] - h;
return true;
}
//==========================================================================
//
//
//
//==========================================================================
bool checkRangeOfWallSprite(DCoreActor* itActor, const DVector3& pos, double maxdist, double* theZs)
{
int t = itActor->time;
if ((t >= 485 && t <= 487) || t == 315)
{
int a = 0;
}
DVector2 verts[2];
GetWallSpritePosition(&itActor->spr, itActor->spr.pos.XY(), verts);
if (IsCloseToLine(pos.XY(), verts[0], verts[1], maxdist) == EClose::Outside) return false;
double h;
theZs[0] = itActor->spr.pos.Z + itActor->GetOffsetAndHeight(h);
theZs[1] = theZs[0] - h;
return true;
}
//==========================================================================
//
//
//
//==========================================================================
bool checkRangeOfFloorSprite(DCoreActor* itActor, const DVector3& pos, double maxdist, double& theZ)
{
int cstat = itActor->spr.cstat;
if ((cstat & (CSTAT_SPRITE_ALIGNMENT_MASK)) < (CSTAT_SPRITE_ALIGNMENT_FLOOR))
return false;
double fdaz = spriteGetZOfSlopef(&itActor->spr, pos.XY(), spriteGetSlope(itActor));
// Only check if sprite's 2-sided or your on the 1-sided side
if (((cstat & CSTAT_SPRITE_ONE_SIDE) != 0) && ((pos.Z > fdaz) == ((cstat & CSTAT_SPRITE_YFLIP) == 0)))
return false;
DVector2 out[4];
GetFlatSpritePosition(itActor, itActor->spr.pos.XY(), out);
// expand the area to cover 'maxdist' units more on each side. (i.e. move the edges out)
auto expand = (itActor->spr.angle - DAngle45).ToVector() * (maxdist + 0.25); // that's surely not accurate but here we must match Build's original value.
out[0] += expand;
out[1] += expand.Rotated90CCW();
out[2] -= expand;
out[3] += expand.Rotated90CW();
if (!insidePoly(pos.X, pos.Y, out, 4)) return false;
theZ = fdaz;
return true;
}
//==========================================================================
//
//
//
//==========================================================================
void getzrange(const DVector3& pos, sectortype* sect, double* ceilz, CollisionBase& ceilhit, double* florz, CollisionBase& florhit, double maxdist, uint32_t cliptype)
{
if (sect == nullptr)
{
*ceilz = -FLT_MAX; ceilhit.setVoid();
*florz = FLT_MAX; florhit.setVoid();
return;
}
const EWallFlags dawalclipmask = EWallFlags::FromInt(cliptype & 65535);
const ESpriteFlags dasprclipmask = ESpriteFlags::FromInt(cliptype >> 16);
auto closest = pos.XY();
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
SquareDistToSector(closest.X, closest.Y, sect, &closest);
getzsofslopeptr(sect, closest, ceilz, florz);
ceilhit.setSector(sect);
florhit.setSector(sect);
double theZs[2];
BFSSectorSearch search(sect);
while (auto sec = search.GetNext())
{
for (auto& wal : wallsofsector(sec))
{
if (checkRangeOfWall(&wal, EWallFlags::FromInt(dawalclipmask), pos, maxdist + 1 / 16., theZs))
{
auto nsec = wal.nextSector();
search.Add(nsec);
if (/*(pos.Z > theZs[0]) &&*/ (theZs[0] > *ceilz))
{
*ceilz = theZs[0];
ceilhit.setSector(nsec);
}
if (/*(pos.Z < theZs[1]) &&*/ (theZs[1] < *florz))
{
*florz = theZs[1];
florhit.setSector(nsec);
}
}
}
}
if (dasprclipmask)
{
search.Rewind();
while (auto sec = search.GetNext())
{
TSectIterator<DCoreActor> it(sec);
while (auto actor = it.Next())
{
const int32_t cstat = actor->spr.cstat;
if (actor->spr.cstat2 & CSTAT2_SPRITE_NOFIND) continue;
if (cstat & dasprclipmask)
{
switch (cstat & CSTAT_SPRITE_ALIGNMENT_MASK)
{
case CSTAT_SPRITE_ALIGNMENT_FACING:
if (!checkRangeOfFaceSprite(actor, pos, maxdist + 1 / 16., theZs)) continue;
break;
case CSTAT_SPRITE_ALIGNMENT_WALL:
if (!checkRangeOfWallSprite(actor, pos, maxdist + 1 / 16., theZs)) continue;
break;
case CSTAT_SPRITE_ALIGNMENT_FLOOR:
case CSTAT_SPRITE_ALIGNMENT_SLOPE:
if (!checkRangeOfFloorSprite(actor, pos, maxdist, theZs[0])) continue;
theZs[1] = theZs[0];
break;
}
// Clipping time!
if ((pos.Z > theZs[0]) && (theZs[0] > *ceilz))
{
*ceilz = theZs[0];
ceilhit.setSprite(actor);
}
if ((pos.Z < theZs[1]) && (theZs[1] < *florz))
{
*florz = theZs[1];
florhit.setSprite(actor);
}
}
}
}
}
}
//==========================================================================
//
//

View file

@ -253,6 +253,12 @@ void neartag(const DVector3& start, sectortype* sect, DAngle angle, HitInfoBase&
int testpointinquad(const DVector2& pt, const DVector2* quad);
int hitscan(const DVector3& start, const sectortype* startsect, const DVector3& vect, HitInfoBase& hitinfo, unsigned cliptype, double maxrange = -1);
bool checkRangeOfWall(walltype* wal, EWallFlags flagmask, const DVector3& pos, double maxdist, double* theZs);
bool checkRangeOfFaceSprite(DCoreActor* itActor, const DVector3& pos, double maxdist, double* theZs);
bool checkRangeOfWallSprite(DCoreActor* itActor, const DVector3& pos, double maxdist, double* theZs);
bool checkRangeOfFloorSprite(DCoreActor* itActor, const DVector3& pos, double maxdist, double& theZ);
void getzrange(const DVector3& pos, sectortype* sect, double* ceilz, CollisionBase& ceilhit, double* florz, CollisionBase& florhit, double walldist, uint32_t cliptype);

View file

@ -1242,7 +1242,7 @@ void HWWall::ProcessWallSprite(HWDrawInfo* di, tspritetype* spr, sectortype* sec
return; // nothing left to render.
// If the sprite is backward, flip it around so that we have guaranteed orientation when this is about to be sorted.
if (PointOnLineSide(di->Viewpoint.Pos.XY(), DVector2(glseg.x1, glseg.y1), DVector2(glseg.x2, glseg.y2)) < 0)
if (PointOnLineSide(di->Viewpoint.Pos.X, di->Viewpoint.Pos.Y, glseg.x1, glseg.y1, glseg.x2 - glseg.x1, glseg.y2 - glseg.y1 ) < 0)
{
std::swap(glseg.x1, glseg.x2);
std::swap(glseg.y1, glseg.y2);