- started refactoring ZatPoint calls which need to be portal aware.

To summarize, anything that just works with map geometry doesn't need to bother, as does the renderer. (i.e. nearly all r_* files, p_floor.cpp, p_ceiling.cpp et.al)
But all calls that are somehow related to actor positions need to be made aware of potential portal transitions:

 * added FloorAtPoint, CeilingAtPoint and PlaneAtPoint methods to sector_t, which can be used to calculate a plane's height with relation to a given actor, even if that actor is on the other side of a portal.
 * added HighestCeilingAt and LowestFloorAt methods which traverse all ceiling/floor portals until they find an impassable plane.
This commit is contained in:
Christoph Oelckers 2016-02-16 16:40:53 +01:00
parent 45108e9bb8
commit 1b88052bba
11 changed files with 230 additions and 42 deletions

View file

@ -81,16 +81,50 @@ union QWORD_UNION
typedef SDWORD fixed_t;
typedef DWORD dsfixed_t; // fixedpt used by span drawer
struct fixedvec3
{
fixed_t x, y, z;
};
struct fixedvec2
{
fixed_t x, y;
fixedvec2 &operator +=(const fixedvec2 &other)
{
x += other.x;
y += other.y;
return *this;
}
};
struct fixedvec3
{
fixed_t x, y, z;
fixedvec3 &operator +=(const fixedvec3 &other)
{
x += other.x;
y += other.y;
z += other.z;
return *this;
}
fixedvec3 &operator +=(const fixedvec2 &other)
{
x += other.x;
y += other.y;
return *this;
}
};
inline fixedvec2 operator +(const fixedvec2 &v1, const fixedvec2 &v2)
{
fixedvec2 v = { v1.x + v2.x, v1.y + v2.y };
return v;
}
inline fixedvec3 operator +(const fixedvec3 &v1, const fixedvec3 &v2)
{
fixedvec3 v = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };
return v;
}
#define FIXED_MAX (signed)(0x7fffffff)
#define FIXED_MIN (signed)(0x80000000)

View file

