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

octree.cpp

Go to the documentation of this file.
00001 // octree.cpp
00002 //
00003 // Octree-based visibility determination for a star database.
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 #include <cmath>
00013 #include "astro.h"
00014 #include "octree.h"
00015 
00016 using namespace std;
00017 
00018 
00019 // There are two classed implemented in this module: StarOctree and
00020 // DynamicStarOctree.  The DynamicStarOctree is built first by inserting
00021 // stars from a database and is then 'compiled' into a StarOctree.  In the
00022 // process of building the StarOctree, the original star database is
00023 // reorganized, with stars in the same octree node all placed adjacent to each
00024 // other.  This spatial sorting of the stars dramatically improves the
00025 // performance of octree operations through much more coherent memory access.
00026 //
00027 // The octree node into which a star is placed is dependent on two properties:
00028 // its position and its luminosity--the fainter the star, the deeper the node
00029 // in which it will reside.  Each node stores an absolute magnitude; no child
00030 // of the node is allowed contain star brighter than this value, making it
00031 // possible to determine quickly whether or not to cull subtrees.
00032 
00033 enum
00034 {
00035     XPos = 1,
00036     YPos = 2,
00037     ZPos = 4,
00038 };
00039 
00040 // The splitThreshold is the number of stars a node must contain before it's
00041 // children are generated.  Increasing this number will decrease the number of
00042 // octree nodes in the tree, which will use less memory but make culling less
00043 // efficient.  In testing, changing splitThreshold from 100 to 50 nearly
00044 // doubled the number of nodes in the tree, but provided only between a
00045 // 0 to 5 percent frame rate improvement.
00046 static const unsigned int splitThreshold = 100;
00047 
00048 static const float sqrt3 = 1.732050808f;
00049 
00050 
00051 DynamicStarOctree::DynamicStarOctree(const Point3f& _center, float _absMag) :
00052     stars(NULL), children(NULL), center(_center), absMag(_absMag)
00053 {
00054 }
00055 
00056 DynamicStarOctree::~DynamicStarOctree()
00057 {
00058     if (children != NULL)
00059     {
00060         for (int i = 0; i < 8; i++)
00061         {
00062             delete children[i];
00063         }
00064         delete [] children;
00065     }
00066 }
00067 
00068 
00069 static int childIndex(const Star& star, const Point3f& center)
00070 {
00071     Point3f pos = star.getPosition();
00072 
00073     int child = 0;
00074     child |= pos.x < center.x ? 0 : XPos;
00075     child |= pos.y < center.y ? 0 : YPos;
00076     child |= pos.z < center.z ? 0 : ZPos;
00077     
00078     return child;
00079 }
00080 
00081 
00082 inline bool
00083 starOrbitStraddlesNodes(const Star& star, const Point3f& center)
00084 {
00085     float orbitalRadius = star.getOrbitalRadius();
00086     if (orbitalRadius == 0.0f)
00087         return false;
00088 
00089     Point3f starPos = star.getPosition();
00090     return (abs(starPos.x - center.x) < orbitalRadius ||
00091             abs(starPos.y - center.y) < orbitalRadius ||
00092             abs(starPos.z - center.z) < orbitalRadius);
00093 }
00094 
00095 
00096 void DynamicStarOctree::insertStar(const Star& star, float scale)
00097 {
00098     // If the star is brighter than the node's magnitude, insert
00099     // it here now.  Also, if the star is in an orbit in a multiple
00100     // star system, check for the case where the sphere containing
00101     // the orbit straddles two or more child nodes.  If it does, we
00102     // must keep the star in the parent.
00103     if (star.getAbsoluteMagnitude() <= absMag ||
00104         starOrbitStraddlesNodes(star, center))
00105     {
00106         addStar(star);
00107     }
00108     else
00109     {
00110         // If we haven't allocated child nodes yet, try to fit
00111         // the star in this node, even though it's fainter than
00112         // this node's magnitude.  Only if there are more than
00113         // splitThreshold stars in the node will we attempt to
00114         // place the star into a child node.  This is done in order
00115         // to avoid having the octree degenerate into one star per node.
00116         if (children == NULL)
00117         {
00118             // Make sure that there's enough room left in this node
00119             if (stars != NULL && stars->size() >= splitThreshold)
00120                 split(scale * 0.5f);
00121             addStar(star);
00122         }
00123         else
00124         {
00125             // We've already allocated child nodes; place the star
00126             // into the appropriate one.
00127             children[childIndex(star, center)]->insertStar(star, scale * 0.5f);
00128         }
00129     }
00130 }
00131 
00132 
00133 void DynamicStarOctree::addStar(const Star& star)
00134 {
00135     if (stars == NULL)
00136         stars = new vector<const Star*>();
00137     stars->insert(stars->end(), &star);
00138 }
00139 
00140 
00141 void DynamicStarOctree::split(float scale)
00142 {
00143     children = new DynamicStarOctree*[8];
00144     for (int i = 0; i < 8; i++)
00145     {
00146         Point3f p(center);
00147         p.x += ((i & XPos) != 0) ? scale : -scale;
00148         p.y += ((i & YPos) != 0) ? scale : -scale;
00149         p.z += ((i & ZPos) != 0) ? scale : -scale;
00150         children[i] = new DynamicStarOctree(p, astro::lumToAbsMag(astro::absMagToLum(absMag) / 4.0f));
00151     }
00152     sortStarsIntoChildNodes();
00153 }
00154 
00155 
00156 // Sort this node's stars into stars that are bright enough to remain
00157 // in the node, and stars that should be placed into one of the eight
00158 // child nodes.
00159 void DynamicStarOctree::sortStarsIntoChildNodes()
00160 {
00161     int nBrightStars = 0;
00162 
00163     for (unsigned int i = 0; i < stars->size(); i++)
00164     {
00165         const Star* star = (*stars)[i];
00166 
00167         if (star->getAbsoluteMagnitude() <= absMag ||
00168             starOrbitStraddlesNodes(*star, center))
00169         {
00170             // If the star is bright enough or if it's in an orbit in
00171             // a multiple star system with an orbit straddling two or
00172             // more child nodes, then keep the star in the parent...
00173             (*stars)[nBrightStars] = (*stars)[i];
00174             nBrightStars++;
00175         }
00176         else
00177         {
00178             // ...otherwise, assign it to a child node.
00179             children[childIndex(*star, center)]->addStar(*star);
00180         }
00181     }
00182 
00183     stars->resize(nBrightStars);
00184 }
00185 
00186 
00187 void DynamicStarOctree::rebuildAndSort(StarOctree*& node, Star*& sortedStars)
00188 {
00189     Star* firstStar = sortedStars;
00190 
00191     if (stars != NULL)
00192     {
00193         for (vector<const Star*>::const_iterator iter = stars->begin();
00194              iter != stars->end(); iter++)
00195         {
00196             *sortedStars++ = **iter;
00197         }
00198     }
00199 
00200     uint32 nStars = (uint32) (sortedStars - firstStar);
00201     node = new StarOctree(center, absMag, firstStar, nStars);
00202 
00203     if (children != NULL)
00204     {
00205         node->children = new StarOctree*[8];
00206         for (int i = 0; i < 8; i++)
00207             children[i]->rebuildAndSort(node->children[i], sortedStars);
00208     }
00209 }
00210 
00211 
00212 
00213 StarOctree::StarOctree(const Point3f& _center,
00214                        float _absMag,
00215                        Star* _firstStar,
00216                        uint32 _nStars) :
00217     center(_center),
00218     absMag(_absMag),
00219     firstStar(_firstStar),
00220     nStars(_nStars),
00221     children(NULL)
00222 {
00223 }
00224 
00225 StarOctree::~StarOctree()
00226 {
00227     if (children != NULL)
00228     {
00229         for (int i = 0; i < 8; i++)
00230             delete children[i];
00231         delete [] children;
00232     }
00233 }
00234 
00235 
00236 // This method searches the octree for stars that are likely to be visible
00237 // to a viewer with the specified position and limiting magnitude.  The
00238 // starHandler is invoked for each potentially visible star--no star with
00239 // a magnitude greater than the limiting magnitude will be processed, but
00240 // stars that are outside the view frustum may be.  Frustum tests are performed
00241 // only at the node level to optimize the octree traversal, so an exact test
00242 // (if one is required) is the responsibility of the callback method.
00243 void StarOctree::findVisibleStars(StarHandler& starHandler,
00244                                   const Point3f& position,
00245                                   const Planef* frustumPlanes,
00246                                   float limitingMag,
00247                                   float scale) const
00248 {
00249     // See if this node lies within the view frustum
00250     {
00251         // Test the cubic octree node against each one of the five
00252         // planes that define the infinite view frustum.
00253         for (int i = 0; i < 5; i++)
00254         {
00255             const Planef* plane = frustumPlanes + i;
00256             float r = scale * (abs(plane->normal.x) +
00257                                abs(plane->normal.y) +
00258                                abs(plane->normal.z));
00259             if (plane->normal * Vec3f(center.x, center.y, center.z) - plane->d < -r)
00260                 return;
00261         }
00262     }
00263 
00264     // Compute the distance to node; this is equal to the distance to
00265     // the center of the node minus the radius of the node, scale * sqrt3.
00266     float minDistance = (position - center).length() - scale * sqrt3;
00267 
00268     // Process the stars in this node
00269     float dimmest = minDistance > 0 ? astro::appToAbsMag(limitingMag, minDistance) : 1000;
00270     for (unsigned int i = 0; i < nStars; i++)
00271     {
00272         Star& star = firstStar[i];
00273         if (star.getAbsoluteMagnitude() < dimmest)
00274         {
00275             float distance = position.distanceTo(star.getPosition());
00276             float appMag = astro::absToAppMag(star.getAbsoluteMagnitude(), distance);
00277             if (appMag < limitingMag)
00278                 starHandler.process(star, distance, appMag);
00279         }
00280     }
00281 
00282     // See if any of the stars in child nodes are potentially bright enough
00283     // that we need to recurse deeper.
00284     if (minDistance <= 0 || astro::absToAppMag(absMag, minDistance) <= limitingMag)
00285     {
00286         // Recurse into the child nodes
00287         if (children != NULL)
00288         {
00289             for (int i = 0; i < 8; i++)
00290             {
00291                 children[i]->findVisibleStars(starHandler,
00292                                               position,
00293                                               frustumPlanes,
00294                                               limitingMag,
00295                                               scale * 0.5f);
00296             }
00297         }
00298     }
00299 }
00300 
00301 
00302 void StarOctree::findCloseStars(StarHandler& starHandler,
00303                                 const Point3f& position,
00304                                 float radius,
00305                                 float scale) const
00306 {
00307     // Compute the distance to node; this is equal to the distance to
00308     // the center of the node minus the radius of the node, scale * sqrt3.
00309     float nodeDistance = (position - center).length() - scale * sqrt3;
00310     if (nodeDistance > radius)
00311         return;
00312 
00313     // At this point, we've determined that the center of the node is
00314     // close enough that we must check individual stars for proximity.
00315 
00316     // Compute distance squared to avoid having to sqrt for distance
00317     // comparison.
00318     float radiusSquared = radius * radius;
00319 
00320     // Check all the stars in the node.
00321     for (unsigned int i = 0; i < nStars; i++)
00322     {
00323         Star& star = firstStar[i];
00324         if (position.distanceToSquared(star.getPosition()) < radiusSquared)
00325         {
00326             float distance = position.distanceTo(star.getPosition());
00327             float appMag = astro::absToAppMag(star.getAbsoluteMagnitude(),
00328                                               distance);
00329             starHandler.process(star, distance, appMag);
00330         }
00331     }
00332 
00333     // Recurse into the child nodes
00334     if (children != NULL)
00335     {
00336         for (int i = 0; i < 8; i++)
00337         {
00338             children[i]->findCloseStars(starHandler,
00339                                         position,
00340                                         radius,
00341                                         scale * 0.5f);
00342         }
00343     }
00344 }
00345 
00346 
00347 
00348 int StarOctree::countChildren() const
00349 {
00350     if (children == NULL)
00351     {
00352         return 0;
00353     }
00354     else
00355     {
00356         int nChildren = 0;
00357         for (int i = 0; i < 8; i++)
00358             nChildren += 1 + children[i]->countChildren();
00359 
00360         return nChildren;
00361     }
00362 }
00363 
00364 int StarOctree::countStars() const
00365 {
00366     int count = nStars;
00367 
00368     if (children != NULL)
00369     {
00370         for (int i = 0; i < 8; i++)
00371             count += children[i]->countStars();
00372     }
00373 
00374     return count;
00375 }

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