1 //------------------------------------------------------------------------------
2 // File : SkyBVTree.hpp
3 //------------------------------------------------------------------------------
4 // Empyrean / SkyWorks : Copyright 2002 Mark J. Harris
5 //------------------------------------------------------------------------------
9 * This source was written by Wesley Hunt. Many thanks to him for providing it.
11 //-----------------------------------------------------------------------------
14 // Author: Wesley Hunt (hunt@cs.unc.edu)
16 //-----------------------------------------------------------------------------
19 // declare an SkyBVTree with whatever object type and bounding volume type you
20 // want. The hard part is to pass in a NodeSplitter class that determines how
21 // to split a node up. See below for a description of its requirements.
22 // Finally, use that tree like so:
24 // * AddObject(Object, ObjectBV)
27 // < Do whatever you want with GetRoot() >
28 // < Use GetLeftChild() and GetRightChild() to traverse the nodes >
30 // The class hierarchy is designed for a flexible, simple public interface.
31 // This pushes some extra complexity into the class hierarchy, but there are two
32 // main advantages to the final approach:
33 // 1. There are no interface requirements for the Object and BoundingVolume
34 // template parameters. This means you don't have to modify your existing
35 // class interfaces to use them with the tree.
36 // 2. All the dependent logic for dealing with the bounding volumes is pushed
37 // into the NodeSplitter class. So there is one centralized location for
38 // adapting your bounding volume classes to work with the tree. See the
39 // description of the NodeSplitter template requirements for more details.
46 // declares the node class that the tree holds. It exposes
47 // the public functions of the nodes and defines protected accessors that
48 // the derived tree class can use to manipulate nodes and build the tree.
52 // This is the class that gives access to the tree. You can access each
53 // object and bounding volume owned by a node, along with that node's
58 // An aggregation of an object and its associated bounding volume.
59 // Each node in the tree essentially owns an array of NodeObjects. This
60 // array is passed to the NodeSplitter class to allow it to examine a
61 // node's contained objects before making a decision on how to split the
66 // The main tree class. To use it, supply and object type, a bounding volume
67 // type, and a NodeSplitter class that is used to split the nodes up during
70 // Template class requirements
71 // ---------------------------
83 // This is the user-supplied class that decides how to split a node during
84 // tree construction. It is given an array of NodeObjects that the node owns and
85 // is responsible for determining the node's bounding volume as well as how to
86 // split the node up into left and right children.
87 // The required API is as follows:
89 // * a constructor that takes an array of NodeObject and an unsigned int giving
90 // the size of the array. These are the objects owned by the node to be split.
91 // * a method GetNodeBV() that returns the BoundingVolume of the node.
92 // Typically this is the union of the bounding volumes of the objects owned
94 // * A unary function operator used to partition the objects in the node. The
95 // operator must take a single NodeObject as a parameter and return as
96 // a bool whether to place the NodeObject in the left or right child.
97 // Basically, it should be compatible with std::partition using NodeObjects.
98 // * A binary function operator used to sort the objects. If a partition fails,
99 // the nodes are then sorted based on this function and half are sent to each child.
100 // This operator must define a total ordering of the NodeObjects.
101 // Basically, it should be compatible with std::sort using NodeObjects.
104 // struct NodeSplitter
106 // NodeSplitter(const NodeObject* objs, unsigned int numObjs);
107 // BoundingVolume& GetNodeBV() const;
108 // // Partition predicate
109 // bool operator()(const NodeObject& obj) const;
111 // bool operator()(const NodeObject& obj1, const NodeObject& obj2) const;
114 //-----------------------------------------------------------------------------
115 #ifndef __SKYBVTREE_HPP__
116 #define __SKYBVTREE_HPP__
121 //-----------------------------------------------------------------------------
122 // SkyBaseBVTree<Object, BoundingVolume>
123 //-----------------------------------------------------------------------------
124 // See header description for details.
125 //-----------------------------------------------------------------------------
126 template <class Object, class BoundingVolume>
130 typedef BoundingVolume BV;
136 friend class SkyBaseBVTree<Object, BoundingVolume>;
138 Node() : _pObjs(NULL), _iNumObjs(0) {}
139 Node(NodeObject* pObjs, unsigned int iNumObjs) : _pObjs(pObjs), _iNumObjs(iNumObjs) {}
142 const Object& GetObj(unsigned int index) const { assert(_iNumObjs != 0 && _pObjs != NULL && index < _iNumObjs); return _pObjs[index].GetObj(); }
143 const BV& GetBV(unsigned int index) const { assert(_iNumObjs != 0 && _pObjs != NULL && index < _iNumObjs); return _pObjs[index].GetBV(); }
144 unsigned int GetNumObjs() const { assert(_iNumObjs != 0 && _pObjs != NULL); return _iNumObjs; }
145 const BV& GetNodeBV() const { assert(_iNumObjs != 0 && _pObjs != NULL); return _volume; }
146 const Node* GetLeftChild() const { assert(_iNumObjs != 0 && _pObjs != NULL); return this+1; }
147 const Node* GetRightChild() const { assert(_iNumObjs != 0 && _pObjs != NULL); return this+(GetLeftChild()->GetNumObjs()<<1); }
148 bool IsLeaf() const { assert(_iNumObjs != 0 && _pObjs != NULL); return _iNumObjs == 1; }
151 // List of Objects owned by the node
153 unsigned int _iNumObjs;
161 NodeObject(const Object& o, const BV& v) : _obj(o), _volume(v) {}
163 const Object& GetObj() const { return _obj; }
164 const BV& GetBV() const { return _volume; }
171 // Give non-const access to the node for descendant classes to build the tree
172 BV& GetNodeBV(Node* pNode) { assert(pNode->_iNumObjs != 0 && pNode->_pObjs != NULL); return pNode->_volume; }
173 Node* GetLeftChild(Node* pNode) { assert(pNode->_iNumObjs != 0 && pNode->_pObjs != NULL); return pNode+1; }
174 Node* GetRightChild(Node* pNode) { assert(pNode->_iNumObjs != 0 && pNode->_pObjs != NULL); return pNode+(GetLeftChild(pNode)->GetNumObjs()<<1); }
175 NodeObject* GetObjs(Node* pNode) { assert(pNode->_iNumObjs != 0 && pNode->_pObjs != NULL); return pNode->_pObjs; }
177 // Links a node's children by assigning the given number of objects to each child
178 // assumes the node has already had it's objects partitioned
179 void LinkNodeChildren(Node* pNode, unsigned int iLeftNumObjs)
181 assert(pNode->_iNumObjs != 0 && pNode->_pObjs != NULL);
182 GetLeftChild(pNode)->_pObjs = pNode->_pObjs;
183 GetLeftChild(pNode)->_iNumObjs = iLeftNumObjs;
184 GetRightChild(pNode)->_pObjs = pNode->_pObjs + iLeftNumObjs;
185 GetRightChild(pNode)->_iNumObjs = pNode->_iNumObjs - iLeftNumObjs;
190 //------------------------------------------------------------------------------
191 // Function : ClearVector
193 //------------------------------------------------------------------------------
195 * @fn ClearVector(std::vector<T>& vec)
196 * @brief This utility function uses the std::vector::swap trick to free the memory of a vector
198 * This is necessary since clear() doesn't actually free anything.
201 void ClearVector(std::vector<T>& vec)
203 std::vector<T>().swap(vec);
207 //-----------------------------------------------------------------------------
208 // SkyBVTree<Object, BoundingVolume, NodeSplitter>
209 //-----------------------------------------------------------------------------
210 // See header description for details.
211 //-----------------------------------------------------------------------------
212 template <class Object, class BoundingVolume, class NodeSplitter>
213 class SkyBVTree : public SkyBaseBVTree<Object, BoundingVolume>
216 typedef SkyBaseBVTree<Object, BoundingVolume> BaseTree;
217 typedef BaseTree::BV BV;
218 typedef BaseTree::NodeObject NodeObject;
219 typedef BaseTree::Node Node;
226 void BeginTree(unsigned int iNumObjs = 0)
228 ClearVector(_objList);
230 if (iNumObjs > 0) _objList.reserve(iNumObjs);
233 void AddObject(const Object &obj, const BV& volume)
235 _objList.push_back(NodeObject(obj, volume));
240 if (_objList.size() == 0) return;
241 // Initialize the root node with all the objects
242 _nodes.push_back(Node(&_objList[0], _objList.size()));
243 // create room for the other nodes. They are initialized in BuildTree().
244 _nodes.reserve(_objList.size()*2-1);
245 _nodes.resize(_objList.size()*2-1);
246 BuildTree(&_nodes[0]);
249 const Node *GetRoot() const { return _nodes.empty() ? NULL : &_nodes[0]; }
252 unsigned int CalcMemUsage() const
254 unsigned int usage = 0;
255 usage += sizeof(*this);
256 usage += _objList.capacity() * sizeof(_objList[0]);
257 usage += _nodes.capacity() * sizeof(_nodes[0]);
263 // Does the real work
264 void BuildTree(Node *pCurNode)
268 // Initialize the node splitter with the current node
269 NodeSplitter splitter(GetObjs(pCurNode), pCurNode->GetNumObjs());
271 // set the node's bounding volume using the splitter
272 GetNodeBV(pCurNode) = splitter.GetNodeBV();
274 // When a node has one object we can stop
275 if (pCurNode->GetNumObjs() == 1) return;
277 // Try and partition the objects
278 iLeftNumObjs = std::partition(GetObjs(pCurNode), &GetObjs(pCurNode)[pCurNode->GetNumObjs()], splitter) - GetObjs(pCurNode);
280 if ((iLeftNumObjs == 0) || (iLeftNumObjs == pCurNode->GetNumObjs()))
282 // Partition failed. Sort and split again to force a complete tree
283 std::sort(GetObjs(pCurNode), &GetObjs(pCurNode)[pCurNode->GetNumObjs()], splitter);
284 iLeftNumObjs = pCurNode->GetNumObjs() / 2;
288 LinkNodeChildren(pCurNode, iLeftNumObjs);
289 BuildTree(GetLeftChild(pCurNode));
290 BuildTree(GetRightChild(pCurNode));
293 std::vector<Node> _nodes;
294 std::vector<NodeObject> _objList;
297 #endif //__SKYBVTREE_HPP__