- Merge BSP-able polyobjects back into the trunk.

SVN r2480 (trunk)
This commit is contained in:
Randy Heit 2010-08-01 02:41:56 +00:00
parent 8d5ca6501a
commit 677d07f837
17 changed files with 1242 additions and 810 deletions

View file

@ -64,13 +64,6 @@ set( CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${REL_C_FLAGS}" )
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${REL_C_FLAGS}" ) set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${REL_C_FLAGS}" )
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEB_C_FLAGS} -D_DEBUG" ) set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEB_C_FLAGS} -D_DEBUG" )
if( CMAKE_COMPILER_IS_GNUCXX AND PROFILE )
set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" )
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" )
set( CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -pg" )
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -pg" )
endif( CMAKE_COMPILER_IS_GNUCXX AND PROFILE )
if( ZLIB_FOUND ) if( ZLIB_FOUND )
message( STATUS "Using system zlib" ) message( STATUS "Using system zlib" )
else( ZLIB_FOUND ) else( ZLIB_FOUND )

View file

@ -330,6 +330,13 @@ endif( NOT NO_ASM )
# Set up flags for GCC # Set up flags for GCC
if( CMAKE_COMPILER_IS_GNUCXX ) if( CMAKE_COMPILER_IS_GNUCXX )
if( PROFILE )
set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" )
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" )
set( CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -pg" )
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -pg" )
endif( PROFILE )
set( REL_CXX_FLAGS "-fno-rtti" ) set( REL_CXX_FLAGS "-fno-rtti" )
if( NOT PROFILE ) if( NOT PROFILE )
set( REL_CXX_FLAGS "${REL_CXX_FLAGS} -fomit-frame-pointer" ) set( REL_CXX_FLAGS "${REL_CXX_FLAGS} -fomit-frame-pointer" )

View file

@ -1672,6 +1672,22 @@ void AM_drawSeg(seg_t *seg, const AMColor &color)
AM_drawMline(&l, color); AM_drawMline(&l, color);
} }
void AM_drawPolySeg(FPolySeg *seg, const AMColor &color)
{
mline_t l;
l.a.x = seg->v1.x >> FRACTOMAPBITS;
l.a.y = seg->v1.y >> FRACTOMAPBITS;
l.b.x = seg->v2.x >> FRACTOMAPBITS;
l.b.y = seg->v2.y >> FRACTOMAPBITS;
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
AM_rotatePoint (&l.a.x, &l.a.y);
AM_rotatePoint (&l.b.x, &l.b.y);
}
AM_drawMline(&l, color);
}
void AM_showSS() void AM_showSS()
{ {
if (am_showsubsector >= 0 && am_showsubsector < numsubsectors) if (am_showsubsector >= 0 && am_showsubsector < numsubsectors)
@ -1682,13 +1698,13 @@ void AM_showSS()
red.FromRGB(255,0,0); red.FromRGB(255,0,0);
subsector_t *sub = &subsectors[am_showsubsector]; subsector_t *sub = &subsectors[am_showsubsector];
for(unsigned int i=0;i<sub->numlines;i++) for (unsigned int i = 0; i < sub->numlines; i++)
{ {
AM_drawSeg(&segs[sub->firstline+i], yellow); AM_drawSeg(sub->firstline + i, yellow);
} }
PO_LinkToSubsectors(); PO_LinkToSubsectors();
for(int i=0;i<po_NumPolyobjs;i++) for (int i = 0; i <po_NumPolyobjs; i++)
{ {
FPolyObj *po = &polyobjs[i]; FPolyObj *po = &polyobjs[i];
FPolyNode *pnode = po->subsectorlinks; FPolyNode *pnode = po->subsectorlinks;
@ -1697,9 +1713,9 @@ void AM_showSS()
{ {
if (pnode->subsector == sub) if (pnode->subsector == sub)
{ {
for(unsigned j=0;j<pnode->segs.Size();j++) for (unsigned j = 0; j < pnode->segs.Size(); j++)
{ {
AM_drawSeg(&pnode->segs[j], red); AM_drawPolySeg(&pnode->segs[j], red);
} }
} }
pnode = pnode->snext; pnode = pnode->snext;

View file

@ -62,10 +62,17 @@ const int AAPreference = 16;
#define D(x) do{}while(0) #define D(x) do{}while(0)
#endif #endif
FNodeBuilder::FNodeBuilder(FLevel &level)
: Level(level), GLNodes(false), SegsStuffed(0)
{
VertexMap = NULL;
}
FNodeBuilder::FNodeBuilder (FLevel &level, FNodeBuilder::FNodeBuilder (FLevel &level,
TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors, TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors,
bool makeGLNodes, bool enableSSE2) bool makeGLNodes)
: Level(level), GLNodes(makeGLNodes), EnableSSE2(enableSSE2), SegsStuffed(0) : Level(level), GLNodes(makeGLNodes), SegsStuffed(0)
{ {
VertexMap = new FVertexMap (*this, Level.MinX, Level.MinY, Level.MaxX, Level.MaxY); VertexMap = new FVertexMap (*this, Level.MinX, Level.MinY, Level.MaxX, Level.MaxY);
FindUsedVertices (Level.Vertices, Level.NumVertices); FindUsedVertices (Level.Vertices, Level.NumVertices);
@ -83,6 +90,33 @@ FNodeBuilder::~FNodeBuilder()
} }
} }
void FNodeBuilder::BuildMini(bool makeGLNodes)
{
GLNodes = makeGLNodes;
GroupSegPlanesSimple();
BuildTree();
}
void FNodeBuilder::Clear()
{
SegsStuffed = 0;
Nodes.Clear();
Subsectors.Clear();
SubsectorSets.Clear();
Segs.Clear();
Vertices.Clear();
SegList.Clear();
PlaneChecked.Clear();
Planes.Clear();
Touched.Clear();
Colinear.Clear();
SplitSharers.Clear();
if (VertexMap == NULL)
{
VertexMap = new FVertexMapSimple(*this);
}
}
void FNodeBuilder::BuildTree () void FNodeBuilder::BuildTree ()
{ {
fixed_t bbox[4]; fixed_t bbox[4];
@ -90,35 +124,38 @@ void FNodeBuilder::BuildTree ()
C_InitTicker ("Building BSP", FRACUNIT); C_InitTicker ("Building BSP", FRACUNIT);
HackSeg = DWORD_MAX; HackSeg = DWORD_MAX;
HackMate = DWORD_MAX; HackMate = DWORD_MAX;
CreateNode (0, bbox); CreateNode (0, Segs.Size(), bbox);
CreateSubsectorsForReal (); CreateSubsectorsForReal ();
C_InitTicker (NULL, 0); C_InitTicker (NULL, 0);
} }
int FNodeBuilder::CreateNode (DWORD set, fixed_t bbox[4]) int FNodeBuilder::CreateNode (DWORD set, unsigned int count, fixed_t bbox[4])
{ {
node_t node; node_t node;
int skip, count, selstat; int skip, selstat;
DWORD splitseg; DWORD splitseg;
count = CountSegs (set); skip = int(count / MaxSegs);
skip = count / MaxSegs;
// When building GL nodes, count may not be an exact count of the number of segs
// in the set. That's okay, because we just use it to get a skip count, so an
// estimate is fine.
if ((selstat = SelectSplitter (set, node, splitseg, skip, true)) > 0 || if ((selstat = SelectSplitter (set, node, splitseg, skip, true)) > 0 ||
(skip > 0 && (selstat = SelectSplitter (set, node, splitseg, 1, true)) > 0) || (skip > 0 && (selstat = SelectSplitter (set, node, splitseg, 1, true)) > 0) ||
(selstat < 0 && (SelectSplitter (set, node, splitseg, skip, false) > 0 || (selstat < 0 && (SelectSplitter (set, node, splitseg, skip, false) > 0 ||
(skip > 0 && SelectSplitter (set, node, splitseg, 1, false)))) || (skip > 0 && SelectSplitter (set, node, splitseg, 1, false)))) ||
CheckSubsector (set, node, splitseg, count)) CheckSubsector (set, node, splitseg))
{ {
// Create a normal node // Create a normal node
DWORD set1, set2; DWORD set1, set2;
unsigned int count1, count2;
SplitSegs (set, node, splitseg, set1, set2); SplitSegs (set, node, splitseg, set1, set2, count1, count2);
D(PrintSet (1, set1)); D(PrintSet (1, set1));
D(Printf (PRINT_LOG, "(%d,%d) delta (%d,%d) from seg %d\n", node.x>>16, node.y>>16, node.dx>>16, node.dy>>16, splitseg)); D(Printf (PRINT_LOG, "(%d,%d) delta (%d,%d) from seg %d\n", node.x>>16, node.y>>16, node.dx>>16, node.dy>>16, splitseg));
D(PrintSet (2, set2)); D(PrintSet (2, set2));
node.intchildren[0] = CreateNode (set1, node.bbox[0]); node.intchildren[0] = CreateNode (set1, count1, node.bbox[0]);
node.intchildren[1] = CreateNode (set2, node.bbox[1]); node.intchildren[1] = CreateNode (set2, count2, node.bbox[1]);
bbox[BOXTOP] = MAX (node.bbox[0][BOXTOP], node.bbox[1][BOXTOP]); bbox[BOXTOP] = MAX (node.bbox[0][BOXTOP], node.bbox[1][BOXTOP]);
bbox[BOXBOTTOM] = MIN (node.bbox[0][BOXBOTTOM], node.bbox[1][BOXBOTTOM]); bbox[BOXBOTTOM] = MIN (node.bbox[0][BOXBOTTOM], node.bbox[1][BOXBOTTOM]);
bbox[BOXLEFT] = MIN (node.bbox[0][BOXLEFT], node.bbox[1][BOXLEFT]); bbox[BOXLEFT] = MIN (node.bbox[0][BOXLEFT], node.bbox[1][BOXLEFT]);
@ -173,17 +210,15 @@ void FNodeBuilder::CreateSubsectorsForReal ()
subsector_t sub; subsector_t sub;
unsigned int i; unsigned int i;
sub.validcount = 0;
sub.CenterX = 0; // Code in p_setup.cpp will set these for us later.
sub.CenterY = 0;
sub.sector = NULL; sub.sector = NULL;
sub.polys = NULL; sub.polys = NULL;
sub.BSP = NULL;
for (i = 0; i < SubsectorSets.Size(); ++i) for (i = 0; i < SubsectorSets.Size(); ++i)
{ {
DWORD set = SubsectorSets[i]; DWORD set = SubsectorSets[i];
DWORD firstline = (DWORD)SegList.Size();
sub.firstline = (DWORD)SegList.Size();
while (set != DWORD_MAX) while (set != DWORD_MAX)
{ {
USegPtr ptr; USegPtr ptr;
@ -192,14 +227,15 @@ void FNodeBuilder::CreateSubsectorsForReal ()
SegList.Push (ptr); SegList.Push (ptr);
set = ptr.SegPtr->next; set = ptr.SegPtr->next;
} }
sub.numlines = (DWORD)(SegList.Size() - sub.firstline); sub.numlines = (DWORD)(SegList.Size() - firstline);
sub.firstline = (seg_t *)firstline;
// Sort segs by linedef for special effects // Sort segs by linedef for special effects
qsort (&SegList[sub.firstline], sub.numlines, sizeof(USegPtr), SortSegs); qsort (&SegList[firstline], sub.numlines, sizeof(USegPtr), SortSegs);
// Convert seg pointers into indices // Convert seg pointers into indices
D(Printf (PRINT_LOG, "Output subsector %d:\n", Subsectors.Size())); D(Printf (PRINT_LOG, "Output subsector %d:\n", Subsectors.Size()));
for (unsigned int i = sub.firstline; i < SegList.Size(); ++i) for (unsigned int i = firstline; i < SegList.Size(); ++i)
{ {
D(Printf (PRINT_LOG, " Seg %5d%c(%5d,%5d)-(%5d,%5d)\n", SegList[i].SegPtr - &Segs[0], D(Printf (PRINT_LOG, " Seg %5d%c(%5d,%5d)-(%5d,%5d)\n", SegList[i].SegPtr - &Segs[0],
SegList[i].SegPtr->linedef == -1 ? '+' : ' ', SegList[i].SegPtr->linedef == -1 ? '+' : ' ',
@ -273,24 +309,12 @@ int STACK_ARGS FNodeBuilder::SortSegs (const void *a, const void *b)
} }
} }
int FNodeBuilder::CountSegs (DWORD set) const
{
int count = 0;
while (set != DWORD_MAX)
{
count++;
set = Segs[set].next;
}
return count;
}
// Given a set of segs, checks to make sure they all belong to a single // Given a set of segs, checks to make sure they all belong to a single
// sector. If so, false is returned, and they become a subsector. If not, // sector. If so, false is returned, and they become a subsector. If not,
// a splitter is synthesized, and true is returned to continue processing // a splitter is synthesized, and true is returned to continue processing
// down this branch of the tree. // down this branch of the tree.
bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int setsize) bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg)
{ {
sector_t *sec; sector_t *sec;
DWORD seg; DWORD seg;
@ -506,7 +530,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
int realSegs[2] = { 0, 0 }; int realSegs[2] = { 0, 0 };
int specialSegs[2] = { 0, 0 }; int specialSegs[2] = { 0, 0 };
DWORD i = set; DWORD i = set;
int sidev1, sidev2; int sidev[2];
int side; int side;
bool splitter = false; bool splitter = false;
unsigned int max, m2, p, q; unsigned int max, m2, p, q;
@ -525,7 +549,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
} }
else else
{ {
side = ClassifyLine (node, test, sidev1, sidev2); side = ClassifyLine (node, &Vertices[test->v1], &Vertices[test->v2], sidev);
} }
switch (side) switch (side)
{ {
@ -535,9 +559,9 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
// The "right" thing to do in this case is to only reject it if there is // The "right" thing to do in this case is to only reject it if there is
// another nosplit seg from the same sector at this vertex. Note that a line // another nosplit seg from the same sector at this vertex. Note that a line
// that lies exactly on top of the splitter is okay. // that lies exactly on top of the splitter is okay.
if (test->loopnum && honorNoSplit && (sidev1 == 0 || sidev2 == 0)) if (test->loopnum && honorNoSplit && (sidev[0] == 0 || sidev[1] == 0))
{ {
if ((sidev1 | sidev2) != 0) if ((sidev[0] | sidev[1]) != 0)
{ {
max = Touched.Size(); max = Touched.Size();
for (p = 0; p < max; ++p) for (p = 0; p < max; ++p)
@ -735,8 +759,10 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
return score; return score;
} }
void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1) void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1, unsigned int &count0, unsigned int &count1)
{ {
unsigned int _count0 = 0;
unsigned int _count1 = 0;
outset0 = DWORD_MAX; outset0 = DWORD_MAX;
outset1 = DWORD_MAX; outset1 = DWORD_MAX;
@ -749,18 +775,18 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
FPrivSeg *seg = &Segs[set]; FPrivSeg *seg = &Segs[set];
int next = seg->next; int next = seg->next;
int sidev1, sidev2, side; int sidev[2], side;
if (HackSeg == set) if (HackSeg == set)
{ {
HackSeg = DWORD_MAX; HackSeg = DWORD_MAX;
side = 1; side = 1;
sidev1 = sidev2 = 0; sidev[0] = sidev[1] = 0;
hack = true; hack = true;
} }
else else
{ {
side = ClassifyLine (node, seg, sidev1, sidev2); side = ClassifyLine (node, &Vertices[seg->v1], &Vertices[seg->v2], sidev);
hack = false; hack = false;
} }
@ -769,11 +795,13 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
case 0: // seg is entirely in front case 0: // seg is entirely in front
seg->next = outset0; seg->next = outset0;
outset0 = set; outset0 = set;
_count0++;
break; break;
case 1: // seg is entirely in back case 1: // seg is entirely in back
seg->next = outset1; seg->next = outset1;
outset1 = set; outset1 = set;
_count1++;
break; break;
default: // seg needs to be split default: // seg needs to be split
@ -803,18 +831,20 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
Printf("SelectVertexClose selected endpoint of seg %u\n", set); Printf("SelectVertexClose selected endpoint of seg %u\n", set);
} }
seg2 = SplitSeg (set, vertnum, sidev1); seg2 = SplitSeg (set, vertnum, sidev[0]);
Segs[seg2].next = outset0; Segs[seg2].next = outset0;
outset0 = seg2; outset0 = seg2;
Segs[set].next = outset1; Segs[set].next = outset1;
outset1 = set; outset1 = set;
_count0++;
_count1++;
// Also split the seg on the back side // Also split the seg on the back side
if (Segs[set].partner != DWORD_MAX) if (Segs[set].partner != DWORD_MAX)
{ {
int partner1 = Segs[set].partner; int partner1 = Segs[set].partner;
int partner2 = SplitSeg (partner1, vertnum, sidev2); int partner2 = SplitSeg (partner1, vertnum, sidev[1]);
// The newly created seg stays in the same set as the // The newly created seg stays in the same set as the
// back seg because it has not been considered for splitting // back seg because it has not been considered for splitting
// yet. If it had been, then the front seg would have already // yet. If it had been, then the front seg would have already
@ -835,17 +865,17 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
} }
if (side >= 0 && GLNodes) if (side >= 0 && GLNodes)
{ {
if (sidev1 == 0) if (sidev[0] == 0)
{ {
double dist1 = AddIntersection (node, seg->v1); double dist1 = AddIntersection (node, seg->v1);
if (sidev2 == 0) if (sidev[1] == 0)
{ {
double dist2 = AddIntersection (node, seg->v2); double dist2 = AddIntersection (node, seg->v2);
FSplitSharer share = { dist1, set, dist2 > dist1 }; FSplitSharer share = { dist1, set, dist2 > dist1 };
SplitSharers.Push (share); SplitSharers.Push (share);
} }
} }
else if (sidev2 == 0) else if (sidev[1] == 0)
{ {
AddIntersection (node, seg->v2); AddIntersection (node, seg->v2);
} }
@ -881,6 +911,8 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
{ {
AddMinisegs (node, splitseg, outset0, outset1); AddMinisegs (node, splitseg, outset0, outset1);
} }
count0 = _count0;
count1 = _count1;
} }
void FNodeBuilder::SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const void FNodeBuilder::SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const
@ -1038,3 +1070,92 @@ void FNodeBuilder::PrintSet (int l, DWORD set)
} }
Printf (PRINT_LOG, "*\n"); Printf (PRINT_LOG, "*\n");
} }
#ifdef BACKPATCH
#ifdef _WIN32
extern "C" {
__declspec(dllimport) int __stdcall VirtualProtect(void *, unsigned long, unsigned long, unsigned long *);
}
#define PAGE_EXECUTE_READWRITE 64
#else
#include <sys/mman.h>
#include <limits.h>
#endif
#ifdef __GNUC__
extern "C" int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
#else
static int *CallerOffset;
int ClassifyLineBackpatchC (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
#endif
{
// Select the routine based on SSE2 availability and patch the caller so that
// they call that routine directly next time instead of going through here.
int *calleroffset;
int diff;
int (*func)(node_t &, const FSimpleVert *, const FSimpleVert *, int[2]);
#ifdef __GNUC__
calleroffset = (int *)__builtin_return_address(0);
#else
calleroffset = CallerOffset;
#endif
// printf ("Patching for SSE %d @ %p %d\n", SSELevel, calleroffset, *calleroffset);
if (CPU.bSSE2)
{
func = ClassifyLineSSE2;
diff = (char *)ClassifyLineSSE2 - (char *)calleroffset;
}
else
{
func = ClassifyLine2;
diff = (char *)ClassifyLine2 - (char *)calleroffset;
}
calleroffset--;
// Patch the caller.
#ifdef _WIN32
unsigned long oldprotect;
if (VirtualProtect (calleroffset, 4, PAGE_EXECUTE_READWRITE, &oldprotect))
#else
// must make this page-aligned for mprotect
long pagesize = sysconf(_SC_PAGESIZE);
char *callerpage = (char *)((intptr_t)calleroffset & ~(pagesize - 1));
size_t protectlen = (intptr_t)calleroffset + sizeof(void*) - (intptr_t)callerpage;
int ptect;
if (!(ptect = mprotect(callerpage, protectlen, PROT_READ|PROT_WRITE|PROT_EXEC)))
#endif
{
*calleroffset = diff;
#ifdef _WIN32
VirtualProtect (calleroffset, sizeof(void*), oldprotect, &oldprotect);
#else
mprotect(callerpage, protectlen, PROT_READ|PROT_EXEC);
#endif
}
// And return by calling the real function.
return func (node, v1, v2, sidev);
}
#ifndef __GNUC__
// The ClassifyLineBackpatch() function here is a stub that uses inline assembly and nakedness
// to retrieve the return address of the stack before sending control to the real
// ClassifyLineBackpatchC() function. Since BACKPATCH shouldn't be defined on 64-bit builds,
// we're okay that VC++ can't do inline assembly on that target.
extern "C" __declspec(noinline) __declspec(naked) int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
{
// We store the return address in a global, so as not to need to mess with the parameter list.
__asm
{
mov eax, [esp]
mov CallerOffset, eax
jmp ClassifyLineBackpatchC
}
}
#endif
#endif

View file

@ -1,6 +1,10 @@
#include "doomdata.h" #include "doomdata.h"
#include "tarray.h" #include "tarray.h"
#include "r_defs.h" #include "r_defs.h"
#include "x86.h"
struct FPolySeg;
struct FMiniBSP;
struct FEventInfo struct FEventInfo
{ {
@ -40,6 +44,27 @@ private:
FEvent *Predecessor (FEvent *event) const; FEvent *Predecessor (FEvent *event) const;
}; };
struct FSimpleVert
{
fixed_t x, y;
};
extern "C"
{
int ClassifyLine2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
#ifndef DISABLE_SSE
int ClassifyLineSSE1 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
int ClassifyLineSSE2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
#ifdef BACKPATCH
#ifdef __GNUC__
int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) __attribute__((noinline));
#else
int __declspec(noinline) ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
#endif
#endif
#endif
}
class FNodeBuilder class FNodeBuilder
{ {
struct FPrivSeg struct FPrivSeg
@ -60,9 +85,8 @@ class FNodeBuilder
bool planefront; bool planefront;
FPrivSeg *hashnext; FPrivSeg *hashnext;
}; };
struct FPrivVert struct FPrivVert : FSimpleVert
{ {
fixed_t x, y;
DWORD segs; // segs that use this vertex as v1 DWORD segs; // segs that use this vertex as v1
DWORD segs2; // segs that use this vertex as v2 DWORD segs2; // segs that use this vertex as v2
@ -88,7 +112,17 @@ class FNodeBuilder
}; };
// Like a blockmap, but for vertices instead of lines // Like a blockmap, but for vertices instead of lines
class FVertexMap class IVertexMap
{
public:
virtual ~IVertexMap();
virtual int SelectVertexExact(FPrivVert &vert) = 0;
virtual int SelectVertexClose(FPrivVert &vert) = 0;
private:
IVertexMap &operator=(const IVertexMap &);
};
class FVertexMap : public IVertexMap
{ {
public: public:
FVertexMap (FNodeBuilder &builder, fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy); FVertexMap (FNodeBuilder &builder, fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy);
@ -116,12 +150,23 @@ class FNodeBuilder
assert (y <= MaxY); assert (y <= MaxY);
return (unsigned(x - MinX) >> BLOCK_SHIFT) + (unsigned(y - MinY) >> BLOCK_SHIFT) * BlocksWide; return (unsigned(x - MinX) >> BLOCK_SHIFT) + (unsigned(y - MinY) >> BLOCK_SHIFT) * BlocksWide;
} }
};
FVertexMap &operator= (const FVertexMap &) { return *this; } class FVertexMapSimple : public IVertexMap
{
public:
FVertexMapSimple(FNodeBuilder &builder);
int SelectVertexExact(FPrivVert &vert);
int SelectVertexClose(FPrivVert &vert);
private:
int InsertVertex(FPrivVert &vert);
FNodeBuilder &MyBuilder;
}; };
friend class FVertexMap; friend class FVertexMap;
friend class FVertexMapSimple;
public: public:
struct FLevel struct FLevel
@ -132,7 +177,14 @@ public:
fixed_t MinX, MinY, MaxX, MaxY; fixed_t MinX, MinY, MaxX, MaxY;
void FindMapBounds (); void FindMapBounds();
void ResetMapBounds()
{
MinX = FIXED_MAX;
MinY = FIXED_MAX;
MaxX = FIXED_MIN;
MaxY = FIXED_MIN;
}
}; };
struct FPolyStart struct FPolyStart
@ -141,9 +193,10 @@ public:
fixed_t x, y; fixed_t x, y;
}; };
FNodeBuilder (FLevel &level);
FNodeBuilder (FLevel &level, FNodeBuilder (FLevel &level,
TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors, TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors,
bool makeGLNodes, bool enableSSE2); bool makeGLNodes);
~FNodeBuilder (); ~FNodeBuilder ();
void Extract (node_t *&nodes, int &nodeCount, void Extract (node_t *&nodes, int &nodeCount,
@ -151,6 +204,13 @@ public:
subsector_t *&ssecs, int &subCount, subsector_t *&ssecs, int &subCount,
vertex_t *&verts, int &vertCount); vertex_t *&verts, int &vertCount);
// These are used for building sub-BSP trees for polyobjects.
void Clear();
void AddPolySegs(FPolySeg *segs, int numsegs);
void AddSegs(seg_t *segs, int numsegs);
void BuildMini(bool makeGLNodes);
void ExtractMini(FMiniBSP *bsp);
static angle_t PointToAngle (fixed_t dx, fixed_t dy); static angle_t PointToAngle (fixed_t dx, fixed_t dy);
// < 0 : in front of line // < 0 : in front of line
@ -160,7 +220,7 @@ public:
static inline int PointOnSide (int x, int y, int x1, int y1, int dx, int dy); static inline int PointOnSide (int x, int y, int x1, int y1, int dx, int dy);
private: private:
FVertexMap *VertexMap; IVertexMap *VertexMap;
TArray<node_t> Nodes; TArray<node_t> Nodes;
TArray<subsector_t> Subsectors; TArray<subsector_t> Subsectors;
@ -181,7 +241,6 @@ private:
DWORD HackMate; // Seg to use in front of hack seg DWORD HackMate; // Seg to use in front of hack seg
FLevel &Level; FLevel &Level;
bool GLNodes; // Add minisegs to make GL nodes? bool GLNodes; // Add minisegs to make GL nodes?
bool EnableSSE2;
// Progress meter stuff // Progress meter stuff
int SegsStuffed; int SegsStuffed;
@ -191,29 +250,27 @@ private:
void MakeSegsFromSides (); void MakeSegsFromSides ();
int CreateSeg (int linenum, int sidenum); int CreateSeg (int linenum, int sidenum);
void GroupSegPlanes (); void GroupSegPlanes ();
void GroupSegPlanesSimple ();
void FindPolyContainers (TArray<FPolyStart> &spots, TArray<FPolyStart> &anchors); void FindPolyContainers (TArray<FPolyStart> &spots, TArray<FPolyStart> &anchors);
bool GetPolyExtents (int polynum, fixed_t bbox[4]); bool GetPolyExtents (int polynum, fixed_t bbox[4]);
int MarkLoop (DWORD firstseg, int loopnum); int MarkLoop (DWORD firstseg, int loopnum);
void AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg); void AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg);
int CreateNode (DWORD set, fixed_t bbox[4]); int CreateNode (DWORD set, unsigned int count, fixed_t bbox[4]);
int CreateSubsector (DWORD set, fixed_t bbox[4]); int CreateSubsector (DWORD set, fixed_t bbox[4]);
void CreateSubsectorsForReal (); void CreateSubsectorsForReal ();
bool CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int setsize); bool CheckSubsector (DWORD set, node_t &node, DWORD &splitseg);
bool CheckSubsectorOverlappingSegs (DWORD set, node_t &node, DWORD &splitseg); bool CheckSubsectorOverlappingSegs (DWORD set, node_t &node, DWORD &splitseg);
bool ShoveSegBehind (DWORD set, node_t &node, DWORD seg, DWORD mate); int SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int step, bool nosplit); bool ShoveSegBehind (DWORD set, node_t &node, DWORD seg, DWORD mate); int SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int step, bool nosplit);
void SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1); void SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1, unsigned int &count0, unsigned int &count1);
DWORD SplitSeg (DWORD segnum, int splitvert, int v1InFront); DWORD SplitSeg (DWORD segnum, int splitvert, int v1InFront);
int Heuristic (node_t &node, DWORD set, bool honorNoSplit); int Heuristic (node_t &node, DWORD set, bool honorNoSplit);
int CountSegs (DWORD set) const;
// Returns: // Returns:
// 0 = seg is in front // 0 = seg is in front
// 1 = seg is in back // 1 = seg is in back
// -1 = seg cuts the node // -1 = seg cuts the node
inline int ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2); inline int ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]);
int ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2);
int ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2);
void FixSplitSharers (const node_t &node); void FixSplitSharers (const node_t &node);
double AddIntersection (const node_t &node, int vertex); double AddIntersection (const node_t &node, int vertex);
@ -273,21 +330,27 @@ inline int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int
return s_num > 0.0 ? -1 : 1; return s_num > 0.0 ? -1 : 1;
} }
inline int FNodeBuilder::ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) inline int FNodeBuilder::ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2])
{ {
#if !defined(_M_IX86) && !defined(_M_X64) && !defined(__i386__) && !defined(__amd64__) #ifdef DISABLE_SSE
return ClassifyLine2 (node, seg, sidev1, sidev2); return ClassifyLine2 (node, v1, v2, sidev);
#elif defined(__SSE2__) #else
#if defined(__SSE2__) || defined(_M_IX64)
// If compiling with SSE2 support everywhere, just use the SSE2 version. // If compiling with SSE2 support everywhere, just use the SSE2 version.
return ClassifyLineSSE2 (node, seg, sidev1, sidev2); return ClassifyLineSSE2 (node, v1, v2, sidev);
#elif defined(_MSC_VER) && _MSC_VER < 1300 #elif defined(_MSC_VER) && _MSC_VER < 1300
// VC 6 does not support SSE2 optimizations. // VC 6 does not support SSE optimizations.
return ClassifyLine2 (node, seg, sidev1, sidev2); return ClassifyLine2 (node, v1, v2, sidev);
#else #else
// Select the routine based on our flag. // Select the routine based on our flag.
if (EnableSSE2) #ifdef BACKPATCH
return ClassifyLineSSE2 (node, seg, sidev1, sidev2); return ClassifyLineBackpatch (node, v1, v2, sidev);
#else
if (CPU.bSSE2)
return ClassifyLineSSE2 (node, v1, v2, sidev);
else else
return ClassifyLine2 (node, seg, sidev1, sidev2); return ClassifyLine2 (node, v1, v2, sidev);
#endif
#endif
#endif #endif
} }

