mirror of
https://github.com/tribes2/engine.git
synced 2026-01-20 03:34:48 +00:00
221 lines
6.9 KiB
C++
221 lines
6.9 KiB
C++
//-----------------------------------------------------------------------------
|
|
// V12 Engine
|
|
//
|
|
// Copyright (c) 2001 GarageGames.Com
|
|
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifndef _GRAPHBSP_H_
|
|
#define _GRAPHBSP_H_
|
|
|
|
template <class T> class AxisAlignedBSP
|
|
{
|
|
public:
|
|
enum {Lower, Upper};
|
|
|
|
class BSPNode {
|
|
public:
|
|
U16 mAxis;
|
|
U16 mCount;
|
|
union {
|
|
U16 mBranch[2];
|
|
T * mLeaf;
|
|
} X;
|
|
F32 mDivide;
|
|
BSPNode()
|
|
{
|
|
mAxis = 0;
|
|
mCount = 0;
|
|
mDivide = 0.0f;
|
|
X.mBranch[Lower] = X.mBranch[Upper] = 0;
|
|
X.mLeaf = NULL;
|
|
}
|
|
};
|
|
|
|
typedef Vector<BSPNode> BSPPool;
|
|
|
|
protected:
|
|
static S32 sSortAxis;
|
|
BSPPool mTree;
|
|
VectorPtr<T*> mList;
|
|
|
|
protected:
|
|
static S32 QSORT_CALLBACK compareAxis(const void* , const void* );
|
|
void sortOnAxis(VectorPtr<T*>& list, S32 axis);
|
|
void traverse(S32 ind, const Box3F& box, VectorPtr<T*>* result) const;
|
|
U16 partition(S32 start, S32 N);
|
|
|
|
public:
|
|
void makeTree(const VectorPtr<T*>& listIn);
|
|
S32 getIntersecting(VectorPtr<T*>& listOut, Box3F box) const;
|
|
void clear();
|
|
};
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
|
|
template <class T> S32 AxisAlignedBSP<T>::sSortAxis;
|
|
|
|
template <class T>
|
|
S32 QSORT_CALLBACK AxisAlignedBSP<T>::compareAxis(const void* a,const void* b)
|
|
{
|
|
const F32 * locA = (*(T**)a)->location();
|
|
const F32 * locB = (*(T**)b)->location();
|
|
F32 A = locA[sSortAxis], B = locB[sSortAxis];
|
|
return (A < B ? -1 : (A > B ? 1 : 0));
|
|
}
|
|
|
|
template <class T> void AxisAlignedBSP<T>::sortOnAxis(VectorPtr<T*>& list, S32 axis)
|
|
{
|
|
sSortAxis = axis;
|
|
dQsort((void* )(list.address()), list.size(), sizeof(T* ), compareAxis);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
|
|
// Main tree building routine. Divides up the space at each level until done.
|
|
template <class T> U16 AxisAlignedBSP<T>::partition(S32 start, S32 N)
|
|
{
|
|
S32 place = mTree.size();
|
|
BSPNode assembleNode;
|
|
|
|
if (N < 2)
|
|
{
|
|
AssertFatal(N == 1, "Bad call to axis-aligned BSP partition");
|
|
assembleNode.mCount = 1;
|
|
assembleNode.X.mLeaf = mList[start];
|
|
mTree.push_back(assembleNode);
|
|
}
|
|
else
|
|
{
|
|
S32 mid1 = (N >> 1);
|
|
S32 mid0 = mid1 - 1;
|
|
F32 bestSeparation = -1;
|
|
VectorPtr<T*> divisions[3];
|
|
|
|
// Try all three divisions. We take the one whose halves are furthest apart
|
|
// at the divide.
|
|
for (S32 axis = 0; axis < 3; axis++)
|
|
{
|
|
divisions[axis].setSize(N);
|
|
dMemcpy(&(divisions[axis][0]), &mList[start], N * sizeof(T*));
|
|
sortOnAxis(divisions[axis], axis);
|
|
|
|
const F32 * endOfFirst = divisions[axis][mid0]->location();
|
|
const F32 * startOf2nd = divisions[axis][mid1]->location();
|
|
|
|
F32 low = endOfFirst[axis];
|
|
F32 high = startOf2nd[axis];
|
|
F32 separation = (high - low);
|
|
|
|
AssertFatal(separation >= 0, "Bad sort in axis-aligned BSP construction");
|
|
|
|
if (separation > bestSeparation)
|
|
{
|
|
bestSeparation = separation;
|
|
assembleNode.mCount = N;
|
|
assembleNode.mAxis = axis;
|
|
assembleNode.mDivide = low + (separation * 0.5f);
|
|
}
|
|
}
|
|
|
|
// Copy over the sorted elements along the axis we're dividing-
|
|
dMemcpy(&mList[start], &(divisions[assembleNode.mAxis][0]), N * sizeof(T*));
|
|
|
|
// Install the node proper, and call down to further partition.
|
|
mTree.push_back(assembleNode);
|
|
|
|
// NOTE!! This code doesn't work in release build if we haven't pre-reserved the
|
|
// !!!!!! tree. It looks like a compiler bug to me, not 100% sure though.
|
|
mTree[place].X.mBranch[Lower] = partition(start, mid1);
|
|
mTree[place].X.mBranch[Upper] = partition(start + mid1, N - mid1);
|
|
}
|
|
|
|
// Return where we're at in the tree.
|
|
return place;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// Make a tree whose leaves are the nodes passed in.
|
|
|
|
template <class T> void AxisAlignedBSP<T>::makeTree(const VectorPtr<T*>& listIn)
|
|
{
|
|
mList = listIn;
|
|
mTree.clear();
|
|
|
|
AssertFatal(mList.size() < 32000, "AxisAlignedBSP::makeTree() - too many nodes");
|
|
|
|
if (mList.size()) {
|
|
mTree.reserve(mList.size() * 3);
|
|
partition(0, mList.size());
|
|
mTree.compact();
|
|
}
|
|
|
|
mList.clear();
|
|
mList.compact();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
|
|
// We want our check to be inclusive of both ends (Box3F method isn't above)
|
|
inline bool boxContainsPt(const Box3F& B, const Point3F& P)
|
|
{
|
|
if (P.x >= B.min.x && P.x <= B.max.x)
|
|
if (P.y >= B.min.y && P.y <= B.max.y)
|
|
return (P.z >= B.min.z && P.z <= B.max.z);
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
|
|
|
|
template <class T> void AxisAlignedBSP<T>::traverse(S32 ind, const Box3F& box,
|
|
VectorPtr<T*>* result) const
|
|
{
|
|
const BSPNode & node = mTree[ind];
|
|
|
|
if (node.mCount == 1) { // At node-
|
|
if (boxContainsPt(box, node.X.mLeaf->location()))
|
|
result->push_back(node.X.mLeaf);
|
|
}
|
|
else {
|
|
AssertFatal(node.mCount > 1, "AxisAlignedBSP::traverse()");
|
|
if (node.mDivide > ((const F32*)(box.max))[node.mAxis])
|
|
traverse(node.X.mBranch[Lower], box, result);
|
|
else if (node.mDivide < ((const F32*)(box.min))[node.mAxis])
|
|
traverse(node.X.mBranch[Upper], box, result);
|
|
else {
|
|
Box3F splitUpper(box);
|
|
Box3F splitLower(box);
|
|
((F32*)(splitUpper.min))[node.mAxis] = node.mDivide;
|
|
((F32*)(splitLower.max))[node.mAxis] = node.mDivide;
|
|
traverse(node.X.mBranch[Lower], splitLower, result);
|
|
traverse(node.X.mBranch[Upper], splitUpper, result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// Push all nodes intersecting the box onto the user-supplied list. We don't clear
|
|
// list because we'll probably have two trees - for small nodes and large nodes.
|
|
|
|
template <class T>
|
|
S32 AxisAlignedBSP<T>::getIntersecting(VectorPtr<T*>& result, Box3F box) const
|
|
{
|
|
if (mTree.size())
|
|
traverse(0, box, &result);
|
|
return result.size();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
|
|
template <class T> void AxisAlignedBSP<T>::clear()
|
|
{
|
|
mTree.clear(); mTree.compact();
|
|
mList.clear(); mList.compact();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
|
|
#endif
|