// ---------------------------------------------------------------------------- // // // OpenSteer -- Steering Behaviors for Autonomous Characters // // Copyright (c) 2002-2005, Sony Computer Entertainment America // Original author: Craig Reynolds // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // // // ---------------------------------------------------------------------------- // // // Proximity // // Data structures for accelerating proximity/locality/neighborhood queries // // 10-04-04 bk: put everything into the OpenSteer namespace // 06-20-01 cwr: created // // // ---------------------------------------------------------------------------- #ifndef OPENSTEER_PROXIMITY_H #define OPENSTEER_PROXIMITY_H #include #include #include "OpenSteer/Vec3.h" #include "OpenSteer/lq.h" // XXX temp? namespace OpenSteer { // ---------------------------------------------------------------------------- // "tokens" are the objects manipulated by the spatial database template class AbstractTokenForProximityDatabase { public: virtual ~AbstractTokenForProximityDatabase () {} // the client object calls this each time its position changes virtual void updateForNewPosition (const Vec3& position) = 0; // find all neighbors within the given sphere (as center and radius) virtual void findNeighbors (const Vec3& center, const float radius, std::vector& results) = 0; #ifndef NO_LQ_BIN_STATS // only meaningful for LQProximityDatabase, provide dummy default virtual void getBinPopulationStats (int& min, int& max, float& average) {min=max=0; average=0.0;} #endif // NO_LQ_BIN_STATS }; // ---------------------------------------------------------------------------- // abstract type for all kinds of proximity databases template class AbstractProximityDatabase { public: // type for the "tokens" manipulated by this spatial database typedef AbstractTokenForProximityDatabase tokenType; virtual ~AbstractProximityDatabase() { /* Nothing to do? */ } // allocate a token to represent a given client object in this database virtual tokenType* allocateToken (ContentType parentObject) = 0; // insert // XXX maybe this should return an iterator? // XXX see http://www.sgi.com/tech/stl/set.html // virtual void insert (const ContentType& x) = 0; // XXX name? // returns the number of tokens in the proximity database virtual int getPopulation (void) = 0; }; // ---------------------------------------------------------------------------- // This is the "brute force" O(n^2) approach implemented in terms of the // AbstractProximityDatabase protocol so it can be compared directly to other // approaches. (e.g. the Boids plugin allows switching at runtime.) template class BruteForceProximityDatabase : public AbstractProximityDatabase { public: // constructor BruteForceProximityDatabase (void) { } // destructor virtual ~BruteForceProximityDatabase () { } // "token" to represent objects stored in the database class tokenType : public AbstractTokenForProximityDatabase { public: // constructor tokenType (ContentType parentObject, BruteForceProximityDatabase& pd) { // store pointer to our associated database and the object this // token represents, and store this token on the database's vector bfpd = &pd; object = parentObject; bfpd->group.push_back (this); } // destructor virtual ~tokenType () { // remove this token from the database's vector bfpd->group.erase (std::find (bfpd->group.begin(), bfpd->group.end(), this)); } // the client object calls this each time its position changes void updateForNewPosition (const Vec3& newPosition) { position = newPosition; } // find all neighbors within the given sphere (as center and radius) void findNeighbors (const Vec3& center, const float radius, std::vector& results) { // loop over all tokens const float r2 = radius * radius; for (tokenIterator i = bfpd->group.begin(); i != bfpd->group.end(); i++) { const Vec3 offset = center - (**i).position; const float d2 = offset.lengthSquared(); // push onto result vector when within given radius if (d2 < r2) results.push_back ((**i).object); } } private: BruteForceProximityDatabase* bfpd; ContentType object; Vec3 position; }; typedef std::vector tokenVector; typedef typename tokenVector::const_iterator tokenIterator; // allocate a token to represent a given client object in this database tokenType* allocateToken (ContentType parentObject) { return new tokenType (parentObject, *this); } // return the number of tokens currently in the database int getPopulation (void) { return (int) group.size(); } private: // STL vector containing all tokens in database tokenVector group; }; // ---------------------------------------------------------------------------- // A AbstractProximityDatabase-style wrapper for the LQ bin lattice system template class LQProximityDatabase : public AbstractProximityDatabase { public: // constructor LQProximityDatabase (const Vec3& center, const Vec3& dimensions, const Vec3& divisions) { const Vec3 halfsize (dimensions * 0.5f); const Vec3 origin (center - halfsize); lq = lqCreateDatabase (origin.x, origin.y, origin.z, dimensions.x, dimensions.y, dimensions.z, (int) round (divisions.x), (int) round (divisions.y), (int) round (divisions.z)); } // destructor virtual ~LQProximityDatabase () { lqDeleteDatabase (lq); lq = NULL; } // "token" to represent objects stored in the database class tokenType : public AbstractTokenForProximityDatabase { public: // constructor tokenType (ContentType parentObject, LQProximityDatabase& lqsd) { lqInitClientProxy (&proxy, parentObject); lq = lqsd.lq; } // destructor virtual ~tokenType (void) { lqRemoveFromBin (&proxy); } // the client object calls this each time its position changes void updateForNewPosition (const Vec3& p) { lqUpdateForNewLocation (lq, &proxy, p.x, p.y, p.z); } // find all neighbors within the given sphere (as center and radius) void findNeighbors (const Vec3& center, const float radius, std::vector& results) { lqMapOverAllObjectsInLocality (lq, center.x, center.y, center.z, radius, perNeighborCallBackFunction, (void*)&results); } // called by LQ for each clientObject in the specified neighborhood: // push that clientObject onto the ContentType vector in void* // clientQueryState // (parameter names commented out to prevent compiler warning from "-W") static void perNeighborCallBackFunction (void* clientObject, float /*distanceSquared*/, void* clientQueryState) { typedef std::vector ctv; ctv& results = *((ctv*) clientQueryState); results.push_back ((ContentType) clientObject); } #ifndef NO_LQ_BIN_STATS // Get statistics about bin populations: min, max and // average of non-empty bins. void getBinPopulationStats (int& min, int& max, float& average) { lqGetBinPopulationStats (lq, &min, &max, &average); } #endif // NO_LQ_BIN_STATS private: lqClientProxy proxy; lqDB* lq; }; // allocate a token to represent a given client object in this database tokenType* allocateToken (ContentType parentObject) { return new tokenType (parentObject, *this); } // count the number of tokens currently in the database int getPopulation (void) { int count = 0; lqMapOverAllObjects (lq, counterCallBackFunction, &count); return count; } // (parameter names commented out to prevent compiler warning from "-W") static void counterCallBackFunction (void* /*clientObject*/, float /*distanceSquared*/, void* clientQueryState) { int& counter = *(int*)clientQueryState; counter++; } private: lqDB* lq; }; } // namespace OpenSteer // ---------------------------------------------------------------------------- #endif // OPENSTEER_PROXIMITY_H