zdbsp/nodebuild_gl.cpp

389 lines
10 KiB
C++
Raw Permalink Normal View History

/*
Routines only necessary for building GL-friendly nodes.
Copyright (C) 2002,2003 Randy Heit
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <assert.h>
#include "zdbsp.h"
#include "nodebuild.h"
#define Printf printf
#if 0
#include <stdio.h>
#define D(x) x
#else
#define D(x) do{}while(0)
#endif
double FNodeBuilder::AddIntersection (const node_t &node, int vertex)
{
static const FEventInfo defaultInfo =
{
-1, DWORD_MAX
};
// Calculate signed distance of intersection vertex from start of splitter.
// Only ordering is important, so we don't need a sqrt.
FPrivVert *v = &Vertices[vertex];
double dx = double(v->x - node.x);
double dy = double(v->y - node.y);
double dist = dx*dx + dy*dy;
if (node.dx != 0)
{
if ((node.dx > 0 && dx < 0.0) || (node.dx < 0 && dx > 0.0))
{
dist = -dist;
}
}
else
{
if ((node.dy > 0 && dy < 0.0) || (node.dy < 0 && dy > 0.0))
{
dist = -dist;
}
}
FEvent *event = Events.FindEvent (dist);
if (event == NULL)
{
event = Events.GetNewNode ();
event->Distance = dist;
event->Info = defaultInfo;
event->Info.Vertex = vertex;
Events.Insert (event);
}
return dist;
}
// If there are any segs on the splitter that span more than two events, they
// must be split. Alien Vendetta is one example wad that is quite bad about
// having overlapping lines. If we skip this step, these segs will still be
// split later, but minisegs will erroneously be added for them, and partner
// seg information will be messed up in the generated tree.
void FNodeBuilder::FixSplitSharers ()
{
for (size_t i = 0; i < SplitSharers.Size(); ++i)
{
DWORD seg = SplitSharers[i].Seg;
int v2 = Segs[seg].v2;
FEvent *event = Events.FindEvent (SplitSharers[i].Distance);
FEvent *next;
if (event == NULL)
{ // Should not happen
continue;
}
if (SplitSharers[i].Forward)
{
event = Events.GetSuccessor (event);
if (event == NULL)
{
continue;
}
next = Events.GetSuccessor (event);
}
else
{
event = Events.GetPredecessor (event);
if (event == NULL)
{
continue;
}
next = Events.GetPredecessor (event);
}
while (event != NULL && next != NULL && event->Info.Vertex != v2)
{
D(Printf("Forced split of seg %d(%d->%d) at %d(%d,%d)\n", seg,
Segs[seg].v1, Segs[seg].v2,
event->Info.Vertex,
Vertices[event->Info.Vertex].x>>16,
Vertices[event->Info.Vertex].y>>16));
DWORD newseg = SplitSeg (seg, event->Info.Vertex, 1);
Segs[newseg].next = Segs[seg].next;
Segs[seg].next = newseg;
DWORD partner = Segs[seg].partner;
if (partner != DWORD_MAX)
{
int endpartner = SplitSeg (partner, event->Info.Vertex, 1);
Segs[endpartner].next = Segs[partner].next;
Segs[partner].next = endpartner;
Segs[seg].partner = endpartner;
//Segs[endpartner].partner = seg;
Segs[partner].partner = newseg;
assert (Segs[Segs[seg].partner].partner == seg);
assert (Segs[Segs[newseg].partner].partner == newseg);
assert (Segs[seg].v1 == Segs[endpartner].v2);
assert (Segs[seg].v2 == Segs[endpartner].v1);
assert (Segs[partner].v1 == Segs[newseg].v2);
assert (Segs[partner].v2 == Segs[newseg].v1);
}
seg = newseg;
if (SplitSharers[i].Forward)
{
event = next;
next = Events.GetSuccessor (next);
}
else
{
event = next;
next = Events.GetPredecessor (next);
}
}
}
}
void FNodeBuilder::AddMinisegs (const node_t &node, DWORD splitseg, DWORD &fset, DWORD &bset)
{
FEvent *event = Events.GetMinimum (), *prev = NULL;
while (event != NULL)
{
if (prev != NULL)
{
DWORD fseg1, bseg1, fseg2, bseg2;
DWORD fnseg, bnseg;
// Minisegs should only be added when they can create valid loops on both the front and
// back of the splitter. This means some subsectors could be unclosed if their sectors
// are unclosed, but at least we won't be needlessly creating subsectors in void space.
// Unclosed subsectors can be closed trivially once the BSP tree is complete.
if ((fseg1 = CheckLoopStart (node.dx, node.dy, prev->Info.Vertex, event->Info.Vertex)) != DWORD_MAX &&
(bseg1 = CheckLoopStart (-node.dx, -node.dy, event->Info.Vertex, prev->Info.Vertex)) != DWORD_MAX &&
(fseg2 = CheckLoopEnd (node.dx, node.dy, event->Info.Vertex)) != DWORD_MAX &&
(bseg2 = CheckLoopEnd (-node.dx, -node.dy, prev->Info.Vertex)) != DWORD_MAX)
{
// Add miniseg on the front side
fnseg = AddMiniseg (prev->Info.Vertex, event->Info.Vertex, DWORD_MAX, fseg1, splitseg);
Segs[fnseg].next = fset;
fset = fnseg;
// Add miniseg on the back side
bnseg = AddMiniseg (event->Info.Vertex, prev->Info.Vertex, fnseg, bseg1, splitseg);
Segs[bnseg].next = bset;
bset = bnseg;
int fsector, bsector;
fsector = Segs[fseg1].frontsector;
bsector = Segs[bseg1].frontsector;
Segs[fnseg].frontsector = fsector;
Segs[fnseg].backsector = bsector;
Segs[bnseg].frontsector = bsector;
Segs[bnseg].backsector = fsector;
// Only print the warning if this might be bad.
if (fsector != bsector &&
fsector != Segs[fseg1].backsector &&
bsector != Segs[bseg1].backsector)
{
Warn ("Sectors %d at (%d,%d) and %d at (%d,%d) don't match.\n",
Segs[fseg1].frontsector,
Vertices[prev->Info.Vertex].x>>FRACBITS, Vertices[prev->Info.Vertex].y>>FRACBITS,
Segs[bseg1].frontsector,
Vertices[event->Info.Vertex].x>>FRACBITS, Vertices[event->Info.Vertex].y>>FRACBITS
);
}
D(Printf ("**Minisegs** %d/%d added %d(%d,%d)->%d(%d,%d)\n", fnseg, bnseg,
prev->Info.Vertex,
Vertices[prev->Info.Vertex].x>>16, Vertices[prev->Info.Vertex].y>>16,
event->Info.Vertex,
Vertices[event->Info.Vertex].x>>16, Vertices[event->Info.Vertex].y>>16));
}
}
prev = event;
event = Events.GetSuccessor (event);
}
}
DWORD FNodeBuilder::AddMiniseg (int v1, int v2, DWORD partner, DWORD seg1, DWORD splitseg)
{
DWORD nseg;
FPrivSeg *seg = &Segs[seg1];
FPrivSeg newseg;
newseg.sidedef = NO_INDEX;
newseg.linedef = -1;
newseg.loopnum = 0;
newseg.next = DWORD_MAX;
newseg.planefront = true;
if (splitseg != DWORD_MAX)
{
newseg.planenum = Segs[splitseg].planenum;
}
else
{
newseg.planenum = -1;
}
newseg.v1 = v1;
newseg.v2 = v2;
newseg.nextforvert = Vertices[v1].segs;
newseg.nextforvert2 = Vertices[v2].segs2;
newseg.next = seg->next;
if (partner != DWORD_MAX)
{
newseg.partner = partner;
assert (Segs[partner].v1 == newseg.v2);
assert (Segs[partner].v2 == newseg.v1);
}
else
{
newseg.partner = DWORD_MAX;
}
nseg = Segs.Push (newseg);
if (newseg.partner != DWORD_MAX)
{
Segs[partner].partner = nseg;
}
Vertices[v1].segs = nseg;
Vertices[v2].segs2 = nseg;
//Printf ("Between %d and %d::::\n", seg1, seg2);
return nseg;
}
DWORD FNodeBuilder::CheckLoopStart (fixed_t dx, fixed_t dy, int vertex, int vertex2)
{
FPrivVert *v = &Vertices[vertex];
angle_t splitAngle = PointToAngle (dx, dy);
DWORD segnum;
angle_t bestang;
DWORD bestseg;
// Find the seg ending at this vertex that forms the smallest angle
// to the splitter.
segnum = v->segs2;
bestang = ANGLE_MAX;
bestseg = DWORD_MAX;
while (segnum != DWORD_MAX)
{
FPrivSeg *seg = &Segs[segnum];
angle_t segAngle = PointToAngle (Vertices[seg->v1].x - v->x, Vertices[seg->v1].y - v->y);
angle_t diff = splitAngle - segAngle;
if (diff < ANGLE_EPSILON &&
PointOnSide (Vertices[seg->v1].x, Vertices[seg->v1].y, v->x, v->y, dx, dy) == 0)
{
// If a seg lies right on the splitter, don't count it
}
else
{
if (diff <= bestang)
{
bestang = diff;
bestseg = segnum;
}
}
segnum = seg->nextforvert2;
}
if (bestseg == DWORD_MAX)
{
return DWORD_MAX;
}
// Now make sure there are no segs starting at this vertex that form
// an even smaller angle to the splitter.
segnum = v->segs;
while (segnum != DWORD_MAX)
{
FPrivSeg *seg = &Segs[segnum];
if (seg->v2 == vertex2)
{
return DWORD_MAX;
}
angle_t segAngle = PointToAngle (Vertices[seg->v2].x - v->x, Vertices[seg->v2].y - v->y);
angle_t diff = splitAngle - segAngle;
if (diff < bestang && seg->partner != bestseg)
{
return DWORD_MAX;
}
segnum = seg->nextforvert;
}
return bestseg;
}
DWORD FNodeBuilder::CheckLoopEnd (fixed_t dx, fixed_t dy, int vertex)
{
FPrivVert *v = &Vertices[vertex];
angle_t splitAngle = PointToAngle (dx, dy) + ANGLE_180;
DWORD segnum;
angle_t bestang;
DWORD bestseg;
// Find the seg starting at this vertex that forms the smallest angle
// to the splitter.
segnum = v->segs;
bestang = ANGLE_MAX;
bestseg = DWORD_MAX;
while (segnum != DWORD_MAX)
{
FPrivSeg *seg = &Segs[segnum];
angle_t segAngle = PointToAngle (Vertices[seg->v2].x - v->x, Vertices[seg->v2].y - v->y);
angle_t diff = segAngle - splitAngle;
if (diff < ANGLE_EPSILON &&
PointOnSide (Vertices[seg->v1].x, Vertices[seg->v1].y, v->x, v->y, dx, dy) == 0)
{
// If a seg lies right on the splitter, don't count it
}
else
{
if (diff <= bestang)
{
bestang = diff;
bestseg = segnum;
}
}
segnum = seg->nextforvert;
}
if (bestseg == DWORD_MAX)
{
return DWORD_MAX;
}
// Now make sure there are no segs ending at this vertex that form
// an even smaller angle to the splitter.
segnum = v->segs2;
while (segnum != DWORD_MAX)
{
FPrivSeg *seg = &Segs[segnum];
angle_t segAngle = PointToAngle (Vertices[seg->v1].x - v->x, Vertices[seg->v1].y - v->y);
angle_t diff = segAngle - splitAngle;
if (diff < bestang && seg->partner != bestseg)
{
return DWORD_MAX;
}
segnum = seg->nextforvert2;
}
return bestseg;
}