@ -122,9 +122,9 @@ void A_PainShootSkull (AActor *self, angle_t angle, PClassActor *spawntype, int
// Check to see if the new Lost Soul's z value is above the
// ceiling of its new sector, or below the floor. If so, kill it.
if ((other->Z() >
(other->Sector->HighestCeiling(other) - other->height)) ||
(other->Z() < other->Sector->LowestFloor(other)))
if ((other->Top() >
(other->Sector->HighestCeilingAt(other))) ||
(other->Z() < other->Sector->LowestFloorAt(other)))
{
// kill it immediately
P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);// ^

View file

@ -82,7 +82,7 @@ void AFastProjectile::Tick ()
if (tm.ceilingline &&
tm.ceilingline->backsector &&
tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum &&
Z() >= tm.ceilingline->backsector->ceilingplane.ZatPoint (this))
Z() >= tm.ceilingline->backsector->CeilingAtPoint(this))
{
// Hack to prevent missiles exploding against the sky.
// Does not handle sky floors.

View file

@ -427,7 +427,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition)
self->floorz = sec->floorplane.ZatPoint(_x, _y);
self->ceilingz = sec->ceilingplane.ZatPoint(_x, _y);
self->SetZ(self->floorz);
P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS);
P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS | FFCF_NOPORTALS); // no pprtal checks here so that things get spawned in this sector.
if (self->flags & MF_SPAWNCEILING)
{
@ -451,6 +451,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition)
self->SetZ(self->SpawnPoint[2] + self->floorz);
}
// Redo floor/ceiling check, in case of 3D floors
// we need to get the actual floor and ceiling heights including portals here
self->floorz = sec->LowestFloorAt(self, &self->floorsector);
self->ceilingz = sec->HighestCeilingAt(self, &self->ceilingsector);
P_FindFloorCeiling(self, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT);
if (self->Z() < self->floorz)
{ // Do not reappear under the floor, even if that's where we were for the

View file

@ -658,7 +658,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CheckTerrain)
sector_t *sec = self->Sector;
if (self->Z() == sec->floorplane.ZatPoint(self))
if (self->Z() == sec->floorplane.ZatPoint(self) && sec->PortalBlocksMovement(sector_t::floor))
{
if (sec->special == Damage_InstantDeath)
{

View file

@ -558,7 +558,7 @@ bool P_Move (AActor *actor)
else
{ // The monster just hit the floor, so trigger any actions.
if (actor->floorsector->SecActTarget != NULL &&
actor->floorz == actor->floorsector->floorplane.ZatPoint(actor))
actor->floorz == actor->floorsector->FloorAtPoint(actor))
{
actor->floorsector->SecActTarget->TriggerAction(actor, SECSPAC_HitFloor);
}
@ -868,8 +868,8 @@ void P_NewChaseDir(AActor * actor)
box.Bottom() < line->bbox[BOXTOP] &&
box.BoxOnLineSide(line) == -1)
{
fixed_t front = line->frontsector->floorplane.ZatPoint(actor);
fixed_t back = line->backsector->floorplane.ZatPoint(actor);
fixed_t front = line->frontsector->FloorAtPoint(actor);
fixed_t back = line->backsector->FloorAtPoint(actor);
angle_t angle;
// The monster must contact one of the two floors,

View file

@ -287,6 +287,7 @@ enum
FFCF_SAMESECTOR = 2,
FFCF_ONLY3DFLOORS = 4, // includes 3D midtexes
FFCF_3DRESTRICT = 8, // ignore 3D midtexes and floors whose floorz are above thing's z
FFCF_NOPORTALS = 16, // ignore portals (considers them impassable.)
};
void P_FindFloorCeiling (AActor *actor, int flags=0);

View file

@ -30,6 +30,7 @@
#include "po_man.h"
#include "farchive.h"
#include "r_utility.h"
#include "portal.h"
#include "a_sharedglobal.h"
#include "r_data/colormaps.h"
@ -882,7 +883,78 @@ void sector_t::CheckPortalPlane(int plane)
planes[plane].Flags = (planes[plane].Flags & ~PLANEF_OBSTRUCTED) | obstructed;
}
//===========================================================================
//
// Finds the highest ceiling at the given position, all portals considered
//
//===========================================================================
fixed_t sector_t::HighestCeilingAt(fixed_t x, fixed_t y, sector_t **resultsec)
{
sector_t *check = this;
fixed_t planeheight = FIXED_MIN;
// Continue until we find a blocking portal or a portal below where we actually are.
while (!check->PortalBlocksMovement(ceiling) && planeheight < check->SkyBoxes[ceiling]->threshold)
{
FDisplacement &disp = check->CeilingDisplacement();
x += disp.pos.x;
y += disp.pos.y;
planeheight = check->SkyBoxes[ceiling]->threshold;
check = P_PointInSector(x, y);
}
if (resultsec) *resultsec = check;
return check->ceilingplane.ZatPoint(x, y);
}
//===========================================================================
//
// Finds the lowest floor at the given position, all portals considered
//
//===========================================================================
fixed_t sector_t::LowestFloorAt(fixed_t x, fixed_t y, sector_t **resultsec)
{
sector_t *check = this;
fixed_t planeheight = FIXED_MAX;
// Continue until we find a blocking portal or a portal above where we actually are.
while (!check->PortalBlocksMovement(floor) && planeheight > check->SkyBoxes[floor]->threshold)
{
FDisplacement &disp = check->FloorDisplacement();
x += disp.pos.x;
y += disp.pos.y;
planeheight = check->SkyBoxes[floor]->threshold;
check = P_PointInSector(x, y);
}
if (resultsec) *resultsec = check;
return check->ceilingplane.ZatPoint(x, y);
}
//===========================================================================
//
// Calculates the height of a sector plane, respecting portal offsets
// between two spots
//
//===========================================================================
fixed_t sector_t::PlaneAtPoint(const secplane_t &plane, fixed_t x, fixed_t y, int refgroup) const
{
if (refgroup != PortalGroup)
{
FDisplacement &disp = Displacements(PortalGroup, refgroup);
x += disp.pos.x;
y += disp.pos.y;
}
return plane.ZatPoint(x, y);
}
//===========================================================================
//
//
//
//===========================================================================
FArchive &operator<< (FArchive &arc, secspecial_t &p)
{
@ -908,6 +980,11 @@ FArchive &operator<< (FArchive &arc, secspecial_t &p)
}
//===========================================================================
//
//
//
//===========================================================================
bool secplane_t::CopyPlaneIfValid (secplane_t *dest, const secplane_t *opp) const
{

View file

@ -774,13 +774,13 @@ static void AddDisplacementForPortal(AStackPoint *portal)
FDisplacement & disp = Displacements(thisgroup, othergroup);
if (!disp.isSet)
{
disp.x = portal->scaleX;
disp.y = portal->scaleY;
disp.pos.x = portal->scaleX;
disp.pos.y = portal->scaleY;
disp.isSet = true;
}
else
{
if (disp.x != portal->scaleX || disp.y != portal->scaleY)
if (disp.pos.x != portal->scaleX || disp.pos.y != portal->scaleY)
{
Printf("Portal between sectors %d and %d has displacement mismatch and will be disabled\n", portal->Sector->sectornum, portal->Mate->Sector->sectornum);
portal->special1 = portal->Mate->special1 = SKYBOX_PORTAL;
@ -810,13 +810,13 @@ static void AddDisplacementForPortal(FLinePortal *portal)
FDisplacement & disp = Displacements(thisgroup, othergroup);
if (!disp.isSet)
{
disp.x = portal->mXDisplacement;
disp.y = portal->mYDisplacement;
disp.pos.x = portal->mXDisplacement;
disp.pos.y = portal->mYDisplacement;
disp.isSet = true;
}
else
{
if (disp.x != portal->mXDisplacement || disp.y != portal->mYDisplacement)
if (disp.pos.x != portal->mXDisplacement || disp.pos.y != portal->mYDisplacement)
{
Printf("Portal between lines %d and %d has displacement mismatch\n", int(portal->mOrigin - lines), int(portal->mDestination - lines));
portal->mType = linePortals[portal->mDestination->portalindex].mType = PORTT_TELEPORT;
@ -857,15 +857,14 @@ static bool ConnectGroups()
FDisplacement &dispxz = Displacements(x, z);
if (dispxz.isSet)
{
if (dispxy.x + dispyz.x != dispxz.x || dispxy.y + dispyz.y != dispxz.y)
if (dispxy.pos.x + dispyz.pos.x != dispxz.pos.x || dispxy.pos.y + dispyz.pos.y != dispxz.pos.y)
{
bogus = true;
}
}
else
{
dispxz.x = dispxy.x + dispyz.x;
dispxz.y = dispxy.y + dispyz.y;
dispxz.pos = dispxy.pos + dispyz.pos;
dispxz.isSet = true;
dispxz.indirect = indirect;
changed = true;
@ -995,7 +994,7 @@ void P_CreateLinkedPortals()
FDisplacement &dispxy = Displacements(x, y);
FDisplacement &dispyx = Displacements(y, x);
if (dispxy.isSet && dispyx.isSet &&
(dispxy.x != -dispyx.x || dispxy.y != -dispyx.y))
(dispxy.pos.x != -dispyx.pos.x || dispxy.pos.y != -dispyx.pos.y))
{
int sec1 = -1, sec2 = -1;
for (int i = 0; i < numsectors && (sec1 == -1 || sec2 == -1); i++)
@ -1083,7 +1082,7 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal
FDisplacement &disp = Displacements(thisgroup, othergroup);
if (!disp.isSet) continue; // no connection.
FBoundingBox box(newx + disp.x, newy + disp.y, actor->radius);
FBoundingBox box(newx + disp.pos.x, newy + disp.pos.y, actor->radius);
if (box.Right() <= ld->bbox[BOXLEFT]
|| box.Left() >= ld->bbox[BOXRIGHT]
@ -1117,8 +1116,8 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal
{
sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector;
FDisplacement &disp = Displacements(actor->Sector->PortalGroup, othersec->PortalGroup);
fixed_t dx = newx + disp.x;
fixed_t dy = newx + disp.y;
fixed_t dx = newx + disp.pos.x;
fixed_t dy = newx + disp.pos.y;
processMask.setBit(othersec->PortalGroup);
out.Add(othersec->PortalGroup);
wsec = P_PointInSector(dx, dy); // get upper sector at the exact spot we want to check and repeat
@ -1129,8 +1128,8 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal
{
sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector;
FDisplacement &disp = Displacements(actor->Sector->PortalGroup, othersec->PortalGroup);
fixed_t dx = newx + disp.x;
fixed_t dy = newx + disp.y;
fixed_t dx = newx + disp.pos.x;
fixed_t dy = newx + disp.pos.y;
processMask.setBit(othersec->PortalGroup);
out.Add(othersec->PortalGroup);
wsec = P_PointInSector(dx, dy); // get lower sector at the exact spot we want to check and repeat
@ -1153,7 +1152,7 @@ CCMD(dumplinktable)
for (int y = 1; y < Displacements.size; y++)
{
FDisplacement &disp = Displacements(x, y);
Printf("%c%c(%6d, %6d)", TEXTCOLOR_ESCAPE, 'C' + disp.indirect, disp.x >> FRACBITS, disp.y >> FRACBITS);
Printf("%c%c(%6d, %6d)", TEXTCOLOR_ESCAPE, 'C' + disp.indirect, disp.pos.x >> FRACBITS, disp.pos.y >> FRACBITS);
}
Printf("\n");
}

View file

@ -30,9 +30,14 @@ struct FPortalGroupArray;
struct FDisplacement
{
fixed_t x, y;
fixedvec2 pos;
bool isSet;
BYTE indirect; // just for illustration.
operator fixedvec2()
{
return pos;
}
};
struct FDisplacementTable
@ -222,4 +227,15 @@ inline bool sector_t::PortalBlocksSound(int plane)
return !!(planes[plane].Flags & (PLANEF_BLOCKSOUND | PLANEF_DISABLED | PLANEF_OBSTRUCTED));
}
// These may only be called if the portal has been validated
inline FDisplacement &sector_t::FloorDisplacement()
{
return Displacements(PortalGroup, SkyBoxes[sector_t::floor]->Sector->PortalGroup);
}
inline FDisplacement &sector_t::CeilingDisplacement()
{
return Displacements(PortalGroup, SkyBoxes[sector_t::ceiling]->Sector->PortalGroup);
}
#endif

View file

@ -55,6 +55,7 @@ enum
};
extern size_t MaxDrawSegs;
struct FDisplacement;
enum
@ -242,6 +243,16 @@ struct secplane_t
return ic < 0 ? d : -d;
}
fixed_t ZatPoint(const fixedvec2 &spot) const
{
return FixedMul(ic, -d - DMulScale16(a, spot.x, b, spot.y));
}
fixed_t ZatPoint(const fixedvec3 &spot) const
{
return FixedMul(ic, -d - DMulScale16(a, spot.x, b, spot.y));
}
// Returns the value of z at (x,y)
fixed_t ZatPoint (fixed_t x, fixed_t y) const
{
@ -702,17 +713,6 @@ struct sector_t
return pos == floor? floorplane:ceilingplane;
}
fixed_t HighestCeiling(AActor *a) const
{
return ceilingplane.ZatPoint(a);
}
fixed_t LowestFloor(AActor *a) const
{
return floorplane.ZatPoint(a);
}
bool isSecret() const
{
return !!(Flags & SECF_SECRET);
@ -750,6 +750,63 @@ struct sector_t
void SetSpecial(const secspecial_t *spec);
bool PlaneMoving(int pos);
// Portal-aware height calculation
fixed_t HighestCeilingAt(fixed_t x, fixed_t y, sector_t **resultsec = NULL);
fixed_t LowestFloorAt(fixed_t x, fixed_t y, sector_t **resultsec = NULL);
fixed_t HighestCeilingAt(AActor *a, sector_t **resultsec = NULL)
{
return HighestCeilingAt(a->X(), a->X(), resultsec);
}
fixed_t LowestFloorAt(AActor *a, sector_t **resultsec = NULL)
{
return LowestFloorAt(a->X(), a->Y(), resultsec);
}
// ... for ceilings
fixed_t CeilingAtPoint(fixed_t x, fixed_t y, int refgroup) const
{
return PlaneAtPoint(ceilingplane, x, y, refgroup);
}
fixed_t CeilingAtPoint(AActor *actor) const
{
return PlaneAtPoint(ceilingplane, actor->X(), actor->Y(), actor->Sector->PortalGroup);
}
fixed_t CeilingAtPoint(fixed_t x, fixed_t y, sector_t *refsector) const
{
return PlaneAtPoint(ceilingplane, x, y, refsector->PortalGroup);
}
// ... for floors
fixed_t FloorAtPoint(fixed_t x, fixed_t y, int refgroup) const
{
return PlaneAtPoint(floorplane, x, y, refgroup);
}
fixed_t FloorAtPoint(AActor *actor) const
{
return PlaneAtPoint(floorplane, actor->X(), actor->Y(), actor->Sector->PortalGroup);
}
fixed_t FloorAtPoint(fixed_t x, fixed_t y, sector_t *refsector) const
{
return PlaneAtPoint(floorplane, x, y, refsector->PortalGroup);
}
// ... for control sectors
fixed_t PlaneAtPoint(const secplane_t &plane, AActor *actor) const
{
return PlaneAtPoint(plane, actor->X(), actor->Y(), actor->Sector->PortalGroup);
}
fixed_t PlaneAtPoint(const secplane_t &plane, fixed_t x, fixed_t y, sector_t *refsector) const
{
return PlaneAtPoint(plane, x, y, refsector->PortalGroup);
}
// The worker function for all the above.
fixed_t PlaneAtPoint(const secplane_t &plane, fixed_t x, fixed_t y, int refgroup) const;
FDisplacement &FloorDisplacement();
FDisplacement &CeilingDisplacement();
// Member variables
fixed_t CenterFloor () const { return floorplane.ZatPoint (soundorg[0], soundorg[1]); }
@ -1165,5 +1222,6 @@ inline sector_t *P_PointInSector(fixed_t x, fixed_t y)
return P_PointInSubsector(x, y)->sector;
}
#define _ZatPoint ZatPoint // so that it still compiles during the transition
#endif