- Merged in recent ZDBSP fixes:

- Added code to explicitly handle outputting overlapping segs when
   building GL nodes with ZDBSP, removing the check that discarded
   them early on.
 - AddIntersection() should convert to doubles before subtracting the vertex
   from the node, not after, to avoid integer overflow. (See cah.wad, MAP12
   and MAP13.) A simpler dot product will also suffice for distance calculation.
 - Splitters that come too close to a vertex should be avoided. (See cata.wad.)
 - Red-Black Tree implementation was broken and colored every node red.
 - Moved most of the code for outputting degenerate GL subsectors into another
   function.


SVN r160 (trunk)
This commit is contained in:
Randy Heit 2006-06-01 01:43:16 +00:00
commit 4325fb8993
7 changed files with 202 additions and 280 deletions

View file

@ -88,6 +88,7 @@ void FNodeBuilder::BuildTree ()
C_InitTicker ("Building BSP", FRACUNIT);
HackSeg = DWORD_MAX;
HackMate = DWORD_MAX;
CreateNode (0, bbox);
CreateSubsectorsForReal ();
C_InitTicker (NULL, 0);
@ -320,7 +321,11 @@ bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int
} while (seg != DWORD_MAX);
if (seg == DWORD_MAX)
{ // It's a valid subsector
{ // It's a valid non-GL subsector, and probably a valid GL subsector too.
if (GLNodes)
{
return CheckSubsectorOverlappingSegs (set, node, splitseg);
}
return false;
}
@ -331,20 +336,60 @@ bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int
// from multiple sectors, and it seems ZenNode does something
// similar. It is the only technique I could find that makes the
// "transparent water" in nb_bmtrk.wad work properly.
//
// The seg is marked to indicate that it should be forced to the
// back of the splitter. Because these segs already form a convex
// set, all the other segs will be in front of the splitter. Since
// the splitter is formed from this seg, the back of the splitter
// will have a one-dimensional subsector. SplitSegs() will add two
// new minisegs to close it: one seg replaces this one on the front
// of the splitter, and the other is its partner on the back.
//
// Old code that will actually create valid two-dimensional sectors
// is included below for reference but is not likely to be used again.
return ShoveSegBehind (set, node, seg, DWORD_MAX);
}
// When creating GL nodes, we need to check for segs with the same start and
// end vertices and split them into two subsectors.
bool FNodeBuilder::CheckSubsectorOverlappingSegs (DWORD set, node_t &node, DWORD &splitseg)
{
int v1, v2;
DWORD seg1, seg2;
for (seg1 = set; seg1 != DWORD_MAX; seg1 = Segs[seg1].next)
{
if (Segs[seg1].linedef == -1)
{ // Do not check minisegs.
continue;
}
v1 = Segs[seg1].v1;
v2 = Segs[seg1].v2;
for (seg2 = Segs[seg1].next; seg2 != DWORD_MAX; seg2 = Segs[seg2].next)
{
if (Segs[seg2].v1 == v1 && Segs[seg2].v2 == v2)
{
if (Segs[seg2].linedef == -1)
{ // Do not put minisegs into a new subsector.
swap (seg1, seg2);
}
D(Printf("Need to synthesize a splitter for set %d on seg %d (ov)\n", set, seg2));
splitseg = DWORD_MAX;
return ShoveSegBehind (set, node, seg2, seg1);
}
}
}
// It really is a good subsector.
return false;
}
// The seg is marked to indicate that it should be forced to the
// back of the splitter. Because these segs already form a convex
// set, all the other segs will be in front of the splitter. Since
// the splitter is formed from this seg, the back of the splitter
// will have a one-dimensional subsector. SplitSegs() will add one
// or two new minisegs to close it: If mate is DWORD_MAX, then a
// new seg is created to replace this one on the front of the
// splitter. Otherwise, mate takes its place. In either case, the
// seg in front of the splitter is partnered with a new miniseg on
// the back so that the back will have two segs.
bool FNodeBuilder::ShoveSegBehind (DWORD set, node_t &node, DWORD seg, DWORD mate)
{
SetNodeFromSeg (node, &Segs[seg]);
HackSeg = seg;
HackMate = mate;
if (!Segs[seg].planefront)
{
node.x += node.dx;
@ -353,79 +398,6 @@ bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int
node.dy = -node.dy;
}
return Heuristic (node, set, false) != 0;
#if 0
// If there are only two segs in the set, and they form two sides
// of a triangle, the splitter should pass through their shared
// point and the (imaginary) third side of the triangle
if (setsize == 2)
{
FPrivVert *v1, *v2, *v3;
if (Vertices[Segs[set].v2] == Vertices[Segs[seg].v1])
{
v1 = &Vertices[Segs[set].v1];
v2 = &Vertices[Segs[seg].v2];
v3 = &Vertices[Segs[set].v2];
}
else if (Vertices[Segs[set].v1] == Vertices[Segs[seg].v2])
{
v1 = &Vertices[Segs[seg].v1];
v2 = &Vertices[Segs[set].v2];
v3 = &Vertices[Segs[seg].v2];
}
else
{
v1 = v2 = v3 = NULL;
}
if (v1 != NULL)
{
node.x = v3->x;
node.y = v3->y;
node.dx = v1->x + (v2->x-v1->x)/2 - node.x;
node.dy = v1->y + (v2->y-v1->y)/2 - node.y;
return Heuristic (node, set, false) != 0;
}
}
bool nosplit = true;
int firsthit = seg;
do
{
seg = firsthit;
do
{
if (Segs[seg].linedef != -1 &&
Segs[seg].frontsector != sec &&
Segs[seg].frontsector == Segs[seg].backsector)
{
node.x = Vertices[Segs[set].v1].x;
node.y = Vertices[Segs[set].v1].y;
node.dx = Vertices[Segs[seg].v2].x - node.x;
node.dy = Vertices[Segs[seg].v2].y - node.y;
if (Heuristic (node, set, nosplit) != 0)
{
return true;
}
node.dx = Vertices[Segs[seg].v1].x - node.x;
node.dy = Vertices[Segs[seg].v1].y - node.y;
if (Heuristic (node, set, nosplit) != 0)
{
return true;
}
}
seg = Segs[seg].next;
} while (seg != DWORD_MAX);
} while ((nosplit ^= 1) == 0);
// Give up.
return false;
#endif
}
// Splitters are chosen to coincide with segs in the given set. To reduce the
@ -525,6 +497,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
int side;
bool splitter = false;
unsigned int max, m2, p, q;
double frac;
Touched.Clear ();
Colinear.Clear ();
@ -616,6 +589,13 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
}
}
// Splitters that are too close to a vertex are bad.
frac = InterceptVector (node, *test);
if (frac < 0.001 || frac > 0.999)
{
score -= int(1 / frac);
}
counts[0]++;
counts[1]++;
if (test->linedef != -1)
@ -900,15 +880,24 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
DWORD newback, newfront;
newback = AddMiniseg (seg->v2, seg->v1, DWORD_MAX, set, splitseg);
newfront = AddMiniseg (Segs[set].v1, Segs[set].v2, newback, set, splitseg);
if (HackMate == DWORD_MAX)
{
newfront = AddMiniseg (Segs[set].v1, Segs[set].v2, newback, set, splitseg);
Segs[newfront].next = outset0;
outset0 = newfront;
}
else
{
newfront = HackMate;
Segs[newfront].partner = newback;
Segs[newback].partner = newfront;
}
Segs[newback].frontsector = Segs[newback].backsector =
Segs[newfront].frontsector = Segs[newfront].backsector =
Segs[set].frontsector;
Segs[newback].next = outset1;
outset1 = newback;
Segs[newfront].next = outset0;
outset0 = newfront;
}
set = next;
}