mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-24 21:11:39 +00:00
- added a special portal blockmap. This only contains all lines which have a crossable line portal assigned. This is to speed up offset calculations because those are quite frequent and would be too inefficient with the full blockmap.
- made some minor changes to FPathTraverse so that the Add*Intercepts methods can be virtually overridden. - removed the PortalTracer class because in its existing form it was too costly. Replaced with a P_GetOffsetPosition function that does the minimum required work to get to the translated destination and that's better suited for being called from the Vec*Offset methods. Other use cases will require some changes to FPathTraverse anyway, or some wrapping class like the FMultiBlock iterators.
This commit is contained in:
parent
a9db998700
commit
c7bce8b346
4 changed files with 197 additions and 141 deletions
|
@ -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()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
|
|
233
src/portal.cpp
233
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<FLinePortal> linePortals;
|
||||
TArray<FLinePortal*> 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; i<block.portallines.Size(); i++)
|
||||
{
|
||||
line_t *ld = block.portallines[i];
|
||||
fixed_t frac;
|
||||
divline_t dl;
|
||||
|
||||
if (ld->validcount == 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
|
||||
|
|
81
src/portal.h
81
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<line_t*> portallines;
|
||||
};
|
||||
|
||||
struct FPortalBlockmap
|
||||
{
|
||||
TArray<FPortalBlock> 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
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue