- hitscan fully rewritten.

No traces of Build code left here! :)
This commit is contained in:
Christoph Oelckers 2022-09-26 15:04:20 +02:00
parent babbad83af
commit 74cf9a2ff2
7 changed files with 194 additions and 237 deletions

View file

@ -137,21 +137,7 @@ inline void getzrange(const DVector3& pos, sectortype* sect, double* ceilz, Coll
}
extern vec2_t hitscangoal;
struct HitInfoBase;
int hitscan(const vec3_t& start, const sectortype* startsect, const vec3_t& direction, HitInfoBase& hitinfo, unsigned cliptype);
inline int hitscan(const DVector3& start, const sectortype* startsect, const DVector3& direction, HitInfoBase& hitinfo, unsigned cliptype)
{
vec3_t istart(int(start.X * worldtoint), int(start.Y * worldtoint), int(start.Z * zworldtoint) );
vec3_t idir( int(direction.X * worldtoint), int(direction.Y * worldtoint), int(direction.Z * zworldtoint) );
return hitscan(istart, startsect, idir, hitinfo, cliptype);
}
inline int hitscan(const DVector3& start, const sectortype* startsect, const vec3_t& direction, HitInfoBase& hitinfo, unsigned cliptype)
{
vec3_t istart(int(start.X * worldtoint), int(start.Y * worldtoint), int(start.Z * zworldtoint));
return hitscan(istart, startsect, direction, hitinfo, cliptype);
}
inline int32_t krand(void)
{

View file

@ -1091,190 +1091,3 @@ void getzrange(const vec3_t& pos, sectortype* sect, int32_t* ceilz, CollisionBas
}
}
static inline void hit_set(HitInfoBase *hit, sectortype* sect, walltype* wal, DCoreActor* actor, int32_t x, int32_t y, int32_t z)
{
hit->hitSector = sect;
hit->hitWall = wal;
hit->hitActor = actor;
hit->hitpos.X = x * inttoworld;
hit->hitpos.Y = y * inttoworld;
hit->hitpos.Z = z * zinttoworld;
}
// stat, heinum, z: either ceiling- or floor-
// how: -1: behave like ceiling, 1: behave like floor
static int32_t hitscan_trysector(const vec3_t *sv, sectortype* sec, HitInfoBase *hit,
int32_t vx, int32_t vy, int32_t vz,
uint16_t stat, int16_t heinum, int32_t z, int32_t how)
{
int32_t x1 = INT32_MAX, y1 = 0, z1 = 0;
int32_t i;
if (stat&2)
{
auto const wal = sec->firstWall();
auto const wal2 = wal->point2Wall();
int32_t j, dax=wal2->wall_int_pos().X-wal->wall_int_pos().X, day=wal2->wall_int_pos().Y-wal->wall_int_pos().Y;
i = ksqrt(compat_maybe_truncate_to_int32(uhypsq(dax,day))); if (i == 0) return 1; //continue;
i = DivScale(heinum,i, 15);
dax *= i; day *= i;
j = (vz<<8)-DMulScale(dax,vy,-day,vx, 15);
if (j != 0)
{
i = ((z - sv->Z)<<8)+DMulScale(dax,sv->Y-wal->wall_int_pos().Y,-day,sv->X-wal->wall_int_pos().X, 15);
if (((i^j) >= 0) && ((abs(i)>>1) < abs(j)))
{
i = DivScale(i,j, 30);
x1 = sv->X + MulScale(vx,i, 30);
y1 = sv->Y + MulScale(vy,i, 30);
z1 = sv->Z + MulScale(vz,i, 30);
}
}
}
else if ((how*vz > 0) && (how*sv->Z <= how*z))
{
z1 = z; i = z1-sv->Z;
if ((abs(i)>>1) < vz*how)
{
i = DivScale(i,vz, 30);
x1 = sv->X + MulScale(vx,i, 30);
y1 = sv->Y + MulScale(vy,i, 30);
}
}
if ((x1 != INT32_MAX) && (abs(x1-sv->X)+abs(y1-sv->Y) < abs((hit->int_hitpos().X)-sv->X)+abs((hit->int_hitpos().Y)-sv->Y)))
{
if (inside(x1,y1,sec) == 1)
{
hit_set(hit, sec, nullptr, nullptr, x1, y1, z1);
}
}
return 0;
}
//
// hitscan
//
int hitscan(const vec3_t& start, const sectortype* startsect, const vec3_t& direction, HitInfoBase& hitinfo, unsigned cliptype)
{
auto const sv = &start;
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);
hitinfo.clearObj(); // note that this case leaves hitpos untouched.
if (startsect == nullptr)
return -1;
hitinfo.hitpos.X = hitscangoal.X * inttoworld;
hitinfo.hitpos.Y = hitscangoal.Y * inttoworld;
BFSSectorSearch search(startsect);
while (auto sec = search.GetNext())
{
i = 1;
if (hitscan_trysector(sv, sec, &hitinfo, vx,vy,vz, sec->ceilingstat, sec->ceilingheinum, sec->int_ceilingz(), -i))
continue;
if (hitscan_trysector(sv, sec, &hitinfo, vx,vy,vz, sec->floorstat, sec->floorheinum, sec->int_floorz(), i))
continue;
////////// Walls //////////
for(auto& w : wallsofsector(sec))
{
auto wal = &w;
auto wal2 = wal->point2Wall();
auto const nextsect = wal->nextSector();
x1 = wal->wall_int_pos().X; y1 = wal->wall_int_pos().Y; x2 = wal2->wall_int_pos().X; y2 = wal2->wall_int_pos().Y;
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;
if (rintersect(sv->X,sv->Y,sv->Z, vx,vy,vz, x1,y1, x2,y2, &intx,&inty,&intz) == -1) 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;
}
int32_t daz2;
int_getzsofslopeptr(nextsect,intx,inty,&daz,&daz2);
if (intz <= daz || intz >= daz2)
{
hitfactor = newfactor;
hit_set(&hitinfo, sec, wal, nullptr, intx, inty, intz);
continue;
}
search.Add(nextsect);
}
////////// Sprites //////////
if (dasprclipmask==0)
continue;
DVector3 start(sv->X * inttoworld, sv->Y * inttoworld, sv->Z * zinttoworld);
DVector3 vect(vx * inttoworld, vy * inttoworld, vz * zinttoworld);
TSectIterator<DCoreActor> it(sec);
while (auto actor = it.Next())
{
uint32_t const cstat = actor->spr.cstat;
if (actor->spr.cstat2 & CSTAT2_SPRITE_NOFIND)
continue;
if ((cstat&dasprclipmask) == 0)
continue;
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 CSTAT_SPRITE_ALIGNMENT_FACING:
hit = intersectSprite(actor, start, vect, v, hitfactor);
break;
case CSTAT_SPRITE_ALIGNMENT_WALL:
hit = intersectWallSprite(actor, start, vect, v, hitfactor, (picanm[actor->spr.picnum].sf & PICANM_TEXHITSCAN_BIT));
break;
case CSTAT_SPRITE_ALIGNMENT_FLOOR:
hit = intersectFloorSprite(actor, start, vect, v, hitfactor);
break;
case CSTAT_SPRITE_ALIGNMENT_SLOPE:
hit = intersectSlopeSprite(actor, start, vect, v, hitfactor);
break;
}
if (hit > 0)
{
hitfactor = hit;
hitinfo.set(sec, nullptr, actor, v);
}
}
}
return 0;
}

