diff --git a/source/build/include/build.h b/source/build/include/build.h index 923517cfb..feedd47cc 100644 --- a/source/build/include/build.h +++ b/source/build/include/build.h @@ -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) diff --git a/source/build/src/clip.cpp b/source/build/src/clip.cpp index 0b1382cbc..49ba3ea2b 100644 --- a/source/build/src/clip.cpp +++ b/source/build/src/clip.cpp @@ -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= 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= 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= 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= 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, §or[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 = §or[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, §or[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 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); - } - } - } - } - } -} diff --git a/source/common/utility/geometry.h b/source/common/utility/geometry.h index ff8d9ddf1..7f462c087 100644 --- a/source/common/utility/geometry.h +++ b/source/common/utility/geometry.h @@ -51,12 +51,6 @@ inline double PointOnLineSide(double x, double y, double linex, double liney, do return (x - linex) * deltay - (y - liney) * deltax; } -template -inline double PointOnLineSide(const TVector2& pos, const TVector2& linestart, const TVector2& 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); +} diff --git a/source/core/gamefuncs.cpp b/source/core/gamefuncs.cpp index f85ba7949..8164c8429 100644 --- a/source/core/gamefuncs.cpp +++ b/source/core/gamefuncs.cpp @@ -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 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); + } + } + } + } + } +} + + //========================================================================== // // diff --git a/source/core/gamefuncs.h b/source/core/gamefuncs.h index acb940568..22d88d87d 100644 --- a/source/core/gamefuncs.h +++ b/source/core/gamefuncs.h @@ -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); + diff --git a/source/core/rendering/scene/hw_walls.cpp b/source/core/rendering/scene/hw_walls.cpp index b6c67f0f6..40e1c2b6f 100644 --- a/source/core/rendering/scene/hw_walls.cpp +++ b/source/core/rendering/scene/hw_walls.cpp @@ -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);