]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/clouds3d/SkyBVTree.hpp
Clouds3D crashes because there is no Light
[simgear.git] / simgear / scene / sky / clouds3d / SkyBVTree.hpp
1 //------------------------------------------------------------------------------
2 // File : SkyBVTree.hpp
3 //------------------------------------------------------------------------------
4 // Empyrean / SkyWorks : Copyright 2002 Mark J. Harris
5 //------------------------------------------------------------------------------
6 /**
7 * @file SkyBVTree.hpp
8
9 * This source was written by Wesley Hunt.  Many thanks to him for providing it.
10 */
11 //-----------------------------------------------------------------------------
12 // SkyBVTree.hpp
13 //
14 // Author:      Wesley Hunt (hunt@cs.unc.edu)
15 // Date:        2000/12/19
16 //-----------------------------------------------------------------------------
17 //      Overview
18 //      --------
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:
23 //                      * BeginTree()
24 //                      *   AddObject(Object, ObjectBV)
25 //                      *   ...
26 //                      * EndTree()
27 //                      < Do whatever you want with GetRoot() >
28 //                      < Use GetLeftChild() and GetRightChild() to traverse the nodes >
29 //
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.
40 //
41 //      Class Descriptions
42 //      ------------------
43 //
44 //              SkyBaseBVTree
45 //              -------------
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.
49 //
50 //                      Node
51 //                      ----
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
54 //                      children.
55 //
56 //                      NodeObject
57 //                      ----------------
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 
62 //                      node up.
63 //
64 //              SkyBVTree
65 //              ---------
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
68 //              tree construction.
69 //
70 //      Template class requirements
71 //      ---------------------------
72 //
73 //              Object
74 //              ------
75 //              None
76 //
77 //              BoundingVolume
78 //              --------------
79 //              None
80 //
81 //              NodeSplitter
82 //              ------------
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:
88 //              
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 
93 //                        by the node.
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.
102 //                        
103 //              Example:
104 //                      struct NodeSplitter
105 //                      {
106 //                              NodeSplitter(const NodeObject* objs, unsigned int numObjs);
107 //                              BoundingVolume& GetNodeBV() const;
108 //                              // Partition predicate
109 //                              bool operator()(const NodeObject& obj) const;
110 //                              // Sort predicate
111 //                              bool operator()(const NodeObject& obj1, const NodeObject& obj2) const;
112 //                      };
113 //
114 //-----------------------------------------------------------------------------
115 #ifndef __SKYBVTREE_HPP__
116 #define __SKYBVTREE_HPP__
117
118 #include <algorithm>
119 #include <vector>
120
121 //-----------------------------------------------------------------------------
122 // SkyBaseBVTree<Object, BoundingVolume>
123 //-----------------------------------------------------------------------------
124 // See header description for details.
125 //-----------------------------------------------------------------------------
126 template <class Object, class BoundingVolume>
127 class SkyBaseBVTree
128 {
129 public:
130   typedef BoundingVolume BV;
131   class NodeObject;
132   
133 public:
134   class Node
135   {
136     friend class SkyBaseBVTree<Object, BoundingVolume>;
137   public:
138     Node() : _pObjs(NULL), _iNumObjs(0) {}
139     Node(NodeObject* pObjs, unsigned int iNumObjs) : _pObjs(pObjs), _iNumObjs(iNumObjs)  {}
140   
141     // Public interface
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; }
149   
150   private:
151     // List of Objects owned by the node
152     NodeObject    *_pObjs;
153     unsigned int  _iNumObjs;
154     BV            _volume;
155   };
156
157 public:
158   class NodeObject
159   {
160   public:
161     NodeObject(const Object& o, const BV& v) : _obj(o), _volume(v) {}
162   
163     const Object& GetObj() const { return _obj;    }
164     const BV&     GetBV() const  { return _volume; }
165   private:
166     Object _obj;
167     BV     _volume;
168   };
169       
170 protected:
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; }
176   
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)
180   {
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;
186   }        
187 };
188
189
190 //------------------------------------------------------------------------------
191 // Function               : ClearVector
192 // Description      : 
193 //------------------------------------------------------------------------------
194 /**
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
197  *
198  * This is necessary since clear() doesn't actually free anything.
199  */ 
200 template<class T>
201 void ClearVector(std::vector<T>& vec)
202 {
203   std::vector<T>().swap(vec);
204 }
205
206
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>
214 {
215 public:
216   typedef SkyBaseBVTree<Object, BoundingVolume> BaseTree;
217   typedef BaseTree::BV BV;
218   typedef BaseTree::NodeObject NodeObject;
219   typedef BaseTree::Node Node;
220   
221   void Clear()
222   {
223     BeginTree();
224   }
225   
226   void BeginTree(unsigned int iNumObjs = 0)
227   {
228     ClearVector(_objList);
229     ClearVector(_nodes);
230     if (iNumObjs > 0) _objList.reserve(iNumObjs);
231   }
232   
233   void AddObject(const Object &obj, const BV& volume)
234   {
235     _objList.push_back(NodeObject(obj, volume));
236   }
237   
238   void EndTree()
239   {
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]);
247   }
248   
249   const Node *GetRoot() const { return _nodes.empty() ? NULL : &_nodes[0]; }
250   
251   // Memory usage info
252   unsigned int CalcMemUsage() const
253   {
254     unsigned int usage = 0;
255     usage += sizeof(*this);
256     usage += _objList.capacity() * sizeof(_objList[0]);
257     usage += _nodes.capacity() * sizeof(_nodes[0]);
258     return usage;
259   }
260   
261 public:
262 private:
263   // Does the real work
264   void BuildTree(Node *pCurNode)
265   {
266     int iLeftNumObjs;
267     {
268       // Initialize the node splitter with the current node
269       NodeSplitter splitter(GetObjs(pCurNode), pCurNode->GetNumObjs());
270       
271       // set the node's bounding volume using the splitter
272       GetNodeBV(pCurNode) = splitter.GetNodeBV();
273       
274       // When a node has one object we can stop
275       if (pCurNode->GetNumObjs() == 1) return;
276       
277       // Try and partition the objects
278       iLeftNumObjs = std::partition(GetObjs(pCurNode), &GetObjs(pCurNode)[pCurNode->GetNumObjs()], splitter) - GetObjs(pCurNode);
279       
280       if ((iLeftNumObjs == 0) || (iLeftNumObjs == pCurNode->GetNumObjs()))
281       {
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;
285       }
286     }
287     
288     LinkNodeChildren(pCurNode, iLeftNumObjs);
289     BuildTree(GetLeftChild(pCurNode));
290     BuildTree(GetRightChild(pCurNode));
291   }
292   
293   std::vector<Node>       _nodes;
294   std::vector<NodeObject> _objList;
295 };
296
297 #endif //__SKYBVTREE_HPP__