View file

@ -3,11 +3,8 @@
#define FAR_ENOUGH 17179869184.f // 4<<32 #define FAR_ENOUGH 17179869184.f // 4<<32
int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) extern "C" int ClassifyLine2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
{ {
const FPrivVert *v1 = &Vertices[seg->v1];
const FPrivVert *v2 = &Vertices[seg->v2];
double d_x1 = double(node.x); double d_x1 = double(node.x);
double d_y1 = double(node.y); double d_y1 = double(node.y);
double d_dx = double(node.dx); double d_dx = double(node.dx);
@ -26,13 +23,13 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1,
{ {
if (s_num2 <= -FAR_ENOUGH) if (s_num2 <= -FAR_ENOUGH)
{ {
sidev1 = sidev2 = 1; sidev[0] = sidev[1] = 1;
return 1; return 1;
} }
if (s_num2 >= FAR_ENOUGH) if (s_num2 >= FAR_ENOUGH)
{ {
sidev1 = 1; sidev[0] = 1;
sidev2 = -1; sidev[1] = -1;
return -1; return -1;
} }
nears = 1; nears = 1;
@ -41,13 +38,13 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1,
{ {
if (s_num2 >= FAR_ENOUGH) if (s_num2 >= FAR_ENOUGH)
{ {
sidev1 = sidev2 = -1; sidev[0] = sidev[1] = -1;
return 0; return 0;
} }
if (s_num2 <= -FAR_ENOUGH) if (s_num2 <= -FAR_ENOUGH)
{ {
sidev1 = -1; sidev[0] = -1;
sidev2 = 1; sidev[1] = 1;
return -1; return -1;
} }
nears = 1; nears = 1;
@ -65,41 +62,41 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1,
double dist = s_num1 * s_num1 * l; double dist = s_num1 * s_num1 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON) if (dist < SIDE_EPSILON*SIDE_EPSILON)
{ {
sidev1 = 0; sidev[0] = 0;
} }
else else
{ {
sidev1 = s_num1 > 0.0 ? -1 : 1; sidev[0] = s_num1 > 0.0 ? -1 : 1;
} }
} }
else else
{ {
sidev1 = s_num1 > 0.0 ? -1 : 1; sidev[0] = s_num1 > 0.0 ? -1 : 1;
} }
if (nears & 1) if (nears & 1)
{ {
double dist = s_num2 * s_num2 * l; double dist = s_num2 * s_num2 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON) if (dist < SIDE_EPSILON*SIDE_EPSILON)
{ {
sidev2 = 0; sidev[1] = 0;
} }
else else
{ {
sidev2 = s_num2 > 0.0 ? -1 : 1; sidev[1] = s_num2 > 0.0 ? -1 : 1;
} }
} }
else else
{ {
sidev2 = s_num2 > 0.0 ? -1 : 1; sidev[1] = s_num2 > 0.0 ? -1 : 1;
} }
} }
else else
{ {
sidev1 = s_num1 > 0.0 ? -1 : 1; sidev[0] = s_num1 > 0.0 ? -1 : 1;
sidev2 = s_num2 > 0.0 ? -1 : 1; sidev[1] = s_num2 > 0.0 ? -1 : 1;
} }
if ((sidev1 | sidev2) == 0) if ((sidev[0] | sidev[1]) == 0)
{ // seg is coplanar with the splitter, so use its orientation to determine { // seg is coplanar with the splitter, so use its orientation to determine
// which child it ends up in. If it faces the same direction as the splitter, // which child it ends up in. If it faces the same direction as the splitter,
// it goes in front. Otherwise, it goes in back. // it goes in front. Otherwise, it goes in back.
@ -127,11 +124,11 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1,
} }
} }
} }
else if (sidev1 <= 0 && sidev2 <= 0) else if (sidev[0] <= 0 && sidev[1] <= 0)
{ {
return 0; return 0;
} }
else if (sidev1 >= 0 && sidev2 >= 0) else if (sidev[0] >= 0 && sidev[1] >= 0)
{ {
return 1; return 1;
} }

