diff --git a/src/actor.h b/src/actor.h index 42865543c..4c636638e 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1185,21 +1185,10 @@ public: fixedvec3 ret = { X(), Y(), Z() }; return ret; } - fixedvec3 PosRelative(AActor *other) const - { - fixedvec3 ret = { X(), Y(), Z() }; - return ret; - } - fixedvec3 PosRelative(sector_t *sec) const - { - fixedvec3 ret = { X(), Y(), Z() }; - return ret; - } - fixedvec3 PosRelative(line_t *line) const - { - fixedvec3 ret = { X(), Y(), Z() }; - return ret; - } + fixedvec3 PosRelative(AActor *other) const; + fixedvec3 PosRelative(sector_t *sec) const; + fixedvec3 PosRelative(line_t *line) const; + fixed_t SoundX() const { return X(); diff --git a/src/p_checkposition.h b/src/p_checkposition.h index 6eee5374a..4ab67634e 100644 --- a/src/p_checkposition.h +++ b/src/p_checkposition.h @@ -2,63 +2,6 @@ #define P_CHECKPOS_H -//============================================================================ -// -// This is a dynamic array which holds its first MAX_STATIC entries in normal -// variables to avoid constant allocations which this would otherwise -// require. -// -// When collecting touched portal groups the normal cases are either -// no portals == one group or -// two portals = two groups -// -// Anything with more can happen but far less infrequently, so this -// organization helps avoiding the overhead from heap allocations -// in the vast majority of situations. -// -//============================================================================ - -struct FPortalGroupArray -{ - enum - { - MAX_STATIC = 2 - }; - - FPortalGroupArray() - { - varused = 0; - } - - void Clear() - { - data.Clear(); - varused = 0; - } - - void Add(DWORD num) - { - if (varused < MAX_STATIC) entry[varused++] = num; - else data.Push(num); - } - - unsigned Size() - { - return varused + data.Size(); - } - - DWORD operator[](unsigned index) - { - return index < MAX_STATIC ? entry[index] : data[index - MAX_STATIC]; - } - -private: - DWORD entry[MAX_STATIC]; - unsigned varused; - TArray data; -}; - - //============================================================================ // // Used by P_CheckPosition and P_TryMove in place of the original @@ -95,7 +38,6 @@ struct FCheckPosition bool DoRipping; TMap LastRipped; - FPortalGroupArray Groups; int PushTime; FCheckPosition(bool rip=false) diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 12fdaf427..80190e624 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -39,6 +39,7 @@ #include "p_3dmidtex.h" #include "p_blockmap.h" #include "r_utility.h" +#include "portal.h" // State. #include "r_state.h" @@ -572,7 +573,7 @@ FBlockLinesIterator::FBlockLinesIterator(int _minx, int _miny, int _maxx, int _m Reset(); } -FBlockLinesIterator::FBlockLinesIterator(const FBoundingBox &box) +void FBlockLinesIterator::init(const FBoundingBox &box) { validcount++; maxy = GetSafeBlockY(box.Top() - bmaporgy); @@ -582,6 +583,10 @@ FBlockLinesIterator::FBlockLinesIterator(const FBoundingBox &box) Reset(); } +FBlockLinesIterator::FBlockLinesIterator(const FBoundingBox &box) +{ + init(box); +} //=========================================================================== // @@ -681,6 +686,82 @@ line_t *FBlockLinesIterator::Next() } } +//=========================================================================== +// +// FMultiBlockLinesIterator :: FMultiBlockLinesIterator +// +// An iterator that can check multiple portal groups. +// +//=========================================================================== + +FMultiBlockLinesIterator::FMultiBlockLinesIterator(AActor *origin, fixed_t checkx, fixed_t checky, fixed_t checkradius) +{ + checkpoint = origin->Pos(); + if (checkx != FIXED_MAX) checkpoint.x = checkx; + if (checky != FIXED_MAX) checkpoint.y = checky; + P_CollectConnectedGroups(origin->Sector->PortalGroup, checkpoint, origin->Top(), checkradius, checklist); + checkpoint.z = checkradius; + basegroup = origin->Sector->PortalGroup; + Reset(); +} + +bool FMultiBlockLinesIterator::Next(FMultiBlockLinesIterator::CheckResult *item) +{ + line_t *line = blockIterator.Next(); + if (line != NULL) + { + item->line = line; + item->position = offset; + item->portalposition = portalposition; + return true; + } + else if (checklist[index] & FPortalGroupArray::UPPER) + { + if (continueup) + { + sector_t *sector = P_PointInSector(offset.x, offset.y); + if (!sector->PortalBlocksMovement(sector_t::ceiling)) + { + startIteratorForGroup(sector->SkyBoxes[sector_t::ceiling]->Sector->PortalGroup); + return Next(item); + } + } + } + else if (checklist[index] & FPortalGroupArray::LOWER) + { + if (continuedown) + { + sector_t *sector = P_PointInSector(offset.x, offset.y); + if (!sector->PortalBlocksMovement(sector_t::floor)) + { + startIteratorForGroup(sector->SkyBoxes[sector_t::floor]->Sector->PortalGroup); + return Next(item); + } + } + } + index++; + if (index >= checklist.Size()) return false; + startIteratorForGroup(checklist[index]); + return Next(item); +} + +void FMultiBlockLinesIterator::startIteratorForGroup(int group) +{ + offset = Displacements(basegroup, group); + offset.x += checkpoint.x; + offset.y += checkpoint.y; + FBoundingBox box(offset.x, offset.y, checkpoint.z); + blockIterator.init(box); +} + +void FMultiBlockLinesIterator::Reset() +{ + continueup = continueup = true; + index = -1; + portalposition = PP_ORIGIN; + startIteratorForGroup(basegroup); +} + //=========================================================================== // // FBlockThingsIterator :: FBlockThingsIterator diff --git a/src/p_maputl.h b/src/p_maputl.h index dbc4eaf9a..0686b507b 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -108,8 +108,72 @@ void P_LineOpening (FLineOpening &open, AActor *thing, const line_t *linedef, fi class FBoundingBox; struct polyblock_t; +//============================================================================ +// +// This is a dynamic array which holds its first MAX_STATIC entries in normal +// variables to avoid constant allocations which this would otherwise +// require. +// +// When collecting touched portal groups the normal cases are either +// no portals == one group or +// two portals = two groups +// +// Anything with more can happen but far less infrequently, so this +// organization helps avoiding the overhead from heap allocations +// in the vast majority of situations. +// +//============================================================================ + +struct FPortalGroupArray +{ + enum + { + LOWER = 0x4000, + UPPER = 0x8000, + FLAT = 0xc000, + }; + + enum + { + MAX_STATIC = 4 + }; + + FPortalGroupArray() + { + varused = 0; + } + + void Clear() + { + data.Clear(); + varused = 0; + } + + void Add(DWORD num) + { + if (varused < MAX_STATIC) entry[varused++] = (WORD)num; + else data.Push((WORD)num); + } + + unsigned Size() + { + return varused + data.Size(); + } + + DWORD operator[](unsigned index) + { + return index < MAX_STATIC ? entry[index] : data[index - MAX_STATIC]; + } + +private: + WORD entry[MAX_STATIC]; + unsigned varused; + TArray data; +}; + class FBlockLinesIterator { + friend class FMultiBlockLinesIterator; int minx, maxx; int miny, maxy; @@ -120,6 +184,8 @@ class FBlockLinesIterator void StartBlock(int x, int y); + FBlockLinesIterator() {} + void init(const FBoundingBox &box); public: FBlockLinesIterator(int minx, int miny, int maxx, int maxy, bool keepvalidcount = false); FBlockLinesIterator(const FBoundingBox &box); @@ -127,6 +193,52 @@ public: void Reset() { StartBlock(minx, miny); } }; +class FMultiBlockLinesIterator +{ + fixedvec3 checkpoint; + fixedvec2 offset; + short basegroup; + short portalposition; + WORD index; + bool continueup; + bool continuedown; + FBlockLinesIterator blockIterator; + FPortalGroupArray checklist; + + void startIteratorForGroup(int group); + +public: + + enum + { + PP_ORIGIN, + PP_ABOVE, + PP_BELOW, + PP_THROUGHLINE + }; + + struct CheckResult + { + line_t *line; + fixedvec2 position; + int portalposition; + }; + + FMultiBlockLinesIterator(AActor *origin, fixed_t checkx = FIXED_MAX, fixed_t checky = FIXED_MAX, fixed_t checkradius = -1); + bool Next(CheckResult *item); + void Reset(); + // for stopping group traversal through portals. Only the calling code can decide whether this is needed so this needs to be set from the outside. + void StopUp() + { + continueup = false; + } + void StopDown() + { + continuedown = false; + } +}; + + class FBlockThingsIterator { int minx, maxx; diff --git a/src/portal.cpp b/src/portal.cpp index b787ae9f3..a247aafcc 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -1053,7 +1053,7 @@ void P_CreateLinkedPortals() // //============================================================================ -bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortalGroupArray &out) +bool P_CollectConnectedGroups(int startgroup, const fixedvec3 &position, fixed_t upperz, fixed_t checkradius, FPortalGroupArray &out) { // Keep this temporary work stuff static. This function can never be called recursively // and this would have to be reallocated for each call otherwise. @@ -1071,9 +1071,9 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal processMask.clear(); foundPortals.Clear(); - int thisgroup = actor->Sector->PortalGroup; + int thisgroup = startgroup; processMask.setBit(thisgroup); - out.Add(thisgroup); + //out.Add(thisgroup); for (unsigned i = 0; i < linkedPortals.Size(); i++) { @@ -1082,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.pos.x, newy + disp.pos.y, actor->radius); + FBoundingBox box(position.x + disp.pos.x, position.y + disp.pos.y, checkradius); if (box.Right() <= ld->bbox[BOXLEFT] || box.Left() >= ld->bbox[BOXRIGHT] @@ -1110,27 +1110,27 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal } } } - sector_t *sec = P_PointInSector(newx, newy); + sector_t *sec = P_PointInSector(position.x, position.y); sector_t *wsec = sec; - while (!wsec->PortalBlocksMovement(sector_t::ceiling) && actor->Top() > wsec->SkyBoxes[sector_t::ceiling]->threshold) + while (!wsec->PortalBlocksMovement(sector_t::ceiling) && upperz > wsec->SkyBoxes[sector_t::ceiling]->threshold) { sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector; - FDisplacement &disp = Displacements(actor->Sector->PortalGroup, othersec->PortalGroup); - fixed_t dx = newx + disp.pos.x; - fixed_t dy = newx + disp.pos.y; + FDisplacement &disp = Displacements(startgroup, othersec->PortalGroup); + fixed_t dx = position.x + disp.pos.x; + fixed_t dy = position.y + disp.pos.y; processMask.setBit(othersec->PortalGroup); - out.Add(othersec->PortalGroup); + out.Add(othersec->PortalGroup|FPortalGroupArray::UPPER); wsec = P_PointInSector(dx, dy); // get upper sector at the exact spot we want to check and repeat retval = true; } wsec = sec; - while (!wsec->PortalBlocksMovement(sector_t::floor) && actor->Z() < wsec->SkyBoxes[sector_t::floor]->threshold) + while (!wsec->PortalBlocksMovement(sector_t::floor) && position.z < wsec->SkyBoxes[sector_t::floor]->threshold) { - sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector; - FDisplacement &disp = Displacements(actor->Sector->PortalGroup, othersec->PortalGroup); - fixed_t dx = newx + disp.pos.x; - fixed_t dy = newx + disp.pos.y; - processMask.setBit(othersec->PortalGroup); + sector_t *othersec = wsec->SkyBoxes[sector_t::floor]->Sector; + FDisplacement &disp = Displacements(startgroup, othersec->PortalGroup); + fixed_t dx = position.x + disp.pos.x; + fixed_t dy = position.y + disp.pos.y; + processMask.setBit(othersec->PortalGroup|FPortalGroupArray::LOWER); out.Add(othersec->PortalGroup); wsec = P_PointInSector(dx, dy); // get lower sector at the exact spot we want to check and repeat retval = true; diff --git a/src/portal.h b/src/portal.h index 1e00acf81..fcf6c545b 100644 --- a/src/portal.h +++ b/src/portal.h @@ -125,7 +125,7 @@ void P_SpawnLinePortal(line_t* line); void P_FinalizePortals(); bool P_ChangePortal(line_t *ln, int thisid, int destid); void P_CreateLinkedPortals(); -bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortalGroupArray &out); +bool P_CollectConnectedGroups(int startgroup, const fixedvec3 &position, fixed_t upperz, fixed_t checkradius, FPortalGroupArray &out); void P_CollectLinkedPortals(); inline int P_NumPortalGroups() { @@ -245,4 +245,25 @@ inline FDisplacement §or_t::CeilingDisplacement() return Displacements(PortalGroup, SkyBoxes[sector_t::ceiling]->Sector->PortalGroup); } +inline fixedvec3 AActor::PosRelative(AActor *other) const +{ + FDisplacement &disp = Displacements(Sector->PortalGroup, other->Sector->PortalGroup); + fixedvec3 ret = { X() + disp.pos.x, Y() + disp.pos.y, Z() }; + return ret; +} + +inline fixedvec3 AActor::PosRelative(sector_t *sec) const +{ + FDisplacement &disp = Displacements(Sector->PortalGroup, sec->PortalGroup); + fixedvec3 ret = { X() + disp.pos.x, Y() + disp.pos.y, Z() }; + return ret; +} + +inline fixedvec3 AActor::PosRelative(line_t *line) const +{ + FDisplacement &disp = Displacements(Sector->PortalGroup, line->frontsector->PortalGroup); + fixedvec3 ret = { X() + disp.pos.x, Y() + disp.pos.y, Z() }; + return ret; +} + #endif \ No newline at end of file