CollisionAreaComponent.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 #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.

SourceForge.net Logo