View file

@ -1,18 +1,16 @@
#ifndef DISABLE_SSE
#include "doomtype.h" #include "doomtype.h"
#include "nodebuild.h" #include "nodebuild.h"
#define FAR_ENOUGH 17179869184.f // 4<<32 #define FAR_ENOUGH 17179869184.f // 4<<32
// This function is identical to the ClassifyLine2 version. So how does it use SSE2? // You may notice that this function is identical to ClassifyLine2.
// Easy! By explicitly enabling SSE2 in the configuration properties for this one // The reason it is SSE2 is because this file is explicitly compiled
// file, we can build it with SSE2 enabled without forcing SSE2 on the rest of the // with SSE2 math enabled, but the other files are not.
// project.
int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) extern "C" int ClassifyLineSSE2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
{ {
const FPrivVert *v1 = &Vertices[seg->v1];
const FPrivVert *v2 = &Vertices[seg->v2];
double d_x1 = double(node.x); double d_x1 = double(node.x);
double d_y1 = double(node.y); double d_y1 = double(node.y);
double d_dx = double(node.dx); double d_dx = double(node.dx);
@ -31,13 +29,13 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side
{ {
if (s_num2 <= -FAR_ENOUGH) if (s_num2 <= -FAR_ENOUGH)
{ {
sidev1 = sidev2 = 1; sidev[0] = sidev[1] = 1;
return 1; return 1;
} }
if (s_num2 >= FAR_ENOUGH) if (s_num2 >= FAR_ENOUGH)
{ {
sidev1 = 1; sidev[0] = 1;
sidev2 = -1; sidev[1] = -1;
return -1; return -1;
} }
nears = 1; nears = 1;
@ -46,13 +44,13 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side
{ {
if (s_num2 >= FAR_ENOUGH) if (s_num2 >= FAR_ENOUGH)
{ {
sidev1 = sidev2 = -1; sidev[0] = sidev[1] = -1;
return 0; return 0;
} }
if (s_num2 <= -FAR_ENOUGH) if (s_num2 <= -FAR_ENOUGH)
{ {
sidev1 = -1; sidev[0] = -1;
sidev2 = 1; sidev[1] = 1;
return -1; return -1;
} }
nears = 1; nears = 1;
@ -70,41 +68,41 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side
double dist = s_num1 * s_num1 * l; double dist = s_num1 * s_num1 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON) if (dist < SIDE_EPSILON*SIDE_EPSILON)
{ {
sidev1 = 0; sidev[0] = 0;
} }
else else
{ {
sidev1 = s_num1 > 0.0 ? -1 : 1; sidev[0] = s_num1 > 0.0 ? -1 : 1;
} }
} }
else else
{ {
sidev1 = s_num1 > 0.0 ? -1 : 1; sidev[0] = s_num1 > 0.0 ? -1 : 1;
} }
if (nears & 1) if (nears & 1)
{ {
double dist = s_num2 * s_num2 * l; double dist = s_num2 * s_num2 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON) if (dist < SIDE_EPSILON*SIDE_EPSILON)
{ {
sidev2 = 0; sidev[1] = 0;
} }
else else
{ {
sidev2 = s_num2 > 0.0 ? -1 : 1; sidev[1] = s_num2 > 0.0 ? -1 : 1;
} }
} }
else else
{ {
sidev2 = s_num2 > 0.0 ? -1 : 1; sidev[1] = s_num2 > 0.0 ? -1 : 1;
} }
} }
else else
{ {
sidev1 = s_num1 > 0.0 ? -1 : 1; sidev[0] = s_num1 > 0.0 ? -1 : 1;
sidev2 = s_num2 > 0.0 ? -1 : 1; sidev[1] = s_num2 > 0.0 ? -1 : 1;
} }
if ((sidev1 | sidev2) == 0) if ((sidev[0] | sidev[1]) == 0)
{ // seg is coplanar with the splitter, so use its orientation to determine { // seg is coplanar with the splitter, so use its orientation to determine
// which child it ends up in. If it faces the same direction as the splitter, // which child it ends up in. If it faces the same direction as the splitter,
// it goes in front. Otherwise, it goes in back. // it goes in front. Otherwise, it goes in back.
@ -132,13 +130,15 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side
} }
} }
} }
else if (sidev1 <= 0 && sidev2 <= 0) else if (sidev[0] <= 0 && sidev[1] <= 0)
{ {
return 0; return 0;
} }
else if (sidev1 >= 0 && sidev2 >= 0) else if (sidev[0] >= 0 && sidev[1] >= 0)
{ {
return 1; return 1;
} }
return -1; return -1;
} }
#endif

