123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873 |
- //-----------------------------------------------------------------------------
- // 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 "T3D/components/collision/collisionComponent.h"
- #include "scene/sceneObject.h"
- #include "T3D/entity.h"
- #include "console/engineAPI.h"
- #include "T3D/trigger.h"
- #include "materials/baseMatInstance.h"
- #include "collision/extrudedPolyList.h"
- #include "opcode/Opcode.h"
- #include "opcode/Ice/IceAABB.h"
- #include "opcode/Ice/IcePoint.h"
- #include "opcode/OPC_AABBTree.h"
- #include "opcode/OPC_AABBCollider.h"
- #include "collision/clippedPolyList.h"
- static F32 sTractionDistance = 0.04f;
- IMPLEMENT_CONOBJECT(CollisionComponent);
- CollisionComponent::CollisionComponent() : Component()
- {
- mFriendlyName = "Collision Component";
- mComponentType = "Collision";
- mDescription = getDescriptionText("A stub component class that collision components should inherit from.");
- mBlockColliding = true;
- CollisionMoveMask = (TerrainObjectType | PlayerObjectType |
- StaticShapeObjectType | VehicleObjectType |
- VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType);
- mPhysicsRep = nullptr;
- mPhysicsWorld = nullptr;
- mTimeoutList = nullptr;
- }
- CollisionComponent::~CollisionComponent()
- {
- for (S32 i = 0; i < mFields.size(); ++i)
- {
- ComponentField &field = mFields[i];
- SAFE_DELETE_ARRAY(field.mFieldDescription);
- }
- SAFE_DELETE_ARRAY(mDescription);
- SAFE_DELETE(mPhysicsRep);
- }
- bool CollisionComponent::checkCollisions(const F32 travelTime, Point3F *velocity, Point3F start)
- {
- return false;
- }
- bool CollisionComponent::updateCollisions(F32 time, VectorF vector, VectorF velocity)
- {
- return false;
- }
- void CollisionComponent::updateWorkingCollisionSet(const U32 mask)
- {
- }
- void CollisionComponent::handleCollisionList( CollisionList &collisionList, VectorF velocity )
- {
- Collision bestCol;
- mCollisionList = collisionList;
- for (U32 i=0; i < collisionList.getCount(); ++i)
- {
- Collision& colCheck = collisionList[i];
- if (colCheck.object)
- {
- if (colCheck.object->getTypeMask() & PlayerObjectType)
- {
- handleCollision( colCheck, velocity );
- }
- else if (colCheck.object->getTypeMask() & TriggerObjectType)
- {
- // We've hit it's bounding box, that's close enough for triggers
- Trigger* pTrigger = static_cast<Trigger*>(colCheck.object);
- Component *comp = dynamic_cast<Component*>(this);
- pTrigger->potentialEnterObject(comp->getOwner());
- }
- else if (colCheck.object->getTypeMask() & DynamicShapeObjectType)
- {
- Con::printf("HIT A GENERICALLY DYNAMIC OBJECT");
- handleCollision(colCheck, velocity);
- }
- else if(colCheck.object->getTypeMask() & EntityObjectType)
- {
- Entity* ent = dynamic_cast<Entity*>(colCheck.object);
- if (ent)
- {
- CollisionComponent *colObjectInterface = ent->getComponent<CollisionComponent>();
- if (colObjectInterface)
- {
- //convert us to our component
- Component *thisComp = dynamic_cast<Component*>(this);
- if (thisComp)
- {
- colObjectInterface->onCollisionSignal.trigger(thisComp->getOwner());
- //TODO: properly do this
- Collision oppositeCol = colCheck;
- oppositeCol.object = thisComp->getOwner();
- colObjectInterface->handleCollision(oppositeCol, velocity);
- }
- }
- }
- }
- else
- {
- handleCollision(colCheck, velocity);
- }
- }
- }
- }
- void CollisionComponent::handleCollision( Collision &col, VectorF velocity )
- {
- if (col.object && (mContactInfo.contactObject == NULL ||
- col.object->getId() != mContactInfo.contactObject->getId()))
- {
- queueCollision(col.object, velocity - col.object->getVelocity());
- //do the callbacks to script for this collision
- Component *comp = dynamic_cast<Component*>(this);
- if (comp->isMethod("onCollision"))
- {
- S32 matId = col.material != NULL ? col.material->getMaterial()->getId() : 0;
- Con::executef(comp, "onCollision", col.object, col.normal, col.point, matId, velocity);
- }
- if (comp->getOwner()->isMethod("onCollisionEvent"))
- {
- S32 matId = col.material != NULL ? col.material->getMaterial()->getId() : 0;
- Con::executef(comp->getOwner(), "onCollisionEvent", col.object, col.normal, col.point, matId, velocity);
- }
- }
- }
- void CollisionComponent::handleCollisionNotifyList()
- {
- //special handling for any collision components we should notify that a collision happened.
- for (U32 i = 0; i < mCollisionNotifyList.size(); ++i)
- {
- //convert us to our component
- Component *thisComp = dynamic_cast<Component*>(this);
- if (thisComp)
- {
- mCollisionNotifyList[i]->onCollisionSignal.trigger(thisComp->getOwner());
- }
- }
- mCollisionNotifyList.clear();
- }
- Chunker<CollisionComponent::CollisionTimeout> sCollisionTimeoutChunker;
- CollisionComponent::CollisionTimeout* CollisionComponent::sFreeTimeoutList = 0;
- void CollisionComponent::queueCollision( SceneObject *obj, const VectorF &vec)
- {
- // Add object to list of collisions.
- SimTime time = Sim::getCurrentTime();
- S32 num = obj->getId();
- CollisionTimeout** adr = &mTimeoutList;
- CollisionTimeout* ptr = mTimeoutList;
- while (ptr)
- {
- if (ptr->objectNumber == num)
- {
- if (ptr->expireTime < time)
- {
- ptr->expireTime = time + CollisionTimeoutValue;
- ptr->object = obj;
- ptr->vector = vec;
- }
- return;
- }
- // Recover expired entries
- if (ptr->expireTime < time)
- {
- CollisionTimeout* cur = ptr;
- *adr = ptr->next;
- ptr = ptr->next;
- cur->next = sFreeTimeoutList;
- sFreeTimeoutList = cur;
- }
- else
- {
- adr = &ptr->next;
- ptr = ptr->next;
- }
- }
- // New entry for the object
- if (sFreeTimeoutList != NULL)
- {
- ptr = sFreeTimeoutList;
- sFreeTimeoutList = ptr->next;
- ptr->next = NULL;
- }
- else
- {
- ptr = sCollisionTimeoutChunker.alloc();
- }
- ptr->object = obj;
- ptr->objectNumber = obj->getId();
- ptr->vector = vec;
- ptr->expireTime = time + CollisionTimeoutValue;
- ptr->next = mTimeoutList;
- mTimeoutList = ptr;
- }
- bool CollisionComponent::checkEarlyOut(Point3F start, VectorF velocity, F32 time, Box3F objectBox, Point3F objectScale,
- Box3F collisionBox, U32 collisionMask, CollisionWorkingList &colWorkingList)
- {
- Point3F end = start + velocity * time;
- Point3F distance = end - start;
- Box3F scaledBox = objectBox;
- scaledBox.minExtents.convolve(objectScale);
- scaledBox.maxExtents.convolve(objectScale);
- if (mFabs(distance.x) < objectBox.len_x() &&
- mFabs(distance.y) < objectBox.len_y() &&
- mFabs(distance.z) < objectBox.len_z())
- {
- // We can potentially early out of this. If there are no polys in the clipped polylist at our
- // end position, then we can bail, and just set start = end;
- Box3F wBox = scaledBox;
- wBox.minExtents += end;
- wBox.maxExtents += end;
- static EarlyOutPolyList eaPolyList;
- eaPolyList.clear();
- eaPolyList.mNormal.set(0.0f, 0.0f, 0.0f);
- eaPolyList.mPlaneList.clear();
- eaPolyList.mPlaneList.setSize(6);
- eaPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1.0f, 0.0f, 0.0f));
- eaPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0.0f, 1.0f, 0.0f));
- eaPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1.0f, 0.0f, 0.0f));
- eaPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0.0f, -1.0f, 0.0f));
- eaPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0.0f, 0.0f, -1.0f));
- eaPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0.0f, 0.0f, 1.0f));
- // Build list from convex states here...
- CollisionWorkingList& rList = colWorkingList;
- CollisionWorkingList* pList = rList.wLink.mNext;
- while (pList != &rList)
- {
- Convex* pConvex = pList->mConvex;
- if (pConvex->getObject()->getTypeMask() & collisionMask)
- {
- Box3F convexBox = pConvex->getBoundingBox();
- if (wBox.isOverlapped(convexBox))
- {
- // No need to separate out the physical zones here, we want those
- // to cause a fallthrough as well...
- pConvex->getPolyList(&eaPolyList);
- }
- }
- pList = pList->wLink.mNext;
- }
- if (eaPolyList.isEmpty())
- {
- return true;
- }
- }
- return false;
- }
- Collision* CollisionComponent::getCollision(S32 col)
- {
- if(col < mCollisionList.getCount() && col >= 0)
- return &mCollisionList[col];
- else
- return NULL;
- }
- Point3F CollisionComponent::getContactNormal()
- {
- return mContactInfo.contactNormal;
- }
- bool CollisionComponent::hasContact()
- {
- if (mContactInfo.contactObject)
- return true;
- else
- return false;
- }
- S32 CollisionComponent::getCollisionCount()
- {
- return mCollisionList.getCount();
- }
- Point3F CollisionComponent::getCollisionNormal(S32 collisionIndex)
- {
- if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
- return Point3F::Zero;
- return mCollisionList[collisionIndex].normal;
- }
- F32 CollisionComponent::getCollisionAngle(S32 collisionIndex, Point3F upVector)
- {
- if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
- return 0.0f;
- return mRadToDeg(mAcos(mDot(mCollisionList[collisionIndex].normal, upVector)));
- }
- S32 CollisionComponent::getBestCollision(Point3F upVector)
- {
- S32 bestCollision = -1;
- F32 bestAngle = 360.f;
- S32 count = mCollisionList.getCount();
- for (U32 i = 0; i < count; ++i)
- {
- F32 angle = mRadToDeg(mAcos(mDot(mCollisionList[i].normal, upVector)));
- if (angle < bestAngle)
- {
- bestCollision = i;
- bestAngle = angle;
- }
- }
- return bestCollision;
- }
- F32 CollisionComponent::getBestCollisionAngle(VectorF upVector)
- {
- S32 bestCol = getBestCollision(upVector);
- if (bestCol == -1)
- return 0;
- return getCollisionAngle(bestCol, upVector);
- }
- //
- bool CollisionComponent::buildConvexOpcode(TSShapeInstance* sI, S32 dl, const Box3F &bounds, Convex *c, Convex *list)
- {
- AssertFatal(dl >= 0 && dl < sI->getShape()->details.size(), "TSShapeInstance::buildConvexOpcode");
- TSShape* shape = sI->getShape();
- const MatrixF &objMat = mOwner->getObjToWorld();
- const Point3F &objScale = mOwner->getScale();
- // get subshape and object detail
- const TSDetail * detail = &shape->details[dl];
- S32 ss = detail->subShapeNum;
- S32 od = detail->objectDetailNum;
- // nothing emitted yet...
- bool emitted = false;
- S32 start = shape->subShapeFirstObject[ss];
- S32 end = shape->subShapeNumObjects[ss] + start;
- if (start<end)
- {
- MatrixF initialMat = objMat;
- Point3F initialScale = objScale;
- // set up for first object's node
- MatrixF mat;
- MatrixF scaleMat(true);
- F32* p = scaleMat;
- p[0] = initialScale.x;
- p[5] = initialScale.y;
- p[10] = initialScale.z;
- const MatrixF * previousMat = &sI->mMeshObjects[start].getTransform();
- mat.mul(initialMat, scaleMat);
- mat.mul(*previousMat);
- // Update our bounding box...
- Box3F localBox = bounds;
- MatrixF otherMat = mat;
- otherMat.inverse();
- otherMat.mul(localBox);
- // run through objects and collide
- for (S32 i = start; i<end; i++)
- {
- TSShapeInstance::MeshObjectInstance * meshInstance = &sI->mMeshObjects[i];
- if (od >= meshInstance->object->numMeshes)
- continue;
- if (&meshInstance->getTransform() != previousMat)
- {
- // different node from before, set up for this node
- previousMat = &meshInstance->getTransform();
- if (previousMat != NULL)
- {
- mat.mul(initialMat, scaleMat);
- mat.mul(*previousMat);
- // Update our bounding box...
- otherMat = mat;
- otherMat.inverse();
- localBox = bounds;
- otherMat.mul(localBox);
- }
- }
- // collide... note we pass the original mech transform
- // here so that the convex data returned is in mesh space.
- TSMesh * mesh = meshInstance->getMesh(od);
- if (mesh && !meshInstance->forceHidden && meshInstance->visible > 0.01f && localBox.isOverlapped(mesh->getBounds()))
- emitted |= buildMeshOpcode(mesh, *previousMat, localBox, c, list);
- else
- emitted |= false;
- }
- }
- return emitted;
- }
- bool CollisionComponent::buildMeshOpcode(TSMesh *mesh, const MatrixF &meshToObjectMat,
- const Box3F &nodeBox, Convex *convex, Convex *list)
- {
- /*PROFILE_SCOPE(MeshCollider_buildConvexOpcode);
- // This is small... there is no win for preallocating it.
- Opcode::AABBCollider opCollider;
- opCollider.SetPrimitiveTests(true);
- // This isn't really needed within the AABBCollider as
- // we don't use temporal coherance... use a static to
- // remove the allocation overhead.
- static Opcode::AABBCache opCache;
- IceMaths::AABB opBox;
- opBox.SetMinMax(Point(nodeBox.minExtents.x, nodeBox.minExtents.y, nodeBox.minExtents.z),
- Point(nodeBox.maxExtents.x, nodeBox.maxExtents.y, nodeBox.maxExtents.z));
- Opcode::CollisionAABB opCBox(opBox);
- if (!opCollider.Collide(opCache, opCBox, *mesh->mOptTree))
- return false;
- U32 cnt = opCollider.GetNbTouchedPrimitives();
- const udword *idx = opCollider.GetTouchedPrimitives();
- Opcode::VertexPointers vp;
- for (S32 i = 0; i < cnt; i++)
- {
- // First, check our active convexes for a potential match (and clean things
- // up, too.)
- const U32 curIdx = idx[i];
- // See if the square already exists as part of the working set.
- bool gotMatch = false;
- CollisionWorkingList& wl = convex->getWorkingList();
- for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext)
- {
- if (itr->mConvex->getType() != TSPolysoupConvexType)
- continue;
- const MeshColliderPolysoupConvex *chunkc = static_cast<MeshColliderPolysoupConvex*>(itr->mConvex);
- if (chunkc->getObject() != mOwner)
- continue;
- if (chunkc->mesh != mesh)
- continue;
- if (chunkc->idx != curIdx)
- continue;
- // A match! Don't need to add it.
- gotMatch = true;
- break;
- }
- if (gotMatch)
- continue;
- // Get the triangle...
- mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, idx[i]);
- Point3F a(vp.Vertex[0]->x, vp.Vertex[0]->y, vp.Vertex[0]->z);
- Point3F b(vp.Vertex[1]->x, vp.Vertex[1]->y, vp.Vertex[1]->z);
- Point3F c(vp.Vertex[2]->x, vp.Vertex[2]->y, vp.Vertex[2]->z);
- // Transform the result into object space!
- meshToObjectMat.mulP(a);
- meshToObjectMat.mulP(b);
- meshToObjectMat.mulP(c);
- //If we're not doing debug rendering on the client, then set up our convex list as normal
- PlaneF p(c, b, a);
- Point3F peak = ((a + b + c) / 3.0f) - (p * 0.15f);
- // Set up the convex...
- MeshColliderPolysoupConvex *cp = new MeshColliderPolysoupConvex();
- list->registerObject(cp);
- convex->addToWorkingList(cp);
- cp->mesh = mesh;
- cp->idx = curIdx;
- cp->mObject = mOwner;
- cp->normal = p;
- cp->verts[0] = a;
- cp->verts[1] = b;
- cp->verts[2] = c;
- cp->verts[3] = peak;
- // Update the bounding box.
- Box3F &bounds = cp->box;
- bounds.minExtents.set(F32_MAX, F32_MAX, F32_MAX);
- bounds.maxExtents.set(-F32_MAX, -F32_MAX, -F32_MAX);
- bounds.minExtents.setMin(a);
- bounds.minExtents.setMin(b);
- bounds.minExtents.setMin(c);
- bounds.minExtents.setMin(peak);
- bounds.maxExtents.setMax(a);
- bounds.maxExtents.setMax(b);
- bounds.maxExtents.setMax(c);
- bounds.maxExtents.setMax(peak);
- }
- return true;*/
- return false;
- }
- bool CollisionComponent::castRayOpcode(S32 dl, const Point3F & startPos, const Point3F & endPos, RayInfo *info)
- {
- // if dl==-1, nothing to do
- //if (dl == -1 || !getShapeInstance())
- return false;
- /*TSShape *shape = getShapeInstance()->getShape();
- AssertFatal(dl >= 0 && dl < shape->details.size(), "TSShapeInstance::castRayOpcode");
- info->t = 100.f;
- // get subshape and object detail
- const TSDetail * detail = &shape->details[dl];
- S32 ss = detail->subShapeNum;
- if (ss < 0)
- return false;
- S32 od = detail->objectDetailNum;
- // nothing emitted yet...
- bool emitted = false;
- const MatrixF* saveMat = NULL;
- S32 start = shape->subShapeFirstObject[ss];
- S32 end = shape->subShapeNumObjects[ss] + start;
- if (start<end)
- {
- MatrixF mat;
- const MatrixF * previousMat = &getShapeInstance()->mMeshObjects[start].getTransform();
- mat = *previousMat;
- mat.inverse();
- Point3F localStart, localEnd;
- mat.mulP(startPos, &localStart);
- mat.mulP(endPos, &localEnd);
- // run through objects and collide
- for (S32 i = start; i<end; i++)
- {
- TSShapeInstance::MeshObjectInstance * meshInstance = &getShapeInstance()->mMeshObjects[i];
- if (od >= meshInstance->object->numMeshes)
- continue;
- if (&meshInstance->getTransform() != previousMat)
- {
- // different node from before, set up for this node
- previousMat = &meshInstance->getTransform();
- if (previousMat != NULL)
- {
- mat = *previousMat;
- mat.inverse();
- mat.mulP(startPos, &localStart);
- mat.mulP(endPos, &localEnd);
- }
- }
- // collide...
- TSMesh * mesh = meshInstance->getMesh(od);
- if (mesh && !meshInstance->forceHidden && meshInstance->visible > 0.01f)
- {
- if (castRayMeshOpcode(mesh, localStart, localEnd, info, getShapeInstance()->mMaterialList))
- {
- saveMat = previousMat;
- emitted = true;
- }
- }
- }
- }
- if (emitted)
- {
- saveMat->mulV(info->normal);
- info->point = endPos - startPos;
- info->point *= info->t;
- info->point += startPos;
- }
- return emitted;*/
- }
- static Point3F texGenAxis[18] =
- {
- Point3F(0,0,1), Point3F(1,0,0), Point3F(0,-1,0),
- Point3F(0,0,-1), Point3F(1,0,0), Point3F(0,1,0),
- Point3F(1,0,0), Point3F(0,1,0), Point3F(0,0,1),
- Point3F(-1,0,0), Point3F(0,1,0), Point3F(0,0,-1),
- Point3F(0,1,0), Point3F(1,0,0), Point3F(0,0,1),
- Point3F(0,-1,0), Point3F(-1,0,0), Point3F(0,0,-1)
- };
- bool CollisionComponent::castRayMeshOpcode(TSMesh *mesh, const Point3F &s, const Point3F &e, RayInfo *info, TSMaterialList *materials)
- {
- Opcode::RayCollider ray;
- Opcode::CollisionFaces cfs;
- IceMaths::Point dir(e.x - s.x, e.y - s.y, e.z - s.z);
- const F32 rayLen = dir.Magnitude();
- IceMaths::Ray vec(Point(s.x, s.y, s.z), dir.Normalize());
- ray.SetDestination(&cfs);
- ray.SetFirstContact(false);
- ray.SetClosestHit(true);
- ray.SetPrimitiveTests(true);
- ray.SetCulling(true);
- ray.SetMaxDist(rayLen);
- AssertFatal(ray.ValidateSettings() == NULL, "invalid ray settings");
- // Do collision.
- bool safety = ray.Collide(vec, *mesh->mOptTree);
- AssertFatal(safety, "CollisionComponent::castRayOpcode - no good ray collide!");
- // If no hit, just skip out.
- if (cfs.GetNbFaces() == 0)
- return false;
- // Got a hit!
- AssertFatal(cfs.GetNbFaces() == 1, "bad");
- const Opcode::CollisionFace &face = cfs.GetFaces()[0];
- // If the cast was successful let's check if the t value is less than what we had
- // and toggle the collision boolean
- // Stupid t... i prefer coffee
- const F32 t = face.mDistance / rayLen;
- if (t < 0.0f || t > 1.0f)
- return false;
- if (t <= info->t)
- {
- info->t = t;
- // Calculate the normal.
- Opcode::VertexPointers vp;
- mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, face.mFaceID);
- if (materials && vp.MatIdx >= 0 && vp.MatIdx < materials->size())
- info->material = materials->getMaterialInst(vp.MatIdx);
- // Get the two edges.
- IceMaths::Point baseVert = *vp.Vertex[0];
- IceMaths::Point a = *vp.Vertex[1] - baseVert;
- IceMaths::Point b = *vp.Vertex[2] - baseVert;
- IceMaths::Point n;
- n.Cross(a, b);
- n.Normalize();
- info->normal.set(n.x, n.y, n.z);
- // generate UV coordinate across mesh based on
- // matching normals, this isn't done by default and is
- // primarily of interest in matching a collision point to
- // either a GUI control coordinate or finding a hit pixel in texture space
- if (info->generateTexCoord)
- {
- baseVert = *vp.Vertex[0];
- a = *vp.Vertex[1];
- b = *vp.Vertex[2];
- Point3F facePoint = (1.0f - face.mU - face.mV) * Point3F(baseVert.x, baseVert.y, baseVert.z)
- + face.mU * Point3F(a.x, a.y, a.z) + face.mV * Point3F(b.x, b.y, b.z);
- U32 faces[1024];
- U32 numFaces = 0;
- for (U32 i = 0; i < mesh->mOptTree->GetMeshInterface()->GetNbTriangles(); i++)
- {
- if (i == face.mFaceID)
- {
- faces[numFaces++] = i;
- }
- else
- {
- IceMaths::Point n2;
- mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, i);
- baseVert = *vp.Vertex[0];
- a = *vp.Vertex[1] - baseVert;
- b = *vp.Vertex[2] - baseVert;
- n2.Cross(a, b);
- n2.Normalize();
- F32 eps = .01f;
- if (mFabs(n.x - n2.x) < eps && mFabs(n.y - n2.y) < eps && mFabs(n.z - n2.z) < eps)
- {
- faces[numFaces++] = i;
- }
- }
- if (numFaces == 1024)
- {
- // too many faces in this collision mesh for UV generation
- return true;
- }
- }
- Point3F min(F32_MAX, F32_MAX, F32_MAX);
- Point3F max(-F32_MAX, -F32_MAX, -F32_MAX);
- for (U32 i = 0; i < numFaces; i++)
- {
- mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, faces[i]);
- for (U32 j = 0; j < 3; j++)
- {
- a = *vp.Vertex[j];
- if (a.x < min.x)
- min.x = a.x;
- if (a.y < min.y)
- min.y = a.y;
- if (a.z < min.z)
- min.z = a.z;
- if (a.x > max.x)
- max.x = a.x;
- if (a.y > max.y)
- max.y = a.y;
- if (a.z > max.z)
- max.z = a.z;
- }
- }
- // slerp
- Point3F s = ((max - min) - (facePoint - min)) / (max - min);
- // compute axis
- S32 bestAxis = 0;
- F32 best = 0.f;
- for (U32 i = 0; i < 6; i++)
- {
- F32 dot = mDot(info->normal, texGenAxis[i * 3]);
- if (dot > best)
- {
- best = dot;
- bestAxis = i;
- }
- }
- Point3F xv = texGenAxis[bestAxis * 3 + 1];
- Point3F yv = texGenAxis[bestAxis * 3 + 2];
- S32 sv, tv;
- if (xv.x)
- sv = 0;
- else if (xv.y)
- sv = 1;
- else
- sv = 2;
- if (yv.x)
- tv = 0;
- else if (yv.y)
- tv = 1;
- else
- tv = 2;
- // handle coord translation
- if (bestAxis == 2 || bestAxis == 3)
- {
- S32 x = sv;
- sv = tv;
- tv = x;
- if (yv.z < 0)
- s[sv] = 1.f - s[sv];
- }
- if (bestAxis < 2)
- {
- if (yv.y < 0)
- s[sv] = 1.f - s[sv];
- }
- if (bestAxis > 3)
- {
- s[sv] = 1.f - s[sv];
- if (yv.z > 0)
- s[tv] = 1.f - s[tv];
- }
- // done!
- info->texCoord.set(s[sv], s[tv]);
- }
- return true;
- }
- return false;
- }
|