void VehicleBlocker::buildConvex(const Box3F& box, Convex* convex) { // These should really come out of a pool mConvexList->collectGarbage(); if (box.isOverlapped(getWorldBox()) == false) return; // Just return a box convex for the entire shape... Convex* cc = 0; CollisionWorkingList& wl = convex->getWorkingList(); for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) { if (itr->mConvex->getType() == BoxConvexType && itr->mConvex->getObject() == this) { cc = itr->mConvex; break; } } if (cc) return; // Create a new convex. BoxConvex* cp = new BoxConvex; mConvexList->registerObject(cp); convex->addToWorkingList(cp); cp->init(this); mObjBox.getCenter(&cp->mCenter); cp->mSize.x = mObjBox.len_x() / 2.0f; cp->mSize.y = mObjBox.len_y() / 2.0f; cp->mSize.z = mObjBox.len_z() / 2.0f; }
void Convex::updateWorkingList(const Box3F& box, const U32 colMask) { #if 0 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); #endif }
void GroundPlane::buildConvex( const Box3F& box, Convex* convex ) { mConvexList->collectGarbage(); Box3F planeBox = getPlaneBox(); if ( !box.isOverlapped( planeBox ) ) return; // See if we already have a convex in the working set. BoxConvex *boxConvex = NULL; CollisionWorkingList &wl = convex->getWorkingList(); CollisionWorkingList *itr = wl.wLink.mNext; for ( ; itr != &wl; itr = itr->wLink.mNext ) { if ( itr->mConvex->getType() == BoxConvexType && itr->mConvex->getObject() == this ) { boxConvex = (BoxConvex*)itr->mConvex; break; } } if ( !boxConvex ) { boxConvex = new BoxConvex; mConvexList->registerObject( boxConvex ); boxConvex->init( this ); convex->addToWorkingList( boxConvex ); } // Update our convex to best match the queried box if ( boxConvex ) { Point3F queryCenter = box.getCenter(); boxConvex->mCenter = Point3F( queryCenter.x, queryCenter.y, -GROUND_PLANE_BOX_HEIGHT_HALF ); boxConvex->mSize = Point3F( box.getExtents().x, box.getExtents().y, GROUND_PLANE_BOX_HEIGHT_HALF ); } }
void ConvexShape::buildConvex( const Box3F &box, Convex *convex ) { if ( mGeometry.faces.empty() ) return; mConvexList->collectGarbage(); Box3F realBox = box; mWorldToObj.mul( realBox ); realBox.minExtents.convolveInverse( mObjScale ); realBox.maxExtents.convolveInverse( mObjScale ); if ( realBox.isOverlapped( getObjBox() ) == false ) return; // See if this convex exists in the working set already... Convex *cc = 0; CollisionWorkingList &wl = convex->getWorkingList(); for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) { if ( itr->mConvex->getType() == ConvexShapeCollisionConvexType ) { ConvexShapeCollisionConvex *pConvex = static_cast<ConvexShapeCollisionConvex*>(itr->mConvex); if ( pConvex->pShape == this ) { cc = itr->mConvex; return; } } } // Set up the convex... ConvexShapeCollisionConvex *cp = new ConvexShapeCollisionConvex(); mConvexList->registerObject( cp ); convex->addToWorkingList( cp ); cp->mObject = this; cp->pShape = this; }
void Forest::buildConvex( const Box3F &box, Convex *convex ) { mConvexList->collectGarbage(); // Get all ForestItem(s) within the box. Vector<ForestItem> trees; mData->getItems( box, &trees ); if ( trees.empty() ) return; for ( U32 i = 0; i < trees.size(); i++ ) { const ForestItem &forestItem = trees[i]; Box3F realBox = box; mWorldToObj.mul( realBox ); realBox.minExtents.convolveInverse( mObjScale ); realBox.maxExtents.convolveInverse( mObjScale ); // JCF: is this really necessary if we already got this ForestItem // as a result from getItems? if ( realBox.isOverlapped( getObjBox() ) == false ) continue; TSForestItemData *data = (TSForestItemData*)forestItem.getData(); // Find CollisionDetail(s) that are defined... const Vector<S32> &details = data->getCollisionDetails(); for ( U32 j = 0; j < details.size(); j++ ) { // JCFHACK: need to fix this if we want this to work with speedtree // or other cases in which we don't have a TSForestItemData. // Most likely via preventing this method and other torque collision // specific stuff from ever getting called. if ( details[j] == -1 ) continue; // See if this convex exists in the working set already... Convex* cc = 0; CollisionWorkingList& wl = convex->getWorkingList(); for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) { if ( itr->mConvex->getType() == ForestConvexType ) { ForestConvex *pConvex = static_cast<ForestConvex*>(itr->mConvex); if ( pConvex->mObject == this && pConvex->mForestItemKey == forestItem.getKey() && pConvex->hullId == j ) { cc = itr->mConvex; break; } } } if (cc) continue; // Then we need to make one. ForestConvex *cp = new ForestConvex; mConvexList->registerObject(cp); convex->addToWorkingList(cp); cp->mObject = this; cp->mForestItemKey = forestItem.getKey(); cp->mData = data; cp->mScale = forestItem.getScale(); cp->hullId = j; cp->box = forestItem.getObjBox(); cp->calculateTransform( forestItem.getTransform() ); } } }
void Item::updatePos(const U32 /*mask*/, const F32 dt) { // Try and move Point3F pos; mObjToWorld.getColumn(3,&pos); delta.posVec = pos; bool contact = false; bool nonStatic = false; bool stickyNotify = false; CollisionList collisionList; F32 time = dt; static Polyhedron sBoxPolyhedron; static ExtrudedPolyList sExtrudedPolyList; static EarlyOutPolyList sEarlyOutPolyList; MatrixF collisionMatrix(true); Point3F end = pos + mVelocity * time; U32 mask = isServerObject() ? sServerCollisionMask : sClientCollisionMask; // Part of our speed problem here is that we don't track contact surfaces, like we do // with the player. In order to handle the most common and performance impacting // instance of this problem, we'll use a ray cast to detect any contact surfaces below // us. This won't be perfect, but it only needs to catch a few of these to make a // big difference. We'll cast from the top center of the bounding box at the tick's // beginning to the bottom center of the box at the end. Point3F startCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5, (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5, mObjBox.maxExtents.z); Point3F endCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5, (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5, mObjBox.minExtents.z); collisionMatrix.setColumn(3, pos); collisionMatrix.mulP(startCast); collisionMatrix.setColumn(3, end); collisionMatrix.mulP(endCast); RayInfo rinfo; bool doToughCollision = true; disableCollision(); if (mCollisionObject) mCollisionObject->disableCollision(); if (getContainer()->castRay(startCast, endCast, mask, &rinfo)) { F32 bd = -mDot(mVelocity, rinfo.normal); if (bd >= 0.0) { // Contact! if (mDataBlock->sticky && rinfo.object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) { mVelocity.set(0, 0, 0); mAtRest = true; mAtRestCounter = 0; stickyNotify = true; mStickyCollisionPos = rinfo.point; mStickyCollisionNormal = rinfo.normal; doToughCollision = false;; } else { // Subtract out velocity into surface and friction VectorF fv = mVelocity + rinfo.normal * bd; F32 fvl = fv.len(); if (fvl) { F32 ff = bd * mDataBlock->friction; if (ff < fvl) { fv *= ff / fvl; fvl = ff; } } bd *= 1 + mDataBlock->elasticity; VectorF dv = rinfo.normal * (bd + 0.002); mVelocity += dv; mVelocity -= fv; // Keep track of what we hit contact = true; U32 typeMask = rinfo.object->getTypeMask(); if (!(typeMask & StaticObjectType)) nonStatic = true; if (isServerObject() && (typeMask & ShapeBaseObjectType)) { ShapeBase* col = static_cast<ShapeBase*>(rinfo.object); queueCollision(col,mVelocity - col->getVelocity()); } } } } enableCollision(); if (mCollisionObject) mCollisionObject->enableCollision(); if (doToughCollision) { U32 count; for (count = 0; count < 3; count++) { // Build list from convex states here... end = pos + mVelocity * time; collisionMatrix.setColumn(3, end); Box3F wBox = getObjBox(); collisionMatrix.mul(wBox); Box3F testBox = wBox; Point3F oldMin = testBox.minExtents; Point3F oldMax = testBox.maxExtents; testBox.minExtents.setMin(oldMin + (mVelocity * time)); testBox.maxExtents.setMin(oldMax + (mVelocity * time)); sEarlyOutPolyList.clear(); sEarlyOutPolyList.mNormal.set(0,0,0); sEarlyOutPolyList.mPlaneList.setSize(6); sEarlyOutPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1,0,0)); sEarlyOutPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0,1,0)); sEarlyOutPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1,0,0)); sEarlyOutPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0,-1,0)); sEarlyOutPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0,0,-1)); sEarlyOutPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0,0,1)); CollisionWorkingList& eorList = mConvex.getWorkingList(); CollisionWorkingList* eopList = eorList.wLink.mNext; while (eopList != &eorList) { if ((eopList->mConvex->getObject()->getTypeMask() & mask) != 0) { Box3F convexBox = eopList->mConvex->getBoundingBox(); if (testBox.isOverlapped(convexBox)) { eopList->mConvex->getPolyList(&sEarlyOutPolyList); if (sEarlyOutPolyList.isEmpty() == false) break; } } eopList = eopList->wLink.mNext; } if (sEarlyOutPolyList.isEmpty()) { pos = end; break; } collisionMatrix.setColumn(3, pos); sBoxPolyhedron.buildBox(collisionMatrix, mObjBox, true); // Build extruded polyList... VectorF vector = end - pos; sExtrudedPolyList.extrude(sBoxPolyhedron, vector); sExtrudedPolyList.setVelocity(mVelocity); sExtrudedPolyList.setCollisionList(&collisionList); CollisionWorkingList& rList = mConvex.getWorkingList(); CollisionWorkingList* pList = rList.wLink.mNext; while (pList != &rList) { if ((pList->mConvex->getObject()->getTypeMask() & mask) != 0) { Box3F convexBox = pList->mConvex->getBoundingBox(); if (testBox.isOverlapped(convexBox)) { pList->mConvex->getPolyList(&sExtrudedPolyList); } } pList = pList->wLink.mNext; } if (collisionList.getTime() < 1.0) { // Set to collision point F32 dt = time * collisionList.getTime(); pos += mVelocity * dt; time -= dt; // Pick the most resistant surface F32 bd = 0; const Collision* collision = 0; for (int c = 0; c < collisionList.getCount(); c++) { const Collision &cp = collisionList[c]; F32 dot = -mDot(mVelocity,cp.normal); if (dot > bd) { bd = dot; collision = &cp; } } if (collision && mDataBlock->sticky && collision->object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) { mVelocity.set(0, 0, 0); mAtRest = true; mAtRestCounter = 0; stickyNotify = true; mStickyCollisionPos = collision->point; mStickyCollisionNormal = collision->normal; break; } else { // Subtract out velocity into surface and friction if (collision) { VectorF fv = mVelocity + collision->normal * bd; F32 fvl = fv.len(); if (fvl) { F32 ff = bd * mDataBlock->friction; if (ff < fvl) { fv *= ff / fvl; fvl = ff; } } bd *= 1 + mDataBlock->elasticity; VectorF dv = collision->normal * (bd + 0.002); mVelocity += dv; mVelocity -= fv; // Keep track of what we hit contact = true; U32 typeMask = collision->object->getTypeMask(); if (!(typeMask & StaticObjectType)) nonStatic = true; if (isServerObject() && (typeMask & ShapeBaseObjectType)) { ShapeBase* col = static_cast<ShapeBase*>(collision->object); queueCollision(col,mVelocity - col->getVelocity()); } } } } else { pos = end; break; } } if (count == 3) { // Couldn't move... mVelocity.set(0, 0, 0); } } // If on the client, calculate delta for backstepping if (isGhost()) { delta.pos = pos; delta.posVec -= pos; delta.dt = 1; } // Update transform MatrixF mat = mObjToWorld; mat.setColumn(3,pos); Parent::setTransform(mat); enableCollision(); if (mCollisionObject) mCollisionObject->enableCollision(); updateContainer(); if ( mPhysicsRep ) mPhysicsRep->setTransform( mat ); // if (contact) { // Check for rest condition if (!nonStatic && mVelocity.len() < sAtRestVelocity) { mVelocity.x = mVelocity.y = mVelocity.z = 0; mAtRest = true; mAtRestCounter = 0; } // Only update the client if we hit a non-static shape or // if this is our final rest pos. if (nonStatic || mAtRest) setMaskBits(PositionMask); } // Collision callbacks. These need to be processed whether we hit // anything or not. if (!isGhost()) { SimObjectPtr<Item> safePtr(this); if (stickyNotify) { notifyCollision(); if(bool(safePtr)) onStickyCollision_callback( getIdString() ); } else notifyCollision(); // water if(bool(safePtr)) { if(!mInLiquid && mWaterCoverage != 0.0f) { mInLiquid = true; if ( !isGhost() ) mDataBlock->onEnterLiquid_callback( this, mWaterCoverage, mLiquidType.c_str() ); } else if(mInLiquid && mWaterCoverage == 0.0f) { mInLiquid = false; if ( !isGhost() ) mDataBlock->onLeaveLiquid_callback( this, mLiquidType.c_str() ); } } } }
//----------------------------------------------------------------------------- // // VActorPhysicsController::findGroundContact( pContactObject, pContactPoint, pContactNormal ); // // ... // //----------------------------------------------------------------------------- bool VActorPhysicsController::findGroundContact( SceneObject *&pContactObject, Point3F &pContactPoint, VectorF &pContactNormal ) { // Setup Collision List. static CollisionList sCollisionList; sCollisionList.clear(); static Polyhedron sBoxPolyhedron; static ExtrudedPolyList sExtrudedPolyList; // Fetch Max Step Height. const F32 stepHeight = mObject->getDataBlock()->getMaxStepHeight(); // Determine Positions. const Point3F preTickPosition = getPosition() + Point3F( 0.f, 0.f, stepHeight ); const VectorF preTickVelocity = getVelocity() + mGravity - VectorF( 0.f, 0.f, stepHeight / TickSec ); const Point3F postTickPosition = preTickPosition + ( preTickVelocity * TickSec ); const VectorF postTickVector = postTickPosition - preTickPosition; // Construct Scaled Box. Box3F scaledBox = mObject->getObjBox(); scaledBox.minExtents.convolve( mObject->getScale() ); scaledBox.maxExtents.convolve( mObject->getScale() ); // Setup Polyherdron. MatrixF collisionMatrix( true ); collisionMatrix.setPosition( preTickPosition ); sBoxPolyhedron.buildBox( collisionMatrix, scaledBox, true ); // Setup Extruded Poly List. sExtrudedPolyList.extrude( sBoxPolyhedron, postTickVector ); sExtrudedPolyList.setVelocity( preTickVelocity ); sExtrudedPolyList.setCollisionList( &sCollisionList ); // Construct World Convex Box & Adjust for Sweep. Box3F convexBox = scaledBox; getTransform().mul( convexBox ); convexBox.minExtents += postTickVector; convexBox.maxExtents += postTickVector; // Build List of Contacts. CollisionWorkingList &rList = mConvex.getWorkingList(); for ( CollisionWorkingList *pList = rList.wLink.mNext; pList != &rList; pList = pList->wLink.mNext ) { Convex *convexShape = pList->mConvex; // Ground Object? if ( !( convexShape->getObject()->getTypeMask() & sGroundCollisionMask ) ) { // No, Continue. continue; } // Overlap? const Box3F &collisionConvexBox = convexShape->getBoundingBox(); if ( convexBox.isOverlapped( collisionConvexBox ) ) { // Build Contact Information. convexShape->getPolyList( &sExtrudedPolyList ); } } // Valid Collision? if ( sCollisionList.getCount() == 0 || sCollisionList.getTime() < 0.f || sCollisionList.getTime() > 1.f ) { // No, Quit Now. return false; } // Use First Collision. Collision *collision = &sCollisionList[0]; // More Collisions? if ( sCollisionList.getCount() > 1 ) { // Check for Better Contacts. for ( Collision *cp = ( collision + 1 ); cp != ( collision + sCollisionList.getCount() ); cp++ ) { if ( cp->faceDot > collision->faceDot ) { // Use this One. collision = cp; } } } // Set Properties. pContactObject = collision->object; //pContactPoint = collision->point; pContactPoint = ( preTickPosition + ( preTickVelocity * TickSec * sCollisionList.getTime() ) ); pContactNormal = collision->normal; // Valid Contact. return true; }
//----------------------------------------------------------------------------- // // VActorPhysicsController::findCollision( pCollision ); // // ... // //----------------------------------------------------------------------------- bool VActorPhysicsController::findCollision( Collision *&pCollision ) { // Setup Collision List. static CollisionList sCollisionList; sCollisionList.clear(); static Polyhedron sBoxPolyhedron; static ExtrudedPolyList sExtrudedPolyList; // Determine Positions. const Point3F preTickPosition = getPosition(); const VectorF preTickVelocity = getVelocity(); const Point3F postTickPosition = preTickPosition + ( preTickVelocity * TickSec ); const VectorF postTickVector = postTickPosition - preTickPosition; // Construct Scaled Box. Box3F scaledBox = mObject->getObjBox(); scaledBox.minExtents.convolve( mObject->getScale() ); scaledBox.maxExtents.convolve( mObject->getScale() ); // Setup Polyherdron. MatrixF collisionMatrix( true ); collisionMatrix.setPosition( preTickPosition ); sBoxPolyhedron.buildBox( collisionMatrix, scaledBox ); // Setup Extruded Poly List. sExtrudedPolyList.extrude( sBoxPolyhedron, postTickVector ); sExtrudedPolyList.setVelocity( preTickVelocity ); sExtrudedPolyList.setCollisionList( &sCollisionList ); // Construct World Convex Box & Adjust for Sweep. Box3F convexBox = scaledBox; getTransform().mul( convexBox ); convexBox.minExtents += postTickVector; convexBox.maxExtents += postTickVector; // Determine the Collision Mask. const U32 collisionMask = ( isInWater() ) ? ( sGroundCollisionMask | sMoveCollisionMask ) : sMoveCollisionMask; // Build List of Contacts. CollisionWorkingList &rList = mConvex.getWorkingList(); for ( CollisionWorkingList *pList = rList.wLink.mNext; pList != &rList; pList = pList->wLink.mNext ) { Convex *convexShape = pList->mConvex; // Valid Collision Target? if ( !( convexShape->getObject()->getTypeMask() & collisionMask ) ) { // No, Continue. continue; } // Overlap? const Box3F &collisionConvexBox = convexShape->getBoundingBox(); if ( convexBox.isOverlapped( collisionConvexBox ) ) { // Build Contact Information. convexShape->getPolyList( &sExtrudedPolyList ); } } // Valid Collision? if ( sCollisionList.getCount() == 0 || sCollisionList.getTime() > 1.f ) { // No, Quit Now. return false; } // Use First Collision. Collision *collision = &sCollisionList[0]; // More Collisions? if ( sCollisionList.getCount() > 1 ) { // Check for Better Contacts. for ( Collision *cp = ( collision + 1 ); cp != ( collision + sCollisionList.getCount() ); cp++ ) { if ( cp->faceDot > collision->faceDot ) { // Use this One. collision = cp; } } } // Store Reference. pCollision = collision; // Valid Collision. return true; }
void Etherform::_findContact( SceneObject **contactObject, VectorF *contactNormal, Vector<SceneObject*> *outOverlapObjects ) { Point3F pos; getTransform().getColumn(3,&pos); Box3F wBox; Point3F exp(0,0,sTractionDistance); wBox.minExtents = pos + mScaledBox.minExtents - exp; wBox.maxExtents.x = pos.x + mScaledBox.maxExtents.x; wBox.maxExtents.y = pos.y + mScaledBox.maxExtents.y; wBox.maxExtents.z = pos.z + mScaledBox.minExtents.z + sTractionDistance; static ClippedPolyList polyList; polyList.clear(); polyList.doConstruct(); polyList.mNormal.set(0.0f, 0.0f, 0.0f); polyList.setInterestNormal(Point3F(0.0f, 0.0f, -1.0f)); polyList.mPlaneList.setSize(6); polyList.mPlaneList[0].setYZ(wBox.minExtents, -1.0f); polyList.mPlaneList[1].setXZ(wBox.maxExtents, 1.0f); polyList.mPlaneList[2].setYZ(wBox.maxExtents, 1.0f); polyList.mPlaneList[3].setXZ(wBox.minExtents, -1.0f); polyList.mPlaneList[4].setXY(wBox.minExtents, -1.0f); polyList.mPlaneList[5].setXY(wBox.maxExtents, 1.0f); Box3F plistBox = wBox; // Expand build box as it will be used to collide with items. // PickupRadius will be at least the size of the box. F32 pd = 0; wBox.minExtents.x -= pd; wBox.minExtents.y -= pd; wBox.maxExtents.x += pd; wBox.maxExtents.y += pd; wBox.maxExtents.z = pos.z + mScaledBox.maxExtents.z; // Build list from convex states here... CollisionWorkingList& rList = mConvex.getWorkingList(); CollisionWorkingList* pList = rList.wLink.mNext; while (pList != &rList) { Convex* pConvex = pList->mConvex; U32 objectMask = pConvex->getObject()->getTypeMask(); if ( ( objectMask & sCollisionMoveMask ) && !( objectMask & PhysicalZoneObjectType ) ) { Box3F convexBox = pConvex->getBoundingBox(); if (plistBox.isOverlapped(convexBox)) pConvex->getPolyList(&polyList); } else outOverlapObjects->push_back( pConvex->getObject() ); pList = pList->wLink.mNext; } if (!polyList.isEmpty()) { // Pick flattest surface F32 bestVd = -1.0f; ClippedPolyList::Poly* poly = polyList.mPolyList.begin(); ClippedPolyList::Poly* end = polyList.mPolyList.end(); for (; poly != end; poly++) { F32 vd = poly->plane.z; // i.e. mDot(Point3F(0,0,1), poly->plane); if (vd > bestVd) { bestVd = vd; *contactObject = poly->object; *contactNormal = poly->plane; } } } }
Point3F Etherform::_move( const F32 travelTime, Collision *outCol ) { // Try and move to new pos F32 totalMotion = 0.0f; // TODO: not used? //F32 initialSpeed = mVelocity.len(); Point3F start; Point3F initialPosition; getTransform().getColumn(3,&start); initialPosition = start; static CollisionList collisionList; static CollisionList physZoneCollisionList; collisionList.clear(); physZoneCollisionList.clear(); MatrixF collisionMatrix(true); collisionMatrix.setColumn(3, start); VectorF firstNormal(0.0f, 0.0f, 0.0f); F32 time = travelTime; U32 count = 0; static Polyhedron sBoxPolyhedron; static ExtrudedPolyList sExtrudedPolyList; static ExtrudedPolyList sPhysZonePolyList; for (; count < sMoveRetryCount; count++) { F32 speed = mVelocity.len(); if(!speed) break; Point3F end = start + mVelocity * time; Point3F distance = end - start; if (mFabs(distance.x) < mObjBox.len_x() && mFabs(distance.y) < mObjBox.len_y() && mFabs(distance.z) < mObjBox.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 = mScaledBox; 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 = mConvex.getWorkingList(); CollisionWorkingList* pList = rList.wLink.mNext; while (pList != &rList) { Convex* pConvex = pList->mConvex; if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) { 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()) { totalMotion += (end - start).len(); start = end; break; } } collisionMatrix.setColumn(3, start); sBoxPolyhedron.buildBox(collisionMatrix, mScaledBox, true); // Setup the bounding box for the extrudedPolyList Box3F plistBox = mScaledBox; collisionMatrix.mul(plistBox); Point3F oldMin = plistBox.minExtents; Point3F oldMax = plistBox.maxExtents; plistBox.minExtents.setMin(oldMin + (mVelocity * time) - Point3F(0.1f, 0.1f, 0.1f)); plistBox.maxExtents.setMax(oldMax + (mVelocity * time) + Point3F(0.1f, 0.1f, 0.1f)); // Build extruded polyList... VectorF vector = end - start; sExtrudedPolyList.extrude(sBoxPolyhedron,vector); sExtrudedPolyList.setVelocity(mVelocity); sExtrudedPolyList.setCollisionList(&collisionList); sPhysZonePolyList.extrude(sBoxPolyhedron,vector); sPhysZonePolyList.setVelocity(mVelocity); sPhysZonePolyList.setCollisionList(&physZoneCollisionList); // Build list from convex states here... CollisionWorkingList& rList = mConvex.getWorkingList(); CollisionWorkingList* pList = rList.wLink.mNext; while (pList != &rList) { Convex* pConvex = pList->mConvex; if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) { Box3F convexBox = pConvex->getBoundingBox(); if (plistBox.isOverlapped(convexBox)) { if (pConvex->getObject()->getTypeMask() & PhysicalZoneObjectType) pConvex->getPolyList(&sPhysZonePolyList); else pConvex->getPolyList(&sExtrudedPolyList); } } pList = pList->wLink.mNext; } // Take into account any physical zones... for (U32 j = 0; j < physZoneCollisionList.getCount(); j++) { AssertFatal(dynamic_cast<PhysicalZone*>(physZoneCollisionList[j].object), "Bad phys zone!"); const PhysicalZone* pZone = (PhysicalZone*)physZoneCollisionList[j].object; if (pZone->isActive()) mVelocity *= pZone->getVelocityMod(); } if (collisionList.getCount() != 0 && collisionList.getTime() < 1.0f) { // Set to collision point F32 velLen = mVelocity.len(); F32 dt = time * getMin(collisionList.getTime(), 1.0f); start += mVelocity * dt; time -= dt; totalMotion += velLen * dt; // Back off... if ( velLen > 0.f ) { F32 newT = getMin(0.01f / velLen, dt); start -= mVelocity * newT; totalMotion -= velLen * newT; } // Pick the surface most parallel to the face that was hit. const Collision *collision = &collisionList[0]; const Collision *cp = collision + 1; const Collision *ep = collision + collisionList.getCount(); for (; cp != ep; cp++) { if (cp->faceDot > collision->faceDot) collision = cp; } F32 bd = -mDot(mVelocity, collision->normal); // Copy this collision out so // we can use it to do impacts // and query collision. *outCol = *collision; // Subtract out velocity VectorF dv = collision->normal * (bd + sNormalElasticity); mVelocity += dv; if (count == 0) { firstNormal = collision->normal; } else { if (count == 1) { // Re-orient velocity along the crease. if (mDot(dv,firstNormal) < 0.0f && mDot(collision->normal,firstNormal) < 0.0f) { VectorF nv; mCross(collision->normal,firstNormal,&nv); F32 nvl = nv.len(); if (nvl) { if (mDot(nv,mVelocity) < 0.0f) nvl = -nvl; nv *= mVelocity.len() / nvl; mVelocity = nv; } } } } } else { totalMotion += (end - start).len(); start = end; break; } } if (count == sMoveRetryCount) { // Failed to move start = initialPosition; mVelocity.set(0.0f, 0.0f, 0.0f); } return start; }