Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

octree.h

Go to the documentation of this file.
00001 // octree.h
00002 //
00003 // Octree-based visibility determination for objects.
00004 //
00005 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
00006 //
00007 // This program is free software; you can redistribute it and/or
00008 // modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2
00010 // of the License, or (at your option) any later version.
00011 
00012 #ifndef _OCTREE_H_
00013 #define _OCTREE_H_
00014 
00015 #include <vector>
00016 #include <celmath/quaternion.h>
00017 #include <celmath/plane.h>
00018 #include <celengine/observer.h>
00019 
00020 
00021 // The DynamicOctree and StaticOctree template arguments are:
00022 // OBJ:  object hanging from the node,
00023 // PREC: floating point precision of the culling operations at node level.
00024 // The hierarchy of octree nodes is built using a single precision value (excludingFactor), which relates to an
00025 // OBJ's limiting property defined by the octree particular specialization: ie. we use [absolute magnitude] for star octrees, etc.
00026 // For details, see notes below.
00027 
00028 
00029 template <class OBJ, class PREC> class OctreeProcessor
00030 {
00031  public:
00032     OctreeProcessor()          {};
00033     virtual ~OctreeProcessor() {};
00034     
00035     virtual void process(const OBJ& obj, PREC distance, float appMag) = 0;
00036 };
00037 
00038 
00039 
00040 
00041 
00042 template <class OBJ, class PREC> class StaticOctree;
00043 template <class OBJ, class PREC> class DynamicOctree
00044 {
00045  typedef std::vector<const OBJ*> ObjectList;
00046 
00047  typedef bool (LimitingFactorPredicate)     (const OBJ&, const float);
00048  typedef bool (StraddlingPredicate)         (const Point3<PREC>&, const OBJ&, const float);
00049  typedef PREC (ExclusionFactorDecayFunction)(const PREC);
00050 
00051  public:
00052     DynamicOctree(const Point3<PREC>& cellCenterPos,
00053                   const float         exclusionFactor);
00054     ~DynamicOctree();
00055 
00056     void insertObject  (const OBJ&, const PREC);
00057     void rebuildAndSort(StaticOctree<OBJ, PREC>*&, OBJ*&);
00058 
00059  private:
00060    static unsigned int SPLIT_THRESHOLD;
00061 
00062    static LimitingFactorPredicate*      limitingFactorPredicate;
00063    static StraddlingPredicate*          straddlingPredicate;
00064    static ExclusionFactorDecayFunction* decayFunction;
00065 
00066  private:
00067     void           add  (const OBJ&);
00068     void           split(const PREC);
00069     void           sortIntoChildNodes();
00070     DynamicOctree* getChild(const OBJ&, const Point3<PREC>&);
00071 
00072     DynamicOctree** _children;
00073     Point3<PREC>    cellCenterPos;
00074     PREC            exclusionFactor;
00075     ObjectList*     _objects;
00076 };
00077 
00078 
00079 
00080 
00081 
00082 template <class OBJ, class PREC> class StaticOctree
00083 {
00084  friend class DynamicOctree<OBJ, PREC>;
00085 
00086  public:
00087     StaticOctree(const Point3<PREC>& cellCenterPos,
00088                  const float         exclusionFactor,
00089                  OBJ*                _firstObject,
00090                  unsigned int        nObjects);
00091     ~StaticOctree();
00092 
00093     // These methods are only declared at the template level; we'll implement them as
00094     // full specializations, allowing for different traversal strategies depending on the
00095     // object type and nature.
00096 
00097     // This method searches the octree for objects that are likely to be visible
00098     // to a viewer with the specified obsPosition and limitingFactor.  The
00099     // octreeProcessor is invoked for each potentially visible object --no object with
00100     // a property greater than limitingFactor will be processed, but
00101     // objects that are outside the view frustum may be.  Frustum tests are performed
00102     // only at the node level to optimize the octree traversal, so an exact test
00103     // (if one is required) is the responsibility of the callback method.
00104     void processVisibleObjects(OctreeProcessor<OBJ, PREC>& processor,
00105                                const Point3<PREC>&         obsPosition,
00106                                const Plane<float>*         frustumPlanes,
00107                                float                       limitingFactor,
00108                                PREC                        scale) const;
00109 
00110     void processCloseObjects(OctreeProcessor<OBJ, PREC>& processor,
00111                              const Point3<PREC>&         obsPosition,
00112                              PREC                        boundingRadius,
00113                              PREC                        scale) const;
00114 
00115     int countChildren() const;
00116     int countObjects()  const;
00117 
00118  private:
00119     static const PREC SQRT3;
00120 
00121  private:
00122     StaticOctree** _children;
00123     Point3<PREC>   cellCenterPos;
00124     float          exclusionFactor;
00125     OBJ*           _firstObject;
00126     unsigned int   nObjects;
00127 };
00128 
00129 
00130 
00131 
00132 
00133 
00134 
00135 
00136 // There are two classes implemented in this module: StaticOctree and
00137 // DynamicOctree.  The DynamicOctree is built first by inserting
00138 // objects from a database or catalog and is then 'compiled' into a StaticOctree.
00139 // In the process of building the StaticOctree, the original object database is
00140 // reorganized, with objects in the same octree node all placed adjacent to each
00141 // other.  This spatial sorting of the objects dramatically improves the
00142 // performance of octree operations through much more coherent memory access.
00143 enum
00144 {
00145     XPos = 1,
00146     YPos = 2,
00147     ZPos = 4,
00148 };
00149 
00150 // The SPLIT_THRESHOLD is the number of objects a node must contain before its
00151 // children are generated. Increasing this number will decrease the number of
00152 // octree nodes in the tree, which will use less memory but make culling less
00153 // efficient.
00154 template <class OBJ, class PREC>
00155 inline DynamicOctree<OBJ, PREC>::DynamicOctree(const Point3<PREC>& cellCenterPos,
00156                                                const float         exclusionFactor):
00157         _children      (NULL),
00158         cellCenterPos  (cellCenterPos),
00159         exclusionFactor(exclusionFactor),
00160         _objects       (NULL)
00161 {
00162 }
00163 
00164 
00165 template <class OBJ, class PREC>
00166 inline DynamicOctree<OBJ, PREC>::~DynamicOctree()
00167 {
00168     if (_children != NULL)
00169     {
00170         for (int i=0; i<8; ++i)
00171         {
00172             delete _children[i];
00173         }
00174 
00175         delete[] _children;
00176     }
00177 }
00178 
00179 
00180 template <class OBJ, class PREC>
00181 inline void DynamicOctree<OBJ, PREC>::insertObject(const OBJ& obj, const PREC scale)
00182 {
00183     // If the object can't be placed into this node's children, then put it here:
00184     if (limitingFactorPredicate(obj, exclusionFactor) || straddlingPredicate(cellCenterPos, obj, exclusionFactor) )
00185         add(obj);
00186     else
00187     {
00188         // If we haven't allocated child nodes yet, try to fit
00189         // the object in this node, even though it could be put
00190         // in a child. Only if there are more than SPLIT_THRESHOLD
00191         // objects in the node will we attempt to place the
00192         // object into a child node.  This is done in order
00193         // to avoid having the octree degenerate into one object
00194         // per node.
00195         if (_children == NULL)
00196         {
00197             // Make sure that there's enough room left in this node
00198             if (_objects != NULL && _objects->size() >= DynamicOctree<OBJ, PREC>::SPLIT_THRESHOLD)
00199                 split(scale * 0.5f);
00200             add(obj);
00201         }
00202         else
00203             // We've already allocated child nodes; place the object
00204             // into the appropriate one.
00205             this->getChild(obj, cellCenterPos)->insertObject(obj, scale * (PREC) 0.5);
00206     }
00207 }
00208 
00209 
00210 template <class OBJ, class PREC>
00211 inline void DynamicOctree<OBJ, PREC>::add(const OBJ& obj)
00212 {
00213     if (_objects == NULL)
00214         _objects = new ObjectList;
00215 
00216     _objects->push_back(&obj);
00217 }
00218 
00219 
00220 template <class OBJ, class PREC>
00221 inline void DynamicOctree<OBJ, PREC>::split(const PREC scale)
00222 {
00223     _children = new DynamicOctree*[8];
00224 
00225     for (int i=0; i<8; ++i)
00226     {
00227         Point3<PREC> centerPos    = cellCenterPos;
00228 
00229         centerPos.x     += ((i & XPos) != 0) ? scale : -scale;
00230         centerPos.y     += ((i & YPos) != 0) ? scale : -scale;
00231         centerPos.z     += ((i & ZPos) != 0) ? scale : -scale;
00232 
00233         _children[i] = new DynamicOctree(centerPos,
00234                                          decayFunction(exclusionFactor));
00235     }
00236     sortIntoChildNodes();
00237 }
00238 
00239 
00240 // Sort this node's objects into objects that can remain here,
00241 // and objects that should be placed into one of the eight
00242 // child nodes.
00243 template <class OBJ, class PREC>
00244 inline void DynamicOctree<OBJ, PREC>::sortIntoChildNodes()
00245 {
00246     unsigned int nKeptInParent = 0;
00247 
00248     for (unsigned int i=0; i<_objects->size(); ++i)
00249     {
00250         const OBJ& obj    = *(*_objects)[i];
00251 
00252         if (straddlingPredicate(cellCenterPos, obj, exclusionFactor) )
00253             (*_objects)[nKeptInParent++] = (*_objects)[i];
00254         else
00255             this->getChild(obj, cellCenterPos)->add(obj);
00256     }
00257 
00258     _objects->resize(nKeptInParent);
00259 }
00260 
00261 
00262 template <class OBJ, class PREC>
00263 inline void DynamicOctree<OBJ, PREC>::rebuildAndSort(StaticOctree<OBJ, PREC>*& _staticNode, OBJ*& _sortedObjects)
00264 {
00265     OBJ* _firstObject = _sortedObjects;
00266 
00267     if (_objects != NULL)
00268         for (typename ObjectList::const_iterator iter = _objects->begin(); iter != _objects->end(); ++iter)
00269         {
00270             *_sortedObjects++ = **iter;
00271         }
00272 
00273     unsigned int nObjects  = (unsigned int) (_sortedObjects - _firstObject);
00274     _staticNode            = new StaticOctree<OBJ, PREC>(cellCenterPos, exclusionFactor, _firstObject, nObjects);
00275 
00276     if (_children != NULL)
00277     {
00278         _staticNode->_children    = new StaticOctree<OBJ, PREC>*[8];
00279 
00280         for (int i=0; i<8; ++i)
00281             _children[i]->rebuildAndSort(_staticNode->_children[i], _sortedObjects);
00282     }
00283 }
00284 
00285 
00286 //MS VC++ wants this to be placed here:
00287 template <class OBJ, class PREC>
00288 const PREC StaticOctree<OBJ, PREC>::SQRT3 = (PREC) 1.732050807568877;
00289 
00290 
00291 template <class OBJ, class PREC>
00292 inline StaticOctree<OBJ, PREC>::StaticOctree(const Point3<PREC>& cellCenterPos,
00293                                              const float         exclusionFactor,
00294                                              OBJ*                _firstObject,
00295                                              unsigned int        nObjects):
00296     _children      (NULL),
00297     cellCenterPos  (cellCenterPos),
00298     exclusionFactor(exclusionFactor),
00299     _firstObject   (_firstObject),
00300     nObjects       (nObjects)
00301 {
00302 }
00303 
00304 
00305 template <class OBJ, class PREC>
00306 inline StaticOctree<OBJ, PREC>::~StaticOctree()
00307 {
00308     if (_children != NULL)
00309     {
00310         for (int i=0; i<8; ++i)
00311             delete _children[i];
00312 
00313         delete[] _children;
00314     }
00315 }
00316 
00317 
00318 template <class OBJ, class PREC>
00319 inline int StaticOctree<OBJ, PREC>::countChildren() const
00320 {
00321     int count    = 0;
00322 
00323     for (int i=0; i<8; ++i)
00324         count    += _children != NULL ? 1 + _children[i]->countChildren() : 0;
00325 
00326     return count;
00327 }
00328 
00329 
00330 template <class OBJ, class PREC>
00331 inline int StaticOctree<OBJ, PREC>::countObjects() const
00332 {
00333     int count    = nObjects;
00334 
00335     if (_children != NULL)
00336         for (int i=0; i<8; ++i)
00337             count    += _children[i]->countObjects();
00338 
00339     return count;
00340 }
00341 
00342 #endif // _OCTREE_H_

Generated on Sat Jan 14 22:30:28 2006 for Celestia by  doxygen 1.4.1