123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730 |
- //-----------------------------------------------------------------------------
- // 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 "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;
- }
|