mirror of
https://github.com/DrBeef/Raze.git
synced 2025-01-31 13:10:39 +00:00
- removed the node builder.
With libtess2 working we do not need this anymore.
This commit is contained in:
parent
bb9313454d
commit
b0e4b49771
10 changed files with 5 additions and 3269 deletions
|
@ -609,8 +609,7 @@ file( GLOB HEADER_FILES
|
|||
core/input/*.h
|
||||
core/rendering/*.h
|
||||
core/rendering/scene/*.h
|
||||
core/nodebuilder/*.h
|
||||
|
||||
|
||||
common/audio/sound/thirdparty/*.h
|
||||
common/audio/sound/*.h
|
||||
common/audio/music/*.h*
|
||||
|
@ -1055,13 +1054,6 @@ set (PCH_SOURCES
|
|||
core/gi.cpp
|
||||
core/defparser.cpp
|
||||
|
||||
core/nodebuilder/nodebuild.cpp
|
||||
core/nodebuilder/nodebuild_classify_nosse2.cpp
|
||||
core/nodebuilder/nodebuild_events.cpp
|
||||
core/nodebuilder/nodebuild_extract.cpp
|
||||
core/nodebuilder/nodebuild_gl.cpp
|
||||
core/nodebuilder/nodebuild_utility.cpp
|
||||
|
||||
core/rendering/hw_entrypoint.cpp
|
||||
core/rendering/hw_models.cpp
|
||||
core/rendering/hw_voxels.cpp
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,471 +0,0 @@
|
|||
/*
|
||||
** nodebuild.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2002-2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||
** covered by the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation; either version 2 of the License, or (at
|
||||
** your option) any later version.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "tarray.h"
|
||||
#include "x86.h"
|
||||
#include "build.h"
|
||||
|
||||
struct FPolySeg;
|
||||
struct FMiniBSP;
|
||||
struct FLevelLocals;
|
||||
|
||||
struct FEventInfo
|
||||
{
|
||||
int Vertex;
|
||||
uint32_t FrontSeg;
|
||||
};
|
||||
|
||||
struct FEvent
|
||||
{
|
||||
FEvent *Parent, *Left, *Right;
|
||||
double Distance;
|
||||
FEventInfo Info;
|
||||
};
|
||||
|
||||
class FEventTree
|
||||
{
|
||||
public:
|
||||
FEventTree ();
|
||||
~FEventTree ();
|
||||
|
||||
FEvent *GetMinimum ();
|
||||
FEvent *GetSuccessor (FEvent *event) const { FEvent *node = Successor(event); return node == &Nil ? NULL : node; }
|
||||
FEvent *GetPredecessor (FEvent *event) const { FEvent *node = Predecessor(event); return node == &Nil ? NULL : node; }
|
||||
|
||||
FEvent *GetNewNode ();
|
||||
void Insert (FEvent *event);
|
||||
FEvent *FindEvent (double distance) const;
|
||||
void DeleteAll ();
|
||||
|
||||
void PrintTree () const { PrintTree (Root); }
|
||||
|
||||
private:
|
||||
FEvent Nil;
|
||||
FEvent *Root;
|
||||
FEvent *Spare;
|
||||
|
||||
void DeletionTraverser (FEvent *event);
|
||||
FEvent *Successor (FEvent *event) const;
|
||||
FEvent *Predecessor (FEvent *event) const;
|
||||
|
||||
void PrintTree (const FEvent *event) const;
|
||||
};
|
||||
|
||||
struct FSimpleVert
|
||||
{
|
||||
fixed_t x, y;
|
||||
};
|
||||
|
||||
typedef int64_t fixed64_t;
|
||||
|
||||
struct vertex_t
|
||||
{
|
||||
DVector2 p;
|
||||
|
||||
void set(fixed_t x, fixed_t y)
|
||||
{
|
||||
p.X = x / 65536.;
|
||||
p.Y = y / 65536.;
|
||||
}
|
||||
|
||||
double fX() const
|
||||
{
|
||||
return p.X;
|
||||
}
|
||||
|
||||
double fY() const
|
||||
{
|
||||
return p.Y;
|
||||
}
|
||||
|
||||
fixed_t fixX() const
|
||||
{
|
||||
return FLOAT2FIXED(p.X);
|
||||
}
|
||||
|
||||
fixed_t fixY() const
|
||||
{
|
||||
return FLOAT2FIXED(p.Y);
|
||||
}
|
||||
};
|
||||
|
||||
struct side_t;
|
||||
|
||||
struct line_t
|
||||
{
|
||||
vertex_t* v1, * v2; // vertices, from v1 to v2
|
||||
side_t* sidedef[2];
|
||||
sectortype* frontsector, * backsector;
|
||||
int linenum;
|
||||
int wallnum;
|
||||
|
||||
int Index() const { return linenum; }
|
||||
|
||||
};
|
||||
|
||||
struct side_t
|
||||
{
|
||||
sectortype* sector; // Sector the SideDef is facing.
|
||||
int sidenum;
|
||||
int Index() const { return sidenum; }
|
||||
};
|
||||
|
||||
struct subsector_t;
|
||||
struct seg_t
|
||||
{
|
||||
vertex_t* v1;
|
||||
vertex_t* v2;
|
||||
|
||||
side_t* sidedef;
|
||||
line_t* linedef;
|
||||
|
||||
// Sector references. Could be retrieved from linedef, too.
|
||||
sectortype* frontsector;
|
||||
sectortype* backsector; // NULL for one-sided lines
|
||||
|
||||
seg_t* PartnerSeg;
|
||||
subsector_t* Subsector;
|
||||
|
||||
int segnum;
|
||||
|
||||
int Index() const { return segnum; }
|
||||
};
|
||||
|
||||
struct glseg_t : public seg_t
|
||||
{
|
||||
uint32_t Partner;
|
||||
};
|
||||
|
||||
struct subsector_t
|
||||
{
|
||||
sectortype* sector;
|
||||
seg_t* firstline;
|
||||
uint32_t numlines;
|
||||
};
|
||||
|
||||
struct node_t
|
||||
{
|
||||
// Partition line.
|
||||
fixed_t x;
|
||||
fixed_t y;
|
||||
fixed_t dx;
|
||||
fixed_t dy;
|
||||
union
|
||||
{
|
||||
float bbox[2][4]; // Bounding box for each child.
|
||||
fixed_t nb_bbox[2][4]; // Used by nodebuilder.
|
||||
};
|
||||
float len;
|
||||
int nodenum;
|
||||
union
|
||||
{
|
||||
void* children[2]; // If bit 0 is set, it's a subsector.
|
||||
int intchildren[2]; // Used by nodebuilder.
|
||||
};
|
||||
|
||||
int Index() const { return nodenum; }
|
||||
};
|
||||
|
||||
struct FLevelLocals
|
||||
{
|
||||
TArray<vertex_t> vertexes;
|
||||
TArray<subsector_t> subsectors;
|
||||
TArray<node_t> nodes;
|
||||
TArray<seg_t> segs;
|
||||
};
|
||||
|
||||
enum { NO_SIDE = 0x7fffffff };
|
||||
|
||||
|
||||
class FNodeBuilder
|
||||
{
|
||||
struct FPrivSeg
|
||||
{
|
||||
int v1, v2;
|
||||
int sidedef;
|
||||
int linedef;
|
||||
sectortype *frontsector;
|
||||
sectortype *backsector;
|
||||
uint32_t next;
|
||||
uint32_t nextforvert;
|
||||
uint32_t nextforvert2;
|
||||
int loopnum; // loop number for split avoidance (0 means splitting is okay)
|
||||
uint32_t partner; // seg on back side
|
||||
uint32_t storedseg; // seg # in the GL_SEGS lump
|
||||
|
||||
int planenum;
|
||||
bool planefront;
|
||||
FPrivSeg *hashnext;
|
||||
};
|
||||
struct FPrivVert : FSimpleVert
|
||||
{
|
||||
uint32_t segs; // segs that use this vertex as v1
|
||||
uint32_t segs2; // segs that use this vertex as v2
|
||||
|
||||
bool operator== (const FPrivVert &other)
|
||||
{
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
};
|
||||
struct FSimpleLine
|
||||
{
|
||||
fixed_t x, y, dx, dy;
|
||||
};
|
||||
union USegPtr
|
||||
{
|
||||
uint32_t SegNum;
|
||||
FPrivSeg *SegPtr;
|
||||
};
|
||||
struct FSplitSharer
|
||||
{
|
||||
double Distance;
|
||||
uint32_t Seg;
|
||||
bool Forward;
|
||||
};
|
||||
|
||||
// Like a blockmap, but for vertices instead of lines
|
||||
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:
|
||||
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;
|
||||
|
||||
fixed64_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 (fixed64_t x, fixed64_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;
|
||||
}
|
||||
};
|
||||
|
||||
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 FVertexMapSimple;
|
||||
|
||||
public:
|
||||
struct FLevel
|
||||
{
|
||||
vertex_t *Vertices; int NumVertices;
|
||||
side_t *Sides; int NumSides;
|
||||
line_t *Lines; int NumLines;
|
||||
|
||||
fixed_t MinX, MinY, MaxX, MaxY;
|
||||
|
||||
void FindMapBounds();
|
||||
void ResetMapBounds()
|
||||
{
|
||||
MinX = FIXED_MAX;
|
||||
MinY = FIXED_MAX;
|
||||
MaxX = FIXED_MIN;
|
||||
MaxY = FIXED_MIN;
|
||||
}
|
||||
};
|
||||
|
||||
struct FPolyStart
|
||||
{
|
||||
int polynum;
|
||||
fixed_t x, y;
|
||||
};
|
||||
|
||||
FNodeBuilder (FLevel &lev);
|
||||
~FNodeBuilder ();
|
||||
|
||||
void Extract(FLevelLocals &lev);
|
||||
const int *GetOldVertexTable();
|
||||
|
||||
// These are used for building sub-BSP trees for polyobjects.
|
||||
void Clear();
|
||||
void AddSegs(seg_t *segs, int numsegs);
|
||||
void BuildMini(bool makeGLNodes);
|
||||
|
||||
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:
|
||||
IVertexMap *VertexMap;
|
||||
int *OldVertexTable;
|
||||
|
||||
TArray<node_t> Nodes;
|
||||
TArray<subsector_t> Subsectors;
|
||||
TArray<uint32_t> SubsectorSets;
|
||||
TArray<FPrivSeg> Segs;
|
||||
TArray<FPrivVert> Vertices;
|
||||
TArray<USegPtr> SegList;
|
||||
TArray<uint8_t> PlaneChecked;
|
||||
TArray<FSimpleLine> Planes;
|
||||
|
||||
TArray<int> Touched; // Loops a splitter touches on a vertex
|
||||
TArray<int> Colinear; // Loops with edges colinear to a splitter
|
||||
FEventTree Events; // Vertices intersected by the current splitter
|
||||
|
||||
TArray<FSplitSharer> SplitSharers; // Segs colinear with the current splitter
|
||||
|
||||
uint32_t HackSeg; // Seg to force to back of splitter
|
||||
uint32_t HackMate; // Seg to use in front of hack seg
|
||||
FLevel &Level;
|
||||
bool GLNodes; // Add minisegs to make GL nodes?
|
||||
|
||||
// Progress meter stuff
|
||||
int SegsStuffed;
|
||||
|
||||
void FindUsedVertices (vertex_t *vertices, int max);
|
||||
void BuildTree ();
|
||||
void MakeSegsFromSides ();
|
||||
int CreateSeg (int linenum, int sidenum);
|
||||
void GroupSegPlanes ();
|
||||
void GroupSegPlanesSimple ();
|
||||
void AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg);
|
||||
int CreateNode (uint32_t set, unsigned int count, fixed_t bbox[4]);
|
||||
int CreateSubsector (uint32_t set, fixed_t bbox[4]);
|
||||
void CreateSubsectorsForReal ();
|
||||
bool CheckSubsector (uint32_t set, node_t &node, uint32_t &splitseg);
|
||||
bool CheckSubsectorOverlappingSegs (uint32_t set, node_t &node, uint32_t &splitseg);
|
||||
bool ShoveSegBehind (uint32_t set, node_t &node, uint32_t seg, uint32_t mate); int SelectSplitter (uint32_t set, node_t &node, uint32_t &splitseg, int step, bool nosplit);
|
||||
void SplitSegs (uint32_t set, node_t &node, uint32_t splitseg, uint32_t &outset0, uint32_t &outset1, unsigned int &count0, unsigned int &count1);
|
||||
uint32_t SplitSeg (uint32_t segnum, int splitvert, int v1InFront);
|
||||
int Heuristic (node_t &node, uint32_t set, bool honorNoSplit);
|
||||
|
||||
// Returns:
|
||||
// 0 = seg is in front
|
||||
// 1 = seg is in back
|
||||
// -1 = seg cuts the node
|
||||
|
||||
int ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]);
|
||||
|
||||
void FixSplitSharers (const node_t &node);
|
||||
double AddIntersection (const node_t &node, int vertex);
|
||||
void AddMinisegs (const node_t &node, uint32_t splitseg, uint32_t &fset, uint32_t &rset);
|
||||
uint32_t CheckLoopStart (fixed_t dx, fixed_t dy, int vertex1, int vertex2);
|
||||
uint32_t CheckLoopEnd (fixed_t dx, fixed_t dy, int vertex2);
|
||||
void RemoveSegFromVert1 (uint32_t segnum, int vertnum);
|
||||
void RemoveSegFromVert2 (uint32_t segnum, int vertnum);
|
||||
uint32_t AddMiniseg (int v1, int v2, uint32_t partner, uint32_t seg1, uint32_t splitseg);
|
||||
void SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const;
|
||||
|
||||
int CloseSubsector (TArray<glseg_t> &segs, int subsector, vertex_t *outVerts);
|
||||
uint32_t PushGLSeg (TArray<glseg_t> &segs, const FPrivSeg *seg, vertex_t *outVerts);
|
||||
void PushConnectingGLSeg (int subsector, TArray<glseg_t> &segs, vertex_t *v1, vertex_t *v2);
|
||||
int OutputDegenerateSubsector (TArray<glseg_t> &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts);
|
||||
|
||||
static int SortSegs (const void *a, const void *b);
|
||||
|
||||
double InterceptVector (const node_t &splitter, const FPrivSeg &seg);
|
||||
|
||||
void PrintSet (int l, uint32_t set);
|
||||
|
||||
FNodeBuilder &operator= (const FNodeBuilder &) { return *this; }
|
||||
};
|
||||
|
||||
// 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 will be considered as the same vertex.
|
||||
#define VERTEX_EPSILON 6 // This is a fixed_t value
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
using sector_t = sectortype;
|
|
@ -1,135 +0,0 @@
|
|||
#include "nodebuild.h"
|
||||
|
||||
#define FAR_ENOUGH 17179869184.f // 4<<32
|
||||
|
||||
int FNodeBuilder::ClassifyLine(node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2])
|
||||
{
|
||||
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)
|
||||
{
|
||||
sidev[0] = sidev[1] = 1;
|
||||
return 1;
|
||||
}
|
||||
if (s_num2 >= FAR_ENOUGH)
|
||||
{
|
||||
sidev[0] = 1;
|
||||
sidev[1] = -1;
|
||||
return -1;
|
||||
}
|
||||
nears = 1;
|
||||
}
|
||||
else if (s_num1 >= FAR_ENOUGH)
|
||||
{
|
||||
if (s_num2 >= FAR_ENOUGH)
|
||||
{
|
||||
sidev[0] = sidev[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
if (s_num2 <= -FAR_ENOUGH)
|
||||
{
|
||||
sidev[0] = -1;
|
||||
sidev[1] = 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)
|
||||
{
|
||||
sidev[0] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sidev[0] = s_num1 > 0.0 ? -1 : 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sidev[0] = s_num1 > 0.0 ? -1 : 1;
|
||||
}
|
||||
if (nears & 1)
|
||||
{
|
||||
double dist = s_num2 * s_num2 * l;
|
||||
if (dist < SIDE_EPSILON*SIDE_EPSILON)
|
||||
{
|
||||
sidev[1] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sidev[1] = s_num2 > 0.0 ? -1 : 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sidev[1] = s_num2 > 0.0 ? -1 : 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sidev[0] = s_num1 > 0.0 ? -1 : 1;
|
||||
sidev[1] = s_num2 > 0.0 ? -1 : 1;
|
||||
}
|
||||
|
||||
if ((sidev[0] | sidev[1]) == 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 (sidev[0] <= 0 && sidev[1] <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (sidev[0] >= 0 && sidev[1] >= 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
/*
|
||||
** nodebuild_events.cpp
|
||||
**
|
||||
** A red-black tree for keeping track of segs that get touched by a splitter.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2002-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||
** covered by the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation; either version 2 of the License, or (at
|
||||
** your option) any later version.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "nodebuild.h"
|
||||
#include "printf.h"
|
||||
|
||||
FEventTree::FEventTree ()
|
||||
: Root (&Nil), Spare (NULL)
|
||||
{
|
||||
memset (&Nil, 0, sizeof(Nil));
|
||||
}
|
||||
|
||||
FEventTree::~FEventTree ()
|
||||
{
|
||||
FEvent *probe;
|
||||
|
||||
DeleteAll ();
|
||||
probe = Spare;
|
||||
while (probe != NULL)
|
||||
{
|
||||
FEvent *next = probe->Left;
|
||||
delete probe;
|
||||
probe = next;
|
||||
}
|
||||
}
|
||||
|
||||
void FEventTree::DeleteAll ()
|
||||
{
|
||||
DeletionTraverser (Root);
|
||||
Root = &Nil;
|
||||
}
|
||||
|
||||
void FEventTree::DeletionTraverser (FEvent *node)
|
||||
{
|
||||
if (node != &Nil && node != NULL)
|
||||
{
|
||||
DeletionTraverser (node->Left);
|
||||
DeletionTraverser (node->Right);
|
||||
node->Left = Spare;
|
||||
Spare = node;
|
||||
}
|
||||
}
|
||||
|
||||
FEvent *FEventTree::GetNewNode ()
|
||||
{
|
||||
FEvent *node;
|
||||
|
||||
if (Spare != NULL)
|
||||
{
|
||||
node = Spare;
|
||||
Spare = node->Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = new FEvent;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void FEventTree::Insert (FEvent *z)
|
||||
{
|
||||
FEvent *y = &Nil;
|
||||
FEvent *x = Root;
|
||||
|
||||
while (x != &Nil)
|
||||
{
|
||||
y = x;
|
||||
if (z->Distance < x->Distance)
|
||||
{
|
||||
x = x->Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = x->Right;
|
||||
}
|
||||
}
|
||||
z->Parent = y;
|
||||
if (y == &Nil)
|
||||
{
|
||||
Root = z;
|
||||
}
|
||||
else if (z->Distance < y->Distance)
|
||||
{
|
||||
y->Left = z;
|
||||
}
|
||||
else
|
||||
{
|
||||
y->Right = z;
|
||||
}
|
||||
z->Left = &Nil;
|
||||
z->Right = &Nil;
|
||||
}
|
||||
|
||||
FEvent *FEventTree::Successor (FEvent *event) const
|
||||
{
|
||||
if (event->Right != &Nil)
|
||||
{
|
||||
event = event->Right;
|
||||
while (event->Left != &Nil)
|
||||
{
|
||||
event = event->Left;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
else
|
||||
{
|
||||
FEvent *y = event->Parent;
|
||||
while (y != &Nil && event == y->Right)
|
||||
{
|
||||
event = y;
|
||||
y = y->Parent;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
FEvent *FEventTree::Predecessor (FEvent *event) const
|
||||
{
|
||||
if (event->Left != &Nil)
|
||||
{
|
||||
event = event->Left;
|
||||
while (event->Right != &Nil)
|
||||
{
|
||||
event = event->Right;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
else
|
||||
{
|
||||
FEvent *y = event->Parent;
|
||||
while (y != &Nil && event == y->Left)
|
||||
{
|
||||
event = y;
|
||||
y = y->Parent;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
FEvent *FEventTree::FindEvent (double key) const
|
||||
{
|
||||
FEvent *node = Root;
|
||||
|
||||
while (node != &Nil)
|
||||
{
|
||||
if (node->Distance == key)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
else if (node->Distance > key)
|
||||
{
|
||||
node = node->Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = node->Right;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FEvent *FEventTree::GetMinimum ()
|
||||
{
|
||||
FEvent *node = Root;
|
||||
|
||||
if (node == &Nil)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
while (node->Left != &Nil)
|
||||
{
|
||||
node = node->Left;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void FEventTree::PrintTree (const FEvent *event) const
|
||||
{
|
||||
// Use the CRT's sprintf so that it shares the same formatting as ZDBSP's output.
|
||||
char buff[100];
|
||||
if (event != &Nil)
|
||||
{
|
||||
PrintTree(event->Left);
|
||||
sprintf(buff, " Distance %g, vertex %d, seg %u\n",
|
||||
g_sqrt(event->Distance/4294967296.0), event->Info.Vertex, (unsigned)event->Info.FrontSeg);
|
||||
Printf(PRINT_LOG, "%s", buff);
|
||||
PrintTree(event->Right);
|
||||
}
|
||||
}
|
|
@ -1,440 +0,0 @@
|
|||
/*
|
||||
** nodebuild_extract.cpp
|
||||
**
|
||||
** Converts the nodes, segs, and subsectors from the node builder's
|
||||
** internal format to the format used by the rest of the game.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2002-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||
** covered by the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation; either version 2 of the License, or (at
|
||||
** your option) any later version.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <float.h>
|
||||
|
||||
#include "nodebuild.h"
|
||||
|
||||
#if 0
|
||||
#define D(x) x
|
||||
#define DD 1
|
||||
#else
|
||||
#define D(x) do{}while(0)
|
||||
#undef DD
|
||||
#endif
|
||||
|
||||
|
||||
void FNodeBuilder::Extract (FLevelLocals &theLevel)
|
||||
{
|
||||
int i;
|
||||
|
||||
auto &outVerts = theLevel.vertexes;
|
||||
int vertCount = Vertices.Size ();
|
||||
outVerts.Alloc(vertCount);
|
||||
|
||||
for (i = 0; i < vertCount; ++i)
|
||||
{
|
||||
outVerts[i].set(Vertices[i].x, Vertices[i].y);
|
||||
}
|
||||
|
||||
auto &outSubs = theLevel.subsectors;
|
||||
auto subCount = Subsectors.Size();
|
||||
outSubs.Alloc(subCount);
|
||||
memset(&outSubs[0], 0, subCount * sizeof(subsector_t));
|
||||
|
||||
auto &outNodes = theLevel.nodes;
|
||||
auto nodeCount = Nodes.Size ();
|
||||
outNodes.Alloc(nodeCount);
|
||||
|
||||
memcpy (&outNodes[0], &Nodes[0], nodeCount*sizeof(node_t));
|
||||
for (unsigned i = 0; i < nodeCount; ++i)
|
||||
{
|
||||
D(Printf(PRINT_LOG, "Node %d: Splitter[%08x,%08x] [%08x,%08x]\n", i,
|
||||
outNodes[i].x, outNodes[i].y, outNodes[i].dx, outNodes[i].dy));
|
||||
// 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 (outNodes[i].intchildren[j] & 0x80000000)
|
||||
{
|
||||
D(Printf(PRINT_LOG, " subsector %d\n", outNodes[i].intchildren[j] & 0x7FFFFFFF));
|
||||
outNodes[i].children[j] = (uint8_t *)(&outSubs[(outNodes[i].intchildren[j] & 0x7fffffff)]) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
D(Printf(PRINT_LOG, " node %d\n", outNodes[i].intchildren[j]));
|
||||
outNodes[i].children[j] = &outNodes[outNodes[i].intchildren[j]];
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < 2; ++j)
|
||||
{
|
||||
for (int k = 0; k < 4; ++k)
|
||||
{
|
||||
outNodes[i].bbox[j][k] = FIXED2FLOAT(outNodes[i].nb_bbox[j][k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto &outSegs = theLevel.segs;
|
||||
if (GLNodes)
|
||||
{
|
||||
TArray<glseg_t> segs (Segs.Size()*5/4);
|
||||
|
||||
for (unsigned i = 0; i < subCount; ++i)
|
||||
{
|
||||
uint32_t numsegs = CloseSubsector (segs, i, &outVerts[0]);
|
||||
outSubs[i].numlines = numsegs;
|
||||
outSubs[i].firstline = (seg_t *)(size_t)(segs.Size() - numsegs);
|
||||
}
|
||||
|
||||
auto segCount = segs.Size ();
|
||||
outSegs.Alloc(segCount);
|
||||
|
||||
for (unsigned i = 0; i < segCount; ++i)
|
||||
{
|
||||
outSegs[i] = *(seg_t *)&segs[i];
|
||||
|
||||
if (segs[i].Partner != UINT_MAX)
|
||||
{
|
||||
const uint32_t storedseg = Segs[segs[i].Partner].storedseg;
|
||||
outSegs[i].PartnerSeg = UINT_MAX == storedseg ? nullptr : &outSegs[storedseg];
|
||||
}
|
||||
else
|
||||
{
|
||||
outSegs[i].PartnerSeg = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (&outSubs[0], &Subsectors[0], subCount*sizeof(subsector_t));
|
||||
auto segCount = Segs.Size ();
|
||||
outSegs.Alloc(segCount);
|
||||
for (unsigned i = 0; i < segCount; ++i)
|
||||
{
|
||||
const FPrivSeg *org = &Segs[SegList[i].SegNum];
|
||||
seg_t *out = &outSegs[i];
|
||||
|
||||
D(Printf(PRINT_LOG, "Seg %d: v1(%d) -> v2(%d)\n", i, org->v1, org->v2));
|
||||
|
||||
out->v1 = &outVerts[org->v1];
|
||||
out->v2 = &outVerts[org->v2];
|
||||
out->backsector = org->backsector;
|
||||
out->frontsector = org->frontsector;
|
||||
out->linedef = Level.Lines + org->linedef;
|
||||
out->sidedef = Level.Sides + org->sidedef;
|
||||
out->PartnerSeg = nullptr;
|
||||
}
|
||||
}
|
||||
for (unsigned 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));
|
||||
|
||||
for (i = 0; i < Level.NumLines; ++i)
|
||||
{
|
||||
Level.Lines[i].v1 = &outVerts[(size_t)Level.Lines[i].v1];
|
||||
Level.Lines[i].v2 = &outVerts[(size_t)Level.Lines[i].v2];
|
||||
}
|
||||
}
|
||||
|
||||
int FNodeBuilder::CloseSubsector (TArray<glseg_t> &segs, int subsector, vertex_t *outVerts)
|
||||
{
|
||||
FPrivSeg *seg, *prev;
|
||||
angle_t prevAngle;
|
||||
double accumx, accumy;
|
||||
fixed_t midx, midy;
|
||||
int firstVert;
|
||||
uint32_t first, max, count, i, j;
|
||||
bool diffplanes;
|
||||
int firstplane;
|
||||
|
||||
first = (uint32_t)(size_t)Subsectors[subsector].firstline;
|
||||
max = first + Subsectors[subsector].numlines;
|
||||
count = 0;
|
||||
|
||||
accumx = accumy = 0.0;
|
||||
diffplanes = false;
|
||||
firstplane = Segs[SegList[first].SegNum].planenum;
|
||||
|
||||
// Calculate the midpoint of the subsector and also check for degenerate subsectors.
|
||||
// A subsector is degenerate if it exists in only one dimension, which can be
|
||||
// detected when all the segs lie in the same plane. This can happen if you have
|
||||
// outward-facing lines in the void that don't point toward any sector. (Some of the
|
||||
// polyobjects in Hexen are constructed like this.)
|
||||
for (i = first; i < max; ++i)
|
||||
{
|
||||
seg = &Segs[SegList[i].SegNum];
|
||||
accumx += double(Vertices[seg->v1].x) + double(Vertices[seg->v2].x);
|
||||
accumy += double(Vertices[seg->v1].y) + double(Vertices[seg->v2].y);
|
||||
if (firstplane != seg->planenum)
|
||||
{
|
||||
diffplanes = true;
|
||||
}
|
||||
}
|
||||
|
||||
midx = fixed_t(accumx / (max - first) / 2);
|
||||
midy = fixed_t(accumy / (max - first) / 2);
|
||||
|
||||
seg = &Segs[SegList[first].SegNum];
|
||||
prevAngle = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy);
|
||||
seg->storedseg = PushGLSeg (segs, seg, outVerts);
|
||||
count = 1;
|
||||
prev = seg;
|
||||
firstVert = seg->v1;
|
||||
|
||||
#ifdef DD
|
||||
Printf(PRINT_LOG, "--%d--\n", subsector);
|
||||
for (j = first; j < max; ++j)
|
||||
{
|
||||
seg = &Segs[SegList[j].SegNum];
|
||||
angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy);
|
||||
Printf(PRINT_LOG, "%d%c %5d(%5d,%5d)->%5d(%5d,%5d) - %3.5f %d,%d [%08x,%08x]-[%08x,%08x]\n", j,
|
||||
seg->linedef == -1 ? '+' : ':',
|
||||
seg->v1, Vertices[seg->v1].x>>16, Vertices[seg->v1].y>>16,
|
||||
seg->v2, Vertices[seg->v2].x>>16, Vertices[seg->v2].y>>16,
|
||||
double(ang/2)*180/(1<<30),
|
||||
seg->planenum, seg->planefront,
|
||||
Vertices[seg->v1].x, Vertices[seg->v1].y,
|
||||
Vertices[seg->v2].x, Vertices[seg->v2].y);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (diffplanes)
|
||||
{ // A well-behaved subsector. Output the segs sorted by the angle formed by connecting
|
||||
// the subsector's center to their first vertex.
|
||||
|
||||
D(Printf(PRINT_LOG, "Well behaved subsector\n"));
|
||||
for (i = first + 1; i < max; ++i)
|
||||
{
|
||||
angle_t bestdiff = ANGLE_MAX;
|
||||
FPrivSeg *bestseg = NULL;
|
||||
uint32_t bestj = UINT_MAX;
|
||||
j = first;
|
||||
do
|
||||
{
|
||||
seg = &Segs[SegList[j].SegNum];
|
||||
angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy);
|
||||
angle_t diff = prevAngle - ang;
|
||||
if (seg->v1 == prev->v2)
|
||||
{
|
||||
bestdiff = diff;
|
||||
bestseg = seg;
|
||||
bestj = j;
|
||||
break;
|
||||
}
|
||||
if (diff < bestdiff && diff > 0)
|
||||
{
|
||||
bestdiff = diff;
|
||||
bestseg = seg;
|
||||
bestj = j;
|
||||
}
|
||||
}
|
||||
while (++j < max);
|
||||
// Is a NULL bestseg actually okay?
|
||||
if (bestseg != NULL)
|
||||
{
|
||||
seg = bestseg;
|
||||
}
|
||||
if (prev->v2 != seg->v1)
|
||||
{
|
||||
// Add a new miniseg to connect the two segs
|
||||
PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[seg->v1]);
|
||||
count++;
|
||||
}
|
||||
#ifdef DD
|
||||
Printf(PRINT_LOG, "+%d\n", bestj);
|
||||
#endif
|
||||
prevAngle -= bestdiff;
|
||||
seg->storedseg = PushGLSeg (segs, seg, outVerts);
|
||||
count++;
|
||||
prev = seg;
|
||||
if (seg->v2 == firstVert)
|
||||
{
|
||||
prev = seg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef DD
|
||||
Printf(PRINT_LOG, "\n");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{ // A degenerate subsector. These are handled in three stages:
|
||||
// Stage 1. Proceed in the same direction as the start seg until we
|
||||
// hit the seg furthest from it.
|
||||
// Stage 2. Reverse direction and proceed until we hit the seg
|
||||
// furthest from the start seg.
|
||||
// Stage 3. Reverse direction again and insert segs until we get
|
||||
// to the start seg.
|
||||
// A dot product serves to determine distance from the start seg.
|
||||
|
||||
D(Printf(PRINT_LOG, "degenerate subsector\n"));
|
||||
|
||||
// Stage 1. Go forward.
|
||||
count += OutputDegenerateSubsector (segs, subsector, true, 0, prev, outVerts);
|
||||
|
||||
// Stage 2. Go backward.
|
||||
count += OutputDegenerateSubsector (segs, subsector, false, DBL_MAX, prev, outVerts);
|
||||
|
||||
// Stage 3. Go forward again.
|
||||
count += OutputDegenerateSubsector (segs, subsector, true, -DBL_MAX, prev, outVerts);
|
||||
}
|
||||
|
||||
if (prev->v2 != firstVert)
|
||||
{
|
||||
PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[firstVert]);
|
||||
count++;
|
||||
}
|
||||
#ifdef DD
|
||||
Printf(PRINT_LOG, "Output GL subsector %d:\n", subsector);
|
||||
for (i = segs.Size() - count; i < (int)segs.Size(); ++i)
|
||||
{
|
||||
Printf(PRINT_LOG, " Seg %5d%c(%5d,%5d)-(%5d,%5d) [%08x,%08x]-[%08x,%08x]\n", i,
|
||||
segs[i].linedef == NULL ? '+' : ' ',
|
||||
segs[i].v1->fixX()>>16,
|
||||
segs[i].v1->fixY()>>16,
|
||||
segs[i].v2->fixX()>>16,
|
||||
segs[i].v2->fixY()>>16,
|
||||
segs[i].v1->fixX(),
|
||||
segs[i].v1->fixY(),
|
||||
segs[i].v2->fixX(),
|
||||
segs[i].v2->fixY());
|
||||
}
|
||||
#endif
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int FNodeBuilder::OutputDegenerateSubsector (TArray<glseg_t> &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts)
|
||||
{
|
||||
static const double bestinit[2] = { -DBL_MAX, DBL_MAX };
|
||||
FPrivSeg *seg;
|
||||
int i, j, first, max, count;
|
||||
double dot, x1, y1, dx, dy, dx2, dy2;
|
||||
bool wantside;
|
||||
|
||||
first = (uint32_t)(size_t)Subsectors[subsector].firstline;
|
||||
max = first + Subsectors[subsector].numlines;
|
||||
count = 0;
|
||||
|
||||
seg = &Segs[SegList[first].SegNum];
|
||||
x1 = Vertices[seg->v1].x;
|
||||
y1 = Vertices[seg->v1].y;
|
||||
dx = Vertices[seg->v2].x - x1;
|
||||
dy = Vertices[seg->v2].y - y1;
|
||||
wantside = seg->planefront ^ !bForward;
|
||||
|
||||
for (i = first + 1; i < max; ++i)
|
||||
{
|
||||
double bestdot = bestinit[bForward];
|
||||
FPrivSeg *bestseg = NULL;
|
||||
for (j = first + 1; j < max; ++j)
|
||||
{
|
||||
seg = &Segs[SegList[j].SegNum];
|
||||
if (seg->planefront != wantside)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
dx2 = Vertices[seg->v1].x - x1;
|
||||
dy2 = Vertices[seg->v1].y - y1;
|
||||
dot = dx*dx2 + dy*dy2;
|
||||
|
||||
if (bForward)
|
||||
{
|
||||
if (dot < bestdot && dot > lastdot)
|
||||
{
|
||||
bestdot = dot;
|
||||
bestseg = seg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dot > bestdot && dot < lastdot)
|
||||
{
|
||||
bestdot = dot;
|
||||
bestseg = seg;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestseg != NULL)
|
||||
{
|
||||
if (prev->v2 != bestseg->v1)
|
||||
{
|
||||
PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[bestseg->v1]);
|
||||
count++;
|
||||
}
|
||||
seg->storedseg = PushGLSeg (segs, bestseg, outVerts);
|
||||
count++;
|
||||
prev = bestseg;
|
||||
lastdot = bestdot;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t FNodeBuilder::PushGLSeg (TArray<glseg_t> &segs, const FPrivSeg *seg, vertex_t *outVerts)
|
||||
{
|
||||
glseg_t newseg;
|
||||
|
||||
newseg.v1 = outVerts + seg->v1;
|
||||
newseg.v2 = outVerts + seg->v2;
|
||||
newseg.backsector = seg->backsector;
|
||||
newseg.frontsector = seg->frontsector;
|
||||
if (seg->linedef != -1)
|
||||
{
|
||||
newseg.linedef = Level.Lines + seg->linedef;
|
||||
newseg.sidedef = Level.Sides + seg->sidedef;
|
||||
}
|
||||
else
|
||||
{
|
||||
newseg.linedef = NULL;
|
||||
newseg.sidedef = NULL;
|
||||
}
|
||||
newseg.Partner = seg->partner;
|
||||
return (uint32_t)segs.Push (newseg);
|
||||
}
|
||||
|
||||
void FNodeBuilder::PushConnectingGLSeg (int subsector, TArray<glseg_t> &segs, vertex_t *v1, vertex_t *v2)
|
||||
{
|
||||
glseg_t newseg;
|
||||
|
||||
newseg.v1 = v1;
|
||||
newseg.v2 = v2;
|
||||
newseg.backsector = NULL;
|
||||
newseg.frontsector = NULL;
|
||||
newseg.linedef = NULL;
|
||||
newseg.sidedef = NULL;
|
||||
newseg.Partner = UINT_MAX;
|
||||
segs.Push (newseg);
|
||||
}
|
|
@ -1,397 +0,0 @@
|
|||
/*
|
||||
** nodebuild_gl.cpp
|
||||
**
|
||||
** Extra functions for the node builder to create minisegs.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2002-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||
** covered by the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation; either version 2 of the License, or (at
|
||||
** your option) any later version.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "nodebuild.h"
|
||||
|
||||
static inline void Warn (const char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
static const angle_t ANGLE_EPSILON = 5000;
|
||||
|
||||
#if 0
|
||||
#define D(x) x
|
||||
#else
|
||||
#define D(x) do{}while(0)
|
||||
#endif
|
||||
|
||||
double FNodeBuilder::AddIntersection (const node_t &node, int vertex)
|
||||
{
|
||||
static const FEventInfo defaultInfo =
|
||||
{
|
||||
-1, UINT_MAX
|
||||
};
|
||||
|
||||
// Calculate signed distance of intersection vertex from start of splitter.
|
||||
// Only ordering is important, so we don't need a sqrt.
|
||||
FPrivVert *v = &Vertices[vertex];
|
||||
double dist = (double(v->x) - node.x)*(node.dx) + (double(v->y) - node.y)*(node.dy);
|
||||
|
||||
FEvent *event = Events.FindEvent (dist);
|
||||
if (event == NULL)
|
||||
{
|
||||
event = Events.GetNewNode ();
|
||||
event->Distance = dist;
|
||||
event->Info = defaultInfo;
|
||||
event->Info.Vertex = vertex;
|
||||
Events.Insert (event);
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
// If there are any segs on the splitter that span more than two events, they
|
||||
// must be split. Alien Vendetta is one example wad that is quite bad about
|
||||
// having overlapping lines. If we skip this step, these segs will still be
|
||||
// split later, but minisegs will erroneously be added for them, and partner
|
||||
// seg information will be messed up in the generated tree.
|
||||
void FNodeBuilder::FixSplitSharers (const node_t &node)
|
||||
{
|
||||
D(Printf(PRINT_LOG, "events:\n"));
|
||||
D(Events.PrintTree());
|
||||
for (unsigned int i = 0; i < SplitSharers.Size(); ++i)
|
||||
{
|
||||
uint32_t seg = SplitSharers[i].Seg;
|
||||
int v2 = Segs[seg].v2;
|
||||
FEvent *event = Events.FindEvent (SplitSharers[i].Distance);
|
||||
FEvent *next;
|
||||
|
||||
if (event == NULL)
|
||||
{ // Should not happen
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use the CRT's printf so the formatting matches ZDBSP's
|
||||
D(char buff[200]);
|
||||
D(sprintf(buff, "Considering events on seg %d(%d[%d,%d]->%d[%d,%d]) [%g:%g]\n", seg,
|
||||
Segs[seg].v1,
|
||||
Vertices[Segs[seg].v1].x>>16,
|
||||
Vertices[Segs[seg].v1].y>>16,
|
||||
Segs[seg].v2,
|
||||
Vertices[Segs[seg].v2].x>>16,
|
||||
Vertices[Segs[seg].v2].y>>16,
|
||||
SplitSharers[i].Distance, event->Distance));
|
||||
D(Printf(PRINT_LOG, "%s", buff));
|
||||
|
||||
if (SplitSharers[i].Forward)
|
||||
{
|
||||
event = Events.GetSuccessor (event);
|
||||
if (event == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
next = Events.GetSuccessor (event);
|
||||
}
|
||||
else
|
||||
{
|
||||
event = Events.GetPredecessor (event);
|
||||
if (event == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
next = Events.GetPredecessor (event);
|
||||
}
|
||||
|
||||
while (event != NULL && next != NULL && event->Info.Vertex != v2)
|
||||
{
|
||||
D(Printf(PRINT_LOG, "Forced split of seg %d(%d->%d) at %d(%d,%d)\n", seg,
|
||||
Segs[seg].v1, Segs[seg].v2,
|
||||
event->Info.Vertex,
|
||||
Vertices[event->Info.Vertex].x>>16,
|
||||
Vertices[event->Info.Vertex].y>>16));
|
||||
|
||||
uint32_t newseg = SplitSeg (seg, event->Info.Vertex, 1);
|
||||
|
||||
Segs[newseg].next = Segs[seg].next;
|
||||
Segs[seg].next = newseg;
|
||||
|
||||
uint32_t partner = Segs[seg].partner;
|
||||
if (partner != UINT_MAX)
|
||||
{
|
||||
int endpartner = SplitSeg (partner, event->Info.Vertex, 1);
|
||||
|
||||
Segs[endpartner].next = Segs[partner].next;
|
||||
Segs[partner].next = endpartner;
|
||||
|
||||
Segs[seg].partner = endpartner;
|
||||
Segs[partner].partner = newseg;
|
||||
}
|
||||
|
||||
seg = newseg;
|
||||
if (SplitSharers[i].Forward)
|
||||
{
|
||||
event = next;
|
||||
next = Events.GetSuccessor (next);
|
||||
}
|
||||
else
|
||||
{
|
||||
event = next;
|
||||
next = Events.GetPredecessor (next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FNodeBuilder::AddMinisegs (const node_t &node, uint32_t splitseg, uint32_t &fset, uint32_t &bset)
|
||||
{
|
||||
FEvent *event = Events.GetMinimum (), *prev = NULL;
|
||||
|
||||
while (event != NULL)
|
||||
{
|
||||
if (prev != NULL)
|
||||
{
|
||||
uint32_t fseg1, bseg1, fseg2, bseg2;
|
||||
uint32_t fnseg, bnseg;
|
||||
|
||||
// Minisegs should only be added when they can create valid loops on both the front and
|
||||
// back of the splitter. This means some subsectors could be unclosed if their sectors
|
||||
// are unclosed, but at least we won't be needlessly creating subsectors in void space.
|
||||
// Unclosed subsectors can be closed trivially once the BSP tree is complete.
|
||||
|
||||
if ((fseg1 = CheckLoopStart (node.dx, node.dy, prev->Info.Vertex, event->Info.Vertex)) != UINT_MAX &&
|
||||
(bseg1 = CheckLoopStart (-node.dx, -node.dy, event->Info.Vertex, prev->Info.Vertex)) != UINT_MAX &&
|
||||
(fseg2 = CheckLoopEnd (node.dx, node.dy, event->Info.Vertex)) != UINT_MAX &&
|
||||
(bseg2 = CheckLoopEnd (-node.dx, -node.dy, prev->Info.Vertex)) != UINT_MAX)
|
||||
{
|
||||
// Add miniseg on the front side
|
||||
fnseg = AddMiniseg (prev->Info.Vertex, event->Info.Vertex, UINT_MAX, fseg1, splitseg);
|
||||
Segs[fnseg].next = fset;
|
||||
fset = fnseg;
|
||||
|
||||
// Add miniseg on the back side
|
||||
bnseg = AddMiniseg (event->Info.Vertex, prev->Info.Vertex, fnseg, bseg1, splitseg);
|
||||
Segs[bnseg].next = bset;
|
||||
bset = bnseg;
|
||||
|
||||
sector_t *fsector, *bsector;
|
||||
|
||||
fsector = Segs[fseg1].frontsector;
|
||||
bsector = Segs[bseg1].frontsector;
|
||||
|
||||
Segs[fnseg].frontsector = fsector;
|
||||
Segs[fnseg].backsector = bsector;
|
||||
Segs[bnseg].frontsector = bsector;
|
||||
Segs[bnseg].backsector = fsector;
|
||||
|
||||
// Only print the warning if this might be bad.
|
||||
if (fsector != bsector &&
|
||||
fsector != Segs[fseg1].backsector &&
|
||||
bsector != Segs[bseg1].backsector)
|
||||
{
|
||||
Warn ("Sectors %d at (%d,%d) and %d at (%d,%d) don't match.\n",
|
||||
Segs[fseg1].frontsector,
|
||||
Vertices[prev->Info.Vertex].x>>FRACBITS, Vertices[prev->Info.Vertex].y>>FRACBITS,
|
||||
Segs[bseg1].frontsector,
|
||||
Vertices[event->Info.Vertex].x>>FRACBITS, Vertices[event->Info.Vertex].y>>FRACBITS
|
||||
);
|
||||
}
|
||||
|
||||
D(Printf (PRINT_LOG, "**Minisegs** %d/%d added %d(%d,%d)->%d(%d,%d)\n", fnseg, bnseg,
|
||||
prev->Info.Vertex,
|
||||
Vertices[prev->Info.Vertex].x>>16, Vertices[prev->Info.Vertex].y>>16,
|
||||
event->Info.Vertex,
|
||||
Vertices[event->Info.Vertex].x>>16, Vertices[event->Info.Vertex].y>>16));
|
||||
}
|
||||
}
|
||||
prev = event;
|
||||
event = Events.GetSuccessor (event);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FNodeBuilder::AddMiniseg (int v1, int v2, uint32_t partner, uint32_t seg1, uint32_t splitseg)
|
||||
{
|
||||
uint32_t nseg;
|
||||
FPrivSeg *seg = &Segs[seg1];
|
||||
FPrivSeg newseg;
|
||||
|
||||
newseg.sidedef = NO_SIDE;
|
||||
newseg.linedef = -1;
|
||||
newseg.loopnum = 0;
|
||||
newseg.next = UINT_MAX;
|
||||
newseg.planefront = true;
|
||||
newseg.hashnext = NULL;
|
||||
newseg.storedseg = UINT_MAX;
|
||||
newseg.frontsector = NULL;
|
||||
newseg.backsector = NULL;
|
||||
|
||||
if (splitseg != UINT_MAX)
|
||||
{
|
||||
newseg.planenum = Segs[splitseg].planenum;
|
||||
}
|
||||
else
|
||||
{
|
||||
newseg.planenum = -1;
|
||||
}
|
||||
|
||||
newseg.v1 = v1;
|
||||
newseg.v2 = v2;
|
||||
newseg.nextforvert = Vertices[v1].segs;
|
||||
newseg.nextforvert2 = Vertices[v2].segs2;
|
||||
newseg.next = seg->next;
|
||||
if (partner != UINT_MAX)
|
||||
{
|
||||
newseg.partner = partner;
|
||||
}
|
||||
else
|
||||
{
|
||||
newseg.partner = UINT_MAX;
|
||||
}
|
||||
nseg = Segs.Push (newseg);
|
||||
if (newseg.partner != UINT_MAX)
|
||||
{
|
||||
Segs[partner].partner = nseg;
|
||||
}
|
||||
Vertices[v1].segs = nseg;
|
||||
Vertices[v2].segs2 = nseg;
|
||||
//Printf ("Between %d and %d::::\n", seg1, seg2);
|
||||
return nseg;
|
||||
}
|
||||
|
||||
uint32_t FNodeBuilder::CheckLoopStart (fixed_t dx, fixed_t dy, int vertex, int vertex2)
|
||||
{
|
||||
FPrivVert *v = &Vertices[vertex];
|
||||
angle_t splitAngle = PointToAngle (dx, dy);
|
||||
uint32_t segnum;
|
||||
angle_t bestang;
|
||||
uint32_t bestseg;
|
||||
|
||||
// Find the seg ending at this vertex that forms the smallest angle
|
||||
// to the splitter.
|
||||
segnum = v->segs2;
|
||||
bestang = ANGLE_MAX;
|
||||
bestseg = UINT_MAX;
|
||||
while (segnum != UINT_MAX)
|
||||
{
|
||||
FPrivSeg *seg = &Segs[segnum];
|
||||
angle_t segAngle = PointToAngle (Vertices[seg->v1].x - v->x, Vertices[seg->v1].y - v->y);
|
||||
angle_t diff = splitAngle - segAngle;
|
||||
|
||||
if (diff < ANGLE_EPSILON &&
|
||||
PointOnSide (Vertices[seg->v1].x, Vertices[seg->v1].y, v->x, v->y, dx, dy) == 0)
|
||||
{
|
||||
// If a seg lies right on the splitter, don't count it
|
||||
}
|
||||
else
|
||||
{
|
||||
if (diff <= bestang)
|
||||
{
|
||||
bestang = diff;
|
||||
bestseg = segnum;
|
||||
}
|
||||
}
|
||||
segnum = seg->nextforvert2;
|
||||
}
|
||||
if (bestseg == UINT_MAX)
|
||||
{
|
||||
return UINT_MAX;
|
||||
}
|
||||
// Now make sure there are no segs starting at this vertex that form
|
||||
// an even smaller angle to the splitter.
|
||||
segnum = v->segs;
|
||||
while (segnum != UINT_MAX)
|
||||
{
|
||||
FPrivSeg *seg = &Segs[segnum];
|
||||
if (seg->v2 == vertex2)
|
||||
{
|
||||
return UINT_MAX;
|
||||
}
|
||||
angle_t segAngle = PointToAngle (Vertices[seg->v2].x - v->x, Vertices[seg->v2].y - v->y);
|
||||
angle_t diff = splitAngle - segAngle;
|
||||
if (diff < bestang && seg->partner != bestseg)
|
||||
{
|
||||
return UINT_MAX;
|
||||
}
|
||||
segnum = seg->nextforvert;
|
||||
}
|
||||
return bestseg;
|
||||
}
|
||||
|
||||
uint32_t FNodeBuilder::CheckLoopEnd (fixed_t dx, fixed_t dy, int vertex)
|
||||
{
|
||||
FPrivVert *v = &Vertices[vertex];
|
||||
angle_t splitAngle = PointToAngle (dx, dy) + ANGLE_180;
|
||||
uint32_t segnum;
|
||||
angle_t bestang;
|
||||
uint32_t bestseg;
|
||||
|
||||
// Find the seg starting at this vertex that forms the smallest angle
|
||||
// to the splitter.
|
||||
segnum = v->segs;
|
||||
bestang = ANGLE_MAX;
|
||||
bestseg = UINT_MAX;
|
||||
while (segnum != UINT_MAX)
|
||||
{
|
||||
FPrivSeg *seg = &Segs[segnum];
|
||||
angle_t segAngle = PointToAngle (Vertices[seg->v2].x - v->x, Vertices[seg->v2].y - v->y);
|
||||
angle_t diff = segAngle - splitAngle;
|
||||
|
||||
if (diff < ANGLE_EPSILON &&
|
||||
PointOnSide (Vertices[seg->v1].x, Vertices[seg->v1].y, v->x, v->y, dx, dy) == 0)
|
||||
{
|
||||
// If a seg lies right on the splitter, don't count it
|
||||
}
|
||||
else
|
||||
{
|
||||
if (diff <= bestang)
|
||||
{
|
||||
bestang = diff;
|
||||
bestseg = segnum;
|
||||
}
|
||||
}
|
||||
segnum = seg->nextforvert;
|
||||
}
|
||||
if (bestseg == UINT_MAX)
|
||||
{
|
||||
return UINT_MAX;
|
||||
}
|
||||
// Now make sure there are no segs ending at this vertex that form
|
||||
// an even smaller angle to the splitter.
|
||||
segnum = v->segs2;
|
||||
while (segnum != UINT_MAX)
|
||||
{
|
||||
FPrivSeg *seg = &Segs[segnum];
|
||||
angle_t segAngle = PointToAngle (Vertices[seg->v1].x - v->x, Vertices[seg->v1].y - v->y);
|
||||
angle_t diff = segAngle - splitAngle;
|
||||
if (diff < bestang && seg->partner != bestseg)
|
||||
{
|
||||
return UINT_MAX;
|
||||
}
|
||||
segnum = seg->nextforvert2;
|
||||
}
|
||||
return bestseg;
|
||||
}
|
|
@ -1,527 +0,0 @@
|
|||
/*
|
||||
** nodebuild_utility.cpp
|
||||
**
|
||||
** Miscellaneous node builder utility functions.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2002-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||
** covered by the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation; either version 2 of the License, or (at
|
||||
** your option) any later version.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef _MSC_VER
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include "nodebuild.h"
|
||||
#include "printf.h"
|
||||
#include "m_fixed.h"
|
||||
#include "m_bbox.h"
|
||||
|
||||
#if 0
|
||||
#define D(x) x
|
||||
#else
|
||||
#define D(x) do{}while(0)
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define P(x) x
|
||||
#else
|
||||
#define P(x) do{}while(0)
|
||||
#endif
|
||||
|
||||
angle_t FNodeBuilder::PointToAngle (fixed_t x, fixed_t y)
|
||||
{
|
||||
const double rad2bam = double(1<<30) / M_PI;
|
||||
double ang = g_atan2 (double(y), double(x));
|
||||
// Convert to signed first since negative double to unsigned is undefined.
|
||||
return angle_t(int(ang * rad2bam)) << 1;
|
||||
}
|
||||
|
||||
void FNodeBuilder::FindUsedVertices (vertex_t *oldverts, int max)
|
||||
{
|
||||
int *map = new int[max];
|
||||
int i;
|
||||
FPrivVert newvert;
|
||||
|
||||
memset (&map[0], -1, sizeof(int)*max);
|
||||
|
||||
for (i = 0; i < Level.NumLines; ++i)
|
||||
{
|
||||
ptrdiff_t v1 = Level.Lines[i].v1 - oldverts;
|
||||
ptrdiff_t v2 = Level.Lines[i].v2 - oldverts;
|
||||
|
||||
if (map[v1] == -1)
|
||||
{
|
||||
newvert.x = oldverts[v1].fixX();
|
||||
newvert.y = oldverts[v1].fixY();
|
||||
map[v1] = VertexMap->SelectVertexExact (newvert);
|
||||
}
|
||||
if (map[v2] == -1)
|
||||
{
|
||||
newvert.x = oldverts[v2].fixX();
|
||||
newvert.y = oldverts[v2].fixY();
|
||||
map[v2] = VertexMap->SelectVertexExact (newvert);
|
||||
}
|
||||
|
||||
Level.Lines[i].v1 = (vertex_t *)(size_t)map[v1];
|
||||
Level.Lines[i].v2 = (vertex_t *)(size_t)map[v2];
|
||||
}
|
||||
OldVertexTable = map;
|
||||
}
|
||||
|
||||
// Retrieves the original vertex -> current vertex table.
|
||||
// Doing so prevents the node builder from freeing it.
|
||||
|
||||
const int *FNodeBuilder::GetOldVertexTable()
|
||||
{
|
||||
int *table = OldVertexTable;
|
||||
OldVertexTable = NULL;
|
||||
return table;
|
||||
}
|
||||
|
||||
// For every sidedef in the map, create a corresponding seg.
|
||||
|
||||
void FNodeBuilder::MakeSegsFromSides ()
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < Level.NumLines; ++i)
|
||||
{
|
||||
if (Level.Lines[i].sidedef[0] != NULL)
|
||||
{
|
||||
CreateSeg (i, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf ("Linedef %d does not have a front side.\n", i);
|
||||
}
|
||||
|
||||
if (Level.Lines[i].sidedef[1] != NULL)
|
||||
{
|
||||
j = CreateSeg (i, 1);
|
||||
if (Level.Lines[i].sidedef[0] != NULL)
|
||||
{
|
||||
Segs[j-1].partner = j;
|
||||
Segs[j].partner = j-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int FNodeBuilder::CreateSeg (int linenum, int sidenum)
|
||||
{
|
||||
FPrivSeg seg;
|
||||
int segnum;
|
||||
|
||||
seg.next = UINT_MAX;
|
||||
seg.loopnum = 0;
|
||||
seg.partner = UINT_MAX;
|
||||
seg.hashnext = NULL;
|
||||
seg.planefront = false;
|
||||
seg.planenum = UINT_MAX;
|
||||
seg.storedseg = UINT_MAX;
|
||||
|
||||
if (sidenum == 0)
|
||||
{ // front
|
||||
seg.frontsector = Level.Lines[linenum].frontsector;
|
||||
seg.backsector = Level.Lines[linenum].backsector;
|
||||
seg.v1 = (int)(size_t)Level.Lines[linenum].v1;
|
||||
seg.v2 = (int)(size_t)Level.Lines[linenum].v2;
|
||||
}
|
||||
else
|
||||
{ // back
|
||||
seg.frontsector = Level.Lines[linenum].backsector;
|
||||
seg.backsector = Level.Lines[linenum].frontsector;
|
||||
seg.v2 = (int)(size_t)Level.Lines[linenum].v1;
|
||||
seg.v1 = (int)(size_t)Level.Lines[linenum].v2;
|
||||
}
|
||||
seg.linedef = linenum;
|
||||
side_t *sd = Level.Lines[linenum].sidedef[sidenum];
|
||||
seg.sidedef = sd != NULL? sd->Index() : 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;
|
||||
D(Printf(PRINT_LOG, "Seg %4d: From line %d, side %s (%5d,%5d)-(%5d,%5d) [%08x,%08x]-[%08x,%08x]\n", segnum, linenum, sidenum ? "back " : "front",
|
||||
Vertices[seg.v1].x>>16, Vertices[seg.v1].y>>16, Vertices[seg.v2].x>>16, Vertices[seg.v2].y>>16,
|
||||
Vertices[seg.v1].x, Vertices[seg.v1].y, Vertices[seg.v2].x, Vertices[seg.v2].y));
|
||||
|
||||
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 = UINT_MAX;
|
||||
seg.loopnum = 0;
|
||||
seg.partner = UINT_MAX;
|
||||
seg.hashnext = NULL;
|
||||
seg.planefront = false;
|
||||
seg.planenum = UINT_MAX;
|
||||
seg.storedseg = UINT_MAX;
|
||||
|
||||
seg.frontsector = segs[i].frontsector;
|
||||
seg.backsector = segs[i].backsector;
|
||||
vert.x = segs[i].v1->fixX();
|
||||
vert.y = segs[i].v1->fixY();
|
||||
seg.v1 = VertexMap->SelectVertexExact(vert);
|
||||
vert.x = segs[i].v2->fixX();
|
||||
vert.y = segs[i].v2->fixY();
|
||||
seg.v2 = VertexMap->SelectVertexExact(vert);
|
||||
seg.linedef = segs[i].linedef->Index();
|
||||
seg.sidedef = segs[i].sidedef != NULL ? segs[i].sidedef->Index() : 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Group colinear segs together so that only one seg per line needs to be checked
|
||||
// by SelectSplitter().
|
||||
|
||||
void FNodeBuilder::GroupSegPlanes ()
|
||||
{
|
||||
const int bucketbits = 12;
|
||||
FPrivSeg *buckets[1<<bucketbits] = { 0 };
|
||||
int i, planenum;
|
||||
|
||||
for (i = 0; i < (int)Segs.Size(); ++i)
|
||||
{
|
||||
FPrivSeg *seg = &Segs[i];
|
||||
seg->next = i+1;
|
||||
seg->hashnext = NULL;
|
||||
}
|
||||
|
||||
Segs[Segs.Size()-1].next = UINT_MAX;
|
||||
|
||||
for (i = planenum = 0; i < (int)Segs.Size(); ++i)
|
||||
{
|
||||
FPrivSeg *seg = &Segs[i];
|
||||
fixed_t x1 = Vertices[seg->v1].x;
|
||||
fixed_t y1 = Vertices[seg->v1].y;
|
||||
fixed_t x2 = Vertices[seg->v2].x;
|
||||
fixed_t y2 = Vertices[seg->v2].y;
|
||||
angle_t ang = PointToAngle (x2 - x1, y2 - y1);
|
||||
|
||||
if (ang >= 1u<<31)
|
||||
ang += 1u<<31;
|
||||
|
||||
FPrivSeg *check = buckets[ang >>= 31-bucketbits];
|
||||
|
||||
while (check != NULL)
|
||||
{
|
||||
fixed_t cx1 = Vertices[check->v1].x;
|
||||
fixed_t cy1 = Vertices[check->v1].y;
|
||||
fixed_t cdx = Vertices[check->v2].x - cx1;
|
||||
fixed_t cdy = Vertices[check->v2].y - cy1;
|
||||
if (PointOnSide (x1, y1, cx1, cy1, cdx, cdy) == 0 &&
|
||||
PointOnSide (x2, y2, cx1, cy1, cdx, cdy) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
check = check->hashnext;
|
||||
}
|
||||
if (check != NULL)
|
||||
{
|
||||
seg->planenum = check->planenum;
|
||||
const FSimpleLine *line = &Planes[seg->planenum];
|
||||
if (line->dx != 0)
|
||||
{
|
||||
if ((line->dx > 0 && x2 > x1) || (line->dx < 0 && x2 < x1))
|
||||
{
|
||||
seg->planefront = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
seg->planefront = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((line->dy > 0 && y2 > y1) || (line->dy < 0 && y2 < y1))
|
||||
{
|
||||
seg->planefront = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
seg->planefront = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
seg->hashnext = buckets[ang];
|
||||
buckets[ang] = seg;
|
||||
seg->planenum = planenum++;
|
||||
seg->planefront = true;
|
||||
|
||||
FSimpleLine pline = { Vertices[seg->v1].x,
|
||||
Vertices[seg->v1].y,
|
||||
Vertices[seg->v2].x - Vertices[seg->v1].x,
|
||||
Vertices[seg->v2].y - Vertices[seg->v1].y };
|
||||
Planes.Push (pline);
|
||||
}
|
||||
}
|
||||
|
||||
D(Printf ("%d planes from %d segs\n", planenum, Segs.Size()));
|
||||
|
||||
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 = UINT_MAX;
|
||||
PlaneChecked.Reserve((Segs.Size() + 7) / 8);
|
||||
}
|
||||
|
||||
void FNodeBuilder::AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg)
|
||||
{
|
||||
FPrivVert *v1 = &Vertices[seg->v1];
|
||||
FPrivVert *v2 = &Vertices[seg->v2];
|
||||
|
||||
if (v1->x < bbox[BOXLEFT]) bbox[BOXLEFT] = v1->x;
|
||||
if (v1->x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v1->x;
|
||||
if (v1->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v1->y;
|
||||
if (v1->y > bbox[BOXTOP]) bbox[BOXTOP] = v1->y;
|
||||
|
||||
if (v2->x < bbox[BOXLEFT]) bbox[BOXLEFT] = v2->x;
|
||||
if (v2->x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v2->x;
|
||||
if (v2->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v2->y;
|
||||
if (v2->y > bbox[BOXTOP]) bbox[BOXTOP] = v2->y;
|
||||
}
|
||||
|
||||
void FNodeBuilder::FLevel::FindMapBounds()
|
||||
{
|
||||
double minx, maxx, miny, maxy;
|
||||
|
||||
minx = maxx = Vertices[0].fX();
|
||||
miny = maxy = Vertices[0].fY();
|
||||
|
||||
for (int i = 1; i < NumVertices; ++i)
|
||||
{
|
||||
vertex_t* v = &Vertices[i];
|
||||
if (v->fX() < minx) minx = v->fX();
|
||||
else if (v->fX() > maxx) maxx = v->fX();
|
||||
if (v->fY() < miny) miny = v->fY();
|
||||
else if (v->fY() > maxy) maxy = v->fY();
|
||||
}
|
||||
|
||||
MinX = FLOAT2FIXED(minx);
|
||||
MinY = FLOAT2FIXED(miny);
|
||||
MaxX = FLOAT2FIXED(maxx);
|
||||
MaxY = FLOAT2FIXED(maxy);
|
||||
}
|
||||
|
||||
FNodeBuilder::IVertexMap::~IVertexMap()
|
||||
{
|
||||
}
|
||||
|
||||
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 + fixed64_t(BlocksWide) * BLOCK_SIZE - 1;
|
||||
MaxY = MinY + fixed64_t(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 VERTEX_EPSILON <= 1
|
||||
if (vertices[block[i]].x == vert.x && vertices[block[i]].y == vert.y)
|
||||
#else
|
||||
if (abs(vertices[block[i]].x - vert.x) < VERTEX_EPSILON &&
|
||||
abs(vertices[block[i]].y - vert.y) < VERTEX_EPSILON)
|
||||
#endif
|
||||
{
|
||||
return block[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Not present: add it!
|
||||
return InsertVertex (vert);
|
||||
}
|
||||
|
||||
int FNodeBuilder::FVertexMap::InsertVertex (FNodeBuilder::FPrivVert &vert)
|
||||
{
|
||||
int vertnum;
|
||||
|
||||
vert.segs = UINT_MAX;
|
||||
vert.segs2 = UINT_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.
|
||||
fixed64_t minx = max(MinX, fixed64_t(vert.x) - VERTEX_EPSILON);
|
||||
fixed64_t maxx = min(MaxX, fixed64_t(vert.x) + VERTEX_EPSILON);
|
||||
fixed64_t miny = max(MinY, fixed64_t(vert.y) - VERTEX_EPSILON);
|
||||
fixed64_t maxy = min(MaxY, fixed64_t(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;
|
||||
}
|
||||
|
||||
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 = UINT_MAX;
|
||||
vert.segs2 = UINT_MAX;
|
||||
return (int)MyBuilder.Vertices.Push (vert);
|
||||
}
|
|
@ -42,8 +42,6 @@
|
|||
#include "hw_sections.h"
|
||||
#include "sectorgeometry.h"
|
||||
#include "gamefuncs.h"
|
||||
#include "earcut.hpp"
|
||||
#include "nodebuilder/nodebuild.h"
|
||||
|
||||
|
||||
FMemArena tempsectionArena(102400);
|
||||
|
|
|
@ -88,17 +88,18 @@ void BunchDrawer::Init(HWDrawInfo *_di, Clipper* c, vec2_t& view, binangle a1, b
|
|||
|
||||
void BunchDrawer::StartScene()
|
||||
{
|
||||
unsigned numsections = Sections.Size();
|
||||
LastBunch = 0;
|
||||
StartTime = I_msTime();
|
||||
Bunches.Clear();
|
||||
CompareData.Clear();
|
||||
gotsector.Resize(numsectors);
|
||||
gotsector.Zero();
|
||||
gotsection2.Resize(Sections.Size());
|
||||
gotsection2.Resize(numsections);
|
||||
gotsection2.Zero();
|
||||
gotwall.Zero();
|
||||
sectionstartang.Resize(Sections.Size());
|
||||
sectionendang.Resize(Sections.Size());
|
||||
sectionstartang.Resize(numsections);
|
||||
sectionendang.Resize(numsections);
|
||||
blockwall.Zero();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue