diff --git a/src/p_maputl.h b/src/p_maputl.h index 415d4b83b..838c4e775 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -335,7 +335,6 @@ protected: virtual void AddLineIntercepts(int bx, int by); virtual void AddThingIntercepts(int bx, int by, FBlockThingsIterator &it, bool compatible); - void init(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags); FPathTraverse() {} public: @@ -345,10 +344,28 @@ public: { init(x1, y1, x2, y2, flags); } + void init(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags); virtual ~FPathTraverse(); const divline_t &Trace() const { return trace; } }; +//============================================================================ +// +// A traverser that uses the portal blockmap +// This should be in portal.h but that'd create circular dependencies. +// +//============================================================================ + +class FLinePortalTraverse : public FPathTraverse +{ + void AddLineIntercepts(int bx, int by); + +public: + FLinePortalTraverse() + { + } +}; + // diff --git a/src/portal.cpp b/src/portal.cpp index ef22eade3..8c8032f7e 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -38,6 +38,7 @@ #include "p_local.h" +#include "p_blockmap.h" #include "p_lnspec.h" #include "r_bsp.h" #include "r_segs.h" @@ -57,6 +58,7 @@ CVAR(Int, sv_portal_recursions, 4, CVAR_ARCHIVE|CVAR_SERVERINFO) FDisplacementTable Displacements; +FPortalBlockmap PortalBlockmap; TArray linePortals; TArray linkedPortals; // only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups. @@ -93,7 +95,85 @@ struct FPortalBits } }; +//============================================================================ +// +// BuildBlockmap +// +//============================================================================ +static void BuildBlockmap() +{ + PortalBlockmap.Clear(); + PortalBlockmap.Create(bmapwidth, bmapheight); + for (int y = 0; y < bmapheight; y++) + { + for (int x = 0; x < bmapwidth; x++) + { + int offset = y*bmapwidth + x; + int *list = blockmaplump + *(blockmap + offset) + 1; + FPortalBlock &block = PortalBlockmap(x, y); + + while (*list != -1) + { + line_t *ld = &lines[*list++]; + + if (ld->isLinePortal()) + { + PortalBlockmap.containsLines = true; + block.portallines.Push(ld); + } + } + } + } + if (!PortalBlockmap.containsLines) PortalBlockmap.Clear(); +} + +//=========================================================================== +// +// FLinePortalTraverse :: AddLineIntercepts. +// +// Similar to AddLineIntercepts but checks the portal blockmap for line-to-line portals +// +//=========================================================================== + +void FLinePortalTraverse::AddLineIntercepts(int bx, int by) +{ + FPortalBlock &block = PortalBlockmap(bx, by); + + for (unsigned i = 0; ivalidcount == validcount) continue; // already processed + + if (P_PointOnDivlineSidePrecise (ld->v1->x, ld->v1->y, &trace) == + P_PointOnDivlineSidePrecise (ld->v2->x, ld->v2->y, &trace)) + { + continue; // line isn't crossed + } + P_MakeDivline (ld, &dl); + if (P_PointOnDivlineSidePrecise (trace.x, trace.y, &dl) != 0 || + P_PointOnDivlineSidePrecise (trace.x+trace.dx, trace.y+trace.dy, &dl) != 1) + { + continue; // line isn't crossed from the front side + } + + // hit the line + P_MakeDivline(ld, &dl); + frac = P_InterceptVector(&trace, &dl); + if (frac < 0 || frac > FRACUNIT) continue; // behind source + + intercept_t newintercept; + + newintercept.frac = frac; + newintercept.isaline = true; + newintercept.done = false; + newintercept.d.line = ld; + intercepts.Push(newintercept); + } +} //============================================================================ // @@ -301,6 +381,7 @@ void P_FinalizePortals() P_UpdatePortal(port); } P_CollectLinkedPortals(); + BuildBlockmap(); } //============================================================================ @@ -601,124 +682,72 @@ void P_NormalizeVXVY(fixed_t& vx, fixed_t& vy) //============================================================================ // -// portal tracer code +// P_GetOffsetPosition +// +// Offsets a given coordinate if the trace from the origin crosses an +// interactive line-to-line portal. // //============================================================================ -PortalTracer::PortalTracer(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy) +fixedvec2 P_GetOffsetPosition(AActor *actor, fixed_t dx, fixed_t dy) { - this->startx = startx; - this->starty = starty; - this->endx = endx; - this->endy = endy; - intx = endx; - inty = endy; - intxIn = intx; - intyIn = inty; - z = 0; - angle = 0; - depth = 0; - frac = 0; - in = NULL; - out = NULL; - vx = 0; - vy = 0; -} - -bool PortalTracer::TraceStep() -{ - if (depth > sv_portal_recursions) - return false; - - this->in = NULL; - this->out = NULL; - this->vx = 0; - this->vy = 0; - - int oDepth = depth; - - fixed_t dirx = endx-startx; - fixed_t diry = endy-starty; - P_NormalizeVXVY(dirx, diry); - - dirx = 0; - diry = 0; - - FPathTraverse it(startx-dirx, starty-diry, endx+dirx, endy+diry, PT_ADDLINES | PT_COMPATIBLE); - - intercept_t *in; - while ((in = it.Next())) + fixedvec3 dest = { actor->X() + dx, actor->Y() + dy }; + if (PortalBlockmap.containsLines && actor->Sector->PortalGroup != 0) { - line_t* li; - - if (in->isaline) + fixed_t actx = actor->X(), acty = actor->Y(); + FLinePortalTraverse it; + bool repeat; + do { - li = in->d.line; + it.init(actx, acty, dx, dy, PT_ADDLINES); + intercept_t *in; - if (li->isLinePortal()) + repeat = false; + while ((in = it.Next())) { - if (P_PointOnLineSide(startx-dirx, starty-diry, li)) - continue; // we're at the back side of this line + // hit a portal line. + line_t *line = in->d.line; + FLinePortal *port = line->getPortal(); + line_t* out = port->mDestination; - line_t* out = li->getPortalDestination(); - - this->in = li; - this->out = out; - - // we only know that we crossed it, but we also need to know WHERE we crossed it - fixed_t vx = it.Trace().dx; - fixed_t vy = it.Trace().dy; - - fixed_t x = it.Trace().x + FixedMul(vx, in->frac); - fixed_t y = it.Trace().y + FixedMul(vy, in->frac); - - P_NormalizeVXVY(vx, vy); - - this->vx = vx; - this->vy = vy; - - // teleport our trace - - if (!out->backsector) + // Teleport portals are intentionally ignored since skipping this stuff is their entire reason for existence. + if (port->mFlags & PORTF_INTERACTIVE) { - intx = x + vx; - inty = y + vy; - } - else - { - intx = x - vx; - inty = y - vy; + fixed_t hitdx = FixedMul(it.Trace().dx, in->frac); + fixed_t hitdy = FixedMul(it.Trace().dy, in->frac); + + if (port->mType == PORTT_LINKED) + { + // optimized handling for linked portals where we only need to add an offset. + actx = it.Trace().x + hitdx + port->mXDisplacement; + acty = it.Trace().y + hitdy + port->mYDisplacement; + dest.x += port->mXDisplacement; + dest.y += port->mYDisplacement; + } + else + { + // interactive ones are more complex because the vector may be rotated. + // Note: There is no z-translation here, there's just too much code in the engine that wouldn't be able to handle interactive portals with a height difference. + actx = it.Trace().x + hitdx; + acty = it.Trace().y + hitdy; + + P_TranslatePortalXY(line, out, actx, acty); + P_TranslatePortalXY(line, out, dest.x, dest.y); + } + // update the fields, end this trace and restart from the new position + dx = dest.x - actx; + dy = dest.y - acty; + repeat = true; } - //P_TranslateCoordinatesAndAngle(li, out, startx, starty, noangle); - //P_TranslateCoordinatesAndAngle(li, out, endx, endy, angle); - //if (hdeltaZ) - // P_TranslateZ(li, out, deltaZ); - //P_TranslateCoordinatesAndAngle(li, out, vx, vy, noangle); - - P_TranslatePortalXY(li, out, startx, starty); - P_TranslatePortalVXVY(li, out, this->vx, this->vy); - intxIn = intx; - intyIn = inty; - P_TranslatePortalXY(li, out, intx, inty); - P_TranslatePortalXY(li, out, endx, endy); - P_TranslatePortalAngle(li, out, angle); - P_TranslatePortalZ(li, out, z); - frac += in->frac; - depth++; - break; // breaks to outer loop + break; } - - if (!(li->flags & ML_TWOSIDED) || (li->flags & ML_BLOCKEVERYTHING)) - return false; // stop tracing, 2D blocking line - } + } while (repeat); } - - //Printf("returning %d; vx = %.2f; vy = %.2f\n", (oDepth != depth), FIXED2DBL(this->vx), FIXED2DBL(this->vy)); - - return (oDepth != depth); // if a portal has been found, return false + return dest; } + //============================================================================ // // CollectSectors diff --git a/src/portal.h b/src/portal.h index 5ea0293ac..942a44c4a 100644 --- a/src/portal.h +++ b/src/portal.h @@ -62,6 +62,49 @@ struct FDisplacementTable extern FDisplacementTable Displacements; + +//============================================================================ +// +// A blockmap that only contains crossable portals +// This is used for quick checks if a vector crosses through one. +// +//============================================================================ + +struct FPortalBlock +{ + TArray portallines; +}; + +struct FPortalBlockmap +{ + TArray data; + int dx, dy; + bool containsLines; + + void Create(int blockx, int blocky) + { + data.Resize(blockx*blocky); + dx = blockx; + dy = blocky; + } + + void Clear() + { + data.Clear(); + data.ShrinkToFit(); + dx = dy = 0; + containsLines = false; + } + + FPortalBlock &operator()(int x, int y) + { + return data[x + dx*y]; + } +}; + +extern FPortalBlockmap PortalBlockmap; + + //============================================================================ // // Flags and types for linedef portals @@ -143,44 +186,6 @@ void P_TranslatePortalVXVY(line_t* src, line_t* dst, fixed_t& vx, fixed_t& vy); void P_TranslatePortalAngle(line_t* src, line_t* dst, angle_t& angle); void P_TranslatePortalZ(line_t* src, line_t* dst, fixed_t& z); void P_NormalizeVXVY(fixed_t& vx, fixed_t& vy); - -//============================================================================ -// -// basically, this is a teleporting tracer function, -// which can be used by itself (to calculate portal-aware offsets, say, for projectiles) -// or to teleport normal tracers (like hitscan, railgun, autoaim tracers) -// -//============================================================================ - -class PortalTracer -{ -public: - PortalTracer(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy); - - // trace to next portal - bool TraceStep(); - // trace to last available portal on the path - void TraceAll() { while (TraceStep()) continue; } - - int depth; - fixed_t startx; - fixed_t starty; - fixed_t intx; - fixed_t inty; - fixed_t intxIn; - fixed_t intyIn; - fixed_t endx; - fixed_t endy; - angle_t angle; - fixed_t z; - fixed_t frac; - line_t* in; - line_t* out; - fixed_t vx; - fixed_t vy; -}; - -/* new code */ fixed_t P_PointLineDistance(line_t* line, fixed_t x, fixed_t y); #endif \ No newline at end of file diff --git a/src/r_defs.h b/src/r_defs.h index 78c61ec90..2222fbe05 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -1092,6 +1092,11 @@ struct line_t int locknumber; // [Dusk] lock number for special unsigned portalindex; + FLinePortal *getPortal() const + { + return portalindex >= linePortals.Size() ? (FLinePortal*)NULL : &linePortals[portalindex]; + } + // returns true if the portal is crossable by actors bool isLinePortal() const {