CollisionGrid.cpp

Go to the documentation of this file.
00001 /*
00002 SagaEngine library
00003 Copyright (c) 2002-2006 Skalden Studio AS
00004 
00005 This software is provided 'as-is', without any express or implied 
00006 warranty. In no event will the authors be held liable for any 
00007 damages arising from the use of this software.
00008 
00009 Permission is granted to distribute the library under the terms of the 
00010 Q Public License version 1.0. Be sure to read and understand the license
00011 before using the library. It should be included here, or you may read it
00012 at http://www.trolltech.com/products/qt/licenses/licensing/qpl
00013 
00014 The original version of this library can be located at:
00015 http://www.sagaengine.com/
00016 
00017 Rune Myrland
00018 rune@skalden.com
00019 */
00020 
00021 
00022 #include "CollisionGrid.hpp"
00023 #include "comp/Composite.hpp"
00024 #include "CollisionComponent.hpp"
00025 #include "util/error/Log.hpp"
00026 #include "util/vecmath/Point3.hpp"
00027 #include <cstdio>
00028 
00029 
00030 namespace se_core {
00031     CollisionGrid
00032     ::CollisionGrid(coor_tile_t width, coor_tile_t height, short depth)
00033             : xOffset_(0), zOffset_(0), depth_(depth) {
00034 
00035         // root node size is whichever is greatest of width and height
00036         rootNodeSize_ = (width > height) ? width : height;
00037 
00038         // Force rootNodeSize_ to be n^2 (divides become shifts)
00039         rootNodeShift_ = 1;
00040         while((1L << rootNodeShift_) < rootNodeSize_)
00041             ++rootNodeShift_;
00042         rootNodeSize_ = 1L << rootNodeShift_;
00043 
00044         // Precalc for fast level searches
00045         halfRootNodeSize_ = shiftRight(rootNodeSize_, 1);
00046 
00047         // No point making grid for things with size less than 1.
00048         while(nodeSize(depth_) == 0)
00049             --depth_;
00050 
00051         // How big should the node array be?
00052         totalNodeCount_ = 0;
00053         for(short i = 0; i < depth_; ++i) {
00054             totalNodeCount_ += 1 << (i * 2);
00055         }
00056 
00057         // Create node array
00058         nodes_ = new CollisionGridCollisionComponentList::iterator_type[ totalNodeCount_ ];
00059         // Init nodes to empty
00060         for(int i = 0; i < totalNodeCount_; ++i) {
00061             nodes_[i] = CollisionGridCollisionComponentList::end();
00062         }
00063 
00064         // Create and init array with pointers to
00065         // beginning of each node level
00066         nodeLevels_ = new CollisionGridCollisionComponentList::iterator_type*[ depth_ ];
00067         int nodePos = 0;
00068         for(short i = 0; i < depth_; ++i) {
00069             nodeLevels_[ i ] = &nodes_[ nodePos ];
00070             nodePos += 1 << (i * 2);
00071         }
00072 
00073     }
00074 
00075 
00076     CollisionGrid
00077     ::~CollisionGrid() {
00078         delete[] nodes_;
00079         delete[] nodeLevels_;
00080     }
00081 
00082 
00083     void CollisionGrid
00084     ::setSize(int width, int height) {
00085         rootNodeSize_ = (width > height) ? width : height;
00086         Assert(indexAtLevel(width - 1, height - 1, depth_ - 1) < totalNodeCount_);
00087     }
00088 
00089 
00090     void CollisionGrid
00091     ::setOffset(const Point3& c) {
00092         xOffset_ = c.xTile();
00093         zOffset_ = c.zTile();
00094     }
00095 
00096 
00097     void CollisionGrid
00098     ::insert(const Point3& c, coor_t size, CollisionComponent& thing) {
00099         const coor_tile_t x = c.xTile() - xOffset_;
00100         const coor_tile_t z = c.zTile() - zOffset_;
00101         short level = calcLevel(CoorT::tile(size) + 1);
00102 
00103         AssertFatal(indexAtLevel(x, z, level) >= 0, thing.owner()->name());
00104         AssertFatal(indexAtLevel(x, z, level) < totalNodeCount_, thing.owner()->name());
00105         AssertFatal(x >= 0 || x < rootNodeSize_, thing.owner()->name());
00106         AssertFatal(z >= 0 || z < rootNodeSize_, thing.owner()->name()); 
00107 
00108         Assert(x < rootNodeSize_);
00109         Assert(z < rootNodeSize_);
00110 
00111         // Find the right level
00112 
00113         // Reference to first element pointer. Element pointer may change.
00114         Assert(indexAtLevel(x, z, level) >= 0);
00115         Assert(indexAtLevel(x, z, level) < totalNodeCount_);
00116         CollisionGridCollisionComponentList::iterator_type& it
00117             = nodeLevels_[ level ][ indexAtLevel(x, z, level) ];
00118 
00119         // Insert the element. The element will bed inserted at
00120         // the beginning of the list. Thus often moving elements
00121         // will stay in the beginning of node lists
00122         thingList().add(&thing, it);
00123 
00124         Assert(nodeLevels_[ level ][ indexAtLevel(x, z, level) ] !=
00125                CollisionGridCollisionComponentList::end());
00126     }
00127 
00128 
00129     void CollisionGrid
00130     ::clear() {
00131         for(int i = 0; i < totalNodeCount_; ++i) {
00132             thingList().removeChain(nodes_[i]);
00133         }
00134     }
00135 
00136 
00137     bool CollisionGrid
00138     ::remove(const Point3& c, coor_t size, CollisionComponent& thing) {
00139         const coor_tile_t x = c.xTile() - xOffset_;
00140         const coor_tile_t z = c.zTile() - zOffset_;
00141         const coor_tile_t s = CoorT::tile(size) + 1;
00142 
00143         // Find the right level
00144         short level = calcLevel(s);
00145 
00146         // Reference to first element pointer. Element pointer may change.
00147         int index = indexAtLevel(x, z, level);
00148         if(index < 0) {
00149             LogDetail(c << " - " << size);
00150         }
00151         Assert(indexAtLevel(x, z, level) >= 0);
00152         Assert(indexAtLevel(x, z, level) < (1L << level) * (1L << level));
00153 
00154         CollisionGridCollisionComponentList::iterator_type& it = nodeLevels_[level][index];
00155 
00156         return thingList().remove(&thing, it);
00157     }
00158 
00159 
00160     void CollisionGrid
00161     ::move(const Point3& from, coor_t oldSize
00162            , const Point3& to, coor_t newSize
00163            , CollisionComponent& thing) {
00164 
00165         const coor_tile_t os = CoorT::tile(oldSize) + 1;
00166         const coor_tile_t ox = from.xTile() - xOffset_;
00167         const coor_tile_t oz = from.zTile() - zOffset_;
00168 
00169         const coor_tile_t ns = CoorT::tile(newSize) + 1;
00170         const coor_tile_t nx = to.xTile() - xOffset_;
00171         const coor_tile_t nz = to.zTile() - zOffset_;
00172 
00173         int oldLevel = calcLevel(os);
00174         int oldIndex = indexAtLevel(ox, oz, oldLevel);
00175         int newLevel = (os != ns) ? calcLevel(ns) : oldLevel;
00176         int newIndex = indexAtLevel(nx, nz, newLevel);
00177 
00178         // If still in same cell, don't move
00179         if(oldIndex == newIndex)
00180             return;
00181 
00182         // Different cell, move
00183         CollisionGridCollisionComponentList::iterator_type& oldIt
00184             = nodeLevels_[oldLevel][ oldIndex ];
00185         bool didDelete = thingList().remove(&thing, oldIt);
00186         CollisionGridCollisionComponentList::iterator_type& newIt
00187             = nodeLevels_[newLevel][ newIndex ];
00188         thingList().add(&thing, newIt);
00189         return;
00190     }
00191 
00192 
00193     short CollisionGrid
00194     ::collisionCandidates(const Point3& c, coor_t size, CollisionComponent* things[], short max) {
00195         // Calculate bounds to check inside
00196         coor_tile_t xFrom = CoorT::tile(c.x_ - size) - xOffset_;
00197         coor_tile_t zFrom = CoorT::tile(c.z_ - size) - zOffset_;
00198         coor_tile_t xTo = CoorT::tile(c.x_ + size) + 1 - xOffset_;
00199         coor_tile_t zTo = CoorT::tile(c.z_ + size) + 1 - zOffset_;
00200 
00201         // Number of candidates in array
00202         short count = 0;
00203 
00204         // Check for collision canditates in all levels
00205         for(short level = 0; level < depth_; ++level) {
00206             DbgAssert(rootNodeShift_ - level > 0 || "Depth too high");
00207 
00208             // For subtracting / adding loose bounds
00209             coor_tile_t hns = halfNodeSize(level);
00210 
00211             // Translate bounds coors to level grid coor
00212             coor_tile_t xNodeFrom = shiftRight((-hns + xFrom), (rootNodeShift_ - level));
00213             coor_tile_t xNodeTo = shiftRight((hns + xTo), (rootNodeShift_ - level));
00214             coor_tile_t zNodeFrom = shiftRight((-hns + zFrom), (rootNodeShift_ - level));
00215             coor_tile_t zNodeTo = shiftRight((hns + zTo), (rootNodeShift_ - level));
00216 
00217             // Must do on every level because loose bounds
00218             // differs from one level to next
00219             if(xNodeFrom < 0) xNodeFrom = 0;
00220             if(xNodeTo >= (1L << level)) xNodeTo = (1L << level) - 1;
00221             if(zNodeFrom < 0) zNodeFrom = 0;
00222             if(zNodeTo >= (1L << level)) zNodeTo = (1L << level) - 1;
00223 
00224             // Iterate through all nodes that are inside the bounds
00225             for(coor_tile_t z = zNodeFrom; z <= zNodeTo; ++z) {
00226                 for(coor_tile_t x = xNodeFrom; x <= xNodeTo; ++x) {
00227                     // The first element pointer of this node.
00228                     DbgAssert(indexAtLevelAndNode(x, z, level) >= 0);
00229                     DbgAssert(indexAtLevelAndNode(x, z, level) < totalNodeCount_);
00230                     CollisionGridCollisionComponentList::iterator_type it
00231                         = nodeLevels_[level][ indexAtLevelAndNode(x, z, level) ];
00232 
00233                     // Add members of node to the candidates list
00234                     while(it != CollisionGridCollisionComponentList::end()) {
00235                         Assert(count < max - 1);
00236                         things[ count++ ] =
00237                             thingList().next(it);
00238                     }
00239                 }
00240             }
00241         }
00242 
00243         Assert(count < max);
00244         return count;
00245     }
00246 
00247 
00248     bool CollisionGrid
00249     ::find(CollisionComponent& thing) {
00250         /*
00251         for(int level = 0; level < depth_; ++ level) {
00252             for(int z = 0; z < (1L << level); ++z) {
00253                 for(int x = 0; x < (1L << level); ++x) {
00254                     int i = indexAtLevelAndNode(x, z, level);
00255                     CollisionGridThingList::iterator_type it = nodeLevels_[level][i];
00256                     if(thingList().hasElement(thing, it)) {
00257                         thingList().remove(thing, nodeLevels_[level][i]);
00258                         int i1 = indexAtLevel
00259                             (thing.pos().x_
00260                              , thing.pos().z_
00261                              , calcLevel(thing.pos().radius()));
00262                         int i2 = indexAtLevel
00263                             (thing.nextPos().x_
00264                              , thing.nextPos().z_
00265                              , calcLevel(thing.pos().radius()));
00266                         LogDetail
00267                             ((sprintf
00268                               (
00269                                log_msg(), "%s(%d): rad(%d), pos %d(%d, %d), nextPos %d(%d, %d)"
00270                                , thing.name(), i
00271                                , thing.pos().radius()
00272                                , i1, thing.pos().x_, thing.pos().z_
00273                                , i2, thing.nextPos().x_, thing.nextPos().z_
00274                                ), log_msg()));
00275                         return true;
00276                     }
00277 
00278                 }
00279             }
00280         }
00281         */
00282 
00283         for(int i = 0; i < totalNodeCount_; ++i) {
00284             CollisionGridCollisionComponentList::iterator_type it = nodes_[i];
00285             if(thingList().hasElement(&thing, it)) {
00286                 thingList().remove(&thing, nodes_[i]);
00287                 return true;
00288             }
00289         }
00290 
00291         return false;
00292     }
00293 
00294 }

Home Page | SagaEngine trunk (updated nightly) reference generated Sun Dec 2 20:06:11 2007 by Doxygen version 1.3.9.1.

SourceForge.net Logo