View file

@ -105,7 +105,7 @@ void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount,
{ {
DWORD numsegs = CloseSubsector (segs, i, outVerts); DWORD numsegs = CloseSubsector (segs, i, outVerts);
outSubs[i].numlines = numsegs; outSubs[i].numlines = numsegs;
outSubs[i].firstline = segs.Size() - numsegs; outSubs[i].firstline = (seg_t *)(size_t)(segs.Size() - numsegs);
} }
segCount = segs.Size (); segCount = segs.Size ();
@ -142,6 +142,10 @@ void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount,
out->bPolySeg = false; out->bPolySeg = false;
} }
} }
for (i = 0; i < subCount; ++i)
{
outSubs[i].firstline = &outSegs[(size_t)outSubs[i].firstline];
}
D(Printf("%i segs, %i nodes, %i subsectors\n", segCount, nodeCount, subCount)); D(Printf("%i segs, %i nodes, %i subsectors\n", segCount, nodeCount, subCount));
@ -152,6 +156,86 @@ void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount,
} }
} }
void FNodeBuilder::ExtractMini (FMiniBSP *bsp)
{
unsigned int i;
bsp->Verts.Resize(Vertices.Size());
for (i = 0; i < Vertices.Size(); ++i)
{
bsp->Verts[i].x = Vertices[i].x;
bsp->Verts[i].y = Vertices[i].y;
}
bsp->Subsectors.Resize(Subsectors.Size());
memset(&bsp->Subsectors[0], 0, Subsectors.Size() * sizeof(subsector_t));
bsp->Nodes.Resize(Nodes.Size());
memcpy(&bsp->Nodes[0], &Nodes[0], Nodes.Size()*sizeof(node_t));
for (i = 0; i < Nodes.Size(); ++i)
{
D(Printf(PRINT_LOG, "Node %d:\n", i));
// Go backwards because on 64-bit systems, both of the intchildren are
// inside the first in-game child.
for (int j = 1; j >= 0; --j)
{
if (bsp->Nodes[i].intchildren[j] & 0x80000000)
{
D(Printf(PRINT_LOG, " subsector %d\n", bsp->Nodes[i].intchildren[j] & 0x7FFFFFFF));
bsp->Nodes[i].children[j] = (BYTE *)&bsp->Subsectors[bsp->Nodes[i].intchildren[j] & 0x7fffffff] + 1;
}
else
{
D(Printf(PRINT_LOG, " node %d\n", bsp->Nodes[i].intchildren[j]));
bsp->Nodes[i].children[j] = &bsp->Nodes[bsp->Nodes[i].intchildren[j]];
}
}
}
if (GLNodes)
{
for (i = 0; i < Subsectors.Size(); ++i)
{
DWORD numsegs = CloseSubsector (bsp->Segs, i, &bsp->Verts[0]);
bsp->Subsectors[i].numlines = numsegs;
bsp->Subsectors[i].firstline = &bsp->Segs[bsp->Segs.Size() - numsegs];
}
for (i = 0; i < Segs.Size(); ++i)
{
if (bsp->Segs[i].PartnerSeg != NULL)
{
bsp->Segs[i].PartnerSeg = &bsp->Segs[Segs[(unsigned int)(size_t)bsp->Segs[i].PartnerSeg-1].storedseg];
}
}
}
else
{
memcpy(&bsp->Subsectors[0], &Subsectors[0], Subsectors.Size()*sizeof(subsector_t));
bsp->Segs.Resize(Segs.Size());
for (i = 0; i < Segs.Size(); ++i)
{
const FPrivSeg *org = &Segs[SegList[i].SegNum];
seg_t *out = &bsp->Segs[i];
D(Printf(PRINT_LOG, "Seg %d: v1(%d) -> v2(%d)\n", i, org->v1, org->v2));
out->v1 = &bsp->Verts[org->v1];
out->v2 = &bsp->Verts[org->v2];
out->backsector = org->backsector;
out->frontsector = org->frontsector;
out->linedef = Level.Lines + org->linedef;
out->sidedef = Level.Sides + org->sidedef;
out->PartnerSeg = NULL;
out->bPolySeg = false;
}
for (i = 0; i < bsp->Subsectors.Size(); ++i)
{
bsp->Subsectors[i].firstline = &bsp->Segs[(size_t)bsp->Subsectors[i].firstline];
}
}
}
int FNodeBuilder::CloseSubsector (TArray<seg_t> &segs, int subsector, vertex_t *outVerts) int FNodeBuilder::CloseSubsector (TArray<seg_t> &segs, int subsector, vertex_t *outVerts)
{ {
FPrivSeg *seg, *prev; FPrivSeg *seg, *prev;
@ -163,7 +247,7 @@ int FNodeBuilder::CloseSubsector (TArray<seg_t> &segs, int subsector, vertex_t *
bool diffplanes; bool diffplanes;
int firstplane; int firstplane;
first = Subsectors[subsector].firstline; first = (DWORD)(size_t)Subsectors[subsector].firstline;
max = first + Subsectors[subsector].numlines; max = first + Subsectors[subsector].numlines;
count = 0; count = 0;
@ -322,7 +406,7 @@ int FNodeBuilder::OutputDegenerateSubsector (TArray<seg_t> &segs, int subsector,
double dot, x1, y1, dx, dy, dx2, dy2; double dot, x1, y1, dx, dy, dx2, dy2;
bool wantside; bool wantside;
first = Subsectors[subsector].firstline; first = (DWORD)(size_t)Subsectors[subsector].firstline;
max = first + Subsectors[subsector].numlines; max = first + Subsectors[subsector].numlines;
count = 0; count = 0;
@ -401,7 +485,6 @@ DWORD FNodeBuilder::PushGLSeg (TArray<seg_t> &segs, const FPrivSeg *seg, vertex_
} }
newseg.PartnerSeg = (seg_t *)(seg->partner == DWORD_MAX ? 0 : (size_t)seg->partner + 1); newseg.PartnerSeg = (seg_t *)(seg->partner == DWORD_MAX ? 0 : (size_t)seg->partner + 1);
newseg.bPolySeg = false; newseg.bPolySeg = false;
newseg.Subsector = NULL;
return (DWORD)segs.Push (newseg); return (DWORD)segs.Push (newseg);
} }
@ -417,6 +500,5 @@ void FNodeBuilder::PushConnectingGLSeg (int subsector, TArray<seg_t> &segs, vert
newseg.sidedef = NULL; newseg.sidedef = NULL;
newseg.PartnerSeg = NULL; newseg.PartnerSeg = NULL;
newseg.bPolySeg = false; newseg.bPolySeg = false;
newseg.Subsector = NULL;
segs.Push (newseg); segs.Push (newseg);
} }

