Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

octree.h

Go to the documentation of this file.
00001 /*
00002     Sheep - A Rigid Body Dynamics Engine
00003     Copyright (C) 2001-2004 Francois Beaune
00004     Contact: http://toxicengine.sourceforge.net/
00005 
00006     This file is part of Sheep.
00007 
00008     Sheep is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     Sheep is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with Sheep; if not, write to the Free Software
00020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021 */
00022 
00023 #ifndef SHEEP_MATH_OCTREE_H
00024 #define SHEEP_MATH_OCTREE_H
00025 
00026 #include "common/misc/minmax.h"
00027 #include "aabb3.h"
00028 #include "point3.h"
00029 #include "real.h"
00030 #include "vector3.h"
00031 
00032 #include <cassert>
00033 #include <vector>
00034 
00035 namespace sheep {
00036 
00037     inline
00038     unsigned int choose_next(Real x, Real y, Real z,
00039                              unsigned int a, unsigned int b, unsigned int c)
00040     {
00041         if(x < y) {
00042             if(x < z) return a;
00043             else return c;
00044         } else {
00045             if(y < z) return b;
00046             else return c;
00047         }
00048     }
00049 
00050     template<typename Item>
00051     struct OctreeNode {
00052         AABB3 m_voxel;              //!< The volume occupied by the node.
00053         Point3 m_center;            //!< The center of this volume.
00054 
00055         bool m_is_leaf;             //!< Indicates whether this node is a leaf or not.
00056         OctreeNode *m_children[8];  //!< The children of this node.
00057 
00058         typedef std::vector<Item> ItemVector;
00059         typedef typename ItemVector::const_iterator ItemVectorConstIt;
00060 
00061         ItemVector m_items;         //!< Items owned by this node, if this node is a leaf.
00062 
00063         OctreeNode();
00064         OctreeNode(const AABB3 &voxel);
00065         ~OctreeNode();
00066 
00067         template<typename ItemVoxelIntersector>
00068         void Subdivide(
00069             int pop_threshold,
00070             int remaining_subdiv,
00071             ItemVoxelIntersector &intersector)
00072         {
00073             assert(pop_threshold > 0);
00074             assert(remaining_subdiv > 0);
00075 
00076             if(m_items.size() <= static_cast<typename ItemVector::size_type>(pop_threshold))
00077                 return;
00078 
00079             m_is_leaf = false;
00080 
00081             m_children[0] = new OctreeNode<Item>(
00082                 AABB3(m_voxel.m_min, m_center));
00083 
00084             m_children[1] = new OctreeNode<Item>(
00085                 AABB3(
00086                     Point3(m_voxel.m_min.m_x, m_voxel.m_min.m_y, m_center.m_z),
00087                     Point3(m_center.m_x, m_center.m_y, m_voxel.m_max.m_z)));
00088 
00089             m_children[2] = new OctreeNode<Item>(
00090                 AABB3(
00091                     Point3(m_voxel.m_min.m_x, m_center.m_y, m_voxel.m_min.m_z),
00092                     Point3(m_center.m_x, m_voxel.m_max.m_y, m_center.m_z)));
00093 
00094             m_children[3] = new OctreeNode<Item>(
00095                 AABB3(
00096                     Point3(m_voxel.m_min.m_x, m_center.m_y, m_center.m_z),
00097                     Point3(m_center.m_x, m_voxel.m_max.m_y, m_voxel.m_max.m_z)));
00098 
00099             m_children[4] = new OctreeNode<Item>(
00100                 AABB3(
00101                     Point3(m_center.m_x, m_voxel.m_min.m_y, m_voxel.m_min.m_z),
00102                     Point3(m_voxel.m_max.m_x, m_center.m_y, m_center.m_z)));
00103 
00104             m_children[5] = new OctreeNode<Item>(
00105                 AABB3(
00106                     Point3(m_center.m_x, m_voxel.m_min.m_y, m_center.m_z),
00107                     Point3(m_voxel.m_max.m_x, m_center.m_y, m_voxel.m_max.m_z)));
00108 
00109             m_children[6] = new OctreeNode<Item>(
00110                 AABB3(
00111                     Point3(m_center.m_x, m_center.m_y, m_voxel.m_min.m_z),
00112                     Point3(m_voxel.m_max.m_x, m_voxel.m_max.m_y, m_center.m_z)));
00113 
00114             m_children[7] = new OctreeNode<Item>(
00115                 AABB3(m_center, m_voxel.m_max));
00116 
00117             for(ItemVectorConstIt i = m_items.begin(), e = m_items.end(); i != e; ++i) {
00118                 const Item &item = *i;
00119 
00120                 for(int j = 0; j < 8; ++j) {
00121                     if(intersector.Intersect(item, m_children[j]->m_voxel))
00122                         m_children[j]->m_items.push_back(item);
00123                 }
00124             }
00125 
00126             m_items.clear();
00127 
00128             if(--remaining_subdiv == 0)
00129                 return;
00130 
00131             for(int i = 0; i < 8; ++i)
00132                 m_children[i]->Subdivide(pop_threshold, remaining_subdiv, intersector);
00133         }
00134 
00135         template<typename Visitor>
00136         bool Trace(
00137             const Point3 &origin,
00138             Real tx0, Real ty0, Real tz0,
00139             Real tx1, Real ty1, Real tz1,
00140             unsigned int a,
00141             unsigned int b,
00142             Visitor &visitor) const
00143         {
00144             // Exit if the ray does not intersect the voxel.
00145             if(tx1 < 0.0 || ty1 < 0.0 || tz1 < 0.0)
00146                 return false;   // continue octree traversal
00147 
00148             // If it is a leaf, visit it.
00149             if(m_is_leaf) {
00150                 Real t0 = max<Real>(tx0, ty0, tz0);
00151                 Real t1 = min<Real>(tx1, ty1, tz1);
00152 
00153                 return visitor.Trace(this, t0, t1);
00154             }
00155 
00156             // Compute the intersection between the ray and the three middle planes.
00157 
00158             const Real &x0 = m_voxel.m_min.m_x;
00159             const Real &y0 = m_voxel.m_min.m_y;
00160             const Real &z0 = m_voxel.m_min.m_z;
00161             const Real &x1 = m_voxel.m_max.m_x;
00162             const Real &y1 = m_voxel.m_max.m_y;
00163             const Real &z1 = m_voxel.m_max.m_z;
00164 
00165             const Real INF = 1e9;
00166 
00167             Real txm, tym, tzm;
00168 
00169             switch(b) {
00170             case 0:
00171                 txm = 0.5 * (tx0 + tx1);
00172                 tym = 0.5 * (ty0 + ty1);
00173                 tzm = 0.5 * (tz0 + tz1);
00174                 break;
00175             case 1:
00176                 txm = 0.5 * (tx0 + tx1);
00177                 tym = 0.5 * (ty0 + ty1);
00178                 tzm = origin.m_z < 0.5 * (z0 + z1) ? +INF : -INF;
00179                 break;
00180             case 2:
00181                 txm = 0.5 * (tx0 + tx1);
00182                 tym = origin.m_y < 0.5 * (y0 + y1) ? +INF : -INF;
00183                 tzm = 0.5 * (tz0 + tz1);
00184                 break;
00185             case 3:
00186                 txm = 0.5 * (tx0 + tx1);
00187                 tym = origin.m_y < 0.5 * (y0 + y1) ? +INF : -INF;
00188                 tzm = origin.m_z < 0.5 * (z0 + z1) ? +INF : -INF;
00189                 break;
00190             case 4:
00191                 txm = origin.m_x < 0.5 * (x0 + x1) ? +INF : -INF;
00192                 tym = 0.5 * (ty0 + ty1);
00193                 tzm = 0.5 * (tz0 + tz1);
00194                 break;
00195             case 5:
00196                 txm = origin.m_x < 0.5 * (x0 + x1) ? +INF : -INF;
00197                 tym = 0.5 * (ty0 + ty1);
00198                 tzm = origin.m_z < 0.5 * (z0 + z1) ? +INF : -INF;
00199                 break;
00200             case 6:
00201                 txm = origin.m_x < 0.5 * (x0 + x1) ? +INF : -INF;
00202                 tym = origin.m_y < 0.5 * (y0 + y1) ? +INF : -INF;
00203                 tzm = 0.5 * (tz0 + tz1);
00204                 break;
00205             case 7:
00206                 txm = origin.m_x < 0.5 * (x0 + x1) ? +INF : -INF;
00207                 tym = origin.m_y < 0.5 * (y0 + y1) ? +INF : -INF;
00208                 tzm = origin.m_z < 0.5 * (z0 + z1) ? +INF : -INF;
00209                 break;
00210             default:
00211                 assert(!"Internal failure.");
00212             }
00213 
00214             unsigned int current_octant = 0;
00215 
00216             if(tx0 > ty0) {
00217                 if(tx0 > tz0) {
00218                     // max(tx0, ty0, tz0) is tx0. Entry plane is YZ.
00219                     if(tym < tx0) current_octant |= 2;
00220                     if(tzm < tx0) current_octant |= 1;
00221                 } else {
00222                     // max(tx0, ty0, tz0) is tz0. Entry plane is XY.
00223                     if(txm < tz0) current_octant |= 4;
00224                     if(tym < tz0) current_octant |= 2;
00225                 }
00226             } else {
00227                 if(ty0 > tz0) {
00228                     // max(tx0, ty0, tz0) is ty0. Entry plane is XZ.
00229                     if(txm < ty0) current_octant |= 4;
00230                     if(tzm < ty0) current_octant |= 1;
00231                 } else {
00232                     // max(tx0, ty0, tz0) is tz0. Entry plane is XY.
00233                     if(txm < tz0) current_octant |= 4;
00234                     if(tym < tz0) current_octant |= 2;
00235                 }
00236             }
00237 
00238             // This special state indicates algorithm termination.
00239             const unsigned int END = 8;
00240 
00241             while(true) {
00242                 switch(current_octant) {
00243                 case 0:
00244                     if(m_children[a]->Trace(origin, tx0, ty0, tz0, txm, tym, tzm, a, b, visitor))
00245                         return true;
00246                     current_octant = choose_next(txm, tym, tzm, 4, 2, 1);
00247                     break;
00248                 case 1:
00249                     if(m_children[1 ^ a]->Trace(origin, tx0, ty0, tzm, txm, tym, tz1, a, b, visitor))
00250                         return true;
00251                     current_octant = choose_next(txm, tym, tz1, 5, 3, END);
00252                     break;
00253                 case 2:
00254                     if(m_children[2 ^ a]->Trace(origin, tx0, tym, tz0, txm, ty1, tzm, a, b, visitor))
00255                         return true;
00256                     current_octant = choose_next(txm, ty1, tzm, 6, END, 3);
00257                     break;
00258                 case 3:
00259                     if(m_children[3 ^ a]->Trace(origin, tx0, tym, tzm, txm, ty1, tz1, a, b, visitor))
00260                         return true;
00261                     current_octant = choose_next(txm, ty1, tz1, 7, END, END);
00262                     break;
00263                 case 4:
00264                     if(m_children[4 ^ a]->Trace(origin, txm, ty0, tz0, tx1, tym, tzm, a, b, visitor))
00265                         return true;
00266                     current_octant = choose_next(tx1, tym, tzm, END, 6, 5);
00267                     break;
00268                 case 5:
00269                     if(m_children[5 ^ a]->Trace(origin, txm, ty0, tzm, tx1, tym, tz1, a, b, visitor))
00270                         return true;
00271                     current_octant = choose_next(tx1, tym, tz1, END, 7, END);
00272                     break;
00273                 case 6:
00274                     if(m_children[6 ^ a]->Trace(origin, txm, tym, tz0, tx1, ty1, tzm, a, b, visitor))
00275                         return true;
00276                     current_octant = choose_next(tx1, ty1, tzm, END, END, 7);
00277                     break;
00278                 case 7:
00279                     if(m_children[7 ^ a]->Trace(origin, txm, tym, tzm, tx1, ty1, tz1, a, b, visitor))
00280                         return true;
00281                 case END:
00282                     return false;
00283                 }
00284             }
00285         }
00286 
00287         template<typename Visitor>
00288         void Visit(Visitor &visitor) const {
00289             visitor.Visit(this);
00290 
00291             if(!m_is_leaf) {
00292                 for(int i = 0; i < 8; ++i)
00293                     m_children[i]->Visit(visitor);
00294             }
00295         }
00296     };
00297 
00298     template<typename Item>
00299     class Octree {
00300     public:
00301         Octree();
00302         ~Octree();
00303 
00304         void Insert(const Item &item, const AABB3 &aabb);
00305 
00306         template<typename ItemBoxIntersector>
00307         void Subdivide(int pop_threshold, int max_subdiv, ItemBoxIntersector &intersector) {
00308             assert(m_root);
00309             assert(pop_threshold > 0);
00310             assert(max_subdiv >= 0);
00311 
00312             if(max_subdiv > 0) {
00313                 m_root->m_center = m_root->m_voxel.GetCenter();
00314                 m_root->Subdivide(pop_threshold, max_subdiv, intersector);
00315             }
00316         }
00317 
00318         const AABB3 &GetAABB() const;
00319 
00320         //! Invokes the visitor on the leaf containing a given point.
00321         template<typename Visitor>
00322         void Hit(const Point3 &point, Visitor &visitor) const {
00323             assert(m_root);
00324 
00325             Real x = point.m_x;
00326             Real y = point.m_y;
00327             Real z = point.m_z;
00328 
00329             const Point3 &box_min = m_root->m_voxel.m_min;
00330             const Point3 &box_max = m_root->m_voxel.m_max;
00331 
00332             if( x < box_min.m_x || x > box_max.m_x ||
00333                 y < box_min.m_y || y > box_max.m_y ||
00334                 z < box_min.m_z || z > box_max.m_z)
00335                 return;
00336 
00337             OctreeNode<Item> *node = m_root;
00338 
00339             while(!node->m_is_leaf) {
00340                 unsigned int octant = 0;
00341 
00342                 if(x >= node->m_center.m_x) octant |= 4;
00343                 if(y >= node->m_center.m_y) octant |= 2;
00344                 if(z >= node->m_center.m_z) octant |= 1;
00345 
00346                 node = node->m_children[octant];
00347             }
00348 
00349             visitor.Hit(node);
00350         }
00351 
00352         //! The Trace() method implements the algorithm described in the paper
00353         //! entitled "An Efficient Parametric Algorithm for Octree Traversal" by
00354         //! J. Revelles, C. Urena and M. Lastra. This paper can be found at the
00355         //! following address: http://wscg.zcu.cz/wscg2000/Papers_2000/X31.pdf.
00356         template<typename Visitor>
00357         void Trace(Point3 origin, Vector3 direction, Visitor &visitor) const {
00358             assert(m_root);
00359 
00360             const Point3 &box_min = m_root->m_voxel.m_min;
00361             const Point3 &box_max = m_root->m_voxel.m_max;
00362 
00363             const Real EPSILON = 1e-9;
00364 
00365             unsigned int a = 0;
00366             unsigned int b = 0;
00367 
00368             if(direction.m_x == 0.0) {
00369                 direction.m_x = EPSILON;
00370                 b |= 4;
00371             } else if(direction.m_x < 0.0) {
00372                 origin.m_x = (box_min.m_x + box_max.m_x) - origin.m_x;
00373                 direction.m_x = -direction.m_x;
00374                 a |= 4;
00375             }
00376 
00377             if(direction.m_y == 0.0) {
00378                 direction.m_y = EPSILON;
00379                 b |= 2;
00380             } else if(direction.m_y < 0.0) {
00381                 origin.m_y = (box_min.m_y + box_max.m_y) - origin.m_y;
00382                 direction.m_y = -direction.m_y;
00383                 a |= 2;
00384             }
00385 
00386             if(direction.m_z == 0.0) {
00387                 direction.m_z = EPSILON;
00388                 b |= 1;
00389             } else if(direction.m_z < 0.0) {
00390                 origin.m_z = (box_min.m_z + box_max.m_z) - origin.m_z;
00391                 direction.m_z = -direction.m_z;
00392                 a |= 1;
00393             }
00394 
00395             assert(
00396                 direction.m_x != 0.0 &&
00397                 direction.m_y != 0.0 &&
00398                 direction.m_z != 0.0);
00399 
00400             Real tx0 = (box_min.m_x - origin.m_x) / direction.m_x;
00401             Real tx1 = (box_max.m_x - origin.m_x) / direction.m_x;
00402             Real ty0 = (box_min.m_y - origin.m_y) / direction.m_y;
00403             Real ty1 = (box_max.m_y - origin.m_y) / direction.m_y;
00404             Real tz0 = (box_min.m_z - origin.m_z) / direction.m_z;
00405             Real tz1 = (box_max.m_z - origin.m_z) / direction.m_z;
00406 
00407             if(max(tx0, ty0, tz0) < min(tx1, ty1, tz1))
00408                 m_root->Trace(origin, tx0, ty0, tz0, tx1, ty1, tz1, a, b, visitor);
00409         }
00410 
00411         //! Go through all the nodes and call the Visit() method on each of them.
00412         template<typename Visitor>
00413         void Visit(Visitor &visitor) const {
00414             assert(m_root);
00415             m_root->Visit(visitor);
00416         }
00417 
00418     private:
00419         OctreeNode<Item> *m_root;
00420     };
00421 
00422 #include "octree.inl"
00423 
00424 }
00425 
00426 #endif  // !SHEEP_MATH_OCTREE_H

Generated on Tue May 11 01:31:51 2004 for toxic by doxygen 1.3.6