void CollisionSpace::Collide(void (*callback)(CollisionContact*)) { RebuildObjectTrees(); int mailboxMin = 0; for (std::list<Geom*>::iterator i = m_geoms.begin(); i != m_geoms.end(); ++i) { (*i)->SetMailboxIndex(mailboxMin++); } /* This mailbox nonsense is so: after collision(a,b), we will not * attempt collision(b,a) */ mailboxMin = 1; for (std::list<Geom*>::iterator i = m_geoms.begin(); i != m_geoms.end(); ++i, mailboxMin++) { CollideGeoms(*i, mailboxMin, callback); } }
// This is the generic sweep test for all swept volumes, but not character-controller specific bool SweepTest::DoSweepTest(void* user_data, void* user_data2, NxU32 nb_boxes, const NxExtendedBounds3* boxes, const void** box_user_data, NxU32 nb_capsules, const NxExtendedCapsule* capsules, const void** capsule_user_data, SweptVolume& swept_volume, const NxVec3& direction, NxU32 max_iter, NxU32* nb_collisions, NxU32 group_flags, float min_dist, const NxGroupsMask* groupsMask, bool down_pass) { // Early exit when motion is zero. Since the motion is decomposed into several vectors // and this function is called for each of them, it actually happens quite often. if(direction.isZero()) return false; bool HasMoved = false; mValidTri = false; NxExtendedVec3 CurrentPosition = swept_volume.mCenter; NxExtendedVec3 TargetPosition = swept_volume.mCenter; TargetPosition += direction; NxU32 NbCollisions = 0; while(max_iter--) { gNbIters++; // Compute current direction NxVec3 CurrentDirection = TargetPosition - CurrentPosition; // Make sure the new TBV is still valid { // Compute temporal bounding box. We could use a capsule or an OBB instead: // - the volume would be smaller // - but the query would be slower // Overall it's unclear whether it's worth it or not. // TODO: optimize this part ? NxExtendedBounds3 TemporalBox; swept_volume.ComputeTemporalBox(*this, TemporalBox, CurrentPosition, CurrentDirection); // Gather touched geoms UpdateTouchedGeoms(user_data, swept_volume, nb_boxes, boxes, box_user_data, nb_capsules, capsules, capsule_user_data, group_flags, TemporalBox, groupsMask); } const float Length = CurrentDirection.magnitude(); if(Length<min_dist) break; CurrentDirection /= Length; // From Quake2: "if velocity is against the original velocity, stop dead to avoid tiny occilations in sloping corners" if((CurrentDirection|direction) <= 0.0f) break; // From this point, we're going to update the position at least once HasMoved = true; // Find closest collision SweptContact C; C.mDistance = Length + mSkinWidth; if(!CollideGeoms(this, swept_volume, mGeomStream, CurrentPosition, CurrentDirection, C)) { // no collision found => move to desired position CurrentPosition = TargetPosition; break; } ASSERT(C.mGeom); // If we reach this point, we must have touched a geom if(C.mGeom->mType==TOUCHED_USER_BOX || C.mGeom->mType==TOUCHED_USER_CAPSULE) { // We touched a user object, typically another CCT if(mValidateCallback) UserHitCallback(user_data2, C, CurrentDirection, Length); // Trying to solve the following problem: // - by default, the CCT "friction" is infinite, i.e. a CCT will not slide on a slope (this is by design) // - this produces bad results when a capsule CCT stands on top of another capsule CCT, without sliding. Visually it looks // like the character is standing on the other character's head, it looks bad. So, here, we would like to let the CCT // slide away, i.e. we don't want friction. // So here we simply increase the number of iterations (== let the CCT slide) when the first down collision is with another CCT. if(down_pass && !NbCollisions) max_iter += 9; } else { // We touched a normal object #ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST mValidTri = true; mCN = C.mWorldNormal; #else if(C.mIndex!=INVALID_ID) { mValidTri = true; mTouched = mWorldTriangles[C.mIndex]; } #endif { if(mValidateCallback) ShapeHitCallback(user_data2, C, CurrentDirection, Length); } } NbCollisions++; mContactPointHeight = (float)C.mWorldPos[mUpDirection]; // UBI const float DynSkin = mSkinWidth; if(C.mDistance>DynSkin/*+0.01f*/) CurrentPosition += CurrentDirection*(C.mDistance-DynSkin); NxVec3 WorldNormal = C.mWorldNormal; if(mWalkExperiment) { // Make sure the auto-step doesn't bypass this ! WorldNormal[mUpDirection]=0.0f; WorldNormal.normalize(); } const float Bump = 0.0f; // ### doesn't work when !=0 because of Quake2 hack! const float Friction = 1.0f; CollisionResponse(TargetPosition, CurrentPosition, CurrentDirection, WorldNormal, Bump, Friction, mNormalizeResponse); } if(nb_collisions) *nb_collisions = NbCollisions; // Final box position that should be reflected in the graphics engine swept_volume.mCenter = CurrentPosition; // If we didn't move, don't update the box position at all (keeping possible lazy-evaluated structures valid) return HasMoved; }