View file

@ -228,20 +228,14 @@ extern TArray<walltype> wall;
// Masking these into the object index to keep it in 16 bit was probably the single most dumbest and pointless thing Build ever did.
// Gonna be fun to globally replace these to finally lift the limit this imposes on map size.
// Names taken from DukeGDX
enum EHitBits
{
kHitNone = 0,
kHitTypeMask = 0xC000,
kHitTypeMaskSW = 0x1C000, // SW has one more relevant bit
kHitIndexMask = 0x3FFF,
kHitSector = 0x4000,
kHitWall = 0x8000,
kHitSprite = 0xC000,
kHitVoid = 0x10000, // SW only
};
// This serves as input/output for all functions dealing with collisions, hits, etc.

View file

@ -140,7 +140,7 @@ void calcSlope(const sectortype* sec, double xpos, double ypos, double* pceilz,
double len = wal->Length();
if (len != 0)
{
float fac = (wal->delta().X * (ypos - wal->pos.Y) - wal->delta().Y * (xpos - wal->pos.X)) / len * (1. / SLOPEVAL_FACTOR);
double fac = (wal->delta().X * (ypos - wal->pos.Y) - wal->delta().Y * (xpos - wal->pos.X)) / len * (1. / SLOPEVAL_FACTOR);
if (pceilz && sec->ceilingstat & CSTAT_SECTOR_SLOPE) *pceilz += (sec->ceilingheinum * fac);
if (pflorz && sec->floorstat & CSTAT_SECTOR_SLOPE) *pflorz += (sec->floorheinum * fac);
}
@ -631,9 +631,9 @@ double intersectWallSprite(DCoreActor* actor, const DVector3& start, const DVect
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;
// the wall factor is needed for doing a texture check.
double factor2, 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) return -1;
result = start + factor * direction;
@ -715,6 +715,182 @@ double intersectSlopeSprite(DCoreActor* actor, const DVector3& start, const DVec
return factor;
}
//==========================================================================
//
//
//
//==========================================================================
double checkWallHit(walltype* wal, EWallFlags flagmask, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor)
{
if (PointOnLineSide(start.XY(), wal) > 0) return -1;
double factor = InterceptLineSegments(start.X, start.Y, direction.X, direction.Y, wal->pos.X, wal->pos.Y, wal->delta().X, wal->delta().Y);
if (factor <= 0 || factor > maxfactor) return -1; // did not connect.
result = start + factor * direction;
if (wal->twoSided() && !(wal->cstat & flagmask))
{
// check if the trace passes this wall or hits the upper or lower tier.
double cz, fz;
getzsofslopeptr(wal->nextSector(), result, &cz, &fz);
if (result.Z > cz && result.Z < fz) return -2; // trace will pass this wall, i.e. no hit. Return -2 to tell the caller to go on.
}
return factor;
}
//==========================================================================
//
//
//
//==========================================================================
double checkSectorPlaneHit(sectortype* sec, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor)
{
if (sec->wallnum < 3) return -1;
auto wal = sec->firstWall();
double len = wal->Length();
double startcz, startfz;
calcSlope(sec, start.X, start.Y, &startcz, &startfz);
for (int i = 0; i < 2; i++)
{
int heinum;
double factor = -1, z;
bool sloped;
if (i == 0)
{
if (start.Z <= startcz) continue;
heinum = (sec->ceilingstat & CSTAT_SECTOR_SLOPE) && len > 0 ? sec->ceilingheinum : 0;
z = sec->ceilingz;
}
else
{
if (start.Z >= startfz) continue;
heinum = (sec->floorstat & CSTAT_SECTOR_SLOPE) && len > 0 ? sec->floorheinum : 0;
z = sec->floorz;
}
if (heinum)
{
auto delta = wal->delta() * heinum * (1. / SLOPEVAL_FACTOR) / len;
double den = direction.Z - delta.dot(direction.XY().Rotated90CW());
if (den != 0)
{
auto stdelta = wal->pos - start;
factor = (z - start.Z + delta.dot(stdelta.Rotated90CW())) / den;
}
else continue;
}
else if (direction.Z != 0)
{
factor = (z - start.Z) / direction.Z;
}
if (factor > 0 && factor <= maxfactor)
{
result = start + factor * direction;
return inside(result.X, result.Y, sec);
}
}
return -1;
}
//==========================================================================
//
//
//
//==========================================================================
int hitscan(const DVector3& start, const sectortype* startsect, const DVector3& vect, HitInfoBase& hitinfo, unsigned cliptype, const DVector2* goal)
{
double hitfactor = DBL_MAX;
const auto wallflags = EWallFlags::FromInt(cliptype & 65535);
const auto spriteflags = ESpriteFlags::FromInt(cliptype >> 16);
hitinfo.clearObj();
if (startsect == nullptr)
return -1;
if (goal)
{
hitinfo.hitpos.XY() = *goal;
hitfactor = (*goal - start.XY()).Sum() / vect.Sum();
}
else hitinfo.hitpos.X = hitinfo.hitpos.Y = DBL_MAX;
BFSSectorSearch search(startsect);
while (auto sec = search.GetNext())
{
DVector3 v;
double hit = checkSectorPlaneHit(sec, start, vect, v, hitfactor);
if (hit > 0)
{
hitfactor = hit;
hitinfo.set(sec, nullptr, nullptr, v);
}
// check all walls in this sector
for (auto& w : wallsofsector(sec))
{
hit = checkWallHit(&w, EWallFlags::FromInt(wallflags), start, vect, v, hitfactor);
if (hit > 0)
{
hitfactor = hit;
hitinfo.set(sec, &w, nullptr, v);
}
else if (hit == -2)
search.Add(w.nextSector());
}
if (!spriteflags)
continue;
//Check all sprites in this sector
TSectIterator<DCoreActor> it(sec);
while (auto actor = it.Next())
{
if (actor->spr.cstat2 & CSTAT2_SPRITE_NOFIND)
continue;
if (!(actor->spr.cstat & spriteflags))
continue;
hit = -1;
// we pass hitfactor to the workers because it can shortcut their calculations a lot.
switch (actor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_MASK)
{
case CSTAT_SPRITE_ALIGNMENT_FACING:
hit = intersectSprite(actor, start, vect, v, hitfactor);
break;
case CSTAT_SPRITE_ALIGNMENT_WALL:
hit = intersectWallSprite(actor, start, vect, v, hitfactor, (picanm[actor->spr.picnum].sf & PICANM_TEXHITSCAN_BIT));
break;
case CSTAT_SPRITE_ALIGNMENT_FLOOR:
hit = intersectFloorSprite(actor, start, vect, v, hitfactor);
break;
case CSTAT_SPRITE_ALIGNMENT_SLOPE:
hit = intersectSlopeSprite(actor, start, vect, v, hitfactor);
break;
}
if (hit > 0)
{
hitfactor = hit;
hitinfo.set(sec, nullptr, actor, v);
}
}
}
return 0;
}
//==========================================================================
//
//

View file

@ -274,8 +274,11 @@ double intersectSprite(DCoreActor* actor, const DVector3& start, const DVector3&
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);
double checkWallHit(walltype* wal, EWallFlags flagmask, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor);
double checkSectorPlaneHit(sectortype* sec, 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);
int hitscan(const DVector3& start, const sectortype* startsect, const DVector3& vect, HitInfoBase& hitinfo, unsigned cliptype, const DVector2* goal = nullptr);

View file

@ -289,22 +289,13 @@ int HitScan(DBloodActor* actor, int z, int dx, int dy, int dz, unsigned int nMas
assert(actor != nullptr);
assert(dx != 0 || dy != 0);
gHitInfo.clearObj();
int x = actor->int_pos().X;
int y = actor->int_pos().Y;
auto bakCstat = actor->spr.cstat;
actor->spr.cstat &= ~CSTAT_SPRITE_BLOCK_HITSCAN;
if (nRange)
{
hitscangoal.X = x + MulScale(nRange << 4, Cos(actor->int_ang()), 30);
hitscangoal.Y = y + MulScale(nRange << 4, Sin(actor->int_ang()), 30);
}
else
{
hitscangoal.X = hitscangoal.Y = 0x1fffffff;
}
hitscan(vec3_t( x, y, z ), actor->sector(), { dx, dy, dz << 4 }, gHitInfo, nMask);
DVector2 hitscangoal;
if (nRange) hitscangoal = actor->spr.pos.XY() + actor->spr.angle.ToVector() * nRange;
else hitscangoal.Zero();
hitscan(DVector3(actor->spr.pos.XY(), z * zinttoworld), actor->sector(), DVector3(dx, dy, dz) * inttoworld, gHitInfo, nMask, & hitscangoal);
hitscangoal.X = hitscangoal.Y = 0x1fffffff;
actor->spr.cstat = bakCstat;
if (gHitInfo.actor() != nullptr)
return 3;
@ -340,19 +331,13 @@ int VectorScan(DBloodActor* actor, double nOffset, double nZOffset, const DVecto
auto pos = actor->spr.pos.plusZ(nZOffset) + (actor->spr.angle + DAngle90).ToVector() * nOffset;
auto bakCstat = actor->spr.cstat;
actor->spr.cstat &= ~CSTAT_SPRITE_BLOCK_HITSCAN;
if (nRange)
{
auto goal = pos.XY() + actor->spr.angle.ToVector() * nRange;
hitscangoal.X = goal.X * worldtoint;
hitscangoal.Y = goal.Y * worldtoint;
}
else
{
hitscangoal.X = hitscangoal.Y = 0x1fffffff;
}
hitscan(pos, actor->sector(), vel, gHitInfo, CLIPMASK1);
hitscangoal.X = hitscangoal.Y = 0x1ffffff;
DVector2 hitscangoal;
if (nRange) hitscangoal = actor->spr.pos.XY() + actor->spr.angle.ToVector() * nRange;
else hitscangoal.Zero();
hitscan(pos, actor->sector(), vel, gHitInfo, CLIPMASK1, &hitscangoal);
actor->spr.cstat = bakCstat;
while (nNum--)
{

View file

@ -2815,7 +2815,7 @@ int ParseState::parse(void)
parseifelse( fi.floorspace(g_ac->sector()));
break;
case concmd_ifnotmoving:
parseifelse( (g_ac->movflag&kHitTypeMask) > kHitSector );
parseifelse( g_ac->movflag > kHitSector );
break;
case concmd_respawnhitag:
insptr++;