mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-29 15:22:08 +00:00
- ClassifyLine now chooses either SSE2 or regular x87 math depending on whether
or not SSE2 is available at runtime. Since most of the time is spent in ClassifyLine, using SSE2 in just this one function helps the most. - Nodebuilding is a little faster if we inline PointOnSide. - Changed FEventTree into a regular binary tree, since there just aren't enough nodes inserted into it to make a red-black tree worthwhile. - Added more checks at the start of ClassifyLine so that it has a better chance of avoiding the more complicated checking, and it seems to have paid off with a reasonably modest performance boost. - Added a "vertex map" for ZDBSP's vertex selection. (Think BLOCKMAP for vertices instead of lines.) On large maps, this can result in a very significant speed up. (In one particular map, ZDBSP had previously spent 40% of its time just scanning through all the vertices in the map. Now the time it spends finding vertices is immeasurable.) On small maps, this won't make much of a difference, because the number of vertices to search was so small to begin with. SVN r173 (trunk)
This commit is contained in:
parent
6975103dd9
commit
7a601515df
13 changed files with 604 additions and 369 deletions
|
@ -1,4 +1,10 @@
|
||||||
# created on 4/12/2006 by James Bentler
|
# created on 4/12/2006 by James Bentler
|
||||||
|
|
||||||
|
# RH says: Might I suggest using -MMD instead of -MM? You can create the
|
||||||
|
# .o files and the .d files in the same step and avoid rerunning make.
|
||||||
|
# I'd do it myself, but I don't want to break anything without testing
|
||||||
|
# it first.
|
||||||
|
|
||||||
CXX ?= g++
|
CXX ?= g++
|
||||||
CC ?= gcc
|
CC ?= gcc
|
||||||
NASM ?= nasm
|
NASM ?= nasm
|
||||||
|
@ -105,6 +111,11 @@ _obj_,$(OBJDIR)/$(patsubst %.c,%.o,$(notdir $$$(src))),$(CBUILD_PATTERN)))))
|
||||||
$(OBJDIR)/%.o:
|
$(OBJDIR)/%.o:
|
||||||
$(CXX) -c $(CXXFLAGS) -o $@ -c $<
|
$(CXX) -c $(CXXFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
# Hi, you Linux people. This works with Makefile.mingw, so i assume it works here too.
|
||||||
|
# This file needs special handling so that it actually gets compiled with SSE2 support.
|
||||||
|
$(OBJDIR)/nodebuild_classify_sse2.o: nodebuild_classify_sse2.cpp
|
||||||
|
$(CXX) $(CXXFLAGS) -msse2 -mfpmath=sse -c -o $@ $<
|
||||||
|
|
||||||
# start a new instance of make after dependency files have been made
|
# start a new instance of make after dependency files have been made
|
||||||
deps: $(DEPS)
|
deps: $(DEPS)
|
||||||
ifdef RESTART
|
ifdef RESTART
|
||||||
|
|
|
@ -19,9 +19,9 @@ $(RELEASETARGET): game
|
||||||
$(DEBUGTARGET): debuggame
|
$(DEBUGTARGET): debuggame
|
||||||
|
|
||||||
basetools: ccdv.exe
|
basetools: ccdv.exe
|
||||||
$(MAKE) -C tools/lemon -f Makefile.mgw
|
$(MAKE) -C tools/lemon -f Makefile
|
||||||
$(MAKE) -C tools/re2c -f Makefile.mgw
|
$(MAKE) -C tools/re2c -f Makefile
|
||||||
$(MAKE) -C wadsrc -f Makefile.mgw
|
$(MAKE) -C wadsrc -f Makefile
|
||||||
$(MAKE) -C zlib -f Makefile.mgw
|
$(MAKE) -C zlib -f Makefile.mgw
|
||||||
$(MAKE) -C flac -f Makefile.mgw
|
$(MAKE) -C flac -f Makefile.mgw
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,8 @@ OBJECTS += \
|
||||||
$(OBJDIR)/m_random.o \
|
$(OBJDIR)/m_random.o \
|
||||||
$(OBJDIR)/mus2midi.o \
|
$(OBJDIR)/mus2midi.o \
|
||||||
$(OBJDIR)/nodebuild.o \
|
$(OBJDIR)/nodebuild.o \
|
||||||
|
$(OBJDIR)/nodebuild_classify_nosse2.o \
|
||||||
|
$(OBJDIR)/nodebuild_classify_sse2.o \
|
||||||
$(OBJDIR)/nodebuild_events.o \
|
$(OBJDIR)/nodebuild_events.o \
|
||||||
$(OBJDIR)/nodebuild_extract.o \
|
$(OBJDIR)/nodebuild_extract.o \
|
||||||
$(OBJDIR)/nodebuild_gl.o \
|
$(OBJDIR)/nodebuild_gl.o \
|
||||||
|
@ -393,6 +395,10 @@ all: $(TARGET)
|
||||||
$(OBJDIR)/%.o : %.cpp
|
$(OBJDIR)/%.o : %.cpp
|
||||||
$(CCDV) $(CXX) $(CXXFLAGS) -o $@ -c $<
|
$(CCDV) $(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
# This file needs special handling so that it actually gets compiled with SSE2 support.
|
||||||
|
$(OBJDIR)/nodebuild_classify_sse2.o: nodebuild_classify_sse2.cpp
|
||||||
|
$(CCDV) $(CXX) $(CXXFLAGS) -msse2 -mfpmath=sse -c -o $@ $<
|
||||||
|
|
||||||
$(OBJDIR)/%.o : %.nas
|
$(OBJDIR)/%.o : %.nas
|
||||||
$(CCDV) nasmw -o $@ -f win32 $<
|
$(CCDV) nasmw -o $@ -f win32 $<
|
||||||
|
|
||||||
|
|
|
@ -142,6 +142,10 @@ else
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# How do you pass -msse2 -mfpmath=sse2 with cbuild when compiling nodebuild_classify_sse2.cpp?
|
||||||
|
# Since I don't know the answer, that means you won't get an SSE2-supporting nodebuilder if
|
||||||
|
# you build with cbuild. Use make instead.
|
||||||
|
|
||||||
${COMPILER} "autostart.cpp \
|
${COMPILER} "autostart.cpp \
|
||||||
a.nas \
|
a.nas \
|
||||||
misc.nas \
|
misc.nas \
|
||||||
|
@ -204,6 +208,8 @@ ${COMPILER} "autostart.cpp \
|
||||||
m_random.cpp \
|
m_random.cpp \
|
||||||
mus2midi.cpp \
|
mus2midi.cpp \
|
||||||
nodebuild.cpp \
|
nodebuild.cpp \
|
||||||
|
nodebuild_classify_nosse2.cpp \
|
||||||
|
nodebuild_classify_sse2.cpp \
|
||||||
nodebuild_events.cpp \
|
nodebuild_events.cpp \
|
||||||
nodebuild_extract.cpp \
|
nodebuild_extract.cpp \
|
||||||
nodebuild_gl.cpp \
|
nodebuild_gl.cpp \
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
June 6, 2006
|
||||||
|
- Integrated recent ZDBSP improvements into the internal nodebuilder:
|
||||||
|
- ClassifyLine now chooses either SSE2 or regular x87 math depending on whether
|
||||||
|
or not SSE2 is available at runtime. Since most of the time is spent in
|
||||||
|
ClassifyLine, using SSE2 in just this one function helps the most.
|
||||||
|
- Nodebuilding is a little faster if we inline PointOnSide.
|
||||||
|
- Changed FEventTree into a regular binary tree, since there just aren't enough
|
||||||
|
nodes inserted into it to make a red-black tree worthwhile.
|
||||||
|
- Added more checks at the start of ClassifyLine so that it has a better chance
|
||||||
|
of avoiding the more complicated checking, and it seems to have paid off with
|
||||||
|
a reasonably modest performance boost.
|
||||||
|
- Added a "vertex map" for ZDBSP's vertex selection. (Think BLOCKMAP for
|
||||||
|
vertices instead of lines.) On large maps, this can result in a very
|
||||||
|
significant speed up. (In one particular map, ZDBSP had previously
|
||||||
|
spent 40% of its time just scanning through all the vertices in the
|
||||||
|
map. Now the time it spends finding vertices is immeasurable.) On small maps,
|
||||||
|
this won't make much of a difference, because the number of vertices to search
|
||||||
|
was so small to begin with.
|
||||||
|
|
||||||
June 3, 2006 (Changes by Graf Zahl)
|
June 3, 2006 (Changes by Graf Zahl)
|
||||||
- Added a StartConversation special that allows automatic activation of Strife
|
- Added a StartConversation special that allows automatic activation of Strife
|
||||||
dialogs.
|
dialogs.
|
||||||
|
@ -22,9 +41,6 @@ May 31, 2006
|
||||||
- Red-Black Tree implementation was broken and colored every node red.
|
- Red-Black Tree implementation was broken and colored every node red.
|
||||||
- Moved most of the code for outputting degenerate GL subsectors into another
|
- Moved most of the code for outputting degenerate GL subsectors into another
|
||||||
function.
|
function.
|
||||||
- Removed forgotten debugging file dump from WriteSSectors2().
|
|
||||||
- Enabled reference optimization and COMDAT folding in the linker for a slightly
|
|
||||||
smaller executable.
|
|
||||||
|
|
||||||
May 31, 2006 (Changes by Graf Zahl)
|
May 31, 2006 (Changes by Graf Zahl)
|
||||||
- Fixed: Ammo items dropped by monsters that have a default amount of 1 didn't
|
- Fixed: Ammo items dropped by monsters that have a default amount of 1 didn't
|
||||||
|
|
|
@ -56,14 +56,6 @@ const int MaxSegs = 64;
|
||||||
const int SplitCost = 8;
|
const int SplitCost = 8;
|
||||||
const int AAPreference = 16;
|
const int AAPreference = 16;
|
||||||
|
|
||||||
// Points within this distance of a line will be considered on the line.
|
|
||||||
// Units are in fixed_ts.
|
|
||||||
const double SIDE_EPSILON = 6.5536;
|
|
||||||
|
|
||||||
// Vertices within this distance of each other vertically and horizontally
|
|
||||||
// will be considered as the same vertex.
|
|
||||||
const fixed_t VERTEX_EPSILON = 6;
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#define D(x) x
|
#define D(x) x
|
||||||
#else
|
#else
|
||||||
|
@ -72,9 +64,10 @@ const fixed_t VERTEX_EPSILON = 6;
|
||||||
|
|
||||||
FNodeBuilder::FNodeBuilder (FLevel &level,
|
FNodeBuilder::FNodeBuilder (FLevel &level,
|
||||||
TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors,
|
TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors,
|
||||||
bool makeGLNodes)
|
bool makeGLNodes, bool enableSSE2)
|
||||||
: Level (level), GLNodes (makeGLNodes), SegsStuffed (0)
|
: Level(level), GLNodes(makeGLNodes), EnableSSE2(enableSSE2), SegsStuffed(0)
|
||||||
{
|
{
|
||||||
|
VertexMap = new FVertexMap (*this, Level.MinX, Level.MinY, Level.MaxX, Level.MaxY);
|
||||||
FindUsedVertices (Level.Vertices, Level.NumVertices);
|
FindUsedVertices (Level.Vertices, Level.NumVertices);
|
||||||
MakeSegsFromSides ();
|
MakeSegsFromSides ();
|
||||||
FindPolyContainers (polyspots, anchors);
|
FindPolyContainers (polyspots, anchors);
|
||||||
|
@ -82,6 +75,14 @@ FNodeBuilder::FNodeBuilder (FLevel &level,
|
||||||
BuildTree ();
|
BuildTree ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FNodeBuilder::~FNodeBuilder()
|
||||||
|
{
|
||||||
|
if (VertexMap != 0)
|
||||||
|
{
|
||||||
|
delete VertexMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FNodeBuilder::BuildTree ()
|
void FNodeBuilder::BuildTree ()
|
||||||
{
|
{
|
||||||
fixed_t bbox[4];
|
fixed_t bbox[4];
|
||||||
|
@ -194,7 +195,7 @@ void FNodeBuilder::CreateSubsectorsForReal ()
|
||||||
sub.numlines = (DWORD)(SegList.Size() - sub.firstline);
|
sub.numlines = (DWORD)(SegList.Size() - sub.firstline);
|
||||||
|
|
||||||
// Sort segs by linedef for special effects
|
// Sort segs by linedef for special effects
|
||||||
qsort (&SegList[sub.firstline], sub.numlines, sizeof(int), SortSegs);
|
qsort (&SegList[sub.firstline], sub.numlines, sizeof(USegPtr), SortSegs);
|
||||||
|
|
||||||
// Convert seg pointers into indices
|
// Convert seg pointers into indices
|
||||||
for (unsigned int i = sub.firstline; i < SegList.Size(); ++i)
|
for (unsigned int i = sub.firstline; i < SegList.Size(); ++i)
|
||||||
|
@ -701,52 +702,6 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FNodeBuilder::ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2)
|
|
||||||
{
|
|
||||||
const FPrivVert *v1 = &Vertices[seg->v1];
|
|
||||||
const FPrivVert *v2 = &Vertices[seg->v2];
|
|
||||||
sidev1 = PointOnSide (v1->x, v1->y, node.x, node.y, node.dx, node.dy);
|
|
||||||
sidev2 = PointOnSide (v2->x, v2->y, node.x, node.y, node.dx, node.dy);
|
|
||||||
|
|
||||||
if ((sidev1 | sidev2) == 0)
|
|
||||||
{ // 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,
|
|
||||||
// it goes in front. Otherwise, it goes in back.
|
|
||||||
|
|
||||||
if (node.dx != 0)
|
|
||||||
{
|
|
||||||
if ((node.dx > 0 && v2->x > v1->x) || (node.dx < 0 && v2->x < v1->x))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((node.dy > 0 && v2->y > v1->y) || (node.dy < 0 && v2->y < v1->y))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (sidev1 <= 0 && sidev2 <= 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else if (sidev1 >= 0 && sidev2 >= 0)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
outset0 = DWORD_MAX;
|
outset0 = DWORD_MAX;
|
||||||
|
@ -809,24 +764,7 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
|
||||||
newvert.y = Vertices[seg->v1].y;
|
newvert.y = Vertices[seg->v1].y;
|
||||||
newvert.x += fixed_t(frac * double(Vertices[seg->v2].x - newvert.x));
|
newvert.x += fixed_t(frac * double(Vertices[seg->v2].x - newvert.x));
|
||||||
newvert.y += fixed_t(frac * double(Vertices[seg->v2].y - newvert.y));
|
newvert.y += fixed_t(frac * double(Vertices[seg->v2].y - newvert.y));
|
||||||
for (i = 0; i < Vertices.Size(); ++i)
|
vertnum = VertexMap->SelectVertexClose (newvert);
|
||||||
{
|
|
||||||
if (abs(Vertices[i].x - newvert.x) < VERTEX_EPSILON &&
|
|
||||||
abs(Vertices[i].y - newvert.y) < VERTEX_EPSILON)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i < Vertices.Size())
|
|
||||||
{
|
|
||||||
vertnum = i;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newvert.segs = DWORD_MAX;
|
|
||||||
newvert.segs2 = DWORD_MAX;
|
|
||||||
vertnum = Vertices.Push (newvert);
|
|
||||||
}
|
|
||||||
|
|
||||||
seg2 = SplitSeg (set, vertnum, sidev1);
|
seg2 = SplitSeg (set, vertnum, sidev1);
|
||||||
|
|
||||||
|
@ -1049,33 +987,6 @@ double FNodeBuilder::InterceptVector (const node_t &splitter, const FPrivSeg &se
|
||||||
return frac;
|
return frac;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int dy)
|
|
||||||
{
|
|
||||||
// For most cases, a simple dot product is enough.
|
|
||||||
double d_dx = double(dx);
|
|
||||||
double d_dy = double(dy);
|
|
||||||
double d_x = double(x);
|
|
||||||
double d_y = double(y);
|
|
||||||
double d_x1 = double(x1);
|
|
||||||
double d_y1 = double(y1);
|
|
||||||
|
|
||||||
double s_num = (d_y1-d_y)*d_dx - (d_x1-d_x)*d_dy;
|
|
||||||
|
|
||||||
if (fabs(s_num) < 17179869184.0) // 4<<32
|
|
||||||
{
|
|
||||||
// Either the point is very near the line, or the segment defining
|
|
||||||
// the line is very short: Do a more expensive test to determine
|
|
||||||
// just how far from the line the point is.
|
|
||||||
double l = sqrt(d_dx*d_dx+d_dy*d_dy);
|
|
||||||
double dist = fabs(s_num)/l;
|
|
||||||
if (dist < SIDE_EPSILON)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s_num > 0.0 ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FNodeBuilder::PrintSet (int l, DWORD set)
|
void FNodeBuilder::PrintSet (int l, DWORD set)
|
||||||
{
|
{
|
||||||
Printf ("set %d:\n", l);
|
Printf ("set %d:\n", l);
|
||||||
|
|
122
src/nodebuild.h
122
src/nodebuild.h
|
@ -11,7 +11,6 @@ struct FEventInfo
|
||||||
struct FEvent
|
struct FEvent
|
||||||
{
|
{
|
||||||
FEvent *Parent, *Left, *Right;
|
FEvent *Parent, *Left, *Right;
|
||||||
enum { RED, BLACK } Color;
|
|
||||||
double Distance;
|
double Distance;
|
||||||
FEventInfo Info;
|
FEventInfo Info;
|
||||||
};
|
};
|
||||||
|
@ -28,7 +27,6 @@ public:
|
||||||
|
|
||||||
FEvent *GetNewNode ();
|
FEvent *GetNewNode ();
|
||||||
void Insert (FEvent *event);
|
void Insert (FEvent *event);
|
||||||
void Delete (FEvent *event);
|
|
||||||
FEvent *FindEvent (double distance) const;
|
FEvent *FindEvent (double distance) const;
|
||||||
void DeleteAll ();
|
void DeleteAll ();
|
||||||
|
|
||||||
|
@ -37,9 +35,6 @@ private:
|
||||||
FEvent *Root;
|
FEvent *Root;
|
||||||
FEvent *Spare;
|
FEvent *Spare;
|
||||||
|
|
||||||
void LeftRotate (FEvent *event);
|
|
||||||
void RightRotate (FEvent *event);
|
|
||||||
void DeleteFixUp (FEvent *event);
|
|
||||||
void DeletionTraverser (FEvent *event);
|
void DeletionTraverser (FEvent *event);
|
||||||
FEvent *Successor (FEvent *event) const;
|
FEvent *Successor (FEvent *event) const;
|
||||||
FEvent *Predecessor (FEvent *event) const;
|
FEvent *Predecessor (FEvent *event) const;
|
||||||
|
@ -91,12 +86,51 @@ class FNodeBuilder
|
||||||
DWORD Seg;
|
DWORD Seg;
|
||||||
bool Forward;
|
bool Forward;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Like a blockmap, but for vertices instead of lines
|
||||||
|
class FVertexMap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FVertexMap (FNodeBuilder &builder, fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy);
|
||||||
|
~FVertexMap ();
|
||||||
|
|
||||||
|
int SelectVertexExact (FPrivVert &vert);
|
||||||
|
int SelectVertexClose (FPrivVert &vert);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FNodeBuilder &MyBuilder;
|
||||||
|
TArray<int> *VertexGrid;
|
||||||
|
|
||||||
|
fixed_t MinX, MinY, MaxX, MaxY;
|
||||||
|
int BlocksWide, BlocksTall;
|
||||||
|
|
||||||
|
enum { BLOCK_SHIFT = 8 + FRACBITS };
|
||||||
|
enum { BLOCK_SIZE = 1 << BLOCK_SHIFT };
|
||||||
|
|
||||||
|
int InsertVertex (FPrivVert &vert);
|
||||||
|
inline int GetBlock (fixed_t x, fixed_t y)
|
||||||
|
{
|
||||||
|
assert (x >= MinX);
|
||||||
|
assert (y >= MinY);
|
||||||
|
assert (x <= MaxX);
|
||||||
|
assert (y <= MaxY);
|
||||||
|
return (unsigned(x - MinX) >> BLOCK_SHIFT) + (unsigned(y - MinY) >> BLOCK_SHIFT) * BlocksWide;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
friend class FVertexMap;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct FLevel
|
struct FLevel
|
||||||
{
|
{
|
||||||
vertex_t *Vertices; int NumVertices;
|
vertex_t *Vertices; int NumVertices;
|
||||||
side_t *Sides; int NumSides;
|
side_t *Sides; int NumSides;
|
||||||
line_t *Lines; int NumLines;
|
line_t *Lines; int NumLines;
|
||||||
|
|
||||||
|
fixed_t MinX, MinY, MaxX, MaxY;
|
||||||
|
|
||||||
|
void FindMapBounds ();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FPolyStart
|
struct FPolyStart
|
||||||
|
@ -107,7 +141,8 @@ public:
|
||||||
|
|
||||||
FNodeBuilder (FLevel &level,
|
FNodeBuilder (FLevel &level,
|
||||||
TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors,
|
TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors,
|
||||||
bool makeGLNodes);
|
bool makeGLNodes, bool enableSSE2);
|
||||||
|
~FNodeBuilder ();
|
||||||
|
|
||||||
void Extract (node_t *&nodes, int &nodeCount,
|
void Extract (node_t *&nodes, int &nodeCount,
|
||||||
seg_t *&segs, int &segCount,
|
seg_t *&segs, int &segCount,
|
||||||
|
@ -116,7 +151,15 @@ public:
|
||||||
|
|
||||||
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 : on line
|
||||||
|
// > 0 : behind line
|
||||||
|
|
||||||
|
static inline int PointOnSide (int x, int y, int x1, int y1, int dx, int dy);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
FVertexMap *VertexMap;
|
||||||
|
|
||||||
TArray<node_t> Nodes;
|
TArray<node_t> Nodes;
|
||||||
TArray<subsector_t> Subsectors;
|
TArray<subsector_t> Subsectors;
|
||||||
TArray<DWORD> SubsectorSets;
|
TArray<DWORD> SubsectorSets;
|
||||||
|
@ -130,18 +173,18 @@ private:
|
||||||
TArray<int> Colinear; // Loops with edges colinear to a splitter
|
TArray<int> Colinear; // Loops with edges colinear to a splitter
|
||||||
FEventTree Events; // Vertices intersected by the current splitter
|
FEventTree Events; // Vertices intersected by the current splitter
|
||||||
|
|
||||||
TArray<FSplitSharer> SplitSharers; // Segs collinear with the current splitter
|
TArray<FSplitSharer> SplitSharers; // Segs colinear with the current splitter
|
||||||
|
|
||||||
DWORD HackSeg; // Seg to force to back of splitter
|
DWORD HackSeg; // Seg to force to back of splitter
|
||||||
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;
|
||||||
|
|
||||||
void FindUsedVertices (vertex_t *vertices, int max);
|
void FindUsedVertices (vertex_t *vertices, int max);
|
||||||
int SelectVertexExact (FPrivVert &vertex);
|
|
||||||
void BuildTree ();
|
void BuildTree ();
|
||||||
void MakeSegsFromSides ();
|
void MakeSegsFromSides ();
|
||||||
int CreateSeg (int linenum, int sidenum);
|
int CreateSeg (int linenum, int sidenum);
|
||||||
|
@ -159,9 +202,17 @@ private:
|
||||||
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);
|
||||||
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 ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2);
|
|
||||||
int CountSegs (DWORD set) const;
|
int CountSegs (DWORD set) const;
|
||||||
|
|
||||||
|
// Returns:
|
||||||
|
// 0 = seg is in front
|
||||||
|
// 1 = seg is in back
|
||||||
|
// -1 = seg cuts the node
|
||||||
|
|
||||||
|
inline int ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2);
|
||||||
|
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);
|
||||||
void AddMinisegs (const node_t &node, DWORD splitseg, DWORD &fset, DWORD &rset);
|
void AddMinisegs (const node_t &node, DWORD splitseg, DWORD &fset, DWORD &rset);
|
||||||
|
@ -179,12 +230,55 @@ private:
|
||||||
|
|
||||||
static int STACK_ARGS SortSegs (const void *a, const void *b);
|
static int STACK_ARGS SortSegs (const void *a, const void *b);
|
||||||
|
|
||||||
// < 0 : in front of line
|
|
||||||
// == 0 : on line
|
|
||||||
// > 0 : behind line
|
|
||||||
|
|
||||||
int PointOnSide (int x, int y, int x1, int y1, int dx, int dy);
|
|
||||||
double InterceptVector (const node_t &splitter, const FPrivSeg &seg);
|
double InterceptVector (const node_t &splitter, const FPrivSeg &seg);
|
||||||
|
|
||||||
void PrintSet (int l, DWORD set);
|
void PrintSet (int l, DWORD set);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Points within this distance of a line will be considered on the line.
|
||||||
|
// Units are in fixed_ts.
|
||||||
|
const double SIDE_EPSILON = 6.5536;
|
||||||
|
|
||||||
|
inline int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int dy)
|
||||||
|
{
|
||||||
|
// For most cases, a simple dot product is enough.
|
||||||
|
double d_dx = double(dx);
|
||||||
|
double d_dy = double(dy);
|
||||||
|
double d_x = double(x);
|
||||||
|
double d_y = double(y);
|
||||||
|
double d_x1 = double(x1);
|
||||||
|
double d_y1 = double(y1);
|
||||||
|
|
||||||
|
double s_num = (d_y1-d_y)*d_dx - (d_x1-d_x)*d_dy;
|
||||||
|
|
||||||
|
if (fabs(s_num) < 17179869184.f) // 4<<32
|
||||||
|
{
|
||||||
|
// Either the point is very near the line, or the segment defining
|
||||||
|
// the line is very short: Do a more expensive test to determine
|
||||||
|
// just how far from the line the point is.
|
||||||
|
double l = d_dx*d_dx + d_dy*d_dy; // double l = sqrt(d_dx*d_dx+d_dy*d_dy);
|
||||||
|
double dist = s_num * s_num / l; // double dist = fabs(s_num)/l;
|
||||||
|
if (dist < SIDE_EPSILON*SIDE_EPSILON) // if (dist < SIDE_EPSILON)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s_num > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int FNodeBuilder::ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2)
|
||||||
|
{
|
||||||
|
#ifdef __SSE2__
|
||||||
|
// If compiling with SSE2 support everywhere, just use the SSE2 version.
|
||||||
|
return ClassifyLineSSE2 (node, seg, sidev1, sidev2);
|
||||||
|
#elif defined(_MSC_VER) && _MSC_VER < 1300
|
||||||
|
// VC 6 does not support SSE2 optimizations.
|
||||||
|
return ClassifyLine2 (node, seg, sidev1, sidev2);
|
||||||
|
#else
|
||||||
|
// Select the routine based on our flag.
|
||||||
|
if (EnableSSE2)
|
||||||
|
return ClassifyLineSSE2 (node, seg, sidev1, sidev2);
|
||||||
|
else
|
||||||
|
return ClassifyLine2 (node, seg, sidev1, sidev2);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
139
src/nodebuild_classify_nosse2.cpp
Normal file
139
src/nodebuild_classify_nosse2.cpp
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
#include "doomtype.h"
|
||||||
|
#include "nodebuild.h"
|
||||||
|
|
||||||
|
#define FAR_ENOUGH 17179869184.f // 4<<32
|
||||||
|
|
||||||
|
int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2)
|
||||||
|
{
|
||||||
|
const FPrivVert *v1 = &Vertices[seg->v1];
|
||||||
|
const FPrivVert *v2 = &Vertices[seg->v2];
|
||||||
|
|
||||||
|
double d_x1 = double(node.x);
|
||||||
|
double d_y1 = double(node.y);
|
||||||
|
double d_dx = double(node.dx);
|
||||||
|
double d_dy = double(node.dy);
|
||||||
|
double d_xv1 = double(v1->x);
|
||||||
|
double d_xv2 = double(v2->x);
|
||||||
|
double d_yv1 = double(v1->y);
|
||||||
|
double d_yv2 = double(v2->y);
|
||||||
|
|
||||||
|
double s_num1 = (d_y1 - d_yv1) * d_dx - (d_x1 - d_xv1) * d_dy;
|
||||||
|
double s_num2 = (d_y1 - d_yv2) * d_dx - (d_x1 - d_xv2) * d_dy;
|
||||||
|
|
||||||
|
int nears = 0;
|
||||||
|
|
||||||
|
if (s_num1 <= -FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
if (s_num2 <= -FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
sidev1 = sidev2 = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (s_num2 >= FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
sidev1 = 1;
|
||||||
|
sidev2 = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nears = 1;
|
||||||
|
}
|
||||||
|
else if (s_num1 >= FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
if (s_num2 >= FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
sidev1 = sidev2 = -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (s_num2 <= -FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
sidev1 = -1;
|
||||||
|
sidev2 = 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nears = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nears = 2 | int(fabs(s_num2) < FAR_ENOUGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nears)
|
||||||
|
{
|
||||||
|
double l = 1.f / (d_dx*d_dx + d_dy*d_dy);
|
||||||
|
if (nears & 2)
|
||||||
|
{
|
||||||
|
double dist = s_num1 * s_num1 * l;
|
||||||
|
if (dist < SIDE_EPSILON*SIDE_EPSILON)
|
||||||
|
{
|
||||||
|
sidev1 = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidev1 = s_num1 > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidev1 = s_num1 > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
if (nears & 1)
|
||||||
|
{
|
||||||
|
double dist = s_num2 * s_num2 * l;
|
||||||
|
if (dist < SIDE_EPSILON*SIDE_EPSILON)
|
||||||
|
{
|
||||||
|
sidev2 = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidev2 = s_num2 > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidev2 = s_num2 > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidev1 = s_num1 > 0.0 ? -1 : 1;
|
||||||
|
sidev2 = s_num2 > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sidev1 | sidev2) == 0)
|
||||||
|
{ // 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,
|
||||||
|
// it goes in front. Otherwise, it goes in back.
|
||||||
|
|
||||||
|
if (node.dx != 0)
|
||||||
|
{
|
||||||
|
if ((node.dx > 0 && v2->x > v1->x) || (node.dx < 0 && v2->x < v1->x))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((node.dy > 0 && v2->y > v1->y) || (node.dy < 0 && v2->y < v1->y))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (sidev1 <= 0 && sidev2 <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (sidev1 >= 0 && sidev2 >= 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
144
src/nodebuild_classify_sse2.cpp
Normal file
144
src/nodebuild_classify_sse2.cpp
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
#include "doomtype.h"
|
||||||
|
#include "nodebuild.h"
|
||||||
|
|
||||||
|
#define FAR_ENOUGH 17179869184.f // 4<<32
|
||||||
|
|
||||||
|
// This function is identical to the ClassifyLine2 version. So how does it use SSE2?
|
||||||
|
// Easy! By explicitly enabling SSE2 in the configuration properties for this one
|
||||||
|
// file, we can build it with SSE2 enabled without forcing SSE2 on the rest of the
|
||||||
|
// project.
|
||||||
|
|
||||||
|
int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2)
|
||||||
|
{
|
||||||
|
const FPrivVert *v1 = &Vertices[seg->v1];
|
||||||
|
const FPrivVert *v2 = &Vertices[seg->v2];
|
||||||
|
|
||||||
|
double d_x1 = double(node.x);
|
||||||
|
double d_y1 = double(node.y);
|
||||||
|
double d_dx = double(node.dx);
|
||||||
|
double d_dy = double(node.dy);
|
||||||
|
double d_xv1 = double(v1->x);
|
||||||
|
double d_xv2 = double(v2->x);
|
||||||
|
double d_yv1 = double(v1->y);
|
||||||
|
double d_yv2 = double(v2->y);
|
||||||
|
|
||||||
|
double s_num1 = (d_y1 - d_yv1) * d_dx - (d_x1 - d_xv1) * d_dy;
|
||||||
|
double s_num2 = (d_y1 - d_yv2) * d_dx - (d_x1 - d_xv2) * d_dy;
|
||||||
|
|
||||||
|
int nears = 0;
|
||||||
|
|
||||||
|
if (s_num1 <= -FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
if (s_num2 <= -FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
sidev1 = sidev2 = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (s_num2 >= FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
sidev1 = 1;
|
||||||
|
sidev2 = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nears = 1;
|
||||||
|
}
|
||||||
|
else if (s_num1 >= FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
if (s_num2 >= FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
sidev1 = sidev2 = -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (s_num2 <= -FAR_ENOUGH)
|
||||||
|
{
|
||||||
|
sidev1 = -1;
|
||||||
|
sidev2 = 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nears = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nears = 2 | int(fabs(s_num2) < FAR_ENOUGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nears)
|
||||||
|
{
|
||||||
|
double l = 1.f / (d_dx*d_dx + d_dy*d_dy);
|
||||||
|
if (nears & 2)
|
||||||
|
{
|
||||||
|
double dist = s_num1 * s_num1 * l;
|
||||||
|
if (dist < SIDE_EPSILON*SIDE_EPSILON)
|
||||||
|
{
|
||||||
|
sidev1 = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidev1 = s_num1 > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidev1 = s_num1 > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
if (nears & 1)
|
||||||
|
{
|
||||||
|
double dist = s_num2 * s_num2 * l;
|
||||||
|
if (dist < SIDE_EPSILON*SIDE_EPSILON)
|
||||||
|
{
|
||||||
|
sidev2 = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidev2 = s_num2 > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidev2 = s_num2 > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sidev1 = s_num1 > 0.0 ? -1 : 1;
|
||||||
|
sidev2 = s_num2 > 0.0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sidev1 | sidev2) == 0)
|
||||||
|
{ // 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,
|
||||||
|
// it goes in front. Otherwise, it goes in back.
|
||||||
|
|
||||||
|
if (node.dx != 0)
|
||||||
|
{
|
||||||
|
if ((node.dx > 0 && v2->x > v1->x) || (node.dx < 0 && v2->x < v1->x))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((node.dy > 0 && v2->y > v1->y) || (node.dy < 0 && v2->y < v1->y))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (sidev1 <= 0 && sidev2 <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (sidev1 >= 0 && sidev2 >= 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
|
@ -45,7 +45,6 @@ FEventTree::FEventTree ()
|
||||||
: Root (&Nil), Spare (NULL)
|
: Root (&Nil), Spare (NULL)
|
||||||
{
|
{
|
||||||
memset (&Nil, 0, sizeof(Nil));
|
memset (&Nil, 0, sizeof(Nil));
|
||||||
Nil.Color = FEvent::BLACK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FEventTree::~FEventTree ()
|
FEventTree::~FEventTree ()
|
||||||
|
@ -79,56 +78,6 @@ void FEventTree::DeletionTraverser (FEvent *node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FEventTree::LeftRotate (FEvent *x)
|
|
||||||
{
|
|
||||||
FEvent *y = x->Right;
|
|
||||||
x->Right = y->Left;
|
|
||||||
if (y->Left != &Nil)
|
|
||||||
{
|
|
||||||
y->Left->Parent = x;
|
|
||||||
}
|
|
||||||
y->Parent = x->Parent;
|
|
||||||
if (x->Parent == &Nil)
|
|
||||||
{
|
|
||||||
Root = y;
|
|
||||||
}
|
|
||||||
else if (x == x->Parent->Left)
|
|
||||||
{
|
|
||||||
x->Parent->Left = y;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
x->Parent->Right = y;
|
|
||||||
}
|
|
||||||
y->Left = x;
|
|
||||||
x->Parent = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FEventTree::RightRotate (FEvent *x)
|
|
||||||
{
|
|
||||||
FEvent *y = x->Left;
|
|
||||||
x->Left = y->Right;
|
|
||||||
if (y->Right != &Nil)
|
|
||||||
{
|
|
||||||
y->Right->Parent = x;
|
|
||||||
}
|
|
||||||
y->Parent = x->Parent;
|
|
||||||
if (x->Parent == &Nil)
|
|
||||||
{
|
|
||||||
Root = y;
|
|
||||||
}
|
|
||||||
else if (x == x->Parent->Left)
|
|
||||||
{
|
|
||||||
x->Parent->Left = y;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
x->Parent->Right = y;
|
|
||||||
}
|
|
||||||
y->Right = x;
|
|
||||||
x->Parent = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
FEvent *FEventTree::GetNewNode ()
|
FEvent *FEventTree::GetNewNode ()
|
||||||
{
|
{
|
||||||
FEvent *node;
|
FEvent *node;
|
||||||
|
@ -177,174 +126,6 @@ void FEventTree::Insert (FEvent *z)
|
||||||
}
|
}
|
||||||
z->Left = &Nil;
|
z->Left = &Nil;
|
||||||
z->Right = &Nil;
|
z->Right = &Nil;
|
||||||
|
|
||||||
z->Color = FEvent::RED;
|
|
||||||
while (z != Root && z->Parent->Color == FEvent::RED)
|
|
||||||
{
|
|
||||||
if (z->Parent == z->Parent->Parent->Left)
|
|
||||||
{
|
|
||||||
y = z->Parent->Parent->Right;
|
|
||||||
if (y->Color == FEvent::RED)
|
|
||||||
{
|
|
||||||
z->Parent->Color = FEvent::BLACK;
|
|
||||||
y->Color = FEvent::BLACK;
|
|
||||||
z->Parent->Parent->Color = FEvent::RED;
|
|
||||||
z = z->Parent->Parent;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (z == z->Parent->Right)
|
|
||||||
{
|
|
||||||
z = z->Parent;
|
|
||||||
LeftRotate (z);
|
|
||||||
}
|
|
||||||
z->Parent->Color = FEvent::BLACK;
|
|
||||||
z->Parent->Parent->Color = FEvent::RED;
|
|
||||||
RightRotate (z->Parent->Parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
y = z->Parent->Parent->Left;
|
|
||||||
if (y->Color == FEvent::RED)
|
|
||||||
{
|
|
||||||
z->Parent->Color = FEvent::BLACK;
|
|
||||||
y->Color = FEvent::BLACK;
|
|
||||||
z->Parent->Parent->Color = FEvent::RED;
|
|
||||||
z = z->Parent->Parent;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (z == z->Parent->Left)
|
|
||||||
{
|
|
||||||
z = z->Parent;
|
|
||||||
RightRotate (z);
|
|
||||||
}
|
|
||||||
z->Parent->Color = FEvent::BLACK;
|
|
||||||
z->Parent->Parent->Color = FEvent::RED;
|
|
||||||
LeftRotate (z->Parent->Parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Root->Color = FEvent::BLACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FEventTree::Delete (FEvent *z)
|
|
||||||
{
|
|
||||||
FEvent *x, *y;
|
|
||||||
|
|
||||||
if (z->Left == &Nil || z->Right == &Nil)
|
|
||||||
{
|
|
||||||
y = z;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
y = Successor (z);
|
|
||||||
}
|
|
||||||
if (y->Left != &Nil)
|
|
||||||
{
|
|
||||||
x = y->Left;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
x = y->Right;
|
|
||||||
}
|
|
||||||
x->Parent = y->Parent;
|
|
||||||
if (y->Parent == &Nil)
|
|
||||||
{
|
|
||||||
Root = x;
|
|
||||||
}
|
|
||||||
else if (y == y->Parent->Left)
|
|
||||||
{
|
|
||||||
y->Parent->Left = x;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
y->Parent->Right = x;
|
|
||||||
}
|
|
||||||
if (y != z)
|
|
||||||
{
|
|
||||||
z->Distance = y->Distance;
|
|
||||||
z->Info = y->Info;
|
|
||||||
}
|
|
||||||
if (y->Color == FEvent::BLACK)
|
|
||||||
{
|
|
||||||
DeleteFixUp (x);
|
|
||||||
}
|
|
||||||
|
|
||||||
y->Left = Spare;
|
|
||||||
Spare = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FEventTree::DeleteFixUp (FEvent *x)
|
|
||||||
{
|
|
||||||
FEvent *w;
|
|
||||||
|
|
||||||
while (x != Root && x->Color == FEvent::BLACK)
|
|
||||||
{
|
|
||||||
if (x == x->Parent->Left)
|
|
||||||
{
|
|
||||||
w = x->Parent->Right;
|
|
||||||
if (w->Color == FEvent::RED)
|
|
||||||
{
|
|
||||||
w->Color = FEvent::BLACK;
|
|
||||||
x->Parent->Color = FEvent::RED;
|
|
||||||
LeftRotate (x->Parent);
|
|
||||||
w = x->Parent->Right;
|
|
||||||
}
|
|
||||||
if (w->Left->Color == FEvent::BLACK && w->Right->Color == FEvent::BLACK)
|
|
||||||
{
|
|
||||||
w->Color = FEvent::RED;
|
|
||||||
x = x->Parent;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (w->Right->Color == FEvent::BLACK)
|
|
||||||
{
|
|
||||||
w->Left->Color = FEvent::BLACK;
|
|
||||||
w->Color = FEvent::RED;
|
|
||||||
RightRotate (w);
|
|
||||||
w = x->Parent->Right;
|
|
||||||
}
|
|
||||||
w->Color = x->Parent->Color;
|
|
||||||
x->Parent->Color = FEvent::BLACK;
|
|
||||||
w->Right->Color = FEvent::BLACK;
|
|
||||||
LeftRotate (x->Parent);
|
|
||||||
x = Root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
w = x->Parent->Left;
|
|
||||||
if (w->Color == FEvent::RED)
|
|
||||||
{
|
|
||||||
w->Color = FEvent::BLACK;
|
|
||||||
x->Parent->Color = FEvent::RED;
|
|
||||||
RightRotate (x->Parent);
|
|
||||||
w = x->Parent->Left;
|
|
||||||
}
|
|
||||||
if (w->Right->Color == FEvent::BLACK && w->Left->Color == FEvent::BLACK)
|
|
||||||
{
|
|
||||||
w->Color = FEvent::RED;
|
|
||||||
x = x->Parent;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (w->Left->Color == FEvent::BLACK)
|
|
||||||
{
|
|
||||||
w->Right->Color = FEvent::BLACK;
|
|
||||||
w->Color = FEvent::RED;
|
|
||||||
LeftRotate (w);
|
|
||||||
w = x->Parent->Left;
|
|
||||||
}
|
|
||||||
w->Color = x->Parent->Color;
|
|
||||||
x->Parent->Color = FEvent::BLACK;
|
|
||||||
w->Left->Color = FEvent::BLACK;
|
|
||||||
RightRotate (x->Parent);
|
|
||||||
x = Root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FEvent *FEventTree::Successor (FEvent *event) const
|
FEvent *FEventTree::Successor (FEvent *event) const
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "nodebuild.h"
|
#include "nodebuild.h"
|
||||||
|
#include "templates.h"
|
||||||
#include "m_bbox.h"
|
#include "m_bbox.h"
|
||||||
#include "r_main.h"
|
#include "r_main.h"
|
||||||
#include "i_system.h"
|
#include "i_system.h"
|
||||||
|
@ -52,6 +53,10 @@
|
||||||
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;
|
||||||
|
|
||||||
|
// Vertices within this distance of each other vertically and horizontally
|
||||||
|
// will be considered as the same vertex.
|
||||||
|
const fixed_t VERTEX_EPSILON = 6;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#define D(x) x
|
#define D(x) x
|
||||||
#else
|
#else
|
||||||
|
@ -68,40 +73,33 @@ angle_t FNodeBuilder::PointToAngle (fixed_t x, fixed_t y)
|
||||||
{
|
{
|
||||||
const double rad2bam = double(1<<30) / M_PI;
|
const double rad2bam = double(1<<30) / M_PI;
|
||||||
double ang = atan2 (double(y), double(x));
|
double ang = atan2 (double(y), double(x));
|
||||||
if (ang < 0.0)
|
|
||||||
{
|
|
||||||
ang = 2*M_PI+ang;
|
|
||||||
}
|
|
||||||
return angle_t(ang * rad2bam) << 1;
|
return angle_t(ang * rad2bam) << 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FNodeBuilder::FindUsedVertices (vertex_t *oldverts, int max)
|
void FNodeBuilder::FindUsedVertices (vertex_t *oldverts, int max)
|
||||||
{
|
{
|
||||||
size_t *map = (size_t *)alloca (max*sizeof(size_t));
|
int *map = (int *)alloca (max*sizeof(int));
|
||||||
int i;
|
int i;
|
||||||
FPrivVert newvert;
|
FPrivVert newvert;
|
||||||
|
|
||||||
memset (&map[0], -1, sizeof(size_t)*max);
|
memset (&map[0], -1, sizeof(int)*max);
|
||||||
|
|
||||||
newvert.segs = DWORD_MAX;
|
|
||||||
newvert.segs2 = DWORD_MAX;
|
|
||||||
|
|
||||||
for (i = 0; i < Level.NumLines; ++i)
|
for (i = 0; i < Level.NumLines; ++i)
|
||||||
{
|
{
|
||||||
ptrdiff_t v1 = Level.Lines[i].v1 - oldverts;
|
ptrdiff_t v1 = Level.Lines[i].v1 - oldverts;
|
||||||
ptrdiff_t v2 = Level.Lines[i].v2 - oldverts;
|
ptrdiff_t v2 = Level.Lines[i].v2 - oldverts;
|
||||||
|
|
||||||
if (map[v1] == (size_t)-1)
|
if (map[v1] == -1)
|
||||||
{
|
{
|
||||||
newvert.x = oldverts[v1].x;
|
newvert.x = oldverts[v1].x;
|
||||||
newvert.y = oldverts[v1].y;
|
newvert.y = oldverts[v1].y;
|
||||||
map[v1] = SelectVertexExact (newvert);
|
map[v1] = VertexMap->SelectVertexExact (newvert);
|
||||||
}
|
}
|
||||||
if (map[v2] == (size_t)-1)
|
if (map[v2] == -1)
|
||||||
{
|
{
|
||||||
newvert.x = oldverts[v2].x;
|
newvert.x = oldverts[v2].x;
|
||||||
newvert.y = oldverts[v2].y;
|
newvert.y = oldverts[v2].y;
|
||||||
map[v2] = SelectVertexExact (newvert);
|
map[v2] = VertexMap->SelectVertexExact (newvert);
|
||||||
}
|
}
|
||||||
|
|
||||||
Level.Lines[i].v1 = (vertex_t *)map[v1];
|
Level.Lines[i].v1 = (vertex_t *)map[v1];
|
||||||
|
@ -109,18 +107,6 @@ void FNodeBuilder::FindUsedVertices (vertex_t *oldverts, int max)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int FNodeBuilder::SelectVertexExact (FPrivVert &vertex)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < Vertices.Size(); ++i)
|
|
||||||
{
|
|
||||||
if (Vertices[i].x == vertex.x && Vertices[i].y == vertex.y)
|
|
||||||
{
|
|
||||||
return (int)i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (int)Vertices.Push (vertex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For every sidedef in the map, create a corresponding seg.
|
// For every sidedef in the map, create a corresponding seg.
|
||||||
|
|
||||||
void FNodeBuilder::MakeSegsFromSides ()
|
void FNodeBuilder::MakeSegsFromSides ()
|
||||||
|
@ -279,8 +265,7 @@ void FNodeBuilder::GroupSegPlanes ()
|
||||||
|
|
||||||
D(Printf ("%d planes from %d segs\n", planenum, Segs.Size()));
|
D(Printf ("%d planes from %d segs\n", planenum, Segs.Size()));
|
||||||
|
|
||||||
planenum = (planenum+7)/8;
|
PlaneChecked.Reserve ((planenum + 7) / 8);
|
||||||
PlaneChecked.Reserve (planenum);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -497,3 +482,120 @@ void FNodeBuilder::AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg)
|
||||||
if (v2->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v2->y;
|
if (v2->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v2->y;
|
||||||
if (v2->y > bbox[BOXTOP]) bbox[BOXTOP] = v2->y;
|
if (v2->y > bbox[BOXTOP]) bbox[BOXTOP] = v2->y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FNodeBuilder::FLevel::FindMapBounds ()
|
||||||
|
{
|
||||||
|
fixed_t minx, maxx, miny, maxy;
|
||||||
|
|
||||||
|
minx = maxx = Vertices[0].x;
|
||||||
|
miny = maxy = Vertices[0].y;
|
||||||
|
|
||||||
|
for (int i = 1; i < NumVertices; ++i)
|
||||||
|
{
|
||||||
|
if (Vertices[i].x < minx) minx = Vertices[i].x;
|
||||||
|
else if (Vertices[i].x > maxx) maxx = Vertices[i].x;
|
||||||
|
if (Vertices[i].y < miny) miny = Vertices[i].y;
|
||||||
|
else if (Vertices[i].y > maxy) maxy = Vertices[i].y;
|
||||||
|
}
|
||||||
|
|
||||||
|
MinX = minx;
|
||||||
|
MinY = miny;
|
||||||
|
MaxX = maxx;
|
||||||
|
MaxY = maxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
FNodeBuilder::FVertexMap::FVertexMap (FNodeBuilder &builder,
|
||||||
|
fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy)
|
||||||
|
: MyBuilder(builder)
|
||||||
|
{
|
||||||
|
MinX = minx;
|
||||||
|
MinY = miny;
|
||||||
|
BlocksWide = int(((double(maxx) - minx + 1) + (BLOCK_SIZE - 1)) / BLOCK_SIZE);
|
||||||
|
BlocksTall = int(((double(maxy) - miny + 1) + (BLOCK_SIZE - 1)) / BLOCK_SIZE);
|
||||||
|
MaxX = MinX + BlocksWide * BLOCK_SIZE - 1;
|
||||||
|
MaxY = MinY + BlocksTall * BLOCK_SIZE - 1;
|
||||||
|
VertexGrid = new TArray<int>[BlocksWide * BlocksTall];
|
||||||
|
}
|
||||||
|
|
||||||
|
FNodeBuilder::FVertexMap::~FVertexMap ()
|
||||||
|
{
|
||||||
|
delete[] VertexGrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FNodeBuilder::FVertexMap::SelectVertexExact (FNodeBuilder::FPrivVert &vert)
|
||||||
|
{
|
||||||
|
TArray<int> &block = VertexGrid[GetBlock (vert.x, vert.y)];
|
||||||
|
FPrivVert *vertices = &MyBuilder.Vertices[0];
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < block.Size(); ++i)
|
||||||
|
{
|
||||||
|
if (vertices[block[i]].x == vert.x && vertices[block[i]].y == vert.y)
|
||||||
|
{
|
||||||
|
return block[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not present: add it!
|
||||||
|
return InsertVertex (vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FNodeBuilder::FVertexMap::SelectVertexClose (FNodeBuilder::FPrivVert &vert)
|
||||||
|
{
|
||||||
|
TArray<int> &block = VertexGrid[GetBlock (vert.x, vert.y)];
|
||||||
|
FPrivVert *vertices = &MyBuilder.Vertices[0];
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < block.Size(); ++i)
|
||||||
|
{
|
||||||
|
if (abs(vertices[block[i]].x - vert.x) < VERTEX_EPSILON &&
|
||||||
|
abs(vertices[block[i]].y - vert.y) < VERTEX_EPSILON)
|
||||||
|
{
|
||||||
|
return block[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not present: add it!
|
||||||
|
return InsertVertex (vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FNodeBuilder::FVertexMap::InsertVertex (FNodeBuilder::FPrivVert &vert)
|
||||||
|
{
|
||||||
|
int vertnum;
|
||||||
|
|
||||||
|
vert.segs = DWORD_MAX;
|
||||||
|
vert.segs2 = DWORD_MAX;
|
||||||
|
vertnum = (int)MyBuilder.Vertices.Push (vert);
|
||||||
|
|
||||||
|
// If a vertex is near a block boundary, then it will be inserted on
|
||||||
|
// both sides of the boundary so that SelectVertexClose can find
|
||||||
|
// it by checking in only one block.
|
||||||
|
fixed_t minx = MAX (MinX, vert.x - VERTEX_EPSILON);
|
||||||
|
fixed_t maxx = MIN (MaxX, vert.x + VERTEX_EPSILON);
|
||||||
|
fixed_t miny = MAX (MinY, vert.y - VERTEX_EPSILON);
|
||||||
|
fixed_t maxy = MIN (MaxY, vert.y + VERTEX_EPSILON);
|
||||||
|
|
||||||
|
int blk[4] =
|
||||||
|
{
|
||||||
|
GetBlock (minx, miny),
|
||||||
|
GetBlock (maxx, miny),
|
||||||
|
GetBlock (minx, maxy),
|
||||||
|
GetBlock (maxx, maxy)
|
||||||
|
};
|
||||||
|
unsigned int blkcount[4] =
|
||||||
|
{
|
||||||
|
VertexGrid[blk[0]].Size(),
|
||||||
|
VertexGrid[blk[1]].Size(),
|
||||||
|
VertexGrid[blk[2]].Size(),
|
||||||
|
VertexGrid[blk[3]].Size()
|
||||||
|
};
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (VertexGrid[blk[i]].Size() == blkcount[i])
|
||||||
|
{
|
||||||
|
VertexGrid[blk[i]].Push (vertnum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertnum;
|
||||||
|
}
|
||||||
|
|
|
@ -3244,7 +3244,8 @@ void P_SetupLevel (char *lumpname, int position)
|
||||||
sides, numsides,
|
sides, numsides,
|
||||||
lines, numlines
|
lines, numlines
|
||||||
};
|
};
|
||||||
FNodeBuilder builder (leveldata, polyspots, anchors, genglnodes);
|
leveldata.FindMapBounds ();
|
||||||
|
FNodeBuilder builder (leveldata, polyspots, anchors, genglnodes, CPU.bSSE2);
|
||||||
UsingGLNodes = genglnodes;
|
UsingGLNodes = genglnodes;
|
||||||
delete[] vertexes;
|
delete[] vertexes;
|
||||||
builder.Extract (nodes, numnodes,
|
builder.Extract (nodes, numnodes,
|
||||||
|
|
24
zdoom.vcproj
24
zdoom.vcproj
|
@ -2319,6 +2319,30 @@
|
||||||
RelativePath=".\src\nodebuild.cpp"
|
RelativePath=".\src\nodebuild.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\src\nodebuild_classify_nosse2.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\src\nodebuild_classify_sse2.cpp"
|
||||||
|
>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
EnableEnhancedInstructionSet="2"
|
||||||
|
/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Debug|Win32"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
EnableEnhancedInstructionSet="2"
|
||||||
|
/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\src\nodebuild_events.cpp"
|
RelativePath=".\src\nodebuild_events.cpp"
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in a new issue