mirror of
https://github.com/ZDoom/raze-gles.git
synced 2025-01-13 19:40:43 +00:00
- use ZDoom's node builder for triangulating sectors that fail the simple approach.
As it turned out, the triangulator only works fine for regular polygons, but creates incomplete meshes for sectors with multiple sections or some degenerate areas, which are quite common with swinging doors. The node builder is more costly and creates wall splits, of course, but it does not create broken output for degenerate sectors so it's a good fallback.
This commit is contained in:
parent
3ff3c6f50e
commit
f7dd0ec4a2
10 changed files with 3378 additions and 5 deletions
|
@ -640,6 +640,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
|
||||
|
@ -1071,6 +1072,13 @@ set (PCH_SOURCES
|
|||
core/statusbar2.cpp
|
||||
core/gi.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/scene/hw_clipper.cpp
|
||||
|
|
1059
source/core/nodebuilder/nodebuild.cpp
Normal file
1059
source/core/nodebuilder/nodebuild.cpp
Normal file
File diff suppressed because it is too large
Load diff
470
source/core/nodebuilder/nodebuild.h
Normal file
470
source/core/nodebuilder/nodebuild.h
Normal file
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
** 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 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;
|
135
source/core/nodebuilder/nodebuild_classify_nosse2.cpp
Normal file
135
source/core/nodebuilder/nodebuild_classify_nosse2.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
#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;
|
||||
}
|
226
source/core/nodebuilder/nodebuild_events.cpp
Normal file
226
source/core/nodebuilder/nodebuild_events.cpp
Normal file
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
** 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);
|
||||
}
|
||||
}
|
440
source/core/nodebuilder/nodebuild_extract.cpp
Normal file
440
source/core/nodebuilder/nodebuild_extract.cpp
Normal file
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
** 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);
|
||||
}
|
397
source/core/nodebuilder/nodebuild_gl.cpp
Normal file
397
source/core/nodebuilder/nodebuild_gl.cpp
Normal file
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
** 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;
|
||||
}
|
535
source/core/nodebuilder/nodebuild_utility.cpp
Normal file
535
source/core/nodebuilder/nodebuild_utility.cpp
Normal file
|
@ -0,0 +1,535 @@
|
|||
/*
|
||||
** 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;
|
||||
|
||||
if (Level.NumLines == 0)
|
||||
{
|
||||
I_Error ("Map is empty.\n");
|
||||
}
|
||||
|
||||
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 < NumLines; ++i)
|
||||
{
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
vertex_t *v = (j == 0 ? Lines[i].v1 : Lines[i].v2);
|
||||
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);
|
||||
}
|
|
@ -39,6 +39,7 @@
|
|||
#include "gamefuncs.h"
|
||||
#include "texturemanager.h"
|
||||
#include "earcut.hpp"
|
||||
#include "nodebuilder/nodebuild.h"
|
||||
|
||||
SectorGeometry sectorGeometry;
|
||||
|
||||
|
@ -214,7 +215,7 @@ public:
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2& offset)
|
||||
bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2& offset)
|
||||
{
|
||||
auto sec = §or[secnum];
|
||||
int numvertices = sec->wallnum;
|
||||
|
@ -229,7 +230,6 @@ void SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
FixedBitArray<MAXWALLSB> done;
|
||||
|
||||
int fz = sec->floorz, cz = sec->ceilingz;
|
||||
sec->floorz = sec->ceilingz = 0;
|
||||
|
||||
int vertstoadd = numvertices;
|
||||
|
||||
|
@ -249,7 +249,8 @@ void SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
if (fabs(X) > 32768. || fabs(Y) > 32768.)
|
||||
{
|
||||
// If we get here there's some fuckery going around with the coordinates. Let's better abort and wait for things to realign.
|
||||
return;
|
||||
// Do not try alternative methods if this happens.
|
||||
return true;
|
||||
}
|
||||
curPoly->push_back(std::make_pair(X, Y));
|
||||
done.Set(start);
|
||||
|
@ -279,6 +280,12 @@ void SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
}
|
||||
if (outer != 0) std::swap(polygon[0], polygon[outer]);
|
||||
auto indices = mapbox::earcut(polygon);
|
||||
if (indices.size() < 3 * (sec->wallnum - 2))
|
||||
{
|
||||
// this means that full triangulation failed.
|
||||
return false;
|
||||
}
|
||||
sec->floorz = sec->ceilingz = 0;
|
||||
|
||||
int p = 0;
|
||||
for (size_t a = 0; a < polygon.size(); a++)
|
||||
|
@ -310,7 +317,96 @@ void SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
|
||||
sec->floorz = fz;
|
||||
sec->ceilingz = cz;
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Use ZDoom's node builder if the simple approach fails.
|
||||
// This will create something usable in the vast majority of cases,
|
||||
// even if the result is less efficient.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool SectorGeometry::MakeVertices2(unsigned int secnum, int plane, const FVector2& offset)
|
||||
{
|
||||
|
||||
// Convert our sector into something the node builder understands
|
||||
auto sect = §or[secnum];
|
||||
TArray<vertex_t> vertexes(sect->wallnum, true);
|
||||
TArray<line_t> lines(sect->wallnum, true);
|
||||
TArray<side_t> sides(sect->wallnum, true);
|
||||
for (int i = 0; i < sect->wallnum; i++)
|
||||
{
|
||||
auto wal = &wall[sect->wallptr + i];
|
||||
vertexes[i].p = { wal->x * (1 / 16.), wal->y * (1 / -16.) };
|
||||
lines[i].backsector = nullptr;
|
||||
lines[i].frontsector = sect;
|
||||
lines[i].linenum = i;
|
||||
lines[i].sidedef[0] = &sides[i];
|
||||
lines[i].sidedef[1] = nullptr;
|
||||
lines[i].v1 = &vertexes[i];
|
||||
lines[i].v2 = &vertexes[wal->point2 - sect->wallptr];
|
||||
|
||||
sides[i].sidenum = i;
|
||||
sides[i].sector = sect;
|
||||
}
|
||||
|
||||
|
||||
FNodeBuilder::FLevel leveldata =
|
||||
{
|
||||
&vertexes[0], (int)vertexes.Size(),
|
||||
&sides[0], (int)sides.Size(),
|
||||
&lines[0], (int)lines.Size(),
|
||||
0, 0, 0, 0
|
||||
};
|
||||
leveldata.FindMapBounds();
|
||||
FNodeBuilder builder(leveldata);
|
||||
|
||||
FLevelLocals Level;
|
||||
builder.Extract(Level);
|
||||
|
||||
// Now turn the generated subsectors into triangle meshes
|
||||
|
||||
auto& entry = data[secnum].planes[plane];
|
||||
|
||||
int fz = sect->floorz, cz = sect->ceilingz;
|
||||
sect->floorz = sect->ceilingz = 0;
|
||||
|
||||
for (auto& sub : Level.subsectors)
|
||||
{
|
||||
auto v0 = sub.firstline->v1;
|
||||
for (unsigned i = 1; i < sub.numlines-1; i++)
|
||||
{
|
||||
auto v1 = sub.firstline[i].v1;
|
||||
auto v2 = sub.firstline[i].v2;
|
||||
|
||||
entry.vertices.Push({ (float)v0->fX(), (float)v0->fY(), 0 });
|
||||
entry.vertices.Push({ (float)v1->fX(), (float)v1->fY(), 0 });
|
||||
entry.vertices.Push({ (float)v2->fX(), (float)v2->fY(), 0 });
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// calculate the rest.
|
||||
auto texture = tileGetTexture(plane ? sect->ceilingpicnum : sect->floorpicnum);
|
||||
|
||||
UVCalculator uvcalc(sect, plane, texture, offset);
|
||||
|
||||
entry.texcoords.Resize(entry.vertices.Size());
|
||||
for (unsigned i = 0; i < entry.vertices.Size(); i++)
|
||||
{
|
||||
auto& pt = entry.vertices[i];
|
||||
|
||||
float planez;
|
||||
PlanesAtPoint(sect, (pt.X * 16), (pt.Y * -16), plane ? &planez : nullptr, !plane ? &planez : nullptr);
|
||||
entry.vertices[i].Z = planez;
|
||||
entry.texcoords[i] = uvcalc.GetUV(int(pt.X * 16.), int(pt.Y * -16.), pt.Z);
|
||||
}
|
||||
entry.normal = CalcNormal(sect, plane);
|
||||
sect->floorz = fz;
|
||||
sect->ceilingz = cz;
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -352,5 +448,10 @@ void SectorGeometry::ValidateSector(unsigned int secnum, int plane, const FVecto
|
|||
*compare = *sec;
|
||||
data[secnum].poscompare[plane] = wall[sec->wallptr].pos;
|
||||
data[secnum].poscompare2[plane] = wall[wall[sec->wallptr].point2].pos;
|
||||
MakeVertices(secnum, plane, offset);
|
||||
if (data[secnum].degenerate || !MakeVertices(secnum, plane, offset))
|
||||
{
|
||||
data[secnum].degenerate = true;
|
||||
//Printf(TEXTCOLOR_YELLOW "Normal triangulation failed for sector %d. Retrying with alternative approach\n", secnum);
|
||||
MakeVertices2(secnum, plane, offset);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ struct SectorGeometryData
|
|||
sectortype compare[2] = {};
|
||||
vec2_t poscompare[2] = {};
|
||||
vec2_t poscompare2[2] = {};
|
||||
bool degenerate = false;
|
||||
};
|
||||
|
||||
class SectorGeometry
|
||||
|
@ -24,7 +25,8 @@ class SectorGeometry
|
|||
TArray<SectorGeometryData> data;
|
||||
|
||||
void ValidateSector(unsigned sectnum, int plane, const FVector2& offset);
|
||||
void MakeVertices(unsigned sectnum, int plane, const FVector2& offset);
|
||||
bool MakeVertices(unsigned sectnum, int plane, const FVector2& offset);
|
||||
bool MakeVertices2(unsigned sectnum, int plane, const FVector2& offset);
|
||||
|
||||
public:
|
||||
SectorGeometryPlane* get(unsigned sectnum, int plane, const FVector2& offset)
|
||||
|
|
Loading…
Reference in a new issue