mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-27 22:42:57 +00:00
419 lines
9.7 KiB
C++
419 lines
9.7 KiB
C++
#ifndef __P_MAPUTL_H
|
|
#define __P_MAPUTL_H
|
|
|
|
#include <float.h>
|
|
#include "r_defs.h"
|
|
#include "doomstat.h"
|
|
#include "doomdata.h"
|
|
#include "m_bbox.h"
|
|
|
|
extern int validcount;
|
|
struct FBlockNode;
|
|
|
|
struct divline_t
|
|
{
|
|
double x;
|
|
double y;
|
|
double dx;
|
|
double dy;
|
|
};
|
|
|
|
struct intercept_t
|
|
{
|
|
double frac;
|
|
bool isaline;
|
|
bool done;
|
|
union {
|
|
AActor *thing;
|
|
line_t *line;
|
|
} d;
|
|
};
|
|
|
|
//==========================================================================
|
|
//
|
|
// P_PointOnLineSide
|
|
//
|
|
// Returns 0 (front/on) or 1 (back)
|
|
// [RH] inlined, stripped down, and made more precise
|
|
//
|
|
//==========================================================================
|
|
|
|
inline int P_PointOnLineSidePrecise(double x, double y, const line_t *line)
|
|
{
|
|
return (y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - x) * line->Delta().Y > EQUAL_EPSILON;
|
|
}
|
|
|
|
inline int P_PointOnLineSidePrecise(const DVector2 &pt, const line_t *line)
|
|
{
|
|
return (pt.Y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - pt.X) * line->Delta().Y > EQUAL_EPSILON;
|
|
}
|
|
|
|
inline int P_PointOnLineSide (double x, double y, const line_t *line)
|
|
{
|
|
extern int P_VanillaPointOnLineSide(double x, double y, const line_t* line);
|
|
return (line->flags & ML_COMPATSIDE)
|
|
? P_VanillaPointOnLineSide(x, y, line) : P_PointOnLineSidePrecise(x, y, line);
|
|
}
|
|
|
|
inline int P_PointOnLineSide(const DVector2 & p, const line_t *line)
|
|
{
|
|
return P_PointOnLineSide(p.X, p.Y, line);
|
|
}
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// P_PointOnDivlineSideCompat
|
|
//
|
|
// Same as P_PointOnLineSide except it uses divlines
|
|
// [RH] inlined, stripped down, and made more precise
|
|
//
|
|
//==========================================================================
|
|
|
|
inline int P_PointOnDivlineSide(double x, double y, const divline_t *line)
|
|
{
|
|
return (y - line->y) * line->dx + (line->x - x) * line->dy > EQUAL_EPSILON;
|
|
}
|
|
|
|
inline int P_PointOnDivlineSide(const DVector2 &pos, const divline_t *line)
|
|
{
|
|
return (pos.Y - line->y) * line->dx + (line->x - pos.X) * line->dy > EQUAL_EPSILON;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// P_MakeDivline
|
|
//
|
|
//==========================================================================
|
|
|
|
inline void P_MakeDivline(const line_t *li, divline_t *dl)
|
|
{
|
|
dl->x = li->v1->fX();
|
|
dl->y = li->v1->fY();
|
|
dl->dx = li->Delta().X;
|
|
dl->dy = li->Delta().Y;
|
|
}
|
|
|
|
struct FLineOpening
|
|
{
|
|
double top;
|
|
double bottom;
|
|
double range;
|
|
double lowfloor;
|
|
sector_t *bottomsec;
|
|
sector_t *topsec;
|
|
FTextureID ceilingpic;
|
|
FTextureID floorpic;
|
|
secplane_t frontfloorplane;
|
|
secplane_t backfloorplane;
|
|
int floorterrain;
|
|
bool touchmidtex;
|
|
bool abovemidtex;
|
|
uint8_t lowfloorthroughportal;
|
|
F3DFloor *topffloor;
|
|
F3DFloor *bottomffloor;
|
|
};
|
|
|
|
static const double LINEOPEN_MIN = -FLT_MAX;
|
|
static const double LINEOPEN_MAX = FLT_MAX;
|
|
|
|
void P_LineOpening(FLineOpening &open, AActor *thing, const line_t *linedef, const DVector2 &xy, const DVector2 *ref = nullptr, int flags = 0);
|
|
inline void P_LineOpening(FLineOpening &open, AActor *thing, const line_t *linedef, const DVector2 &xy, const DVector3 *ref, int flags = 0)
|
|
{
|
|
P_LineOpening(open, thing, linedef, xy, reinterpret_cast<const DVector2*>(ref), flags);
|
|
}
|
|
|
|
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
|
|
{
|
|
// Controls how groups are connected
|
|
enum
|
|
{
|
|
PGA_NoSectorPortals,// only collect line portals
|
|
PGA_CheckPosition, // only collects sector portals at the actual position
|
|
PGA_Full3d, // Goes up and down sector portals at any linedef within the bounding box (this is a lot slower and should only be done if really needed.)
|
|
};
|
|
|
|
enum
|
|
{
|
|
LOWER = 0x4000,
|
|
UPPER = 0x8000,
|
|
FLAT = 0xc000,
|
|
};
|
|
|
|
enum
|
|
{
|
|
MAX_STATIC = 4
|
|
};
|
|
|
|
FPortalGroupArray(int collectionmethod = PGA_CheckPosition)
|
|
{
|
|
method = collectionmethod;
|
|
varused = 0;
|
|
inited = false;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
data.Clear();
|
|
varused = 0;
|
|
inited = false;
|
|
}
|
|
|
|
void Add(uint32_t num)
|
|
{
|
|
if (varused < MAX_STATIC) entry[varused++] = (uint16_t)num;
|
|
else data.Push((uint16_t)num);
|
|
}
|
|
|
|
unsigned Size()
|
|
{
|
|
return varused + data.Size();
|
|
}
|
|
|
|
uint32_t operator[](unsigned index)
|
|
{
|
|
return index < MAX_STATIC ? entry[index] : data[index - MAX_STATIC];
|
|
}
|
|
|
|
bool inited;
|
|
int method;
|
|
|
|
private:
|
|
uint16_t entry[MAX_STATIC];
|
|
uint8_t varused;
|
|
TArray<uint16_t> data;
|
|
};
|
|
|
|
class FBlockLinesIterator
|
|
{
|
|
friend class FMultiBlockLinesIterator;
|
|
FLevelLocals *Level;
|
|
int minx, maxx;
|
|
int miny, maxy;
|
|
|
|
int curx, cury;
|
|
polyblock_t *polyLink;
|
|
int polyIndex;
|
|
int *list;
|
|
|
|
void StartBlock(int x, int y);
|
|
|
|
FBlockLinesIterator(FLevelLocals *l) { Level = l; }
|
|
void init(const FBoundingBox &box);
|
|
public:
|
|
FBlockLinesIterator(FLevelLocals *Level, int minx, int miny, int maxx, int maxy, bool keepvalidcount = false);
|
|
FBlockLinesIterator(FLevelLocals *Level, const FBoundingBox &box);
|
|
line_t *Next();
|
|
void Reset() { StartBlock(minx, miny); }
|
|
};
|
|
|
|
class FMultiBlockLinesIterator
|
|
{
|
|
FPortalGroupArray &checklist;
|
|
DVector3 checkpoint;
|
|
DVector2 offset;
|
|
sector_t *startsector;
|
|
sector_t *cursector;
|
|
short basegroup;
|
|
short portalflags;
|
|
short index;
|
|
bool continueup;
|
|
bool continuedown;
|
|
FBlockLinesIterator blockIterator;
|
|
FBoundingBox bbox;
|
|
|
|
bool GoUp(double x, double y);
|
|
bool GoDown(double x, double y);
|
|
bool startIteratorForGroup(int group);
|
|
|
|
public:
|
|
|
|
struct CheckResult
|
|
{
|
|
line_t *line;
|
|
DVector3 Position;
|
|
int portalflags;
|
|
};
|
|
|
|
FMultiBlockLinesIterator(FPortalGroupArray &check, AActor *origin, double checkradius = -1);
|
|
FMultiBlockLinesIterator(FPortalGroupArray &check, FLevelLocals *Level, double checkx, double checky, double checkz, double checkh, double checkradius, sector_t *newsec);
|
|
|
|
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;
|
|
}
|
|
const FBoundingBox &Box() const
|
|
{
|
|
return bbox;
|
|
}
|
|
};
|
|
|
|
|
|
class FBlockThingsIterator
|
|
{
|
|
FLevelLocals *Level;
|
|
int minx, maxx;
|
|
int miny, maxy;
|
|
|
|
int curx, cury;
|
|
|
|
FBlockNode *block;
|
|
|
|
int Buckets[32];
|
|
|
|
struct HashEntry
|
|
{
|
|
AActor *Actor;
|
|
int Next;
|
|
};
|
|
HashEntry FixedHash[10];
|
|
int NumFixedHash;
|
|
TArray<HashEntry> DynHash;
|
|
|
|
HashEntry *GetHashEntry(int i) { return i < (int)countof(FixedHash) ? &FixedHash[i] : &DynHash[i - countof(FixedHash)]; }
|
|
|
|
void StartBlock(int x, int y);
|
|
void SwitchBlock(int x, int y);
|
|
void ClearHash();
|
|
|
|
// The following is only for use in the path traverser
|
|
// and therefore declared private.
|
|
FBlockThingsIterator(FLevelLocals *);
|
|
|
|
friend class FPathTraverse;
|
|
friend class FMultiBlockThingsIterator;
|
|
|
|
public:
|
|
FBlockThingsIterator(FLevelLocals *Level, int minx, int miny, int maxx, int maxy);
|
|
FBlockThingsIterator(FLevelLocals *l, const FBoundingBox &box)
|
|
{
|
|
Level = l;
|
|
init(box);
|
|
}
|
|
void init(const FBoundingBox &box);
|
|
AActor *Next(bool centeronly = false);
|
|
void Reset() { StartBlock(minx, miny); }
|
|
};
|
|
|
|
class FMultiBlockThingsIterator
|
|
{
|
|
FPortalGroupArray &checklist;
|
|
DVector3 checkpoint;
|
|
short basegroup;
|
|
short portalflags;
|
|
short index;
|
|
FBlockThingsIterator blockIterator;
|
|
FBoundingBox bbox;
|
|
|
|
void startIteratorForGroup(int group);
|
|
|
|
protected:
|
|
FMultiBlockThingsIterator(FPortalGroupArray &check, FLevelLocals *Level) : checklist(check), blockIterator(Level) {}
|
|
public:
|
|
|
|
struct CheckResult
|
|
{
|
|
AActor *thing;
|
|
DVector3 Position;
|
|
int portalflags;
|
|
};
|
|
|
|
FMultiBlockThingsIterator(FPortalGroupArray &check, AActor *origin, double checkradius = -1, bool ignorerestricted = false);
|
|
FMultiBlockThingsIterator(FPortalGroupArray &check, FLevelLocals *Level, double checkx, double checky, double checkz, double checkh, double checkradius, bool ignorerestricted, sector_t *newsec);
|
|
bool Next(CheckResult *item);
|
|
void Reset();
|
|
const FBoundingBox &Box() const
|
|
{
|
|
return bbox;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
class FPathTraverse
|
|
{
|
|
protected:
|
|
static TArray<intercept_t> intercepts;
|
|
|
|
FLevelLocals *Level;
|
|
divline_t trace;
|
|
double Startfrac;
|
|
unsigned int intercept_index;
|
|
unsigned int intercept_count;
|
|
unsigned int count;
|
|
|
|
virtual void AddLineIntercepts(int bx, int by);
|
|
virtual void AddThingIntercepts(int bx, int by, FBlockThingsIterator &it, bool compatible);
|
|
FPathTraverse(FLevelLocals *l)
|
|
{
|
|
Level = l;
|
|
}
|
|
public:
|
|
|
|
intercept_t *Next();
|
|
|
|
FPathTraverse(FLevelLocals *l, double x1, double y1, double x2, double y2, int flags, double startfrac = 0)
|
|
{
|
|
Level = l;
|
|
init(x1, y1, x2, y2, flags, startfrac);
|
|
}
|
|
void init(double x1, double y1, double x2, double y2, int flags, double startfrac = 0);
|
|
int PortalRelocate(intercept_t *in, int flags, DVector3 *optpos = nullptr);
|
|
void PortalRelocate(const DVector2 &disp, int flags, double hitfrac);
|
|
virtual ~FPathTraverse();
|
|
const divline_t &Trace() const { return trace; }
|
|
|
|
inline DVector2 InterceptPoint(const intercept_t *in)
|
|
{
|
|
return
|
|
{
|
|
trace.x + trace.dx * in->frac,
|
|
trace.y + trace.dy * in->frac
|
|
};
|
|
}
|
|
|
|
};
|
|
|
|
//
|
|
// P_MAPUTL
|
|
//
|
|
|
|
typedef bool(*traverser_t) (intercept_t *in);
|
|
|
|
int P_AproxDistance (int dx, int dy);
|
|
double P_InterceptVector(const divline_t *v2, const divline_t *v1);
|
|
|
|
#define PT_ADDLINES 1
|
|
#define PT_ADDTHINGS 2
|
|
#define PT_COMPATIBLE 4
|
|
#define PT_DELTA 8 // x2,y2 is passed as a delta, not as an endpoint
|
|
|
|
#endif
|