mirror of
https://github.com/ZDoom/zdbsp.git
synced 2024-11-10 14:41:38 +00:00
279 lines
6.8 KiB
Text
279 lines
6.8 KiB
Text
|
// This is the same algorithm used by DoomBSP:
|
||
|
//
|
||
|
// Represent each sector by its bounding box. Then for each pair of
|
||
|
// sectors, see if any chains of one-sided lines can walk from one
|
||
|
// side of the convex hull for that pair to the other side.
|
||
|
//
|
||
|
// It works, but it's far from being perfect. It's quite easy for
|
||
|
// this algorithm to consider two sectors as being visible from
|
||
|
// each other when they are really not. But it won't erroneously
|
||
|
// flag two sectors as obstructed when they're really not, and that's
|
||
|
// the only thing that really matters when building a REJECT lump.
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#include "zdbsp.h"
|
||
|
#include "nodebuild.h"
|
||
|
#include "rejectbuilder.h"
|
||
|
#include "templates.h"
|
||
|
|
||
|
FRejectBuilder::FRejectBuilder (FLevel &level)
|
||
|
: Level (level)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
i = Level.NumGLSubsectors * Level.NumGLSubsectors;
|
||
|
SubSeeMatrix = new BYTE[i];
|
||
|
memset (SubSeeMatrix, 0, i);
|
||
|
|
||
|
SegSubsectors = new WORD[Level.NumGLSegs];
|
||
|
for (i = 0; i < Level.NumGLSubsectors; ++i)
|
||
|
{
|
||
|
for (int j = 0; j < Level.GLSubsectors[i].numlines; ++j)
|
||
|
{
|
||
|
SegSubsectors[j + Level.GLSubsectors[i].firstline] = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BuildReject ();
|
||
|
}
|
||
|
|
||
|
FRejectBuilder::~FRejectBuilder ()
|
||
|
{
|
||
|
delete[] SubSeeMatrix;
|
||
|
delete[] SegSubsectors;
|
||
|
}
|
||
|
|
||
|
BYTE *FRejectBuilder::GetReject ()
|
||
|
{
|
||
|
int i, j;
|
||
|
|
||
|
int rejectSize = (Level.NumSectors*Level.NumSectors + 7) / 8;
|
||
|
BYTE *reject = new BYTE[rejectSize];
|
||
|
memset (reject, 0xff, rejectSize);
|
||
|
|
||
|
int pvs_size = (Level.NumGLSubsectors * Level.NumGLSubsectors) + 7 / 8;
|
||
|
Level.GLPVS = new BYTE[pvs_size];
|
||
|
Level.GLPVSSize = pvs_size;
|
||
|
memset (Level.GLPVS, 0, pvs_size);
|
||
|
|
||
|
for (i = 0; i < Level.NumGLSubsectors; ++i)
|
||
|
{
|
||
|
int row = i*Level.NumGLSubsectors;
|
||
|
int sector1 =
|
||
|
Level.Sides[
|
||
|
Level.Lines[
|
||
|
Level.GLSegs[
|
||
|
Level.GLSubsectors[i].firstline].linedef]
|
||
|
.sidenum[
|
||
|
Level.GLSegs[
|
||
|
Level.GLSubsectors[i].firstline].side]]
|
||
|
.sector;
|
||
|
int srow = sector1*Level.NumSectors;
|
||
|
for (j = 0; j < Level.NumGLSubsectors; ++j)
|
||
|
{
|
||
|
if (SubSeeMatrix[row + j])
|
||
|
{
|
||
|
int sector2 =
|
||
|
Level.Sides[
|
||
|
Level.Lines[
|
||
|
Level.GLSegs[
|
||
|
Level.GLSubsectors[j].firstline].linedef]
|
||
|
.sidenum[
|
||
|
Level.GLSegs[
|
||
|
Level.GLSubsectors[j].firstline].side]]
|
||
|
.sector;
|
||
|
int l = (srow + sector2) >> 3;
|
||
|
int r = (srow + sector2) & 7;
|
||
|
reject[l] &= ~(1 << r);
|
||
|
|
||
|
l = (row + j) >> 3;
|
||
|
r = (row + j) & 7;
|
||
|
Level.GLPVS[l] |= 1 << r;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return reject;
|
||
|
}
|
||
|
|
||
|
void FRejectBuilder::BuildReject ()
|
||
|
{
|
||
|
int s1, s2;
|
||
|
|
||
|
for (s1 = 0; s1 < Level.NumGLSubsectors; ++s1)
|
||
|
{
|
||
|
//printf (" Reject: %3d%%\r",s1*100/Level.NumGLSubsectors);
|
||
|
printf ("%d/%d\r", s1, Level.NumGLSubsectors);
|
||
|
|
||
|
// A subsector can always see itself
|
||
|
SourceRow = s1 * Level.NumGLSubsectors;
|
||
|
SubSeeMatrix[SourceRow + s1] = 1;
|
||
|
|
||
|
WORD pusher = s1;
|
||
|
|
||
|
for (s2 = 0; s2 < Level.GLSubsectors[s1].numlines; ++s2)
|
||
|
{
|
||
|
int segnum = s2 + Level.GLSubsectors[s1].firstline;
|
||
|
const MapSegGL *seg = &Level.GLSegs[segnum];
|
||
|
if (seg->partner == NO_INDEX)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
SourceSeg = segnum;
|
||
|
SegRow = segnum * Level.NumGLSegs;
|
||
|
|
||
|
TracePath (s1, seg);
|
||
|
}
|
||
|
}
|
||
|
printf (" Reject: 100%%\n");
|
||
|
}
|
||
|
|
||
|
inline const WideVertex *FRejectBuilder::GetVertex (WORD vertnum)
|
||
|
{
|
||
|
if (vertnum & 0x8000)
|
||
|
{
|
||
|
return &Level.GLVertices[vertnum & 0x7fff];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return &Level.Vertices[vertnum];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FRejectBuilder::TracePath (int subsector, const MapSegGL *window)
|
||
|
{
|
||
|
// Neighboring subsectors can always see each other
|
||
|
SubSeeMatrix[SourceRow + SegSubsectors[window->partner]] = 1;
|
||
|
const MapSubsector *backsub = &Level.GLSubsectors[SegSubsectors[window->partner]];
|
||
|
|
||
|
Portal source;
|
||
|
|
||
|
source.Subsector = backsub;
|
||
|
source.Left = GetVertex (window->v1);
|
||
|
source.Right = GetVertex (window->v2);
|
||
|
|
||
|
PortalStack.Clear ();
|
||
|
PortalStack.Push (source);
|
||
|
|
||
|
fixed_t wdx = source.Right->x - source.Left->x;
|
||
|
fixed_t wdy = source.Right->y - source.Left->y;
|
||
|
|
||
|
// printf ("start window %d\n", window - Level.GLSegs);
|
||
|
|
||
|
for (int i = 0; i < backsub->numlines; ++i)
|
||
|
{
|
||
|
int segnum = backsub->firstline + i;
|
||
|
|
||
|
if (segnum == window->partner)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const MapSegGL *cseg = &Level.GLSegs[segnum];
|
||
|
|
||
|
if (cseg->partner == NO_INDEX)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const WideVertex *cv1 = GetVertex (cseg->v1);
|
||
|
const WideVertex *cv2 = GetVertex (cseg->v2);
|
||
|
|
||
|
if (FNodeBuilder::PointOnSide (cv1->x, cv1->y, source.Left->x, source.Left->y, wdx, wdy) == 0 &&
|
||
|
FNodeBuilder::PointOnSide (cv2->x, cv2->y, source.Left->x, source.Left->y, wdx, wdy) == 0)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
TracePathDeep (cseg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FRejectBuilder::TracePathDeep (const MapSegGL *window)
|
||
|
{
|
||
|
SubSeeMatrix[SourceRow + SegSubsectors[window->partner]] = 1;
|
||
|
const MapSubsector *backsub = &Level.GLSubsectors[SegSubsectors[window->partner]];
|
||
|
size_t j;
|
||
|
|
||
|
for (j = PortalStack.Size(); j-- > 0; )
|
||
|
{
|
||
|
if (PortalStack[j].Subsector == backsub)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Portal entrance;
|
||
|
|
||
|
entrance.Subsector = backsub;
|
||
|
entrance.Left = GetVertex (window->v1);
|
||
|
entrance.Right = GetVertex (window->v2);
|
||
|
PortalStack.Push (entrance);
|
||
|
|
||
|
fixed_t wdx = entrance.Right->x - entrance.Left->x;
|
||
|
fixed_t wdy = entrance.Right->y - entrance.Left->y;
|
||
|
|
||
|
//printf ("deep through %d\n", window - Level.GLSegs);
|
||
|
|
||
|
for (int i = 0; i < backsub->numlines; ++i)
|
||
|
{
|
||
|
int segnum = backsub->firstline + i;
|
||
|
|
||
|
if (segnum == window->partner)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const MapSegGL *cseg = &Level.GLSegs[segnum];
|
||
|
|
||
|
if (cseg->partner == NO_INDEX)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const WideVertex *cv1 = GetVertex (cseg->v1);
|
||
|
const WideVertex *cv2 = GetVertex (cseg->v2);
|
||
|
|
||
|
if (FNodeBuilder::PointOnSide (cv1->x, cv1->y, entrance.Left->x, entrance.Left->y, wdx, wdy) <= 0 &&
|
||
|
FNodeBuilder::PointOnSide (cv2->x, cv2->y, entrance.Left->x, entrance.Left->y, wdx, wdy) <= 0)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
fixed_t leftx = PortalStack[0].Left->x;
|
||
|
fixed_t lefty = PortalStack[0].Left->y;
|
||
|
fixed_t rightx = PortalStack[0].Right->x;
|
||
|
fixed_t righty = PortalStack[0].Right->y;
|
||
|
|
||
|
fixed_t leftdx = cv1->x - leftx;
|
||
|
fixed_t leftdy = cv1->y - lefty;
|
||
|
fixed_t rightdx = rightx - cv2->x;
|
||
|
fixed_t rightdy = righty - cv2->y;
|
||
|
|
||
|
if (FNodeBuilder::PointOnSide (cv1->x, cv1->y, rightx, righty, rightdx, rightdy) >= 0 ||
|
||
|
FNodeBuilder::PointOnSide (cv2->x, cv2->y, leftx, lefty, leftdx, leftdy) >= 0)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
for (j = PortalStack.Size(); j-- > 1; )
|
||
|
{
|
||
|
if (FNodeBuilder::PointOnSide (PortalStack[j].Left->x, PortalStack[j].Left->y,
|
||
|
rightx, righty, rightdx, rightdy) >= 0 ||
|
||
|
FNodeBuilder::PointOnSide (PortalStack[j].Right->x, PortalStack[j].Right->y,
|
||
|
leftx, lefty, leftdx, leftdy) >= 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (j == 0)
|
||
|
{
|
||
|
TracePathDeep (cseg);
|
||
|
}
|
||
|
}
|
||
|
PortalStack.Pop (entrance);
|
||
|
}
|