| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// 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.//-----------------------------------------------------------------------------#include "platform/platform.h"#include "collision/convex.h"#include "platform/types.h"#include "core/dataChunker.h"#include "collision/collision.h"#include "scene/sceneObject.h"#include "scene/sceneContainer.h"#include "collision/gjk.h"#include "collision/concretePolyList.h"#include "platform/profiler.h"//----------------------------------------------------------------------------//----------------------------------------------------------------------------static DataChunker sChunker;CollisionStateList CollisionStateList::sFreeList;CollisionWorkingList CollisionWorkingList::sFreeList;F32 sqrDistanceEdges(const Point3F& start0,                     const Point3F& end0,                     const Point3F& start1,                     const Point3F& end1,                     Point3F*       is,                     Point3F*       it);//----------------------------------------------------------------------------// Collision State//----------------------------------------------------------------------------CollisionState::CollisionState(){   mB = mA = NULL;   mDist = 0.0f;   mListb = mLista = 0;}CollisionState::~CollisionState(){   if (mLista)      mLista->free();   if (mListb)      mListb->free();}void CollisionState::swap(){}void CollisionState::set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w){}F32 CollisionState::distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist,                       const MatrixF* w2a, const MatrixF* _w2b){   return 0;}//----------------------------------------------------------------------------// Feature Collision//----------------------------------------------------------------------------void ConvexFeature::reset(){   material = NULL;   mObject = NULL;   mVertexList.clear();   mEdgeList.clear();   mFaceList.clear();}bool ConvexFeature::collide(ConvexFeature& cf,CollisionList* cList, F32 tol){   // Our vertices vs. other faces   const Point3F* vert = mVertexList.begin();   const Point3F* vend = mVertexList.end();   while (vert != vend) {      cf.testVertex(*vert,cList,false, tol);      vert++;   }   // Other vertices vs. our faces   vert = cf.mVertexList.begin();   vend = cf.mVertexList.end();   while (vert != vend) {      U32 storeCount = cList->getCount();      testVertex(*vert,cList,true, tol);      // Fix up last reference.  material and object are copied from this rather      //  than the object we're colliding against.      if (storeCount != cList->getCount())       {         Collision &col = (*cList)[cList->getCount() - 1];         col.material = cf.material;         col.object   = cf.mObject;      }      vert++;   }   // Edge vs. Edge   const Edge* edge = mEdgeList.begin();   const Edge* eend = mEdgeList.end();   while (edge != eend) {      cf.testEdge(this,mVertexList[edge->vertex[0]],         mVertexList[edge->vertex[1]],cList, tol);      edge++;   }   return true;}inline bool isInside(const Point3F& p, const Point3F& a, const Point3F& b, const VectorF& n){   VectorF v;   mCross(n,b - a,&v);   return mDot(v,p - a) < 0.0f;}void ConvexFeature::testVertex(const Point3F& v,CollisionList* cList,bool flip, F32 tol){   // Test vertex against all faces   const Face* face = mFaceList.begin();   const Face* end  = mFaceList.end();   for (; face != end; face++) {      if (cList->getCount() >= CollisionList::MaxCollisions)         return;      const Point3F& p0 = mVertexList[face->vertex[0]];      const Point3F& p1 = mVertexList[face->vertex[1]];      const Point3F& p2 = mVertexList[face->vertex[2]];      // Point near the plane?      F32 distance = mDot(face->normal,v - p0);      if (distance > tol || distance < -tol)         continue;      // Make sure it's within the bounding edges      if (isInside(v,p0,p1,face->normal) && isInside(v,p1,p2,face->normal) &&            isInside(v,p2,p0,face->normal)) {         // Add collision to this face         Collision& info = cList->increment();         info.point = v;         info.normal = face->normal;         if (flip)            info.normal.neg();         info.material = material;         info.object = mObject;         info.distance = distance;      }   }}void ConvexFeature::testEdge(ConvexFeature* cf,const Point3F& s1, const Point3F& e1, CollisionList* cList, F32 tol){   F32 tolSquared = tol*tol;   // Test edges against edges   const Edge* edge = mEdgeList.begin();   const Edge* end  = mEdgeList.end();   for (; edge != end; edge++) {      if (cList->getCount() >= CollisionList::MaxCollisions)         return;      const Point3F& s2 = mVertexList[edge->vertex[0]];      const Point3F& e2 = mVertexList[edge->vertex[1]];      // Get the distance and closest points      Point3F i1,i2;      F32 distance = sqrDistanceEdges(s1, e1, s2, e2, &i1, &i2);      if (distance > tolSquared)         continue;      distance = mSqrt(distance);      // Need to figure out how to orient the collision normal.      // The current test involves checking to see whether the collision      // points are contained within the convex volumes, which is slow.      if (inVolume(i1) || cf->inVolume(i2))         distance = -distance;      // Contact normal      VectorF normal = i1 - i2;      if ( mIsZero( distance ) )         normal.zero();      else         normal *= 1 / distance;      // Return a collision      Collision& info = cList->increment();      info.point    = i1;      info.normal   = normal;      info.distance = distance;      info.material = material;      info.object   = mObject;   }}bool ConvexFeature::inVolume(const Point3F& v){   // Test the point to see if it's inside the volume   const Face* face = mFaceList.begin();   const Face* end  = mFaceList.end();   for (; face != end; face++) {      const Point3F& p0 = mVertexList[face->vertex[0]];      if (mDot(face->normal,v - p0) > 0)         return false;   }   return true;}//----------------------------------------------------------------------------// Collision State management//----------------------------------------------------------------------------//----------------------------------------------------------------------------CollisionStateList::CollisionStateList(){   mPrev = mNext = this;   mState = NULL;}void CollisionStateList::linkAfter(CollisionStateList* ptr){   mPrev = ptr;   mNext = ptr->mNext;   ptr->mNext = this;   mNext->mPrev = this;}void CollisionStateList::unlink(){   mPrev->mNext = mNext;   mNext->mPrev = mPrev;   mPrev = mNext = this;}CollisionStateList* CollisionStateList::alloc(){   if (!sFreeList.isEmpty()) {      CollisionStateList* nxt = sFreeList.mNext;      nxt->unlink();      nxt->mState = NULL;      return nxt;   }   return constructInPlace((CollisionStateList*)sChunker.alloc(sizeof(CollisionStateList)));}void CollisionStateList::free(){   unlink();   linkAfter(&sFreeList);}//----------------------------------------------------------------------------CollisionWorkingList::CollisionWorkingList(){   wLink.mPrev = wLink.mNext = this;   rLink.mPrev = rLink.mNext = this;   mConvex = NULL;}void CollisionWorkingList::wLinkAfter(CollisionWorkingList* ptr){   wLink.mPrev = ptr;   wLink.mNext = ptr->wLink.mNext;   ptr->wLink.mNext = this;   wLink.mNext->wLink.mPrev = this;}void CollisionWorkingList::rLinkAfter(CollisionWorkingList* ptr){   rLink.mPrev = ptr;   rLink.mNext = ptr->rLink.mNext;   ptr->rLink.mNext = this;   rLink.mNext->rLink.mPrev = this;}void CollisionWorkingList::unlink(){   wLink.mPrev->wLink.mNext = wLink.mNext;   wLink.mNext->wLink.mPrev = wLink.mPrev;   wLink.mPrev = wLink.mNext = this;   rLink.mPrev->rLink.mNext = rLink.mNext;   rLink.mNext->rLink.mPrev = rLink.mPrev;   rLink.mPrev = rLink.mNext = this;}CollisionWorkingList* CollisionWorkingList::alloc(){   if (sFreeList.wLink.mNext != &sFreeList) {      CollisionWorkingList* nxt = sFreeList.wLink.mNext;      nxt->unlink();      return nxt;   }   return constructInPlace((CollisionWorkingList*)sChunker.alloc(sizeof(CollisionWorkingList)));}void CollisionWorkingList::free(){   unlink();   wLinkAfter(&sFreeList);}//----------------------------------------------------------------------------// Convex Base Class//----------------------------------------------------------------------------U32 Convex::sTag = (U32)-1;//----------------------------------------------------------------------------Convex::Convex(){   mNext = mPrev = this;   mTag = 0;   mObject = NULL;   mType = ConvexType::BoxConvexType;}Convex::~Convex(){   // Unlink from Convex Database   unlink();   // Delete collision states   while (mList.mNext != &mList)      delete mList.mNext->mState;   // Free up working list   while (mWorking.wLink.mNext != &mWorking)      mWorking.wLink.mNext->free();   // Free up references   while (mReference.rLink.mNext != &mReference)      mReference.rLink.mNext->free();}//----------------------------------------------------------------------------void Convex::collectGarbage(){   // Delete unreferenced Convex Objects   for (Convex* itr = mNext; itr != this; itr = itr->mNext) {      if (itr->mReference.rLink.mNext == &itr->mReference) {         Convex* ptr = itr;         itr = itr->mPrev;         delete ptr;      }   }}void Convex::nukeList(){   // Delete all Convex Objects   for (Convex* itr = mNext; itr != this; itr = itr->mNext) {      Convex* ptr = itr;      itr = itr->mPrev;      delete ptr;   }}void Convex::registerObject(Convex *convex){   convex->linkAfter(this);}//----------------------------------------------------------------------------void Convex::linkAfter(Convex* ptr){   mPrev = ptr;   mNext = ptr->mNext;   ptr->mNext = this;   mNext->mPrev = this;}void Convex::unlink(){   mPrev->mNext = mNext;   mNext->mPrev = mPrev;   mPrev = mNext = this;}//----------------------------------------------------------------------------Point3F Convex::support(const VectorF&) const{   return Point3F(0,0,0);}void Convex::getFeatures(const MatrixF&,const VectorF&,ConvexFeature* f){   f->mObject = NULL;}const MatrixF& Convex::getTransform() const{   return mObject->getTransform();}const Point3F& Convex::getScale() const{   return mObject->getScale();}Box3F Convex::getBoundingBox() const{   return mObject->getWorldBox();}Box3F Convex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const{   Box3F wBox = mObject->getObjBox();   wBox.minExtents.convolve(scale);   wBox.maxExtents.convolve(scale);   mat.mul(wBox);   return wBox;}//----------------------------------------------------------------------------void Convex::addToWorkingList(Convex* ptr){   CollisionWorkingList* cl = CollisionWorkingList::alloc();   cl->wLinkAfter(&mWorking);   cl->rLinkAfter(&ptr->mReference);   cl->mConvex = ptr;};//----------------------------------------------------------------------------void Convex::updateWorkingList(const Box3F& box, const U32 colMask){   PROFILE_SCOPE( Convex_UpdateWorkingList );   sTag++;   // Clear objects off the working list that are no longer intersecting   for (CollisionWorkingList* itr = mWorking.wLink.mNext; itr != &mWorking; itr = itr->wLink.mNext) {      itr->mConvex->mTag = sTag;      if ((!box.isOverlapped(itr->mConvex->getBoundingBox())) || (!itr->mConvex->getObject()->isCollisionEnabled())) {         CollisionWorkingList* cl = itr;         itr = itr->wLink.mPrev;         cl->free();      }   }   // Special processing for the terrain and interiors...   AssertFatal(mObject->getContainer(), "Must be in a container!");   SimpleQueryList sql;   mObject->getContainer()->findObjects(box, colMask,SimpleQueryList::insertionCallback, &sql);   for (U32 i = 0; i < sql.mList.size(); i++)      sql.mList[i]->buildConvex(box, this);}void Convex::clearWorkingList(){   PROFILE_SCOPE( Convex_ClearWorkingList );   sTag++;   for (CollisionWorkingList* itr = mWorking.wLink.mNext; itr != &mWorking; itr = itr->wLink.mNext)   {      itr->mConvex->mTag = sTag;      CollisionWorkingList* cl = itr;      itr = itr->wLink.mPrev;      cl->free();   }}// ---------------------------------------------------------------------------void Convex::updateStateList(const MatrixF& mat, const Point3F& scale, const Point3F* displacement){   PROFILE_SCOPE( Convex_UpdateStateList );   Box3F box1 = getBoundingBox(mat, scale);   box1.minExtents -= Point3F(1, 1, 1);   box1.maxExtents += Point3F(1, 1, 1);   if (displacement) {      Point3F oldMin = box1.minExtents;      Point3F oldMax = box1.maxExtents;      box1.minExtents.setMin(oldMin + *displacement);      box1.minExtents.setMin(oldMax + *displacement);      box1.maxExtents.setMax(oldMin + *displacement);      box1.maxExtents.setMax(oldMax + *displacement);   }   sTag++;   // Destroy states which are no longer intersecting   for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) {      Convex* cv = (itr->mState->mA == this)? itr->mState->mB: itr->mState->mA;      cv->mTag = sTag;      if (!box1.isOverlapped(cv->getBoundingBox())) {         CollisionState* cs = itr->mState;         itr = itr->mPrev;         delete cs;      }   }   // Add collision states for new overlapping objects   for (CollisionWorkingList* itr0 = mWorking.wLink.mNext; itr0 != &mWorking; itr0 = itr0->wLink.mNext) {      Convex* cv = itr0->mConvex;      if (cv->mTag != sTag && box1.isOverlapped(cv->getBoundingBox())) {         CollisionState* state = new GjkCollisionState;         state->set(this,cv,mat,cv->getTransform());         state->mLista->linkAfter(&mList);         state->mListb->linkAfter(&cv->mList);      }   }}//----------------------------------------------------------------------------CollisionState* Convex::findClosestState(const MatrixF& mat, const Point3F& scale, const F32 dontCareDist){   PROFILE_SCOPE( Convex_FindClosestState );   updateStateList(mat, scale);   F32 dist = +1E30f;   CollisionState *st = 0;   // Prepare scaled version of transform   MatrixF axform = mat;   axform.scale(scale);   MatrixF axforminv(true);   MatrixF temp(mat);   axforminv.scale(Point3F(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z));   temp.affineInverse();   axforminv.mul(temp);   for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext)    {      CollisionState* state = itr->mState;      if (state->mLista != itr)         state->swap();      // Prepare scaled version of transform      MatrixF bxform = state->mB->getTransform();      temp = bxform;      Point3F bscale = state->mB->getScale();      bxform.scale(bscale);      MatrixF bxforminv(true);      bxforminv.scale(Point3F(1.0f/bscale.x, 1.0f/bscale.y, 1.0f/bscale.z));      temp.affineInverse();      bxforminv.mul(temp);      //      F32 dd = state->distance(axform, bxform, dontCareDist, &axforminv, &bxforminv);      if (dd < dist)       {         dist = dd;         st = state;      }   }   if (dist < dontCareDist)      return st;   else      return NULL;}//----------------------------------------------------------------------------bool Convex::getCollisionInfo(const MatrixF& mat, const Point3F& scale, CollisionList* cList,F32 tol){   PROFILE_SCOPE( Convex_GetCollisionInfo );   // Making these static prevents needless Vector resizing that occurs   // in the ConvexFeature constructor.   static ConvexFeature fa;   static ConvexFeature fb;   for ( CollisionStateList* itr = mList.mNext;          itr != &mList;          itr = itr->mNext)    {      CollisionState* state = itr->mState;      if (state->mLista != itr)         state->swap();      if (state->mDist <= tol)       {         fa.reset();         fb.reset();         VectorF v;         // The idea is that we need to scale the matrix, so we need to         // make a copy of it, before we can pass it in to getFeatures.         // This is used to scale us for comparison against the other         // convex, which is correctly scaled.         MatrixF omat = mat;         omat.scale(scale);         MatrixF imat = omat;         imat.inverse();         imat.mulV(-state->mDistvec,&v);         getFeatures(omat,v,&fa);         imat = state->mB->getTransform();         imat.scale(state->mB->getScale());         MatrixF bxform = imat;         imat.inverse();         imat.mulV(state->mDistvec,&v);         state->mB->getFeatures(bxform,v,&fb);         fa.collide(fb,cList,tol);      }   }   return (cList->getCount() != 0);}void Convex::getPolyList(AbstractPolyList*){}void Convex::renderWorkingList(){   //bool rendered = false;   CollisionWorkingList& rList = getWorkingList();   CollisionWorkingList* pList = rList.wLink.mNext;   while (pList != &rList) {      Convex* pConvex = pList->mConvex;      pConvex->render();      //rendered = true;      pList = pList->wLink.mNext;   }   //Con::warnf( "convex rendered - %s", rendered ? "YES" : "NO" );}void Convex::render(){   ConcretePolyList polyList;   getPolyList( &polyList );   polyList.render();}//-----------------------------------------------------------------------------// This function based on code originally written for the book:// 3D Game Engine Design, by David H. Eberly//F32 sqrDistanceEdges(const Point3F& start0, const Point3F& end0,   const Point3F& start1, const Point3F& end1,   Point3F* is, Point3F* it){   Point3F direction0 = end0 - start0;   F32 fA00 = direction0.lenSquared();   Point3F direction1 = end1 - start1;   F32 fA11 = direction1.lenSquared();   F32 fA01 = -mDot(direction0, direction1);   Point3F kDiff = start0 - start1;   F32 fC   = kDiff.lenSquared();   F32 fB0  = mDot(kDiff, direction0);   F32 fDet = mFabs(fA00*fA11 - fA01*fA01);   // Since the endpoints are tested as vertices, we're not interested   // in parallel lines, and intersections that don't involve end-points.   if (fDet >= 0.00001) {      // Calculate time of intersection for each line      F32 fB1 = -mDot(kDiff, direction1);      F32 fS  = fA01*fB1-fA11*fB0;      F32 fT  = fA01*fB0-fA00*fB1;      // Only interested in collisions that don't involve the end points      if (fS >= 0.0 && fS <= fDet && fT >= 0.0 && fT <= fDet) {         F32 fInvDet = 1.0 / fDet;         fS *= fInvDet;         fT *= fInvDet;         F32 fSqrDist = (fS*(fA00*fS + fA01*fT + 2.0*fB0) +            fT*(fA01*fS + fA11*fT + 2.0*fB1) + fC);         // Intersection points.         *is = start0 + direction0 * fS;         *it = start1 + direction1 * fT;         return mFabs(fSqrDist);      }   }   // Return a large number in the cases where endpoints are involved.   return 1e10f;}
 |