Serious-Engine/Sources/Engine/Templates/BSP.cpp

1313 lines
41 KiB
C++
Raw Normal View History

2016-03-12 01:20:51 +01:00
/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
2016-03-11 14:57:17 +01:00
#include "Engine/StdH.h"
2016-03-11 14:57:17 +01:00
#include <Engine/Templates/BSP.h>
#include <Engine/Templates/BSP_internal.h>
#include <Engine/Base/Stream.h>
#include <Engine/Base/CRC.h>
2016-03-11 14:57:17 +01:00
#include <Engine/Math/Vector.h>
#include <Engine/Math/Plane.h>
#include <Engine/Math/OBBox.h>
#include <Engine/Math/Functions.h>
#include <Engine/Templates/StaticStackArray.cpp>
#include <Engine/Templates/DynamicArray.cpp>
// epsilon value used for BSP cutting
//#define BSP_EPSILON ((Type) 0.015625) // 1/2^6 ~= 1.5 cm
#define BSP_EPSILON Type((1.0/65536.0)*4*mth_fCSGEpsilon) // 1/2^16
//#define BSP_EPSILON Type(0.00390625) // 1/2^8
//#define EPSILON (1.0f/8388608.0f) // 1/2^23
//#define EPSILON 0.0009765625f // 1/2^10
//#define EPSILON 0.03125f // 1/2^5
//#define EPSILON 0.00390625f // 1/2^8
template <class Type>
inline BOOL EpsilonEq(const Type &a, const Type &b) { return Abs(a-b)<=BSP_EPSILON; };
template <class Type>
inline BOOL EpsilonNe(const Type &a, const Type &b) { return Abs(a-b)> BSP_EPSILON; };
/////////////////////////////////////////////////////////////////////
// BSP vertex
/*
* Assignment operator with coordinates only.
*/
template<class Type, int iDimensions>
BSPVertex<Type, iDimensions> &BSPVertex<Type, iDimensions>::operator=(const Vector<Type, iDimensions> &vCoordinates)
{
*(Vector<Type, iDimensions> *)this = vCoordinates;
return *this;
}
/////////////////////////////////////////////////////////////////////
// BSP vertex container
/*
* Default constructor.
*/
template<class Type, int iDimensions>
BSPVertexContainer<Type, iDimensions>::BSPVertexContainer(void)
{
}
template<class Type, int iDimensions>
void BSPVertexContainer<Type, iDimensions>::AddVertex(const Vector<Type, iDimensions> &vPoint)
{
bvc_aVertices.Push() = vPoint;
}
/*
* Initialize for a direction.
*/
template<class Type, int iDimensions>
void BSPVertexContainer<Type, iDimensions>::Initialize(const Vector<Type, iDimensions> &vDirection)
{
bvc_vDirection = vDirection;
// init array of vertices
bvc_aVertices.SetAllocationStep(32);
// find largest axis of direction vector
INDEX iMaxAxis = 0;
Type tMaxAxis = (Type)0;//vDirection(1);
for( INDEX iAxis=1; iAxis<=iDimensions; iAxis++) {
if( Abs(vDirection(iAxis)) > Abs(tMaxAxis) ) {
tMaxAxis = vDirection(iAxis);
iMaxAxis = iAxis;
}
}
/* This assert would seem natural here, but it is not possible because of parallel planes!
// must be greater or equal than minimal max axis of any normalized vector in that space
ASSERT( Abs(tMaxAxis) > (1.0/sqrt(double(iDimensions))-0.01) );
*/
// remember that axis index and sign for sorting
bvc_iMaxAxis = iMaxAxis;
bvc_tMaxAxisSign = Sgn(tMaxAxis);
}
/*
* Unnitialize.
*/
template<class Type, int iDimensions>
void BSPVertexContainer<Type, iDimensions>::Uninitialize(void)
{
// delete array of vertices
bvc_aVertices.Delete();
// destroy axis index and sign
bvc_iMaxAxis = -1;
bvc_tMaxAxisSign = (Type)0;
}
static INDEX qsort_iCompareAxis;
template<class Type, int iDimensions>
class CVertexComparator {
public:
/*
* Compare two vertices.
*/
static inline int CompareVertices(const Vector<Type, iDimensions> &vx0, const Vector<Type, iDimensions> &vx1, INDEX iAxis)
{
if (vx0(iAxis)<vx1(iAxis)) return -1;
else if (vx0(iAxis)>vx1(iAxis)) return 1;
else return 0;
}
/*
* Compare two vertices for quick-sort.
*/
static int qsort_CompareVertices_plus( const void *pvVertex0, const void *pvVertex1)
{
BSPVertex<Type, iDimensions> &vx0 = *(BSPVertex<Type, iDimensions> *)pvVertex0;
BSPVertex<Type, iDimensions> &vx1 = *(BSPVertex<Type, iDimensions> *)pvVertex1;
return +CompareVertices(vx0, vx1, qsort_iCompareAxis);
}
static int qsort_CompareVertices_minus( const void *pvVertex0, const void *pvVertex1)
{
BSPVertex<Type, iDimensions> &vx0 = *(BSPVertex<Type, iDimensions> *)pvVertex0;
BSPVertex<Type, iDimensions> &vx1 = *(BSPVertex<Type, iDimensions> *)pvVertex1;
return -CompareVertices(vx0, vx1, qsort_iCompareAxis);
}
};
/*
* Sort vertices in this container along the largest axis of container direction.
*/
template<class Type, int iDimensions>
void BSPVertexContainer<Type, iDimensions>::Sort(void)
{
// if there are no vertices, or the container is not line
if (bvc_aVertices.Count()==0 || IsPlannar()) {
// do not attempt to sort
return;
}
// sort by max. axis
qsort_iCompareAxis = bvc_iMaxAxis;
// if the sign of axis is positive
if (bvc_tMaxAxisSign>0) {
// sort them normally
if (bvc_aVertices.Count()>0) {
qsort(&bvc_aVertices[0], bvc_aVertices.Count(), sizeof(BSPVertex<Type, iDimensions>),
CVertexComparator<Type, iDimensions>::qsort_CompareVertices_plus);
}
// if it is negative
} else {
// sort them inversely
if (bvc_aVertices.Count()>0) {
qsort(&bvc_aVertices[0], bvc_aVertices.Count(), sizeof(BSPVertex<Type, iDimensions>),
CVertexComparator<Type, iDimensions>::qsort_CompareVertices_minus);
}
}
}
/*
* Elliminate paired vertices.
*/
template<class Type, int iDimensions>
void BSPVertexContainer<Type, iDimensions>::ElliminatePairedVertices(void)
{
// if there are no vertices, or the container is not line
if (bvc_aVertices.Count()==0 || IsPlannar()) {
// do not attempt to sort
return;
}
// initially, last vertices are far away
Type tLastInside; tLastInside = (Type)32000;
BSPVertex<Type, iDimensions> *pbvxLastInside = NULL;
// for all vertices in container
for (INDEX iVertex=0; iVertex<bvc_aVertices.Count(); iVertex++) {
BSPVertex<Type, iDimensions> &bvx = bvc_aVertices[iVertex]; // reference to this vertex
Type t = bvx(bvc_iMaxAxis); // coordinate along max. axis
// if last inside vertex is next to this one
if ( EpsilonEq(t, tLastInside) ) {
// last vertex is far away
tLastInside = (Type)32000;
IFDEBUG(pbvxLastInside = NULL);
// otherwise
} else {
// make this last inside vertex
tLastInside = t;
pbvxLastInside = &bvx;
}
}
}
/*
* Create edges from vertices in one container -- must be sorted before.
*/
template<class Type, int iDimensions>
void BSPVertexContainer<Type, iDimensions>::CreateEdges(CDynamicArray<BSPEdge<Type, iDimensions> > &abed, size_t ulEdgeTag)
2016-03-11 14:57:17 +01:00
{
// if there are no vertices, or the container is not line
if (bvc_aVertices.Count()==0 || IsPlannar()) {
// do not attempt to sort
return;
}
// initially, edge is inactive
BOOL bActive = FALSE;
BSPEdge<Type, iDimensions> *pbed = NULL;
// for all vertices in container
for (INDEX iVertex=0; iVertex<bvc_aVertices.Count(); iVertex++) {
BSPVertex<Type, iDimensions> &bvx = bvc_aVertices[iVertex]; // reference to this vertex
// if edge is inactive
if (!bActive) {
// create new edge
pbed = abed.New();
pbed->bed_ulEdgeTag = ulEdgeTag;
// set start vertex
pbed->bed_vVertex0 = bvx;
} else {
// set end vertex
pbed->bed_vVertex1 = bvx;
// trash edge pointer
IFDEBUG(pbed = NULL);
}
// toggle edge
bActive = !bActive;
}
}
/////////////////////////////////////////////////////////////////////
// BSP edge
// remove all edges marked for removal
template<class Type, int iDimensions>
void BSPEdge<Type, iDimensions>::RemoveMarkedBSPEdges(CDynamicArray<BSPEdge<Type, iDimensions> > &abed)
{
typedef BSPEdge<Type, iDimensions> edge_t; // local declaration, to fix macro expansion in FOREACHINDYNAMICARRAY
// conut edges left
INDEX ctEdgesLeft = 0;
{FOREACHINDYNAMICARRAY(abed, edge_t, itbed) {
if (itbed->bed_ulEdgeTag != 0) {
ctEdgesLeft++;
}
}}
// make a copy of array without removed edges
CDynamicArray<BSPEdge<Type, iDimensions> > abed2;
abed2.New(ctEdgesLeft);
abed2.Lock();
INDEX iedNew = 0;
{FOREACHINDYNAMICARRAY(abed, edge_t, itbed) {
edge_t &bed = *itbed;
if (bed.bed_ulEdgeTag != 0) {
abed2[iedNew] = bed;
iedNew++;
}
}}
abed2.Unlock();
// use that copy instead the original array
abed.Clear();
abed.MoveArray(abed2);
}
// optimize a polygon made out of BSP edges using tag information
template<class Type, int iDimensions>
void BSPEdge<Type, iDimensions>::OptimizeBSPEdges(CDynamicArray<BSPEdge<Type, iDimensions> > &abed)
{
typedef BSPEdge<Type, iDimensions> edge_t; // local declaration, to fix macro expansion in FOREACHINDYNAMICARRAY
// if there are no edges
if (abed.Count()==0) {
// do nothing
return;
}
BOOL bSomeJoined;
// repeat
do {
bSomeJoined = FALSE;
// for each edge
{FOREACHINDYNAMICARRAY(abed, edge_t, itbed1) {
edge_t &bed1 = *itbed1;
// if it is already marked
if (bed1.bed_ulEdgeTag == 0) {
// skip it
continue;
}
// if it is dummy edge
if (bed1.bed_vVertex0==bed1.bed_vVertex1) {
// mark it for removal
bSomeJoined = TRUE;
bed1.bed_ulEdgeTag = 0;
// skip it
continue;
}
// for each other edge
{FOREACHINDYNAMICARRAY(abed, edge_t, itbed2) {
edge_t &bed2 = *itbed2;
if (&bed1==&bed2) {
continue;
}
// if it is already marked
if (bed2.bed_ulEdgeTag == 0) {
// skip it
continue;
}
// if they originate from same edge (plane)
if (bed1.bed_ulEdgeTag == bed2.bed_ulEdgeTag) {
// if they are complemented
if (bed1.bed_vVertex0==bed2.bed_vVertex1 && bed1.bed_vVertex1==bed2.bed_vVertex0) {
// marked them both
bSomeJoined = TRUE;
bed1.bed_ulEdgeTag = 0;
bed2.bed_ulEdgeTag = 0;
// skip them both
break;
}
// if second one continues after first one
if (bed1.bed_vVertex1==bed2.bed_vVertex0) {
// extend end of first edge to the end of second one
bed1.bed_vVertex1=bed2.bed_vVertex1;
bSomeJoined = TRUE;
// marked second edge
bed2.bed_ulEdgeTag = 0;
// if second one continues before first one
} else if (bed1.bed_vVertex0==bed2.bed_vVertex1) {
// extend start of first edge to the start of second one
bed1.bed_vVertex0=bed2.bed_vVertex0;
bSomeJoined = TRUE;
// marked second edge
bed2.bed_ulEdgeTag = 0;
}
}
}}
}}
// while some edges can be joined
} while(bSomeJoined);
// remove all marked edges
RemoveMarkedBSPEdges(abed);
}
/////////////////////////////////////////////////////////////////////
// BSP polygon
/*
* Add an edge to the polygon.
*/
template<class Type, int iDimensions>
inline void BSPPolygon<Type, iDimensions>::AddEdge(const Vector<Type, iDimensions> &vPoint0, const Vector<Type, iDimensions> &vPoint1, size_t ulTag)
2016-03-11 14:57:17 +01:00
{
*bpo_abedPolygonEdges.New() = BSPEdge<Type, iDimensions>(vPoint0, vPoint1, ulTag);
}
/////////////////////////////////////////////////////////////////////
// BSP node
/*
* Recursive destructor.
*/
template<class Type, int iDimensions>
void BSPNode<Type, iDimensions>::DeleteBSPNodeRecursively(void)
{
// delete sub-trees first, before deleting this node
if (bn_pbnFront!=NULL) {
bn_pbnFront->DeleteBSPNodeRecursively();
}
if (bn_pbnBack!=NULL) {
bn_pbnBack->DeleteBSPNodeRecursively();
}
delete this;
}
/*
* Constructor for a leaf node.
*/
template<class Type, int iDimensions>
BSPNode<Type, iDimensions>::BSPNode(enum BSPNodeLocation bnl)
: bn_bnlLocation(bnl)
, bn_pbnFront(NULL)
, bn_pbnBack(NULL)
{
ASSERT(bnl == BNL_INSIDE || bnl == BNL_OUTSIDE);
}
/*
* Constructor for a branch node.
*/
template<class Type, int iDimensions>
BSPNode<Type, iDimensions>::BSPNode(const Plane<Type, iDimensions> &plSplitPlane, size_t ulPlaneTag,
2016-03-11 14:57:17 +01:00
BSPNode<Type, iDimensions> &bnFront, BSPNode<Type, iDimensions> &bnBack)
: Plane<Type, iDimensions>(plSplitPlane)
, bn_pbnFront(&bnFront)
, bn_pbnBack(&bnBack)
, bn_bnlLocation(BNL_BRANCH)
, bn_ulPlaneTag(ulPlaneTag)
{
}
/*
* Constructor for cloning a bsp (sub)tree.
*/
template<class Type, int iDimensions>
BSPNode<Type, iDimensions>::BSPNode(BSPNode<Type, iDimensions> &bnRoot)
: Plane<Type, iDimensions>(bnRoot) // copy the plane
, bn_bnlLocation(bnRoot.bn_bnlLocation) // copy the location
, bn_ulPlaneTag(bnRoot.bn_ulPlaneTag) // copy the plane tag
{
// if this has a front child
if (bnRoot.bn_pbnFront != NULL) {
// clone front sub tree
bn_pbnFront = new BSPNode<Type, iDimensions>(*bnRoot.bn_pbnFront);
// otherwise
} else {
// no front sub tree
bn_pbnFront = NULL;
}
// if this has a back child
if (bnRoot.bn_pbnBack != NULL) {
// clone back sub tree
bn_pbnBack = new BSPNode<Type, iDimensions>(*bnRoot.bn_pbnBack);
// otherwise
} else {
// no back sub tree
bn_pbnBack = NULL;
}
}
/* Test if a sphere is inside, outside, or intersecting. (Just a trivial rejection test) */
template<class Type, int iDimensions>
FLOAT BSPNode<Type, iDimensions>::TestSphere(const Vector<Type, iDimensions> &vSphereCenter, Type tSphereRadius) const
{
// if this is an inside node
if (bn_bnlLocation == BNL_INSIDE) {
// it is inside
return 1;
// if this is an outside node
} else if (bn_bnlLocation == BNL_OUTSIDE) {
// it is outside
return -1;
// if this is a branch
} else {
ASSERT(bn_bnlLocation == BNL_BRANCH);
// test the sphere against the split plane
Type tCenterDistance = this->PointDistance(vSphereCenter);
2016-03-11 14:57:17 +01:00
// if the sphere is in front of the plane
if (tCenterDistance > +tSphereRadius) {
// recurse down the front node
return bn_pbnFront->TestSphere(vSphereCenter, tSphereRadius);
// if the sphere is behind the plane
} else if (tCenterDistance < -tSphereRadius) {
// recurse down the back node
return bn_pbnBack->TestSphere(vSphereCenter, tSphereRadius);
// if the sphere is split by the plane
} else {
// if front node touches
FLOAT fFront = bn_pbnFront->TestSphere(vSphereCenter, tSphereRadius);
if (fFront==0) {
// it touches
return 0;
}
// if back node touches
FLOAT fBack = bn_pbnBack->TestSphere(vSphereCenter, tSphereRadius);
if (fBack==0) {
// it touches
return 0;
}
// if front and back have same classification
if (fFront==fBack) {
// return it
return fFront;
// if front and back have different classification
} else {
// it touches
return 0;
}
}
}
}
/* Test if a box is inside, outside, or intersecting. (Just a trivial rejection test) */
template<class Type, int iDimensions>
FLOAT BSPNode<Type, iDimensions>::TestBox(const OBBox<Type> &box) const
{
// if this is an inside node
if (bn_bnlLocation == BNL_INSIDE) {
// it is inside
return 1;
// if this is an outside node
} else if (bn_bnlLocation == BNL_OUTSIDE) {
// it is outside
return -1;
// if this is a branch
} else {
ASSERT(bn_bnlLocation == BNL_BRANCH);
// test the box against the split plane
Type tTest = box.TestAgainstPlane(*this);
// if the sphere is in front of the plane
if (tTest>0) {
// recurse down the front node
return bn_pbnFront->TestBox(box);
// if the sphere is behind the plane
} else if (tTest<0) {
// recurse down the back node
return bn_pbnBack->TestBox(box);
// if the sphere is split by the plane
} else {
// if front node touches
FLOAT fFront = bn_pbnFront->TestBox(box);
if (fFront==0) {
// it touches
return 0;
}
// if back node touches
FLOAT fBack = bn_pbnBack->TestBox(box);
if (fBack==0) {
// it touches
return 0;
}
// if front and back have same classification
if (fFront==fBack) {
// return it
return fFront;
// if front and back have different classification
} else {
// it touches
return 0;
}
}
}
}
// find minimum/maximum parameters of points on a line that are inside - recursive
template<class Type, int iDimensions>
void BSPNode<Type, iDimensions>::FindLineMinMax(
BSPLine<Type, iDimensions> &bl,
const Vector<Type, iDimensions> &v0,
const Vector<Type, iDimensions> &v1,
Type t0, Type t1)
{
// if this is an inside node
if (bn_bnlLocation == BNL_INSIDE) {
// just update min/max
bl.bl_tMin = Min(bl.bl_tMin, t0);
bl.bl_tMax = Max(bl.bl_tMax, t1);
return;
// if this is an outside node
} else if (bn_bnlLocation == BNL_OUTSIDE) {
// do nothing
return;
// if this is a branch
} else {
ASSERT(bn_bnlLocation == BNL_BRANCH);
// test the points against the split plane
Type tD0 = this->PointDistance(v0);
Type tD1 = this->PointDistance(v1);
2016-03-11 14:57:17 +01:00
// if both are front
if (tD0>=0 && tD1>=0) {
// recurse down the front node
bn_pbnFront->FindLineMinMax(bl, v0, v1, t0, t1);
return;
// if both are back
} else if (tD0<0 && tD1<0) {
// recurse down the back node
bn_pbnBack->FindLineMinMax(bl, v0, v1, t0, t1);
return;
// if on different sides
} else {
// find split point
Type tFraction = tD0/(tD0-tD1);
Vector<Type, iDimensions> vS = v0+(v1-v0)*tFraction;
Type tS = t0+(t1-t0)*tFraction;
// if first is front
if (tD0>=0) {
// recurse first part down the front node
bn_pbnFront->FindLineMinMax(bl, v0, vS, t0, tS);
// recurse second part down the back node
bn_pbnBack->FindLineMinMax(bl, vS, v1, tS, t1);
return;
// if first is back
} else {
// recurse first part down the back node
bn_pbnBack->FindLineMinMax(bl, v0, vS, t0, tS);
// recurse second part down the front node
bn_pbnFront->FindLineMinMax(bl, vS, v1, tS, t1);
return;
}
}
}
}
/////////////////////////////////////////////////////////////////////
// BSP cutter
/*
* Constructor for splitting a polygon with a BSP tree.
*/
template<class Type, int iDimensions>
BSPCutter<Type, iDimensions>::BSPCutter(BSPPolygon<Type, iDimensions> &bpoPolygon, BSPNode<Type, iDimensions> &bnRoot)
{
// cut the polygon with entire tree
CutPolygon(bpoPolygon, bnRoot);
}
/*
* Destructor.
*/
template<class Type, int iDimensions>
BSPCutter<Type, iDimensions>::~BSPCutter(void)
{
}
/*
* Cut a polygon with a BSP tree.
*/
template<class Type, int iDimensions>
void BSPCutter<Type, iDimensions>::CutPolygon(BSPPolygon<Type, iDimensions> &bpoPolygon, BSPNode<Type, iDimensions> &bn)
{
// if the polygon has no edges
if (bpoPolygon.bpo_abedPolygonEdges.Count()==0) {
// skip cutting
return;
}
// if this node is inside node
if (bn.bn_bnlLocation == BNL_INSIDE) {
// add entire polygon to inside part
bc_abedInside.MoveArray(bpoPolygon.bpo_abedPolygonEdges);
// if this node is outside node
} else if (bn.bn_bnlLocation == BNL_OUTSIDE) {
// add entire polygon to outside part
bc_abedOutside.MoveArray(bpoPolygon.bpo_abedPolygonEdges);
// if this node is a branch
} else if (bn.bn_bnlLocation == BNL_BRANCH) {
BSPPolygon<Type, iDimensions> bpoFront; // part of polygon in front of this splitter
BSPPolygon<Type, iDimensions> bpoBack; // part of polygon behind this splitter
// split the polygon with split plane of this node
BOOL bOnPlane = SplitPolygon(bpoPolygon, (Plane<Type, iDimensions> &)bn, bn.bn_ulPlaneTag, bpoFront, bpoBack);
// if the polygon is not on the split plane
if (!bOnPlane) {
// recursively split front part with front part of bsp
CutPolygon(bpoFront, *bn.bn_pbnFront);
// recursively split back part with back part of bsp
CutPolygon(bpoBack, *bn.bn_pbnBack);
// if the polygon is on the split plane
} else {
BSPNode<Type, iDimensions> *pbnFront; // front node (relative to the polygon orientation)
BSPNode<Type, iDimensions> *pbnBack; // back node (relative to the polygon orientation)
// check the direction of the polygon with the front direction of the split plane
Type tDirection = (Vector<Type, iDimensions> &)bpoPolygon%(Vector<Type, iDimensions> &)bn;
// if the directions are same
if (tDirection > +BSP_EPSILON) {
// make nodes relative to polygon same as relative to the split plane
pbnFront = bn.bn_pbnFront;
pbnBack = bn.bn_pbnBack;
// if the directions are opposite
} else if (tDirection < -BSP_EPSILON) {
// make nodes relative to polygon opposite as relative to the split plane
pbnFront = bn.bn_pbnBack;
pbnBack = bn.bn_pbnFront;
// if the directions are indeterminate
} else {
// that must not be
ASSERT(FALSE);
}
// cut it with front part of bsp
BSPCutter<Type, iDimensions> bcFront(bpoPolygon, *pbnFront);
// there must be no on-border parts
ASSERT(bcFront.bc_abedBorderInside.Count()==0 && bcFront.bc_abedBorderOutside.Count()==0);
// make a polygon from parts that are inside in front part of BSP
BSPPolygon<Type, iDimensions> bpoInsideFront((Plane<Type, iDimensions> &)bpoPolygon, bcFront.bc_abedInside, bpoPolygon.bpo_ulPlaneTag);
// cut them with back part of bsp
BSPCutter<Type, iDimensions> bcBackInsideFront(bpoInsideFront, *pbnBack);
// make a polygon from parts that are outside in front part of BSP
BSPPolygon<Type, iDimensions> bpoOutsideFront((Plane<Type, iDimensions> &)bpoPolygon, bcFront.bc_abedOutside, bpoPolygon.bpo_ulPlaneTag);
// cut them with back part of bsp
BSPCutter<Type, iDimensions> bcBackOutsideFront(bpoOutsideFront, *pbnBack);
// add parts that are inside both in front and back to inside part
bc_abedInside.MoveArray(bcBackInsideFront.bc_abedInside);
// add parts that are outside both in front and back to outside part
bc_abedOutside.MoveArray(bcBackOutsideFront.bc_abedOutside);
// add parts that are inside in front and outside back to on-border-inside-part
bc_abedBorderInside.MoveArray(bcBackInsideFront.bc_abedOutside);
// add parts that are outside in front and inside back to on-border-outside-part
bc_abedBorderOutside.MoveArray(bcBackOutsideFront.bc_abedInside);
}
} else {
ASSERTALWAYS("Bad node type");
}
}
/*
* Split a polygon with a plane.
* -- returns FALSE if polygon is laying on the plane
*/
template<class Type, int iDimensions>
BOOL BSPCutter<Type, iDimensions>::SplitPolygon(BSPPolygon<Type, iDimensions> &bpoPolygon, const Plane<Type, iDimensions> &plSplitPlane, size_t ulPlaneTag,
2016-03-11 14:57:17 +01:00
BSPPolygon<Type, iDimensions> &bpoFront, BSPPolygon<Type, iDimensions> &bpoBack)
{
(Plane<Type, iDimensions> &)bpoFront = (Plane<Type, iDimensions> &)bpoPolygon;
bpoFront.bpo_ulPlaneTag = bpoPolygon.bpo_ulPlaneTag;
(Plane<Type, iDimensions> &)bpoBack = (Plane<Type, iDimensions> &)bpoPolygon;
bpoBack.bpo_ulPlaneTag = bpoPolygon.bpo_ulPlaneTag;
// calculate the direction of split line
Vector<Type, iDimensions> vSplitDirection = ((Vector<Type, iDimensions> &)plSplitPlane) * (Vector<Type, iDimensions> &)bpoPolygon;
// if the polygon is parallel with the split plane
if (vSplitDirection.Length() < +BSP_EPSILON) {
// calculate the distance of the polygon from the split plane
Type fDistance = plSplitPlane.PlaneDistance(bpoPolygon);
// if the polygon is in front of plane
if (fDistance > +BSP_EPSILON) {
// move all edges to front array
bpoFront.bpo_abedPolygonEdges.MoveArray(bpoPolygon.bpo_abedPolygonEdges);
// the polygon is not on the plane
return FALSE;
// if the polygon is behind the plane
} else if (fDistance < -BSP_EPSILON) {
// move all edges to back array
bpoBack.bpo_abedPolygonEdges.MoveArray(bpoPolygon.bpo_abedPolygonEdges);
// the polygon is not on the plane
return FALSE;
// if the polygon is on the plane
} else {
// just return so
return TRUE;
}
// if the polygon is not parallel with the split plane
} else {
// initialize front and back vertex containers
BSPVertexContainer<Type, iDimensions> bvcFront, bvcBack;
bvcFront.Initialize(vSplitDirection);
bvcBack.Initialize(-vSplitDirection);
typedef BSPEdge<Type, iDimensions> edge_t; // local declaration, to fix macro expansion in FOREACHINDYNAMICARRAY
// for each edge in polygon
{FOREACHINDYNAMICARRAY(bpoPolygon.bpo_abedPolygonEdges, edge_t, itbed) {
// split the edge
SplitEdge(itbed->bed_vVertex0, itbed->bed_vVertex1, itbed->bed_ulEdgeTag, plSplitPlane,
bpoFront, bpoBack, bvcFront, bvcBack);
}}
// sort vertex containers
bvcFront.Sort();
bvcBack.Sort();
// elliminate paired vertices
bvcFront.ElliminatePairedVertices();
bvcBack.ElliminatePairedVertices();
// create more front polygon edges from front vertex container
bvcFront.CreateEdges(bpoFront.bpo_abedPolygonEdges, ulPlaneTag);
// create more back polygon edges from back vertex container
bvcBack.CreateEdges(bpoBack.bpo_abedPolygonEdges, ulPlaneTag);
// the polygon is not on the plane
return FALSE;
}
}
/*
* Split an edge with a plane.
*/
template<class Type, int iDimensions>
void BSPCutter<Type, iDimensions>::SplitEdge(const Vector<Type, iDimensions> &vPoint0, const Vector<Type, iDimensions> &vPoint1, size_t ulEdgeTag,
2016-03-11 14:57:17 +01:00
const Plane<Type, iDimensions> &plSplitPlane,
BSPPolygon<Type, iDimensions> &bpoFront, BSPPolygon<Type, iDimensions> &bpoBack,
BSPVertexContainer<Type, iDimensions> &bvcFront, BSPVertexContainer<Type, iDimensions> &bvcBack)
{
// calculate point distances from clip plane
Type tDistance0 = plSplitPlane.PointDistance(vPoint0);
Type tDistance1 = plSplitPlane.PointDistance(vPoint1);
/* ---- first point behind plane ---- */
if (tDistance0 < -BSP_EPSILON) {
// if both are back
if (tDistance1 < -BSP_EPSILON) {
// add the whole edge to back node
bpoBack.AddEdge(vPoint0, vPoint1, ulEdgeTag);
// no split points
// if first is back, second front
} else if (tDistance1 > +BSP_EPSILON) {
// calculate intersection coordinates
Vector<Type, iDimensions> vPointMid = vPoint0-(vPoint0-vPoint1)*tDistance0/(tDistance0-tDistance1);
// add front part to front node
bpoFront.AddEdge(vPointMid, vPoint1, ulEdgeTag);
// add back part to back node
bpoBack.AddEdge(vPoint0, vPointMid, ulEdgeTag);
// add split point to front _and_ back part of splitter
bvcFront.AddVertex(vPointMid);
bvcBack.AddVertex(vPointMid);
// if first is back, second on the plane
} else {
// add the whole edge to back node
bpoBack.AddEdge(vPoint0, vPoint1, ulEdgeTag);
// add second point to back part of splitter
bvcBack.AddVertex(vPoint1);
}
/* ---- first point in front of plane ---- */
} else if (tDistance0 > +BSP_EPSILON) {
// if first is front, second back
if (tDistance1 < -BSP_EPSILON) {
// calculate intersection coordinates
Vector<Type, iDimensions> vPointMid = vPoint1-(vPoint1-vPoint0)*tDistance1/(tDistance1-tDistance0);
// add front part to front node
bpoFront.AddEdge(vPoint0, vPointMid, ulEdgeTag);
// add back part to back node
bpoBack.AddEdge(vPointMid, vPoint1, ulEdgeTag);
// add split point to front _and_ back part of splitter
bvcFront.AddVertex(vPointMid);
bvcBack.AddVertex(vPointMid);
// if both are front
} else if (tDistance1 > +BSP_EPSILON) {
// add the whole edge to front node
bpoFront.AddEdge(vPoint0, vPoint1, ulEdgeTag);
// no split points
// if first is front, second on the plane
} else {
// add the whole edge to front node
bpoFront.AddEdge(vPoint0, vPoint1, ulEdgeTag);
// add second point to front part of splitter
bvcFront.AddVertex(vPoint1);
}
/* ---- first point on the plane ---- */
} else {
// if first is on the plane, second back
if (tDistance1 < -BSP_EPSILON) {
// add the whole edge to back node
bpoBack.AddEdge(vPoint0, vPoint1, ulEdgeTag);
// add first point to back part of splitter
bvcBack.AddVertex(vPoint0);
// if first is on the plane, second in front of the plane
} else if (tDistance1 > +BSP_EPSILON) {
// add the whole edge to front node
bpoFront.AddEdge(vPoint0, vPoint1, ulEdgeTag);
// add first point to front part of splitter
bvcFront.AddVertex(vPoint0);
// if both are on the plane
} else {
// check the direction of the edge with the front direction of the splitter
Type tDirection = (vPoint1-vPoint0)%bvcFront.bvc_vDirection;
// if the directions are same
if (tDirection > +BSP_EPSILON) {
// add the whole edge to front node
bpoFront.AddEdge(vPoint0, vPoint1, ulEdgeTag);
// add both points to front part of the splitter
bvcFront.AddVertex(vPoint0);
bvcFront.AddVertex(vPoint1);
// if the directions are opposite
} else if (tDirection < -BSP_EPSILON) {
// add the whole edge to back node
bpoBack.AddEdge(vPoint0, vPoint1, ulEdgeTag);
// add both points to back part of the splitter
bvcBack.AddVertex(vPoint0);
bvcBack.AddVertex(vPoint1);
// if the directions are indeterminate
} else {
// that must mean that there is no edge in fact
//ASSERT(Type(vPoint1-vPoint0) < 2*BSP_EPSILON); //!!!!
}
}
}
}
/////////////////////////////////////////////////////////////////////
// BSP tree
/*
* Default constructor.
*/
template<class Type, int iDimensions>
BSPTree<Type, iDimensions>::BSPTree(void)
{
bt_pbnRoot = NULL;
}
/*
* Destructor.
*/
template<class Type, int iDimensions>
BSPTree<Type, iDimensions>::~BSPTree(void)
{
Destroy();
}
/*
* Constructor with array of polygons oriented inwards.
*/
template<class Type, int iDimensions>
BSPTree<Type, iDimensions>::BSPTree(CDynamicArray<BSPPolygon<Type, iDimensions> > &abpoPolygons)
{
bt_pbnRoot = NULL;
Create(abpoPolygons);
}
/*
* Create bsp-subtree from array of polygons oriented inwards.
*/
template<class Type, int iDimensions>
BSPNode<Type, iDimensions> *BSPTree<Type, iDimensions>::CreateSubTree(CDynamicArray<BSPPolygon<Type, iDimensions> > &abpoPolygons)
{
// local declarations, to fix macro expansion in FOREACHINDYNAMICARRAY
typedef BSPEdge<Type, iDimensions> edge_t;
typedef BSPPolygon<Type, iDimensions> polygon_t;
ASSERT(abpoPolygons.Count()>=1);
// use first polygon as splitter
abpoPolygons.Lock();
BSPPolygon<Type, iDimensions> bpoSplitter = abpoPolygons[0];
abpoPolygons.Unlock();
// tags must be valid
ASSERT(bpoSplitter.bpo_ulPlaneTag!=-1);
// create two new polygon arrays - back and front
CDynamicArray<BSPPolygon<Type, iDimensions> > abpoFront, abpoBack;
// for each polygon in this array
{FOREACHINDYNAMICARRAY(abpoPolygons, polygon_t, itbpo) {
BSPPolygon<Type, iDimensions> bpoFront, bpoBack;
// tags must be valid
ASSERT(itbpo->bpo_ulPlaneTag!=-1);
// if the polygon has plane tag same as the tag of the splitter
if (itbpo->bpo_ulPlaneTag == bpoSplitter.bpo_ulPlaneTag) {
// they are assumed coplanar, so skip it
continue;
}
// split it by the plane of splitter polygon
BOOL bOnPlane = BSPCutter<Type, iDimensions>::SplitPolygon(itbpo.Current(),
bpoSplitter, bpoSplitter.bpo_ulPlaneTag, bpoFront, bpoBack);
// if the polygon is not coplanar with the splitter
if (!bOnPlane) {
// if there are some parts that are front
if (bpoFront.bpo_abedPolygonEdges.Count()>0) {
// create a polygon in front array and add all inside parts to it
BSPPolygon<Type, iDimensions> *pbpo = abpoFront.New(1);
pbpo->bpo_abedPolygonEdges.MoveArray(bpoFront.bpo_abedPolygonEdges);
*(Plane<Type, iDimensions> *)pbpo = itbpo.Current();
pbpo->bpo_ulPlaneTag = itbpo->bpo_ulPlaneTag;
}
// if there are some parts that are back
if (bpoBack.bpo_abedPolygonEdges.Count()>0) {
// create a polygon in back array and add all outside parts to it
BSPPolygon<Type, iDimensions> *pbpo = abpoBack.New(1);
pbpo->bpo_abedPolygonEdges.MoveArray(bpoBack.bpo_abedPolygonEdges);
*(Plane<Type, iDimensions> *)pbpo = itbpo.Current();
pbpo->bpo_ulPlaneTag = itbpo->bpo_ulPlaneTag;
}
}
}}
// free this array (to not consume too much memory)
abpoPolygons.Clear();
BSPNode<Type, iDimensions> *pbnFront, *pbnBack;
// if there is some polygon in front array
if (abpoFront.Count()>0) {
// create front subtree using front array
pbnFront = CreateSubTree(abpoFront);
// otherwise
} else {
// make front node an inside leaf node
pbnFront = new BSPNode<Type, iDimensions>(BNL_INSIDE);
}
// if there is some polygon in back array
if (abpoBack.Count()>0) {
// create back subtree using back array
pbnBack = CreateSubTree(abpoBack);
// otherwise
} else {
// make back node an outside leaf node
pbnBack = new BSPNode<Type, iDimensions>(BNL_OUTSIDE);
}
// make a splitter node with the front and back nodes
return new BSPNode<Type, iDimensions>(bpoSplitter, bpoSplitter.bpo_ulPlaneTag, *pbnFront, *pbnBack);
}
/*
* Create bsp-tree from array of polygons oriented inwards.
*/
template<class Type, int iDimensions>
void BSPTree<Type, iDimensions>::Create(CDynamicArray<BSPPolygon<Type, iDimensions> > &abpoPolygons)
{
typedef BSPPolygon<Type, iDimensions> polygon_t; // local declaration, to fix macro expansion in FOREACHINDYNAMICARRAY
// free eventual existing tree
Destroy();
// create the tree using the recursive function
bt_pbnRoot = CreateSubTree(abpoPolygons);
// move the tree to array
MoveNodesToArray();
}
/*
* Destroy bsp-tree.
*/
template<class Type, int iDimensions>
void BSPTree<Type, iDimensions>::Destroy(void)
{
// if tree is in array
if (bt_abnNodes.Count()>0) {
// clear array
bt_abnNodes.Clear();
bt_pbnRoot = NULL;
// if there is some free tree
} else if (bt_pbnRoot != NULL) {
// delete it
bt_pbnRoot->DeleteBSPNodeRecursively();
bt_pbnRoot = NULL;
}
}
/* Test if a sphere could touch any of inside nodes. (Just a trivial rejection test) */
template<class Type, int iDimensions>
FLOAT BSPTree<Type, iDimensions>::TestSphere(const Vector<Type, iDimensions> &vSphereCenter, Type tSphereRadius) const
{
if (bt_pbnRoot==NULL) return FALSE;
// just start recursive testing at root node
return bt_pbnRoot->TestSphere(vSphereCenter, tSphereRadius);
}
/* Test if a box is inside, outside, or intersecting. (Just a trivial rejection test) */
template<class Type, int iDimensions>
FLOAT BSPTree<Type, iDimensions>::TestBox(const OBBox<Type> &box) const
{
if (bt_pbnRoot==NULL) return FALSE;
// just start recursive testing at root node
return bt_pbnRoot->TestBox(box);
}
// find minimum/maximum parameters of points on a line that are inside
template<class Type, int iDimensions>
void BSPTree<Type, iDimensions>::FindLineMinMax(
const Vector<Type, iDimensions> &v0,
const Vector<Type, iDimensions> &v1,
Type &tMin,
Type &tMax) const
{
// init line
BSPLine<Type, iDimensions> bl;
bl.bl_tMin = UpperLimit(Type(0));
bl.bl_tMax = LowerLimit(Type(0));
// recursively split it
bt_pbnRoot->FindLineMinMax(bl, v0, v1, Type(0), Type(1));
// return the min/max
tMin = bl.bl_tMin;
tMax = bl.bl_tMax;
}
static INDEX _ctNextIndex;
/* Move one subtree to array. */
template<class Type, int iDimensions>
void BSPTree<Type, iDimensions>::MoveSubTreeToArray(BSPNode<Type, iDimensions> *pbnSubtree)
{
// if this is no node
if (pbnSubtree==NULL) {
// do nothing
return;
}
// first move all subnodes
MoveSubTreeToArray(pbnSubtree->bn_pbnFront);
MoveSubTreeToArray(pbnSubtree->bn_pbnBack);
// get the node in array
BSPNode<Type, iDimensions> &bnInArray = bt_abnNodes[_ctNextIndex];
_ctNextIndex--;
// copy properties to the array node
(Plane<Type, iDimensions>&)bnInArray = (Plane<Type, iDimensions>&)*pbnSubtree;
bnInArray.bn_bnlLocation = pbnSubtree->bn_bnlLocation;
bnInArray.bn_ulPlaneTag = pbnSubtree->bn_ulPlaneTag;
// let plane tag hold pointer to node in array
pbnSubtree->bn_ulPlaneTag = (size_t)&bnInArray;
2016-03-11 14:57:17 +01:00
// remap pointers to subnodes
if (pbnSubtree->bn_pbnFront==NULL) {
bnInArray.bn_pbnFront = NULL;
} else {
bnInArray.bn_pbnFront = (BSPNode<Type, iDimensions>*)pbnSubtree->bn_pbnFront->bn_ulPlaneTag;
}
if (pbnSubtree->bn_pbnBack==NULL) {
bnInArray.bn_pbnBack = NULL;
} else {
bnInArray.bn_pbnBack = (BSPNode<Type, iDimensions>*)pbnSubtree->bn_pbnBack->bn_ulPlaneTag;
}
}
/* Count nodes in subtree. */
template<class Type, int iDimensions>
INDEX BSPTree<Type, iDimensions>::CountNodes(BSPNode<Type, iDimensions> *pbnSubtree)
{
if (pbnSubtree==NULL) {
return 0;
} else {
return 1+
CountNodes(pbnSubtree->bn_pbnFront)+
CountNodes(pbnSubtree->bn_pbnBack);
}
}
/* Move all nodes to array. */
template<class Type, int iDimensions>
void BSPTree<Type, iDimensions>::MoveNodesToArray(void)
{
// if there is no tree
if (bt_pbnRoot == NULL) {
// do nothing
return;
}
// count nodes
INDEX ctNodes = CountNodes(bt_pbnRoot);
// allocate large enough array
bt_abnNodes.New(ctNodes);
// start at the end of array
_ctNextIndex = ctNodes-1;
// recusively remap all nodes
MoveSubTreeToArray(bt_pbnRoot);
// delete the old nodes
bt_pbnRoot->DeleteBSPNodeRecursively();
// first node is always at start of array
bt_pbnRoot = &bt_abnNodes[0];
}
/* Read/write entire bsp tree to disk. */
template<class Type, int iDimensions>
void BSPTree<Type, iDimensions>::Read_t(CTStream &strm) // throw char *
{
// free eventual existing tree
Destroy();
// read current version and size
INDEX iVersion;
SLONG slSize;
strm>>iVersion>>slSize;
ASSERT(iVersion==1);
// read count of nodes and create array
INDEX ctNodes;
strm>>ctNodes;
// This assert was less silly when it was basically sizeof (*this), but to serialize this across targets, it looks different now. --ryan.
ASSERT(slSize==(SLONG)(sizeof(INDEX)+ctNodes*((sizeof(Type)*(iDimensions+1))+16)));
2016-03-11 14:57:17 +01:00
bt_abnNodes.New(ctNodes);
// for each node
for(INDEX iNode=0; iNode<ctNodes; iNode++) {
BSPNode<Type, iDimensions> &bn = bt_abnNodes[iNode];
// read it from disk
//strm.Read_t(&(Plane<Type, iDimensions>&)bn, sizeof(Plane<Type, iDimensions>));
strm >> ((Plane<Type, iDimensions>&)bn);
2016-03-11 14:57:17 +01:00
strm>>(INDEX&)bn.bn_bnlLocation;
INDEX iFront;
strm>>iFront;
if (iFront==-1) {
bn.bn_pbnFront=NULL;
} else {
bn.bn_pbnFront = &bt_abnNodes[iFront];
}
INDEX iBack;
strm>>iBack;
if (iBack==-1) {
bn.bn_pbnBack=NULL;
} else {
bn.bn_pbnBack = &bt_abnNodes[iBack];
}
ULONG ul;
strm>>ul;
bn.bn_ulPlaneTag = ul;
2016-03-11 14:57:17 +01:00
}
// check end id
strm.ExpectID_t("BSPE"); // bsp end
// first node is always at start of array
if (bt_abnNodes.Count()>0) {
bt_pbnRoot = &bt_abnNodes[0];
} else {
bt_pbnRoot = NULL;
}
}
template<class Type, int iDimensions>
void BSPTree<Type, iDimensions>::Write_t(CTStream &strm) // throw char *
{
INDEX ctNodes = bt_abnNodes.Count();
// calculate size of chunk to write
SLONG slSize = sizeof(INDEX)+ctNodes*sizeof(BSPNode<Type, iDimensions>);
// write current version and size
strm<<INDEX(1)<<slSize;
// write count of nodes
strm<<ctNodes;
// for each node
for(INDEX iNode=0; iNode<ctNodes; iNode++) {
BSPNode<Type, iDimensions> &bn = bt_abnNodes[iNode];
// write it to disk
//strm.Write_t(&(Plane<Type, iDimensions>&)bn, sizeof(Plane<Type, iDimensions>));
strm << ((Plane<Type, iDimensions>&)bn);
2016-03-11 14:57:17 +01:00
strm<<(INDEX&)bn.bn_bnlLocation;
INDEX iFront;
if (bn.bn_pbnFront==NULL) {
iFront=-1;
} else {
iFront = bt_abnNodes.Index(bn.bn_pbnFront);
}
strm<<iFront;
INDEX iBack;
if (bn.bn_pbnBack==NULL) {
iBack=-1;
} else {
iBack = bt_abnNodes.Index(bn.bn_pbnBack);
}
strm<<iBack;
strm<<IntPtrToID(bn.bn_ulPlaneTag);
2016-03-11 14:57:17 +01:00
}
// write end id for checking
strm.WriteID_t("BSPE"); // bsp end
}
// instantiate template classes implemented here for needed types
#pragma warning (disable: 4660) // if already instantiated by some class
// remove templates
template class BSPVertex<DOUBLE, 3>; //DOUBLEbspvertex3D;
template class BSPVertexContainer<DOUBLE, 3>;
template class BSPEdge<DOUBLE, 3>;
template class BSPNode<DOUBLE, 3>;
template class BSPPolygon<DOUBLE, 3>;
template class BSPTree<DOUBLE, 3>;
template class BSPCutter<DOUBLE, 3>;
template class BSPVertex<FLOAT, 3>;
template class BSPVertexContainer<FLOAT, 3>;
template class BSPEdge<FLOAT, 3>;
template class BSPNode<FLOAT, 3>;
template class BSPPolygon<FLOAT, 3>;
template class BSPTree<FLOAT, 3>;
template class BSPCutter<FLOAT, 3>;
2016-03-11 14:57:17 +01:00
#pragma warning (default: 4660)