View file

@ -49,6 +49,7 @@
#include "m_bbox.h" #include "m_bbox.h"
#include "r_main.h" #include "r_main.h"
#include "i_system.h" #include "i_system.h"
#include "po_man.h"
static const int PO_LINE_START = 1; static const int PO_LINE_START = 1;
static const int PO_LINE_EXPLICIT = 5; static const int PO_LINE_EXPLICIT = 5;
@ -177,6 +178,86 @@ int FNodeBuilder::CreateSeg (int linenum, int sidenum)
return segnum; return segnum;
} }
// For every seg, create FPrivSegs and FPrivVerts.
void FNodeBuilder::AddSegs(seg_t *segs, int numsegs)
{
assert(numsegs > 0);
for (int i = 0; i < numsegs; ++i)
{
FPrivSeg seg;
FPrivVert vert;
int segnum;
seg.next = DWORD_MAX;
seg.loopnum = 0;
seg.partner = DWORD_MAX;
seg.hashnext = NULL;
seg.planefront = false;
seg.planenum = DWORD_MAX;
seg.storedseg = DWORD_MAX;
seg.frontsector = segs[i].frontsector;
seg.backsector = segs[i].backsector;
vert.x = segs[i].v1->x;
vert.y = segs[i].v1->y;
seg.v1 = VertexMap->SelectVertexExact(vert);
vert.x = segs[i].v2->x;
vert.y = segs[i].v2->y;
seg.v2 = VertexMap->SelectVertexExact(vert);
seg.linedef = int(segs[i].linedef - Level.Lines);
seg.sidedef = segs[i].sidedef != NULL ? int(segs[i].sidedef - Level.Sides) : int(NO_SIDE);
seg.nextforvert = Vertices[seg.v1].segs;
seg.nextforvert2 = Vertices[seg.v2].segs2;
segnum = (int)Segs.Push(seg);
Vertices[seg.v1].segs = segnum;
Vertices[seg.v2].segs2 = segnum;
}
}
void FNodeBuilder::AddPolySegs(FPolySeg *segs, int numsegs)
{
assert(numsegs > 0);
for (int i = 0; i < numsegs; ++i)
{
FPrivSeg seg;
FPrivVert vert;
int segnum;
seg.next = DWORD_MAX;
seg.loopnum = 0;
seg.partner = DWORD_MAX;
seg.hashnext = NULL;
seg.planefront = false;
seg.planenum = DWORD_MAX;
seg.storedseg = DWORD_MAX;
side_t *side = segs[i].wall;
assert(side != NULL);
seg.frontsector = side->sector;
seg.backsector = side->linedef->frontsector == side->sector ? side->linedef->backsector : side->linedef->frontsector;
vert.x = segs[i].v1.x;
vert.y = segs[i].v1.y;
seg.v1 = VertexMap->SelectVertexExact(vert);
vert.x = segs[i].v2.x;
vert.y = segs[i].v2.y;
seg.v2 = VertexMap->SelectVertexExact(vert);
seg.linedef = int(side->linedef - Level.Lines);
seg.sidedef = int(side - Level.Sides);
seg.nextforvert = Vertices[seg.v1].segs;
seg.nextforvert2 = Vertices[seg.v2].segs2;
segnum = (int)Segs.Push(seg);
Vertices[seg.v1].segs = segnum;
Vertices[seg.v2].segs2 = segnum;
}
}
// Group colinear segs together so that only one seg per line needs to be checked // Group colinear segs together so that only one seg per line needs to be checked
// by SelectSplitter(). // by SelectSplitter().
@ -269,6 +350,27 @@ void FNodeBuilder::GroupSegPlanes ()
PlaneChecked.Reserve ((planenum + 7) / 8); PlaneChecked.Reserve ((planenum + 7) / 8);
} }
// Just create one plane per seg. Should be good enough for mini BSPs.
void FNodeBuilder::GroupSegPlanesSimple()
{
Planes.Resize(Segs.Size());
for (int i = 0; i < (int)Segs.Size(); ++i)
{
FPrivSeg *seg = &Segs[i];
FSimpleLine *pline = &Planes[i];
seg->next = i+1;
seg->hashnext = NULL;
seg->planenum = i;
seg->planefront = true;
pline->x = Vertices[seg->v1].x;
pline->y = Vertices[seg->v1].y;
pline->dx = Vertices[seg->v2].x - Vertices[seg->v1].x;
pline->dy = Vertices[seg->v2].y - Vertices[seg->v1].y;
}
Segs.Last().next = DWORD_MAX;
PlaneChecked.Reserve((Segs.Size() + 7) / 8);
}
// Find "loops" of segs surrounding polyobject's origin. Note that a polyobject's origin // Find "loops" of segs surrounding polyobject's origin. Note that a polyobject's origin
// is not solely defined by the polyobject's anchor, but also by the polyobject itself. // is not solely defined by the polyobject's anchor, but also by the polyobject itself.
// For the split avoidance to work properly, you must have a convex, complete loop of // For the split avoidance to work properly, you must have a convex, complete loop of
@ -507,6 +609,10 @@ void FNodeBuilder::FLevel::FindMapBounds ()
MaxY = maxy; MaxY = maxy;
} }
FNodeBuilder::IVertexMap::~IVertexMap()
{
}
FNodeBuilder::FVertexMap::FVertexMap (FNodeBuilder &builder, FNodeBuilder::FVertexMap::FVertexMap (FNodeBuilder &builder,
fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy) fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy)
: MyBuilder(builder) : MyBuilder(builder)
@ -606,3 +712,52 @@ int FNodeBuilder::FVertexMap::InsertVertex (FNodeBuilder::FPrivVert &vert)
return vertnum; return vertnum;
} }
FNodeBuilder::FVertexMapSimple::FVertexMapSimple(FNodeBuilder &builder)
: MyBuilder(builder)
{
}
int FNodeBuilder::FVertexMapSimple::SelectVertexExact(FNodeBuilder::FPrivVert &vert)
{
FPrivVert *verts = &MyBuilder.Vertices[0];
unsigned int stop = MyBuilder.Vertices.Size();
for (unsigned int i = 0; i < stop; ++i)
{
if (verts[i].x == vert.x && verts[i].y == vert.y)
{
return i;
}
}
// Not present: add it!
return InsertVertex(vert);
}
int FNodeBuilder::FVertexMapSimple::SelectVertexClose(FNodeBuilder::FPrivVert &vert)
{
FPrivVert *verts = &MyBuilder.Vertices[0];
unsigned int stop = MyBuilder.Vertices.Size();
for (unsigned int i = 0; i < stop; ++i)
{
#if VERTEX_EPSILON <= 1
if (verts[i].x == vert.x && verts[i].y == y)
#else
if (abs(verts[i].x - vert.x) < VERTEX_EPSILON &&
abs(verts[i].y - vert.y) < VERTEX_EPSILON)
#endif
{
return i;
}
}
// Not present: add it!
return InsertVertex (vert);
}
int FNodeBuilder::FVertexMapSimple::InsertVertex (FNodeBuilder::FPrivVert &vert)
{
vert.segs = DWORD_MAX;
vert.segs2 = DWORD_MAX;
return (int)MyBuilder.Vertices.Push (vert);
}

View file

