diff --git a/source/build/include/build.h b/source/build/include/build.h index ee15f9b42..eebcf4e11 100644 --- a/source/build/include/build.h +++ b/source/build/include/build.h @@ -238,13 +238,6 @@ static inline int64_t compat_maybe_truncate_to_int32(int64_t val) return enginecompatibility_mode != ENGINECOMPATIBILITY_NONE ? (int32_t)val : val; } -extern int32_t rintersect(int32_t x1, int32_t y1, int32_t z1, - int32_t vx_, int32_t vy_, int32_t vz, - int32_t x3, int32_t y3, int32_t x4, int32_t y4, - int32_t *intx, int32_t *inty, int32_t *intz); - - - void updateModelInterpolation(); #endif // build_h_ diff --git a/source/build/src/clip.cpp b/source/build/src/clip.cpp index 7022f1c29..90af8b2a8 100644 --- a/source/build/src/clip.cpp +++ b/source/build/src/clip.cpp @@ -1167,6 +1167,7 @@ int hitscan(const vec3_t& start, const sectortype* startsect, const vec3_t& dire int const vx = direction.X, vy = direction.Y, vz = direction.Z; int32_t x1, y1=0, z1=0, x2, y2, intx, inty, intz; int32_t i, k, daz; + double hitfactor = DBL_MAX; const int32_t dawalclipmask = (cliptype&65535); const int32_t dasprclipmask = (cliptype >> 16); @@ -1202,11 +1203,15 @@ int hitscan(const vec3_t& start, const sectortype* startsect, const vec3_t& dire < compat_maybe_truncate_to_int32((coord_t)(x2-sv->X)*(y1-sv->Y))) continue; if (rintersect(sv->X,sv->Y,sv->Z, vx,vy,vz, x1,y1, x2,y2, &intx,&inty,&intz) == -1) continue; - if (abs(intx-sv->X)+abs(inty-sv->Y) >= abs((hitinfo.int_hitpos().X)-sv->X)+abs((hitinfo.int_hitpos().Y)-sv->Y)) - continue; + // temporary crutch + DVector2 vec(vx, vy); + DVector2 newvec(intx - sv->X, inty - sv->Y); + double newfactor = newvec.Sum() / vec.Sum(); + if (newfactor >= hitfactor) continue; if ((!wal->twoSided()) || (wal->cstat & EWallFlags::FromInt(dawalclipmask))) { + hitfactor = newfactor; hit_set(&hitinfo, sec, wal, nullptr, intx, inty, intz); continue; } @@ -1215,6 +1220,7 @@ int hitscan(const vec3_t& start, const sectortype* startsect, const vec3_t& dire int_getzsofslopeptr(nextsect,intx,inty,&daz,&daz2); if (intz <= daz || intz >= daz2) { + hitfactor = newfactor; hit_set(&hitinfo, sec, wal, nullptr, intx, inty, intz); continue; } @@ -1227,6 +1233,9 @@ int hitscan(const vec3_t& start, const sectortype* startsect, const vec3_t& dire if (dasprclipmask==0) continue; + DVector3 start(sv->X * inttoworld, sv->Y * inttoworld, sv->Z * zinttoworld); + DVector3 vect(vx * inttoworld, vy * inttoworld, vz * zinttoworld); + TSectIterator it(sec); while (auto actor = it.Next()) { @@ -1238,121 +1247,31 @@ int hitscan(const vec3_t& start, const sectortype* startsect, const vec3_t& dire if ((cstat&dasprclipmask) == 0) continue; - x1 = actor->int_pos().X; y1 = actor->int_pos().Y; z1 = actor->int_pos().Z; + DVector3 v; + double hit = -1; + // we pass hitfactor to the workers because it can shortcut their calculations a lot. switch (cstat&CSTAT_SPRITE_ALIGNMENT_MASK) { - case 0: - { - auto v = hitinfo.hitpos; - DVector3 start(sv->X * inttoworld, sv->Y * inttoworld, sv->Z * zinttoworld); - DVector3 direction(vx * inttoworld, vy * inttoworld, vz * zinttoworld); - if (intersectSprite(actor, start, direction, v, 0)) - { - if ((v.XY() - start.XY()).Sum() < (hitinfo.hitpos.XY() - start.XY()).Sum()) - hit_set(&hitinfo, sec, nullptr, actor, v.X * worldtoint, v.Y * worldtoint, v.Z * zworldtoint); - } - + case CSTAT_SPRITE_ALIGNMENT_FACING: + hit = intersectSprite(actor, start, vect, v, hitfactor); break; - } case CSTAT_SPRITE_ALIGNMENT_WALL: - { - int32_t ucoefup16; - int32_t tilenum = actor->spr.picnum; - - get_wallspr_points(actor, &x1, &x2, &y1, &y2); - - if ((cstat & CSTAT_SPRITE_ONE_SIDE) != 0) //back side of 1-way sprite - if (compat_maybe_truncate_to_int32((coord_t)(x1-sv->X)*(y2-sv->Y)) - < compat_maybe_truncate_to_int32((coord_t)(x2-sv->X)*(y1-sv->Y))) continue; - - ucoefup16 = rintersect(sv->X,sv->Y,sv->Z,vx,vy,vz,x1,y1,x2,y2,&intx,&inty,&intz); - if (ucoefup16 == -1) continue; - - if (abs(intx-sv->X)+abs(inty-sv->Y) > abs((hitinfo.int_hitpos().X)-sv->X)+abs((hitinfo.int_hitpos().Y)-sv->Y)) - continue; - - daz = actor->int_pos().Z + actor->GetOffsetAndHeight(k); - if (intz > daz-k && intz < daz) - { - if (picanm[tilenum].sf&PICANM_TEXHITSCAN_BIT) - { - tileUpdatePicnum(&tilenum); - - if (tileLoad(tilenum)) - { - // daz-intz > 0 && daz-intz < k - int32_t xtex = MulScale(ucoefup16, tileWidth(tilenum), 16); - int32_t vcoefup16 = 65536-DivScale(daz-intz, k, 16); - int32_t ytex = MulScale(vcoefup16, tileHeight(tilenum), 16); - - auto texel = (tilePtr(tilenum) + tileHeight(tilenum)*xtex + ytex); - if (*texel == TRANSPARENT_INDEX) - continue; - } - } - - hit_set(&hitinfo, sec, nullptr, actor, intx, inty, intz); - } + hit = intersectWallSprite(actor, start, vect, v, hitfactor, (picanm[actor->spr.picnum].sf & PICANM_TEXHITSCAN_BIT)); break; - } case CSTAT_SPRITE_ALIGNMENT_FLOOR: - { - int32_t x3, y3, x4, y4; - intz = z1; - - if (vz == 0 || ((intz-sv->Z)^vz) < 0) continue; - - if ((cstat & CSTAT_SPRITE_ONE_SIDE) != 0) - if ((sv->Z > intz) == ((cstat & CSTAT_SPRITE_YFLIP)==0)) continue; - - // avoid overflow errors by using 64 bit math. - intx = int(sv->X + (int64_t(intz) - sv->Z) * vx / vz); - inty = int(sv->Y + (int64_t(intz) - sv->Z) * vy / vz); - - if (abs(intx-sv->X)+abs(inty-sv->Y) > abs((hitinfo.int_hitpos().X)-sv->X)+abs((hitinfo.int_hitpos().Y)-sv->Y)) - continue; - - get_floorspr_points(actor, intx, inty, &x1, &x2, &x3, &x4, - &y1, &y2, &y3, &y4); - - if (get_floorspr_clipyou({x1, y1}, {x2, y2}, {x3, y3}, {x4, y4})) - hit_set(&hitinfo, sec, nullptr, actor, intx, inty, intz); - + hit = intersectFloorSprite(actor, start, vect, v, hitfactor); break; - } case CSTAT_SPRITE_ALIGNMENT_SLOPE: - { - int32_t x3, y3, x4, y4; - int32_t const heinum = spriteGetSlope(actor); - int32_t const dax = (heinum * -bsin(actor->int_ang())) << 1; - int32_t const day = (heinum * bcos(actor->int_ang())) << 1; - int32_t const j = (vz << 8) - DMulScale(dax, vy, -day, vx, 15); - if (j == 0) continue; - if ((cstat & 64) != 0) - if ((j < 0) == ((cstat & 8) == 0)) continue; - int32_t dist2 = ((actor->int_pos().Z - sv->Z) << 8) + DMulScale(dax, sv->Y - actor->int_pos().Y, -day, sv->X - actor->int_pos().X, 15); - if ((dist2 ^ j) < 0 || (abs(dist2) >> 1) >= abs(j)) continue; - - dist2 = DivScale(dist2, j, 30); - intx = sv->X + MulScale(vx, dist2, 30); - inty = sv->Y + MulScale(vy, dist2, 30); - intz = sv->Z + MulScale(vz, dist2, 30); - - if (abs(intx - sv->X) + abs(inty - sv->Y) > abs((hitinfo.int_hitpos().X) - sv->X) + abs((hitinfo.int_hitpos().Y) - sv->Y)) - continue; - - get_floorspr_points(actor, intx, inty, &x1, &x2, &x3, &x4, - &y1, &y2, &y3, &y4); - - if (get_floorspr_clipyou({ x1, y1 }, { x2, y2 }, { x3, y3 }, { x4, y4 })) - hit_set(&hitinfo, sec, nullptr, actor, intx, inty, intz); - + hit = intersectSlopeSprite(actor, start, vect, v, hitfactor); break; } - + if (hit > 0) + { + hitfactor = hit; + hitinfo.set(sec, nullptr, actor, v); } } } diff --git a/source/core/coreactor.h b/source/core/coreactor.h index ea1c9690f..1473808d6 100644 --- a/source/core/coreactor.h +++ b/source/core/coreactor.h @@ -259,6 +259,14 @@ struct HitInfoBase hitWall = nullptr; hitActor = nullptr; } + + void set(sectortype* sect, walltype* wal, DCoreActor* actor, const DVector3& pos) + { + hitSector = sect; + hitWall = wal; + hitActor = actor; + hitpos = pos; + } const vec3_t int_hitpos() const { diff --git a/source/core/gamefuncs.cpp b/source/core/gamefuncs.cpp index ff93e9826..80ce69e3b 100644 --- a/source/core/gamefuncs.cpp +++ b/source/core/gamefuncs.cpp @@ -566,6 +566,21 @@ bool cansee(const DVector3& start, sectortype* sect1, const DVector3& end, secto return search.Check(sect2); } +//--------------------------------------------------------------------------- +// +// taken out of SW. +// +//--------------------------------------------------------------------------- + +int testpointinquad(const DVector2& pt, const DVector2* quad) +{ + for (int i = 0; i < 4; i++) + { + double dist = PointOnLineSide(pt.X, pt.Y, quad[i].X, quad[i].Y, quad[(i + 1) & 3].X - quad[i].X, quad[(i + 1) & 3].Y - quad[i].Y); + if (dist > 0) return false; + } + return true; +} //========================================================================== // @@ -573,36 +588,131 @@ bool cansee(const DVector3& start, sectortype* sect1, const DVector3& end, secto // //========================================================================== -bool intersectSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double displacement) +double intersectSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor) { auto end = start + direction; if (direction.isZero()) return false; - // use dot product to check if the sprite is behind us (or ourselves if the result is 0) - auto dotprod = direction.XY().dot(actor->spr.pos.XY() - start.XY()); - if (dotprod <= 0) return false; - // get point on trace that is closest to the sprite - double const length = direction.XY().LengthSquared(); - DVector3 point = start + direction * (dotprod / length); + double factor = NearestPointOnLineFast(actor->spr.pos.X, actor->spr.pos.Y, start.X, start.Y, end.X, end.Y); + if (factor < 0 || factor > maxfactor) return -1; - // This is somewhat smaller than the sprite's actual size, but that's how it was - auto sprwidth = tileWidth(actor->spr.picnum) * actor->spr.xrepeat * (REPEAT_SCALE * 0.5) + displacement; + auto sprwidth = tileWidth(actor->spr.picnum) * actor->spr.xrepeat * (REPEAT_SCALE * 0.5); + auto point = start + direction * factor; // Using proper distance here, Build originally used the sum of x- and y-distance - if ((point.XY() - actor->spr.pos.XY()).LengthSquared() > sprwidth * sprwidth * 0.5) return false; // too far away + if ((point.XY() - actor->spr.pos.XY()).LengthSquared() > sprwidth * sprwidth * 0.5) return -1; // too far away - double newz = point.Z; + double siz, hitz = actor->spr.pos.Z + actor->GetOffsetAndHeight(siz); - double siz; - double const hitz = actor->spr.pos.Z + actor->GetOffsetAndHeight(siz); + if (point.Z < hitz - siz || point.Z > hitz) + return -1; - if (newz < hitz - siz || newz > hitz) - return 0; + result = point; + return factor; +} - result.XY() = point; - result.Z = newz; - return 1; +//========================================================================== +// +// +// +//========================================================================== + +double intersectWallSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor, bool checktex) +{ + DVector2 points[2]; + + GetWallSpritePosition(&actor->spr, actor->spr.pos, points, false); + + points[1] -= points[0]; + if ((actor->spr.cstat & CSTAT_SPRITE_ONE_SIDE)) //check for back side of one way sprite + { + if (PointOnLineSide(start.X, start.Y, points[0].X, points[0].Y, points[1].X, points[1].Y) > 0) + return -1; + } + + double factor2; + double factor = InterceptLineSegments(start.X, start.Y, direction.X, direction.Y, points[0].X, points[0].Y, points[1].X, points[1].Y, &factor2); + if (factor < 0 || factor > maxfactor || factor2 < 0 || factor2 > 1) return -1; + + result = start + factor * direction; + + double height, position = actor->spr.pos.Z + actor->GetOffsetAndHeight(height); + if (result.Z <= position - height || result.Z >= position) return -1; + + if (checktex) + { + int tilenum = actor->spr.picnum; + tileUpdatePicnum(&tilenum); + + if (tileLoad(tilenum)) + { + double zfactor = 1. - (position - result.Z) / height; + + // all other flags have been taken care of already by GetWallSpritePosition and GetOffsetAndHeight + // - but we have to handle the flip flags here to fetch the correct texel. + if (actor->spr.cstat & CSTAT_SPRITE_XFLIP) factor2 = 1 - factor2; + if (actor->spr.cstat & CSTAT_SPRITE_YFLIP) zfactor = 1 - zfactor; + + int xtex = int(factor2 * tileWidth(tilenum)); + int ytex = int(zfactor * tileHeight(tilenum)); + + auto texel = (tilePtr(tilenum) + tileHeight(tilenum) * xtex + ytex); + if (*texel == TRANSPARENT_INDEX) + return -1; + } + } + return factor; +} + +//========================================================================== +// +// +// +//========================================================================== + +double intersectFloorSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor) +{ + if (actor->spr.cstat & CSTAT_SPRITE_ONE_SIDE) + { + if ((start.Z > actor->spr.pos.Z) == ((actor->spr.cstat & CSTAT_SPRITE_YFLIP) == 0)) return -1; + } + + DVector2 points[4]; + GetFlatSpritePosition(actor, actor->spr.pos, points, nullptr, false); + double factor = (actor->spr.pos.Z - start.Z) / direction.Z; + if (factor <= 0 || factor > maxfactor) return -1; + result = start + factor * direction; + if (!testpointinquad(result.XY(), points)) return -1; + return factor; +} + +//========================================================================== +// +// +// +//========================================================================== + +double intersectSlopeSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor) +{ + DVector2 points[4]; + double ptz[4]; + GetFlatSpritePosition(actor, actor->spr.pos, points, ptz, false); + DVector3 pt1(points[0], ptz[0]); + DVector3 pt2(points[1], ptz[1]); + DVector3 pt3(points[2], ptz[2]); + double factor = LinePlaneIntersect(start, direction, pt1, pt2 - pt1, pt3 - pt1); + if (factor <= 0 || factor > maxfactor) return -1; + result = start + factor * direction; + + // we can only do this after calculating the actual intersection spot... + if (actor->spr.cstat & CSTAT_SPRITE_ONE_SIDE) + { + double checkz = spriteGetZOfSlopef(&actor->spr, start.XY(), spriteGetSlope(actor)); + if ((start.Z > checkz) == ((actor->spr.cstat & CSTAT_SPRITE_YFLIP) == 0)) return -1; + } + if (!testpointinquad(result.XY(), points)) return -1; + return factor; } //========================================================================== @@ -672,6 +782,7 @@ void neartag(const DVector3& pos, sectortype* startsect, DAngle angle, HitInfoBa if (!(flags & NT_NoSpriteCheck)) { + double factor = 1; TSectIterator it(sect); while (auto actor = it.Next()) { @@ -681,8 +792,10 @@ void neartag(const DVector3& pos, sectortype* startsect, DAngle angle, HitInfoBa if (checkTag(&actor->spr)) { DVector3 spot; - if (intersectSprite(actor, pos, v, spot, 1 / 256.)) + double newfactor = intersectSprite(actor, pos, v, spot, factor - 1. / 65536.); + if (newfactor > 0) { + factor = newfactor; result.hitActor = actor; // return distance to sprite in a separate variable because there is // no means to determine what is for if both a sprite and wall are found. diff --git a/source/core/gamefuncs.h b/source/core/gamefuncs.h index 69ff3035a..73eaa9a6b 100644 --- a/source/core/gamefuncs.h +++ b/source/core/gamefuncs.h @@ -260,8 +260,12 @@ void loaddefinitionsfile(const char* fn, bool cumulative = false, bool maingrp = bool calcChaseCamPos(DVector3& ppos, DCoreActor* pspr, sectortype** psectnum, DAngle ang, fixedhoriz horiz, double const interpfrac); int getslopeval(sectortype* sect, const DVector3& pos, double bazez); bool cansee(const DVector3& start, sectortype* sect1, const DVector3& end, sectortype* sect2); -bool intersectSprite(DCoreActor* actor, const DVector3& start, const DVector3& end, DVector3& result, double displacement); +double intersectSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor); +double intersectWallSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor, bool checktex = false); +double intersectFloorSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor); +double intersectSlopeSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor); void neartag(const DVector3& start, sectortype* sect, DAngle angle, HitInfoBase& result, double neartagrange, int tagsearch); +int testpointinquad(const DVector2& pt, const DVector2* quad); @@ -500,11 +504,24 @@ inline double SquareDist(double lx1, double ly1, double lx2, double ly2) return dx * dx + dy * dy; } +// This is for cases where only the factor is needed, e.g. intersectSprite which +// needs to validate it before applying and also needs to apply it to a 3D vector. +inline double NearestPointOnLineFast(double px, double py, double lx1, double ly1, double lx2, double ly2) +{ + double wall_length = SquareDist(lx1, ly1, lx2, ly2); + assert(wall_length > 0); + return ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1)) / wall_length; +} + + inline DVector2 NearestPointOnLine(double px, double py, double lx1, double ly1, double lx2, double ly2, bool clamp = true) { double wall_length = SquareDist(lx1, ly1, lx2, ly2); - if (wall_length == 0) return { lx1, ly1 }; + if (wall_length == 0) + { + return { lx1, ly1 }; + } double t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1)) / wall_length; if (clamp) @@ -593,11 +610,6 @@ inline double LinePlaneIntersect(const DVector3& start, const DVector3& trace, c return (dist - dotStart) / dotTrace; // we are only interested in the factor } -inline double LineHeightIntersect(double startz, double tracez, double dist) -{ - return (dist - startz) / tracez; // we are only interested in the factor -} - inline void alignceilslope(sectortype* sect, const DVector3& pos) { sect->setceilingslope(getslopeval(sect, pos, sect->ceilingz)); diff --git a/source/games/sw/src/mclip.cpp b/source/games/sw/src/mclip.cpp index be723d4fd..9406ef1b5 100644 --- a/source/games/sw/src/mclip.cpp +++ b/source/games/sw/src/mclip.cpp @@ -255,22 +255,6 @@ int RectClipMove(PLAYER* pp, DVector2* qpos) // //--------------------------------------------------------------------------- -int testpointinquad(const DVector2& pt, const DVector2* quad) -{ - for (int i = 0; i < 4; i++) - { - double dist = PointOnLineSide(pt.X, pt.Y, quad[i].X, quad[i].Y, quad[(i + 1) & 3].X - quad[i].X, quad[(i + 1) & 3].Y - quad[i].Y); - if (dist > 0) return false; - } - return true; -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - short RectClipTurn(PLAYER* pp, DAngle new_angl, DVector2* qpos, DVector2* opos) { int i; diff --git a/source/games/sw/src/mclip.h b/source/games/sw/src/mclip.h index 4d6ebd6a8..b6cbe194e 100644 --- a/source/games/sw/src/mclip.h +++ b/source/games/sw/src/mclip.h @@ -30,7 +30,6 @@ BEGIN_SW_NS Collision MultiClipMove(PLAYER* pp, double z, double floor_dist); int MultiClipTurn(PLAYER* pp, DAngle new_ang, double zz, double floordist); int RectClipMove(PLAYER* pp, DVector2* qpos); -int testpointinquad(const DVector2& pt, const DVector2* quad); //short RectClipTurn(PLAYER* pp, short new_ang, int z, int floor_dist, int *qx, int *qy); short RectClipTurn(PLAYER* pp, short new_ang, int *qx, int *qy, int *ox, int *oy); END_SW_NS