From 1b88052bbac92a59b73bf751f1acff845c1a7cc5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 16 Feb 2016 16:40:53 +0100 Subject: [PATCH] - 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. --- src/basictypes.h | 44 +++++++++++++++-- src/g_doom/a_painelemental.cpp | 6 +-- src/g_shared/a_fastprojectile.cpp | 2 +- src/g_shared/a_pickups.cpp | 5 +- src/g_strife/a_strifestuff.cpp | 2 +- src/p_enemy.cpp | 6 +-- src/p_local.h | 1 + src/p_sectors.cpp | 77 +++++++++++++++++++++++++++++ src/portal.cpp | 31 ++++++------ src/portal.h | 18 ++++++- src/r_defs.h | 80 ++++++++++++++++++++++++++----- 11 files changed, 230 insertions(+), 42 deletions(-) diff --git a/src/basictypes.h b/src/basictypes.h index 0ae68f39b..6caf40a36 100644 --- a/src/basictypes.h +++ b/src/basictypes.h @@ -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) diff --git a/src/g_doom/a_painelemental.cpp b/src/g_doom/a_painelemental.cpp index 6c02c6517..b73af5e1d 100644 --- a/src/g_doom/a_painelemental.cpp +++ b/src/g_doom/a_painelemental.cpp @@ -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);// ^ diff --git a/src/g_shared/a_fastprojectile.cpp b/src/g_shared/a_fastprojectile.cpp index 29a72c23c..baad73628 100644 --- a/src/g_shared/a_fastprojectile.cpp +++ b/src/g_shared/a_fastprojectile.cpp @@ -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. diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 32a4167bb..256ba010c 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -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 diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp index 724deaee2..e24ca6113 100644 --- a/src/g_strife/a_strifestuff.cpp +++ b/src/g_strife/a_strifestuff.cpp @@ -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) { diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index fed155627..49451c5d6 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -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, diff --git a/src/p_local.h b/src/p_local.h index 44a430c27..c4c5d3914 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -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); diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index a10568388..1a4d218cf 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -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 { diff --git a/src/portal.cpp b/src/portal.cpp index a525b22f0..b787ae9f3 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -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"); } diff --git a/src/portal.h b/src/portal.h index 40c4a30fc..88eee2d64 100644 --- a/src/portal.h +++ b/src/portal.h @@ -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 §or_t::FloorDisplacement() +{ + return Displacements(PortalGroup, SkyBoxes[sector_t::floor]->Sector->PortalGroup); +} + +inline FDisplacement §or_t::CeilingDisplacement() +{ + return Displacements(PortalGroup, SkyBoxes[sector_t::ceiling]->Sector->PortalGroup); +} + #endif \ No newline at end of file diff --git a/src/r_defs.h b/src/r_defs.h index 6913d868b..15b7337cd 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -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