@ -855,7 +855,7 @@ void P_LoadGLZSegs (FileReaderBase &data, int type)
} }
data >> side; data >> side;
seg = &segs[subsectors[i].firstline + j]; seg = subsectors[i].firstline + j;
seg->v1 = &vertexes[v1]; seg->v1 = &vertexes[v1];
if (j == 0) if (j == 0)
{ {
@ -894,7 +894,7 @@ void P_LoadGLZSegs (FileReaderBase &data, int type)
{ {
seg->linedef = NULL; seg->linedef = NULL;
seg->sidedef = NULL; seg->sidedef = NULL;
seg->frontsector = seg->backsector = segs[subsectors[i].firstline].frontsector; seg->frontsector = seg->backsector = subsectors[i].firstline->frontsector;
} }
} }
} }
@ -952,7 +952,7 @@ static void LoadZNodes(FileReaderBase &data, int glnodes)
DWORD numsegs; DWORD numsegs;
data >> numsegs; data >> numsegs;
subsectors[i].firstline = currSeg; subsectors[i].firstline = (seg_t *)(size_t)currSeg; // Oh damn. I should have stored the seg count sooner.
subsectors[i].numlines = numsegs; subsectors[i].numlines = numsegs;
currSeg += numsegs; currSeg += numsegs;
} }
@ -976,6 +976,11 @@ static void LoadZNodes(FileReaderBase &data, int glnodes)
segs = new seg_t[numsegs]; segs = new seg_t[numsegs];
memset (segs, 0, numsegs*sizeof(seg_t)); memset (segs, 0, numsegs*sizeof(seg_t));
for (i = 0; i < numSubs; ++i)
{
subsectors[i].firstline = &segs[(size_t)subsectors[i].firstline];
}
if (glnodes == 0) if (glnodes == 0)
{ {
P_LoadZSegs (data); P_LoadZSegs (data);
@ -1141,6 +1146,11 @@ void P_LoadSegs (MapData * map)
data = new BYTE[lumplen]; data = new BYTE[lumplen];
map->Read(ML_SEGS, data); map->Read(ML_SEGS, data);
for (i = 0; i < numsubsectors; ++i)
{
subsectors[i].firstline = &segs[(size_t)subsectors[i].firstline];
}
// phares: 10/4/98: Vertchanged is an array that represents the vertices. // phares: 10/4/98: Vertchanged is an array that represents the vertices.
// Mark those used by linedefs. A marked vertex is one that is not a // Mark those used by linedefs. A marked vertex is one that is not a
// candidate for movement further down. // candidate for movement further down.
@ -1329,9 +1339,9 @@ void P_LoadSubsectors (MapData * map)
} }
subsectors[i].numlines = subd.numsegs; subsectors[i].numlines = subd.numsegs;
subsectors[i].firstline = subd.firstseg; subsectors[i].firstline = (seg_t *)(size_t)subd.firstseg;
if (subsectors[i].firstline >= maxseg) if ((size_t)subsectors[i].firstline >= maxseg)
{ {
Printf ("Subsector %d contains invalid segs %u-%u\n" Printf ("Subsector %d contains invalid segs %u-%u\n"
"The BSP will be rebuilt.\n", i, subsectors[i].firstline, "The BSP will be rebuilt.\n", i, subsectors[i].firstline,
@ -1341,7 +1351,7 @@ void P_LoadSubsectors (MapData * map)
delete[] subsectors; delete[] subsectors;
break; break;
} }
else if (subsectors[i].firstline + subsectors[i].numlines > maxseg) else if ((size_t)subsectors[i].firstline + subsectors[i].numlines > maxseg)
{ {
Printf ("Subsector %d contains invalid segs %u-%u\n" Printf ("Subsector %d contains invalid segs %u-%u\n"
"The BSP will be rebuilt.\n", i, maxseg, "The BSP will be rebuilt.\n", i, maxseg,
@ -2910,20 +2920,7 @@ static void P_GroupLines (bool buildmap)
times[0].Clock(); times[0].Clock();
for (i = 0; i < numsubsectors; i++) for (i = 0; i < numsubsectors; i++)
{ {
subsectors[i].sector = segs[subsectors[i].firstline].sidedef->sector; subsectors[i].sector = subsectors[i].firstline->sidedef->sector;
subsectors[i].validcount = validcount;
double accumx = 0.0, accumy = 0.0;
for (jj = 0; jj < subsectors[i].numlines; ++jj)
{
seg_t *seg = &segs[subsectors[i].firstline + jj];
seg->Subsector = &subsectors[i];
accumx += seg->v1->x + seg->v2->x;
accumy += seg->v1->y + seg->v2->y;
}
subsectors[i].CenterX = fixed_t(accumx * 0.5 / subsectors[i].numlines);
subsectors[i].CenterY = fixed_t(accumy * 0.5 / subsectors[i].numlines);
} }
times[0].Unclock(); times[0].Unclock();
@ -3369,6 +3366,13 @@ void P_FreeLevelData ()
} }
if (subsectors != NULL) if (subsectors != NULL)
{ {
for (int i = 0; i < numsubsectors; ++i)
{
if (subsectors[i].BSP != NULL)
{
delete subsectors[i].BSP;
}
}
delete[] subsectors; delete[] subsectors;
subsectors = NULL; subsectors = NULL;
} }
@ -3786,7 +3790,7 @@ void P_SetupLevel (char *lumpname, int position)
}; };
leveldata.FindMapBounds (); leveldata.FindMapBounds ();
UsingGLNodes |= genglnodes; UsingGLNodes |= genglnodes;
FNodeBuilder builder (leveldata, polyspots, anchors, UsingGLNodes, CPU.bSSE2); FNodeBuilder builder (leveldata, polyspots, anchors, UsingGLNodes);
delete[] vertexes; delete[] vertexes;
builder.Extract (nodes, numnodes, builder.Extract (nodes, numnodes,
segs, numsegs, segs, numsegs,

View file

@ -142,6 +142,9 @@ static void TranslateToStartSpot (int tag, int originX, int originY);
static void DoMovePolyobj (FPolyObj *po, int x, int y); static void DoMovePolyobj (FPolyObj *po, int x, int y);
static void InitSegLists (); static void InitSegLists ();
static void KillSegLists (); static void KillSegLists ();
static FPolyNode *NewPolyNode();
static void FreePolyNode();
static void ReleaseAllPolyNodes();
// EXTERNAL DATA DECLARATIONS ---------------------------------------------- // EXTERNAL DATA DECLARATIONS ----------------------------------------------
@ -157,6 +160,7 @@ polyspawns_t *polyspawns; // [RH] Let P_SpawnMapThings() find our thingies for u
// PRIVATE DATA DEFINITIONS ------------------------------------------------ // PRIVATE DATA DEFINITIONS ------------------------------------------------
static TArray<SDWORD> KnownPolySides; static TArray<SDWORD> KnownPolySides;
static FPolyNode *FreePolyNodes;
// CODE -------------------------------------------------------------------- // CODE --------------------------------------------------------------------
@ -758,7 +762,6 @@ FPolyObj::FPolyObj()
subsectorlinks = NULL; subsectorlinks = NULL;
specialdata = NULL; specialdata = NULL;
interpolation = NULL; interpolation = NULL;
SVIndex = -1;
} }
//========================================================================== //==========================================================================
@ -1279,20 +1282,6 @@ void FPolyObj::ClosestPoint(fixed_t fx, fixed_t fy, fixed_t &ox, fixed_t &oy, si
} }
} }
vertex_t *FPolyObj::GetNewVertex()
{
if (SVIndex == ~0u || SplitVertices[SVIndex]->used == 10)
{
SVIndex++;
if (SVIndex >= SplitVertices.Size())
{
SplitVertices.Push(new FPolyVertexBlock);
}
SplitVertices[SVIndex]->clear();
}
return &SplitVertices[SVIndex]->vertices[SplitVertices[SVIndex]->used++];
}
//========================================================================== //==========================================================================
// //
// InitBlockMap // InitBlockMap
@ -1699,11 +1688,6 @@ bool PO_Busy (int polyobj)
void FPolyObj::ClearSubsectorLinks() void FPolyObj::ClearSubsectorLinks()
{ {
for(unsigned i=0; i<SplitVertices.Size(); i++)
{
SplitVertices[i]->clear();
}
SVIndex = -1;
while (subsectorlinks != NULL) while (subsectorlinks != NULL)
{ {
assert(subsectorlinks->state == 1337); assert(subsectorlinks->state == 1337);
@ -1725,6 +1709,12 @@ void FPolyObj::ClearSubsectorLinks()
{ {
subsectorlinks->subsector->polys = subsectorlinks->pnext; subsectorlinks->subsector->polys = subsectorlinks->pnext;
} }
if (subsectorlinks->subsector->BSP != NULL)
{
subsectorlinks->subsector->BSP->bDirty = true;
}
subsectorlinks->state = -1; subsectorlinks->state = -1;
delete subsectorlinks; delete subsectorlinks;
subsectorlinks = next; subsectorlinks = next;
@ -1734,10 +1724,11 @@ void FPolyObj::ClearSubsectorLinks()
void FPolyObj::ClearAllSubsectorLinks() void FPolyObj::ClearAllSubsectorLinks()
{ {
for(int i=0;i<po_NumPolyobjs;i++) for (int i = 0; i < po_NumPolyobjs; i++)
{ {
polyobjs[i].ClearSubsectorLinks(); polyobjs[i].ClearSubsectorLinks();
} }
ReleaseAllPolyNodes();
} }
//========================================================================== //==========================================================================
@ -1748,16 +1739,16 @@ void FPolyObj::ClearAllSubsectorLinks()
// //
//========================================================================== //==========================================================================
static bool GetIntersection(seg_t *seg, node_t *bsp, vertex_t *v) static bool GetIntersection(FPolySeg *seg, node_t *bsp, FPolyVertex *v)
{ {
double frac; double frac;
double num; double num;
double den; double den;
double v2x = (double)seg->v1->x; double v2x = (double)seg->v1.x;
double v2y = (double)seg->v1->y; double v2y = (double)seg->v1.y;
double v2dx = (double)(seg->v2->x - seg->v1->x); double v2dx = (double)(seg->v2.x - seg->v1.x);
double v2dy = (double)(seg->v2->y - seg->v1->y); double v2dy = (double)(seg->v2.y - seg->v1.y);
double v1x = (double)bsp->x; double v1x = (double)bsp->x;
double v1y = (double)bsp->y; double v1y = (double)bsp->y;
double v1dx = (double)bsp->dx; double v1dx = (double)bsp->dx;
@ -1786,7 +1777,7 @@ static bool GetIntersection(seg_t *seg, node_t *bsp, vertex_t *v)
// //
//========================================================================== //==========================================================================
static double PartitionDistance(vertex_t *vt, node_t *node) static double PartitionDistance(FPolyVertex *vt, node_t *node)
{ {
return fabs(double(-node->dy) * (vt->x - node->x) + double(node->dx) * (vt->y - node->y)) / node->len; return fabs(double(-node->dy) * (vt->x - node->x) + double(node->dx) * (vt->y - node->y)) / node->len;
} }
@ -1823,7 +1814,7 @@ static void AddToBBox(fixed_t child[4], fixed_t parent[4])
// //
//========================================================================== //==========================================================================
static void AddToBBox(vertex_t *v, fixed_t bbox[4]) static void AddToBBox(FPolyVertex *v, fixed_t bbox[4])
{ {
if (v->x < bbox[BOXLEFT]) if (v->x < bbox[BOXLEFT])
{ {
@ -1851,7 +1842,7 @@ static void AddToBBox(vertex_t *v, fixed_t bbox[4])
static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4]) static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4])
{ {
static TArray<seg_t> lists[2]; static TArray<FPolySeg> lists[2];
static const double POLY_EPSILON = 0.3125; static const double POLY_EPSILON = 0.3125;
if (!((size_t)node & 1)) // Keep going until found a subsector if (!((size_t)node & 1)) // Keep going until found a subsector
@ -1864,7 +1855,7 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4])
lists[1].Clear(); lists[1].Clear();
for(unsigned i=0;i<pnode->segs.Size(); i++) for(unsigned i=0;i<pnode->segs.Size(); i++)
{ {
seg_t *seg = &pnode->segs[i]; FPolySeg *seg = &pnode->segs[i];
// Parts of the following code were taken from Eternity and are // Parts of the following code were taken from Eternity and are
// being used with permission. // being used with permission.
@ -1872,8 +1863,8 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4])
// get distance of vertices from partition line // get distance of vertices from partition line
// If the distance is too small, we may decide to // If the distance is too small, we may decide to
// change our idea of sidedness. // change our idea of sidedness.
double dist_v1 = PartitionDistance(seg->v1, bsp); double dist_v1 = PartitionDistance(&seg->v1, bsp);
double dist_v2 = PartitionDistance(seg->v2, bsp); double dist_v2 = PartitionDistance(&seg->v2, bsp);
// If the distances are less than epsilon, consider the points as being // If the distances are less than epsilon, consider the points as being
// on the same side as the polyobj origin. Why? People like to build // on the same side as the polyobj origin. Why? People like to build
@ -1894,27 +1885,27 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4])
} }
else else
{ {
int side = R_PointOnSide(seg->v2->x, seg->v2->y, bsp); int side = R_PointOnSide(seg->v2.x, seg->v2.y, bsp);
lists[side].Push(*seg); lists[side].Push(*seg);
} }
} }
else if (dist_v2 <= POLY_EPSILON) else if (dist_v2 <= POLY_EPSILON)
{ {
int side = R_PointOnSide(seg->v1->x, seg->v1->y, bsp); int side = R_PointOnSide(seg->v1.x, seg->v1.y, bsp);
lists[side].Push(*seg); lists[side].Push(*seg);
} }
else else
{ {
int side1 = R_PointOnSide(seg->v1->x, seg->v1->y, bsp); int side1 = R_PointOnSide(seg->v1.x, seg->v1.y, bsp);
int side2 = R_PointOnSide(seg->v2->x, seg->v2->y, bsp); int side2 = R_PointOnSide(seg->v2.x, seg->v2.y, bsp);
if(side1 != side2) if(side1 != side2)
{ {
// if the partition line crosses this seg, we must split it. // if the partition line crosses this seg, we must split it.
vertex_t *vert = pnode->poly->GetNewVertex(); FPolyVertex vert;
if (GetIntersection(seg, bsp, vert)) if (GetIntersection(seg, bsp, &vert))
{ {
lists[0].Push(*seg); lists[0].Push(*seg);
lists[1].Push(*seg); lists[1].Push(*seg);
@ -1947,13 +1938,8 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4])
else else
{ {
// create the new node // create the new node
FPolyNode *newnode = new FPolyNode; FPolyNode *newnode = NewPolyNode();
newnode->state = 1337;
newnode->poly = pnode->poly; newnode->poly = pnode->poly;
newnode->pnext = NULL;
newnode->pprev = NULL;
newnode->subsector = NULL;
newnode->snext = NULL;
newnode->segs = lists[1]; newnode->segs = lists[1];
// set segs for original node // set segs for original node
@ -1995,8 +1981,8 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4])
for (unsigned i = 0; i < pnode->segs.Size(); ++i) for (unsigned i = 0; i < pnode->segs.Size(); ++i)
{ {
AddToBBox(pnode->segs[i].v1, subbbox); AddToBBox(&pnode->segs[i].v1, subbbox);
AddToBBox(pnode->segs[i].v2, subbbox); AddToBBox(&pnode->segs[i].v2, subbbox);
} }
// Potentially expand the parent node's bounding box to contain these bits of polyobject. // Potentially expand the parent node's bounding box to contain these bits of polyobject.
AddToBBox(subbbox, bbox); AddToBBox(subbbox, bbox);
@ -2011,31 +1997,23 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4])
void FPolyObj::CreateSubsectorLinks() void FPolyObj::CreateSubsectorLinks()
{ {
FPolyNode *node = new FPolyNode; FPolyNode *node = NewPolyNode();
fixed_t dummybbox[4]; // Even though we don't care about it, we need to initialize this
// bounding box to something so that Valgrind won't complain about it
// when SplitPoly modifies it.
fixed_t dummybbox[4] = { 0 };
node->state = 1337;
node->poly = this; node->poly = this;
node->pnext = NULL;
node->pprev = NULL;
node->snext = NULL;
node->segs.Resize(Sidedefs.Size()); node->segs.Resize(Sidedefs.Size());
for(unsigned i=0; i<Sidedefs.Size(); i++) for(unsigned i=0; i<Sidedefs.Size(); i++)
{ {
seg_t *seg = &node->segs[i]; FPolySeg *seg = &node->segs[i];
side_t *side = Sidedefs[i]; side_t *side = Sidedefs[i];
seg->v1 = side->V1(); seg->v1 = side->V1();
seg->v2 = side->V2(); seg->v2 = side->V2();
seg->sidedef = side; seg->wall = side;
seg->linedef = side->linedef;
seg->frontsector = side->sector;
seg->backsector = side->linedef->frontsector == side->sector?
side->linedef->backsector : side->linedef->frontsector;
seg->Subsector = NULL;
seg->PartnerSeg = NULL;
seg->bPolySeg = true;
} }
SplitPoly(node, nodes + numnodes - 1, dummybbox); SplitPoly(node, nodes + numnodes - 1, dummybbox);
} }
@ -2055,4 +2033,62 @@ void PO_LinkToSubsectors()
polyobjs[i].CreateSubsectorLinks(); polyobjs[i].CreateSubsectorLinks();
} }
} }
} }
//==========================================================================
//
// NewPolyNode
//
//==========================================================================
static FPolyNode *NewPolyNode()
{
FPolyNode *node;
if (FreePolyNodes != NULL)
{
node = FreePolyNodes;
FreePolyNodes = node->pnext;
}
else
{
node = new FPolyNode;
}
node->state = 1337;
node->poly = NULL;
node->pnext = NULL;
node->pprev = NULL;
node->subsector = NULL;
node->snext = NULL;
return node;
}
//==========================================================================
//
// FreePolyNode
//
//==========================================================================
void FreePolyNode(FPolyNode *node)
{
node->segs.Clear();
node->pnext = FreePolyNodes;
FreePolyNodes = node;
}
//==========================================================================
//
// ReleaseAllPolyNodes
//
//==========================================================================
void ReleaseAllPolyNodes()
{
FPolyNode *node, *next;
for (node = FreePolyNodes; node != NULL; node = next)
{
next = node->pnext;
delete node;
}
}

