//----------------------------------------------------------------------------- // V12 Engine // // Copyright (c) 2001 GarageGames.Com // Portions Copyright (c) 2001 by Sierra Online, Inc. //----------------------------------------------------------------------------- #ifndef _GRAPHBSP_H_ #define _GRAPHBSP_H_ template 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 BSPPool; protected: static S32 sSortAxis; BSPPool mTree; VectorPtr mList; protected: static S32 QSORT_CALLBACK compareAxis(const void* , const void* ); void sortOnAxis(VectorPtr& list, S32 axis); void traverse(S32 ind, const Box3F& box, VectorPtr* result) const; U16 partition(S32 start, S32 N); public: void makeTree(const VectorPtr& listIn); S32 getIntersecting(VectorPtr& listOut, Box3F box) const; void clear(); }; //------------------------------------------------------------------------------------- template S32 AxisAlignedBSP::sSortAxis; template S32 QSORT_CALLBACK AxisAlignedBSP::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 void AxisAlignedBSP::sortOnAxis(VectorPtr& 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 U16 AxisAlignedBSP::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 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 void AxisAlignedBSP::makeTree(const VectorPtr& 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 void AxisAlignedBSP::traverse(S32 ind, const Box3F& box, VectorPtr* 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 S32 AxisAlignedBSP::getIntersecting(VectorPtr& result, Box3F box) const { if (mTree.size()) traverse(0, box, &result); return result.size(); } //------------------------------------------------------------------------------------- template void AxisAlignedBSP::clear() { mTree.clear(); mTree.compact(); mList.clear(); mList.compact(); } //------------------------------------------------------------------------------------- #endif