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.