View file

@ -5,13 +5,32 @@
#include "r_defs.h" #include "r_defs.h"
#include "m_bbox.h" #include "m_bbox.h"
struct FPolyVertex
{
fixed_t x, y;
FPolyVertex &operator=(vertex_t *v)
{
x = v->x;
y = v->y;
return *this;
}
};
struct FPolySeg
{
FPolyVertex v1;
FPolyVertex v2;
side_t *wall;
};
// //
// Linked lists of polyobjects // Linked lists of polyobjects
// //
struct FPolyObj; struct FPolyObj;
struct FPolyNode struct FPolyNode
{ {
int state;
FPolyObj *poly; // owning polyobject FPolyObj *poly; // owning polyobject
FPolyNode *pnext; // next polyobj in list FPolyNode *pnext; // next polyobj in list
FPolyNode *pprev; // previous polyobj FPolyNode *pprev; // previous polyobj
@ -19,25 +38,8 @@ struct FPolyNode
subsector_t *subsector; // containimg subsector subsector_t *subsector; // containimg subsector
FPolyNode *snext; // next subsector FPolyNode *snext; // next subsector
TArray<seg_t> segs; // segs for this node TArray<FPolySeg> segs; // segs for this node
fixed_t dist; // distance for sorting int state;
};
struct FPolyVertex
{
fixed_t x, y;
};
struct FPolyVertexBlock
{
int used;
vertex_t vertices[10];
void clear()
{
used = 0;
//memset(vertices, 0, sizeof(vertices));
}
}; };
// ===== Polyobj data ===== // ===== Polyobj data =====
@ -51,8 +53,6 @@ struct FPolyObj
FPolyVertex StartSpot; FPolyVertex StartSpot;
FPolyVertex CenterSpot; FPolyVertex CenterSpot;
FBoundingBox Bounds; // Bounds in map coordinates FBoundingBox Bounds; // Bounds in map coordinates
TDeletingArray<FPolyVertexBlock *> SplitVertices;
unsigned int SVIndex;
angle_t angle; angle_t angle;
int tag; // reference tag assigned in HereticEd int tag; // reference tag assigned in HereticEd
@ -77,7 +77,6 @@ struct FPolyObj
void LinkPolyobj (); void LinkPolyobj ();
void CreateSubsectorLinks(); void CreateSubsectorLinks();
void ClearSubsectorLinks(); void ClearSubsectorLinks();
vertex_t *GetNewVertex();
void CalcCenter(); void CalcCenter();
static void ClearAllSubsectorLinks(); static void ClearAllSubsectorLinks();

View file

