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 #include "CollisionAreaComponent.hpp" 00022 #include "CollisionManager.hpp" 00023 #include "CollisionComponent.hpp" 00024 #include "../schema/SimSchema.hpp" 00025 #include "util/error/Log.hpp" 00026 #include "util/bounds/BoundingBox.hpp" 00027 #include "../thing/Actor.hpp" 00028 #include "../zone/ZoneAreaComponent.hpp" 00029 #include "../pos/PosComponent.hpp" 00030 #include <cstdlib> 00031 00032 00033 00034 namespace se_core { 00035 00036 CollisionAreaComponent 00037 ::CollisionAreaComponent(Composite* owner) 00038 : NodeComponent(sct_COLLISION, owner) 00039 , collisionGrid_(0), areaEdge_(new AreaEdge()) { 00040 } 00041 00042 00043 CollisionAreaComponent 00044 ::~CollisionAreaComponent() { 00045 delete areaEdge_; 00046 } 00047 00048 00049 void CollisionAreaComponent 00050 ::addCollideable(CollisionComponent& cc) { 00051 if(collisionGrid_) { 00052 Point3 p; 00053 cc.areaCovered().center(p); 00054 coor_t speedAndRadius = cc.areaCovered().radius(); 00055 collisionGrid_->insert(p, speedAndRadius, cc); 00056 } 00057 } 00058 00059 00060 void CollisionAreaComponent 00061 ::removeCollideable(CollisionComponent& cc) { 00062 if(!collisionGrid_) 00063 return; 00064 00065 Point3 p; 00066 cc.areaCovered().center(p); 00067 coor_t speedAndRadius = cc.areaCovered().radius(); 00068 bool didDelete = collisionGrid_->remove(p, speedAndRadius, cc); 00069 AssertWarning(didDelete, "Couldn't remove " << cc.owner()->name() << " from collision grid (" << collisionGrid_->find(cc) << ")"); 00070 } 00071 00072 00073 void CollisionAreaComponent 00074 ::setCollisionGrid(CollisionGrid* grid) { 00075 collisionGrid_ = grid; 00076 00077 // Make sure the grid does not have any members 00078 // from previous usage 00079 collisionGrid_->clear(); 00080 00081 // Align the grid coordinate system with 00082 // this areas coordinate system 00083 const Area::Ptr a(this); 00084 const coor_tile_t w = a->width(); 00085 const coor_tile_t h = a->height(); 00086 00087 // TODO: Improve this desperate solution for border creatures (* 2) 00088 // see aldo CollisionManager 00089 collisionGrid_->setSize(w * 2, h * 2); 00090 00091 PosComponent::Ptr pos(*this); 00092 Point3 offset(pos->pos().worldCoor()); 00093 offset.sub(Point3(CoorT::fromTile(w / 2), 0, CoorT::fromTile(h / 2))); 00094 00095 collisionGrid_->setOffset(offset); 00096 00097 // Add collideable elements to grid 00098 NodeComponentList::Iterator it(children_); 00099 while(it.hasNext()) { 00100 CollisionComponent* cc = static_cast<CollisionComponent*>(&it.next()); 00101 if(!cc->isCollideable()) 00102 continue; 00103 00104 cc->updateAreaCovered(); 00105 00106 Point3 newPos; 00107 cc->areaCovered().center(newPos); 00108 coor_t newRadius = cc->areaCovered().radius(); 00109 00110 collisionGrid_->insert(newPos, newRadius, *cc); 00111 } 00112 } 00113 00114 00115 void CollisionAreaComponent 00116 ::resetCollisionGrid() { 00117 if(collisionGrid_) { 00118 CollisionManager::singleton().releaseCollisionGrid(collisionGrid_); 00119 collisionGrid_ = 0; 00120 } 00121 } 00122 00123 00124 void CollisionAreaComponent 00125 ::setActive(bool state) { 00126 if(state) { 00127 setCollisionGrid(CollisionManager::singleton().grabCollisionGrid()); 00128 setParent(CollisionManager::singleton()); 00129 } 00130 else { 00131 resetParent(); 00132 resetCollisionGrid(); 00133 } 00134 } 00135 00136 00137 inline bool _testCollision(CollisionComponent& cc1, 00138 CollisionComponent& cc2) { 00139 if(cc1.shouldIgnore(cc2)) 00140 return false; 00141 00142 if(!cc1.areaCovered().isTouching(cc2.areaCovered())) 00143 return false; 00144 00145 if(!cc1.doesGeometryCollide(cc2)) 00146 return false; 00147 00148 00149 // Inside collision range. Collide. 00150 return true; 00151 } 00152 00153 00154 inline bool _testCollision2(CollisionComponent& cc1, 00155 CollisionComponent& cc2) { 00156 if(cc1.shouldIgnore(cc2)) 00157 return false; 00158 00159 if(!cc1.doesGeometryCollide(cc2)) 00160 return false; 00161 00162 00163 // Inside collision range. Collide. 00164 return true; 00165 } 00166 00167 00168 coor_t CollisionAreaComponent 00169 ::farthestLineOfSight(const se_core::Point3& fromPoint, const se_core::Point3& toPoint) const { 00170 const int MAX_CONTACTS = 128; 00171 static CollisionComponent* candidates[MAX_CONTACTS]; 00172 int candidateCount = 0; 00173 coor_t dist = CoorT::sqrt(fromPoint.xzDistanceSquared(toPoint)); 00174 coor_t speedAndRadius = CoorT::half(dist); 00175 00176 Point3 middle; 00177 middle.add(fromPoint, toPoint); 00178 middle.scale(0.5f); 00179 00180 BoundingBox bounds(middle, speedAndRadius); 00181 bounds.maxY_ = 10.0f; 00182 00183 ZoneAreaComponent::Ptr aZone(*this); 00184 ComponentList::Iterator linkIt(aZone->links()); 00185 while(linkIt.hasNext()) { 00186 ZoneAreaComponent& a = static_cast<ZoneAreaComponent&>(linkIt.next()); 00187 CollisionAreaComponent::Ptr cac(a); 00188 if(!cac->collisionGrid()) 00189 continue; 00190 00191 candidateCount += cac->collisionGrid()->collisionCandidates 00192 (middle, speedAndRadius, &candidates[candidateCount], MAX_CONTACTS - candidateCount); 00193 } 00194 00195 // Test collision with all collision candidates 00196 for(int c = 0; c < candidateCount; ++c) { 00197 // Test for collision 00198 CollisionComponent& cc = *candidates[ c ]; 00199 00200 if(!cc.doObstructView()) 00201 continue; 00202 00203 if(!bounds.isTouching(cc.areaCovered())) 00204 continue; 00205 00206 Point3 losNearest, candidateNearest; 00207 coor_t candRadius = cc.bouncePoints(fromPoint, toPoint, candidateNearest, losNearest); 00208 if(candidateNearest.xzDistanceSquared(losNearest) < candRadius * candRadius 00209 && fromPoint.xzDistanceSquared(losNearest) < (dist + candRadius) * (dist + candRadius) ) { 00210 dist = CoorT::sqrt(fromPoint.xzDistanceSquared(losNearest)); 00211 dist -= candRadius; 00212 } 00213 } 00214 return dist; 00215 } 00216 00217 00218 short CollisionAreaComponent 00219 ::_collisionCandidates(CollisionComponent *cc, const int MAX_THINGS, CollisionComponent *candidates[]) { 00220 ZoneAreaComponent::Ptr aZone(*this); 00221 short candidateCount = 0; 00222 ComponentList::Iterator linkIt(aZone->links()); 00223 while(linkIt.hasNext()) { 00224 ZoneAreaComponent& a = static_cast<ZoneAreaComponent&>(linkIt.next()); 00225 CollisionAreaComponent::Ptr cac(a); 00226 if(!cac->collisionGrid()) 00227 continue; 00228 00229 Point3 p; 00230 cc->areaCovered().center(p); 00231 coor_t speedAndRadius = cc->areaCovered().radius(); 00232 candidateCount += cac->collisionGrid()->collisionCandidates 00233 (p, speedAndRadius, &candidates[candidateCount], MAX_THINGS - candidateCount); 00234 } 00235 return candidateCount; 00236 } 00237 00238 00239 short CollisionAreaComponent 00240 ::_collisionCandidates2(CollisionComponent *cc, const int MAX_THINGS, CollisionComponent *candidates[]) { 00241 ZoneAreaComponent::Ptr aZone(*this); 00242 short candidateCount = 0; 00243 ComponentList::Iterator linkIt(aZone->links()); 00244 while(linkIt.hasNext()) { 00245 ZoneAreaComponent& a = static_cast<ZoneAreaComponent&>(linkIt.next()); 00246 CollisionAreaComponent::Ptr cac(a); 00247 NodeComponentList::TreeIterator it(cac->children()); 00248 while(it.hasNext()) { 00249 CollisionComponent::Ptr cc(it.next()); 00250 if(!cc->isCollideable()) 00251 continue; 00252 00253 candidates[ candidateCount++ ] = cc; 00254 } 00255 } 00256 return candidateCount; 00257 } 00258 00259 00260 void CollisionAreaComponent 00261 ::_testCollisionCandidates(CollisionComponent *cc, short candidateCount, CollisionComponent *candidates[], int maxCollisions, Contact *list, short& count) { 00262 // Test collision with all collision candidates 00263 for(int inner = 0; inner < candidateCount; ++inner) { 00264 // Don't test collision with self 00265 if(candidates[ inner ] == cc) { 00266 continue; 00267 } 00268 // Only collide once (and at least once) 00269 CollisionComponent& cc2 = *candidates[ inner ]; 00270 if(!cc2.isCollideable()) 00271 continue; 00272 //if((size_t)cc >= (size_t)(&cc2)) { 00273 //if(cc >= (&cc2)) { 00274 // continue; 00275 //} 00276 00277 00278 00279 // Test for collision 00280 if(!_testCollision(*cc, cc2)) { 00281 continue; 00282 } 00283 // Only collide once (and at least once) 00284 bool didAlready = false; 00285 for(int i = 0; i < count; ++i) { 00286 if(list[ i ].ci1_.cc_ == &cc2 && list[ i ].ci2_.cc_ == cc) { 00287 didAlready = true; 00288 break; 00289 } 00290 } 00291 00292 if(didAlready) { 00293 continue; 00294 } 00295 00296 Assert(count < maxCollisions); 00297 if(count > 64) { 00298 LogWarning(cc->owner()->name() << " - " << cc2.owner()->name()); 00299 } 00300 Contact& c = list[ count ]; 00301 c.ci1_.cc_ = cc; 00302 c.ci2_.cc_ = &cc2; 00303 c.ci1_.vp_.setViewPoint(cc->posComponent().nextPos().world_); 00304 c.ci2_.vp_.setViewPoint(cc2.posComponent().nextPos().world_); 00305 00306 // Calculate radius and bounce points 00307 Point3& d1 = c.ci1_.bouncePoint_; 00308 Point3& d2 = c.ci2_.bouncePoint_; 00309 c.ci1_.radius_ = cc->bouncePoint(c.ci1_.vp_.coor_, c.ci2_.vp_.coor_, d1); 00310 c.ci2_.radius_ = cc2.bouncePoint(c.ci2_.vp_.coor_, d1, d2); 00311 d1.y_ = d2.y_; 00312 c.radSum_ = c.ci1_.radius_ + c.ci2_.radius_; 00313 00314 ++count; 00315 } 00316 } 00317 00318 00319 void CollisionAreaComponent 00320 ::_testCollisionCandidates2(CollisionComponent *cc, short candidateCount, CollisionComponent *candidates[], int maxCollisions, Contact *list, short& count) { 00321 // Test collision with all collision candidates 00322 for(int inner = 0; inner < candidateCount; ++inner) { 00323 // Don't test collision with self 00324 if(candidates[ inner ] == cc) { 00325 continue; 00326 } 00327 CollisionComponent& cc2 = *candidates[ inner ]; 00328 //if(cc >= (&cc2)) { continue; } 00329 00330 // Test for collision 00331 if(!_testCollision(*cc, cc2)) { 00332 continue; 00333 } 00334 00335 // Only collide once (and at least once) 00336 bool didAlready = false; 00337 for(int i = 0; i < count; ++i) { 00338 if(list[ i ].ci1_.cc_ == &cc2 && list[ i ].ci2_.cc_ == cc) { 00339 didAlready = true; 00340 break; 00341 } 00342 } 00343 00344 if(didAlready) { 00345 continue; 00346 } 00347 00348 Assert(count < maxCollisions); 00349 if(count > 64) { 00350 LogWarning(cc->owner()->name() << " - " << cc2.owner()->name()); 00351 } 00352 Contact& c = list[ count ]; 00353 c.ci1_.cc_ = cc; 00354 c.ci2_.cc_ = &cc2; 00355 c.ci1_.vp_.setViewPoint(cc->posComponent().nextPos().world_); 00356 c.ci2_.vp_.setViewPoint(cc2.posComponent().nextPos().world_); 00357 00358 // Calculate radius and bounce points 00359 Point3& d1 = c.ci1_.bouncePoint_; 00360 Point3& d2 = c.ci2_.bouncePoint_; 00361 c.ci1_.radius_ = cc->bouncePoint(c.ci1_.vp_.coor_, c.ci2_.vp_.coor_, d1); 00362 c.ci2_.radius_ = cc2.bouncePoint(c.ci2_.vp_.coor_, d1, d2); 00363 d1.y_ = d2.y_; 00364 c.radSum_ = c.ci1_.radius_ + c.ci2_.radius_; 00365 00366 ++count; 00367 } 00368 } 00369 00370 00371 void CollisionAreaComponent 00372 ::getContactList(Contact* list, short& count, int maxCollisions) { 00373 Point3 tmp; 00374 00375 if(!collisionGrid_) 00376 return; 00377 00378 static const int MAX_THINGS = 256; 00379 static CollisionComponent* candidates[MAX_THINGS]; 00380 00381 //static Contact dummy[MAX_THINGS]; 00382 //short countCheck = count; 00383 NodeComponentList::TreeIterator it(children_); 00384 while(it.hasNext()) { 00385 CollisionComponent* cc = static_cast<CollisionComponent*>(&it.next()); 00386 if(!cc->isCollideable()) 00387 continue; 00388 00389 //short candidateCount = _collisionCandidates(cc, MAX_THINGS, candidates); 00390 short candidateCount = _collisionCandidates2(cc, MAX_THINGS, candidates); 00391 _testCollisionCandidates(cc, candidateCount, candidates, maxCollisions, list, count); 00392 00393 //candidateCount = _collisionCandidates2(cc, MAX_THINGS, candidates); 00394 //_testCollisionCandidates2(cc, candidateCount, candidates, MAX_THINGS, dummy, countCheck); 00395 00396 //AssertFatal(count == countCheck, "Collision check error: " << count << " != " << countCheck << " - " << cc->owner()->name()); 00397 } 00398 } 00399 00400 }
Home Page | SagaEngine trunk (updated nightly) reference generated Sun Dec 2 20:06:11 2007 by Doxygen version 1.3.9.1.