@ -43,6 +43,7 @@
#include "r_things.h" #include "r_things.h"
#include "a_sharedglobal.h" #include "a_sharedglobal.h"
#include "g_level.h" #include "g_level.h"
#include "nodebuild.h"
// State. // State.
#include "doomstat.h" #include "doomstat.h"
@ -108,6 +109,9 @@ WORD MirrorFlags;
seg_t *ActiveWallMirror; seg_t *ActiveWallMirror;
TArray<size_t> WallMirrors; TArray<size_t> WallMirrors;
static FNodeBuilder::FLevel PolyNodeLevel;
static FNodeBuilder PolyNodeBuilder(PolyNodeLevel);
CVAR (Bool, r_drawflat, false, 0) // [RH] Don't texture segs? CVAR (Bool, r_drawflat, false, 0) // [RH] Don't texture segs?
@ -1009,44 +1013,76 @@ void R_GetExtraLight (int *light, const secplane_t &plane, FExtraLight *el)
} }
static int STACK_ARGS polycmp(const void *a, const void *b)
{
const FPolyNode *A = *(FPolyNode **)a;
const FPolyNode *B = *(FPolyNode **)b;
return A->dist - B->dist;
//==========================================================================
//
// FMiniBSP Constructor
//
//==========================================================================
FMiniBSP::FMiniBSP()
{
bDirty = false;
} }
//==========================================================================
//
// P_BuildPolyBSP
//
//==========================================================================
static void R_BuildPolyBSP(subsector_t *sub)
{
assert((sub->BSP == NULL || sub->BSP->bDirty) && "BSP computed more than once");
// Set up level information for the node builder.
PolyNodeLevel.Sides = sides;
PolyNodeLevel.NumSides = numsides;
PolyNodeLevel.Lines = lines;
PolyNodeLevel.NumLines = numlines;
// Feed segs to the nodebuilder and build the nodes.
PolyNodeBuilder.Clear();
PolyNodeBuilder.AddSegs(sub->firstline, sub->numlines);
for (FPolyNode *pn = sub->polys; pn != NULL; pn = pn->pnext)
{
PolyNodeBuilder.AddPolySegs(&pn->segs[0], (int)pn->segs.Size());
}
PolyNodeBuilder.BuildMini(false);
if (sub->BSP == NULL)
{
sub->BSP = new FMiniBSP;
}
else
{
sub->BSP->bDirty = false;
}
PolyNodeBuilder.ExtractMini(sub->BSP);
for (unsigned int i = 0; i < sub->BSP->Subsectors.Size(); ++i)
{
sub->BSP->Subsectors[i].sector = sub->sector;
}
}
void R_Subsector (subsector_t *sub);
static void R_AddPolyobjs(subsector_t *sub) static void R_AddPolyobjs(subsector_t *sub)
{ {
static TArray<FPolyNode *> sortedpolys; if (sub->BSP == NULL || sub->BSP->bDirty)
FPolyNode *pn = sub->polys;
sortedpolys.Clear();
while (pn != NULL)
{ {
sortedpolys.Push(pn); R_BuildPolyBSP(sub);
pn->dist = R_PointToDist2(pn->poly->CenterSpot.x - viewx, pn->poly->CenterSpot.y - viewy);
pn = pn->pnext;
} }
if (sortedpolys.Size() > 1) if (sub->BSP->Nodes.Size() == 0)
{ {
qsort(&sortedpolys[0], sortedpolys.Size(), sizeof (sortedpolys[0]), polycmp); R_Subsector(&sub->BSP->Subsectors[0]);
} }
else
for(unsigned i=0; i<sortedpolys.Size(); i++)
{ {
pn = sortedpolys[i]; R_RenderBSPNode(&sub->BSP->Nodes.Last());
for(unsigned j=0; j<pn->segs.Size(); j++)
{
R_AddLine(&pn->segs[j]);
}
} }
} }
// //
// R_Subsector // R_Subsector
// Determine floor/ceiling planes. // Determine floor/ceiling planes.
@ -1061,15 +1097,25 @@ void R_Subsector (subsector_t *sub)
int floorlightlevel; // killough 3/16/98: set floor lightlevel int floorlightlevel; // killough 3/16/98: set floor lightlevel
int ceilinglightlevel; // killough 4/11/98 int ceilinglightlevel; // killough 4/11/98
#if 0
#ifdef RANGECHECK #ifdef RANGECHECK
if (sub - subsectors >= (ptrdiff_t)numsubsectors) if (sub - subsectors >= (ptrdiff_t)numsubsectors)
I_Error ("R_Subsector: ss %ti with numss = %i", sub - subsectors, numsubsectors); I_Error ("R_Subsector: ss %ti with numss = %i", sub - subsectors, numsubsectors);
#endif #endif
#endif
assert(sub->sector != NULL);
if (sub->polys)
{ // Render the polyobjs in the subsector first
R_AddPolyobjs(sub);
return;
}
frontsector = sub->sector; frontsector = sub->sector;
frontsector->MoreFlags |= SECF_DRAWN; frontsector->MoreFlags |= SECF_DRAWN;
count = sub->numlines; count = sub->numlines;
line = &segs[sub->firstline]; line = sub->firstline;
// killough 3/8/98, 4/4/98: Deep water / fake ceiling effect // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect
frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel,
@ -1133,15 +1179,13 @@ void R_Subsector (subsector_t *sub)
ceilinglightlevel : floorlightlevel, FakeSide); ceilinglightlevel : floorlightlevel, FakeSide);
// [RH] Add particles // [RH] Add particles
int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel)/2 + r_actualextralight); if ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors)
for (WORD i = ParticlesInSubsec[(unsigned int)(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) { // Only do it for the main BSP.
{ int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel)/2 + r_actualextralight);
R_ProjectParticle (Particles + i, subsectors[sub-subsectors].sector, shade, FakeSide); for (WORD i = ParticlesInSubsec[(unsigned int)(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext)
} {
R_ProjectParticle (Particles + i, subsectors[sub-subsectors].sector, shade, FakeSide);
if (sub->polys) }
{ // Render the polyobjs in the subsector first
R_AddPolyobjs(sub);
} }
while (count--) while (count--)

View file

@ -925,22 +925,7 @@ struct msecnode_t
}; };
struct FPolyNode; struct FPolyNode;
struct FMiniBSP;
//
// A SubSector.
// References a Sector.
// Basically, this is a list of LineSegs indicating the visible walls that
// define (all or some) sides of a convex BSP leaf.
//
struct subsector_t
{
sector_t *sector;
DWORD numlines;
DWORD firstline;
FPolyNode *polys;
int validcount;
fixed_t CenterX, CenterY;
};
// //
// The LineSeg. // The LineSeg.
@ -957,12 +942,27 @@ struct seg_t
sector_t* frontsector; sector_t* frontsector;
sector_t* backsector; // NULL for one-sided lines sector_t* backsector; // NULL for one-sided lines
subsector_t* Subsector;
seg_t* PartnerSeg; seg_t* PartnerSeg;
BITFIELD bPolySeg:1; BITFIELD bPolySeg:1;
}; };
//
// A SubSector.
// References a Sector.
// Basically, this is a list of LineSegs indicating the visible walls that
// define (all or some) sides of a convex BSP leaf.
//
struct subsector_t
{
sector_t *sector;
FPolyNode *polys;
FMiniBSP *BSP;
seg_t *firstline;
DWORD numlines;
};
// //
@ -985,6 +985,22 @@ struct node_t
}; };
// An entire BSP tree.
struct FMiniBSP
{
FMiniBSP();
bool bDirty;
TArray<node_t> Nodes;
TArray<seg_t> Segs;
TArray<subsector_t> Subsectors;
TArray<vertex_t> Verts;
};
// posts are runs of non masked source pixels // posts are runs of non masked source pixels
struct column_t struct column_t
{ {

View file

@ -1401,7 +1401,7 @@ void RP_Subsector (subsector_t *sub)
frontsector = sub->sector; frontsector = sub->sector;
count = sub->numlines; count = sub->numlines;
line = &segs[sub->firstline]; line = sub->firstline;
// killough 3/8/98, 4/4/98: Deep water / fake ceiling effect // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect
frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel,

View file

@ -1480,17 +1480,17 @@ int side_t::GetLightLevel (bool foggy, int baselight, int *pfakecontrast) const
if (((level.flags2 & LEVEL2_SMOOTHLIGHTING) || (Flags & WALLF_SMOOTHLIGHTING) || r_fakecontrast == 2) && if (((level.flags2 & LEVEL2_SMOOTHLIGHTING) || (Flags & WALLF_SMOOTHLIGHTING) || r_fakecontrast == 2) &&
linedef->dx != 0) linedef->dx != 0)
{ {
rel = int // OMG LEE KILLOUGH LIVES! :/ rel = xs_RoundToInt // OMG LEE KILLOUGH LIVES! :/
( (
(float(level.WallHorizLight) level.WallHorizLight
+abs(atan(float(linedef->dy)/float(linedef->dx))/float(1.57079)) + fabs(atan(double(linedef->dy) / linedef->dx) / 1.57079)
*float(level.WallVertLight - level.WallHorizLight)) * (level.WallVertLight - level.WallHorizLight)
); );
} }
else else
{ {
rel = linedef->dx==0? level.WallVertLight : rel = linedef->dx == 0 ? level.WallVertLight :
linedef->dy==0? level.WallHorizLight : 0; linedef->dy == 0 ? level.WallHorizLight : 0;
} }
if (pfakecontrast != NULL) if (pfakecontrast != NULL)
{ {
@ -2008,80 +2008,12 @@ int WallMost (short *mostbuf, const secplane_t &plane)
return bad; return bad;
} }
void PrepWall (fixed_t *swall, fixed_t *lwall, fixed_t walxrepeat) static void PrepWallRoundFix(fixed_t *lwall, fixed_t walxrepeat)
{ // swall = scale, lwall = texturecolumn {
int x;
float top, bot, i;
float xrepeat = (float)walxrepeat;
float ol, l, topinc, botinc;
i = (float)(WallSX1 - centerx);
top = WallUoverZorg + WallUoverZstep * i;
bot = WallInvZorg + WallInvZstep * i;
topinc = WallUoverZstep * 4.f;
botinc = WallInvZstep * 4.f;
x = WallSX1;
l = top / bot;
swall[x] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg);
lwall[x] = xs_RoundToInt(l * xrepeat);
// As long as l is invalid, step one column at a time so that
// we can get as many correct texture columns as possible.
while (l > 1.0 && x+1 < WallSX2)
{
l = (top += WallUoverZstep) / (bot += WallInvZstep);
x++;
swall[x] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg);
lwall[x] = xs_RoundToInt(l * xrepeat);
}
l *= xrepeat;
while (x+4 < WallSX2)
{
top += topinc; bot += botinc;
ol = l; l = top / bot;
swall[x+4] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg);
lwall[x+4] = xs_RoundToInt(l *= xrepeat);
i = (ol+l) * 0.5f;
lwall[x+2] = xs_RoundToInt(i);
lwall[x+1] = xs_RoundToInt((ol+i) * 0.5f);
lwall[x+3] = xs_RoundToInt((l+i) * 0.5f);
swall[x+2] = ((swall[x]+swall[x+4])>>1);
swall[x+1] = ((swall[x]+swall[x+2])>>1);
swall[x+3] = ((swall[x+4]+swall[x+2])>>1);
x += 4;
}
if (x+2 < WallSX2)
{
top += topinc * 0.5f; bot += botinc * 0.5f;
ol = l; l = top / bot;
swall[x+2] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg);
lwall[x+2] = xs_RoundToInt(l *= xrepeat);
lwall[x+1] = xs_RoundToInt((l+ol)*0.5f);
swall[x+1] = (swall[x]+swall[x+2])>>1;
x += 2;
}
if (x+1 < WallSX2)
{
l = (top + WallUoverZstep) / (bot + WallInvZstep);
swall[x+1] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg);
lwall[x+1] = xs_RoundToInt(l * xrepeat);
}
/*
for (x = WallSX1; x < WallSX2; x++)
{
frac = top / bot;
lwall[x] = xs_RoundToInt(frac * xrepeat);
swall[x] = xs_RoundToInt(frac * WallDepthScale + WallDepthOrg);
top += WallUoverZstep;
bot += WallInvZstep;
}
*/
// fix for rounding errors // fix for rounding errors
fixed_t fix = (MirrorFlags & RF_XFLIP) ? walxrepeat-1 : 0; fixed_t fix = (MirrorFlags & RF_XFLIP) ? walxrepeat-1 : 0;
int x;
if (WallSX1 > 0) if (WallSX1 > 0)
{ {
for (x = WallSX1; x < WallSX2; x++) for (x = WallSX1; x < WallSX2; x++)
@ -2110,85 +2042,52 @@ void PrepWall (fixed_t *swall, fixed_t *lwall, fixed_t walxrepeat)
} }
} }
void PrepLWall (fixed_t *lwall, fixed_t walxrepeat) void PrepWall (fixed_t *swall, fixed_t *lwall, fixed_t walxrepeat)
{ // lwall = texturecolumn { // swall = scale, lwall = texturecolumn
int x; double top, bot, i;
float top, bot, i; double xrepeat = walxrepeat;
float xrepeat = (float)walxrepeat; double topinc, botinc;
float ol, l, topinc, botinc;
i = (float)(WallSX1 - centerx); i = WallSX1 - centerx;
top = WallUoverZorg + WallUoverZstep * i; top = WallUoverZorg + WallUoverZstep * i;
bot = WallInvZorg + WallInvZstep * i; bot = WallInvZorg + WallInvZstep * i;
topinc = WallUoverZstep * 4.f; topinc = WallUoverZstep * 4.f;
botinc = WallInvZstep * 4.f; botinc = WallInvZstep * 4.f;
x = WallSX1; for (int x = WallSX1; x < WallSX2; x++)
{
double frac = top / bot;
lwall[x] = xs_RoundToInt(frac * xrepeat);
swall[x] = xs_RoundToInt(frac * WallDepthScale + WallDepthOrg);
top += WallUoverZstep;
bot += WallInvZstep;
}
PrepWallRoundFix(lwall, walxrepeat);
}
l = top / bot; void PrepLWall (fixed_t *lwall, fixed_t walxrepeat)
lwall[x] = xs_RoundToInt(l * xrepeat); { // lwall = texturecolumn
// As long as l is invalid, step one column at a time so that double top, bot, i;
// we can get as many correct texture columns as possible. double xrepeat = walxrepeat;
while (l > 1.0 && x+1 < WallSX2) double topinc, botinc;
{ double topstep;
l = (top += WallUoverZstep) / (bot += WallInvZstep);
lwall[++x] = xs_RoundToInt(l * xrepeat);
}
l *= xrepeat;
while (x+4 < WallSX2)
{
top += topinc; bot += botinc;
ol = l; l = top / bot;
lwall[x+4] = xs_RoundToInt(l *= xrepeat);
i = (ol+l) * 0.5f; i = WallSX1 - centerx;
lwall[x+2] = xs_RoundToInt(i); top = WallUoverZorg + WallUoverZstep * i;
lwall[x+1] = xs_RoundToInt((ol+i) * 0.5f); bot = WallInvZorg + WallInvZstep * i;
lwall[x+3] = xs_RoundToInt((l+i) * 0.5f); topinc = WallUoverZstep * 4.f;
x += 4; botinc = WallInvZstep * 4.f;
}
if (x+2 < WallSX2)
{
top += topinc * 0.5f; bot += botinc * 0.5f;
ol = l; l = top / bot;
lwall[x+2] = xs_RoundToInt(l *= xrepeat);
lwall[x+1] = xs_RoundToInt((l+ol)*0.5f);
x += 2;
}
if (x+1 < WallSX2)
{
l = (top + WallUoverZstep) / (bot + WallInvZstep);
lwall[x+1] = xs_RoundToInt(l * xrepeat);
}
// fix for rounding errors top *= xrepeat;
fixed_t fix = (MirrorFlags & RF_XFLIP) ? walxrepeat-1 : 0; topstep = WallUoverZstep * xrepeat;
if (WallSX1 > 0)
for (int x = WallSX1; x < WallSX2; x++)
{ {
for (x = WallSX1; x < WallSX2; x++) lwall[x] = xs_RoundToInt(top / bot);
{ top += topstep;
if ((unsigned)lwall[x] >= (unsigned)walxrepeat) bot += WallInvZstep;
{
lwall[x] = fix;
}
else
{
break;
}
}
}
fix = walxrepeat - 1 - fix;
for (x = WallSX2-1; x >= WallSX1; x--)
{
if ((unsigned)lwall[x] >= (unsigned)walxrepeat)
{
lwall[x] = fix;
}
else
{
break;
}
} }
PrepWallRoundFix(lwall, walxrepeat);
} }
// pass = 0: when seg is first drawn // pass = 0: when seg is first drawn

File diff suppressed because it is too large Load diff