Showing the changes to libraries/physics/src/CharacterController.cpp (1 of 8 files). Show all
Unified · Split
| @@ -11,14 +11,136 @@ · 10 unmodified lines | ||
| 11 | 11 | |
| 12 | 12 | #include "CharacterController.h" |
| 13 | 13 | |
| 14 | −#include <NumericalConstants.h> | |
| 15 | 14 | #include <AvatarConstants.h> |
| 15 | +#include <NumericalConstants.h> | |
| 16 | +#include <PhysicsCollisionGroups.h> | |
| 16 | 17 | |
| 17 | 18 | #include "ObjectMotionState.h" |
| 18 | 19 | #include "PhysicsHelpers.h" |
| 19 | 20 | #include "PhysicsLogging.h" |
| 20 | 21 | |
| 21 | 22 | const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); |
| 23 | +static bool _flippedThisFrame = false; | |
| 24 | + | |
| 25 | +// Note: flipBackfaceTriangleNormals is registered as a sub-callback to Bullet's gContactAddedCallback feature | |
| 26 | +// when we detect MyAvatar is "stuck". It will reverse the triangles on the backface of mesh shapes, unless it thinks | |
| 27 | +// the old normal would be better at extracting MyAvatar out along its UP direction. | |
| 28 | +bool flipBackfaceTriangleNormals(btManifoldPoint& cp, | |
| 29 | + const btCollisionObjectWrapper* colObj0Wrap, int partId0, int index0, | |
| 30 | + const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1) { | |
| 31 | + static int32_t numCalls = 0; | |
| 32 | + ++numCalls; | |
| 33 | + // This callback is ONLY called on objects with btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK flag | |
| 34 | + // and the flagged object will always be sorted to Obj0. Hence the "other" is always Obj1. | |
| 35 | + const btCollisionObject* other = colObj1Wrap->m_collisionObject; | |
| 36 | + | |
| 37 | + if (other->getCollisionShape()->getShapeType() == TRIANGLE_MESH_SHAPE_PROXYTYPE) { | |
| 38 | + // access the meshInterface | |
| 39 | + auto meshShape = static_cast<const btBvhTriangleMeshShape*>(other->getCollisionShape()); | |
| 40 | + const btStridingMeshInterface* meshInterface = meshShape->getMeshInterface(); | |
| 41 | + | |
| 42 | + // figure out about how to navigate meshInterface | |
| 43 | + const uint8_t* vertexBase; | |
| 44 | + int32_t numverts; | |
| 45 | + PHY_ScalarType vertexType; | |
| 46 | + int32_t vertexStride; | |
| 47 | + const uint8_t* indexBase; | |
| 48 | + int32_t indexStride; | |
| 49 | + int32_t numFaces; | |
| 50 | + PHY_ScalarType indicesType; | |
| 51 | + int32_t subPart = colObj1Wrap->m_partId; | |
| 52 | + // NOTE: all arguments are being passed by reference except the bases (passed by pointer) and subPart (true input) | |
| 53 | + meshInterface->getLockedReadOnlyVertexIndexBase(&vertexBase, numverts, vertexType, vertexStride, &indexBase, indexStride, numFaces, indicesType, subPart); | |
| 54 | + | |
| 55 | + // fetch the triangle vertices | |
| 56 | + int32_t triangleIndex = colObj1Wrap->m_index; | |
| 57 | + assert(vertexType == PHY_FLOAT); // all mesh vertex data is float... | |
| 58 | + // ...but indicesType can vary | |
| 59 | + btVector3 triangleVertex[3]; | |
| 60 | + switch (indicesType) { | |
| 61 | + case PHY_INTEGER: { | |
| 62 | + uint32_t* triangleIndices = (uint32_t*)(indexBase + triangleIndex * indexStride); | |
| 63 | + float* triangleBase; | |
| 64 | + triangleBase = (float*)(vertexBase + triangleIndices[0] * vertexStride); | |
| 65 | + triangleVertex[0].setValue(triangleBase[0], triangleBase[1], triangleBase[2]); | |
| 66 | + triangleBase = (float*)(vertexBase + triangleIndices[1] * vertexStride); | |
| 67 | + triangleVertex[1].setValue(triangleBase[0], triangleBase[1], triangleBase[2]); | |
| 68 | + triangleBase = (float*)(vertexBase + triangleIndices[2] * vertexStride); | |
| 69 | + triangleVertex[2].setValue(triangleBase[0], triangleBase[1], triangleBase[2]); | |
| 70 | + } | |
| 71 | + break; | |
| 72 | + case PHY_SHORT: { | |
| 73 | + uint16_t* triangleIndices = (uint16_t*)(indexBase + triangleIndex * indexStride); | |
| 74 | + float* triangleBase; | |
| 75 | + triangleBase = (float*)(vertexBase + triangleIndices[0] * vertexStride); | |
| 76 | + triangleVertex[0].setValue(triangleBase[0], triangleBase[1], triangleBase[2]); | |
| 77 | + triangleBase = (float*)(vertexBase + triangleIndices[1] * vertexStride); | |
| 78 | + triangleVertex[1].setValue(triangleBase[0], triangleBase[1], triangleBase[2]); | |
| 79 | + triangleBase = (float*)(vertexBase + triangleIndices[2] * vertexStride); | |
| 80 | + triangleVertex[2].setValue(triangleBase[0], triangleBase[1], triangleBase[2]); | |
| 81 | + } | |
| 82 | + break; | |
| 83 | + case PHY_UCHAR: { | |
| 84 | + uint8_t* triangleIndices = (uint8_t*)(indexBase + triangleIndex * indexStride); | |
| 85 | + float* triangleBase; | |
| 86 | + triangleBase = (float*)(vertexBase + triangleIndices[0] * vertexStride); | |
| 87 | + triangleVertex[0].setValue(triangleBase[0], triangleBase[1], triangleBase[2]); | |
| 88 | + triangleBase = (float*)(vertexBase + triangleIndices[1] * vertexStride); | |
| 89 | + triangleVertex[1].setValue(triangleBase[0], triangleBase[1], triangleBase[2]); | |
| 90 | + triangleBase = (float*)(vertexBase + triangleIndices[2] * vertexStride); | |
| 91 | + triangleVertex[2].setValue(triangleBase[0], triangleBase[1], triangleBase[2]); | |
| 92 | + } | |
| 93 | + break; | |
| 94 | + default: | |
| 95 | + return false; | |
| 96 | + } | |
| 97 | + | |
| 98 | + // compute faceNormal | |
| 99 | + btVector3 meshScaling = meshInterface->getScaling(); | |
| 100 | + triangleVertex[0] *= meshScaling; | |
| 101 | + triangleVertex[1] *= meshScaling; | |
| 102 | + triangleVertex[2] *= meshScaling; | |
| 103 | + btVector3 faceNormal = other->getWorldTransform().getBasis() * btCross(triangleVertex[1] - triangleVertex[0], triangleVertex[2] - triangleVertex[0]); | |
| 104 | + float nDotF = btDot(faceNormal, cp.m_normalWorldOnB); | |
| 105 | + if (nDotF <= 0.0f && faceNormal.length2() > EPSILON) { | |
| 106 | + faceNormal.normalize(); | |
| 107 | + // flip the contact normal to be aligned with the face normal... | |
| 108 | + // ...but only if old normal does NOT point along obj0's UP | |
| 109 | + // (because we're "stuck" and UP is the likely path out) | |
| 110 | + btVector3 up = colObj0Wrap->m_collisionObject->getWorldTransform().getBasis() * LOCAL_UP_AXIS; | |
| 111 | + if (cp.m_normalWorldOnB.dot(up) <= 0.0f) { | |
| 112 | + nDotF = btDot(faceNormal, cp.m_normalWorldOnB); | |
| 113 | + cp.m_normalWorldOnB -= 2.0f * nDotF * faceNormal; | |
| 114 | + _flippedThisFrame = true; | |
| 115 | + } | |
| 116 | + // Note: if we're flipping normals it means the "Are we stuck?" logic is concluding "Yes, we are". | |
| 117 | + // But when we flip the normals it typically causes the ContactManifold to discard the modified ManifoldPoint | |
| 118 | + // which in turn causes the "Are we stuck?" logic to incorrectly conclude "No, we are not". | |
| 119 | + // So we set '_flippedThisFrame = true' here and use it later to stay in "stuck" state until flipping stops | |
| 120 | + } | |
| 121 | + } | |
| 122 | + | |
| 123 | + // KEEP THIS: in case we add support for concave shapes which delegate to temporary btTriangleShapes (such as btHeightfieldTerrainShape) | |
| 124 | + //else if (other->getCollisionShape()->getShapeType() == TRIANGLE_SHAPE_PROXYTYPE) { | |
| 125 | + // auto triShape = static_cast<const btTriangleShape*>(other->getCollisionShape()); | |
| 126 | + // const btVector3* v = triShape->m_vertices1; | |
| 127 | + // btVector3 faceNormal = other->getWorldTransform().getBasis() * btCross(v[1] - v[0], v[2] - v[0]); | |
| 128 | + // float nDotF = btDot(faceNormal, cp.m_normalWorldOnB); | |
| 129 | + // if (nDotF <= 0.0f && faceNormal.length2() > EPSILON) { | |
| 130 | + // faceNormal.normalize(); | |
| 131 | + // // flip the contact normal to be aligned with the face normal | |
| 132 | + // cp.m_normalWorldOnB += -2.0f * nDotF * faceNormal; | |
| 133 | + // } | |
| 134 | + //} | |
| 135 | + // KEEP THIS | |
| 136 | + | |
| 137 | + // NOTE: this ManifoldPoint is a candidate and hasn't been accepted yet into the final ContactManifold yet. | |
| 138 | + // So when we modify its parameters we can convince Bullet to discard it. | |
| 139 | + // | |
| 140 | + // by our own convention: | |
| 141 | + // return true when this ManifoldPoint has been modified in a way that would disable it | |
| 142 | + return false; | |
| 143 | +}; | |
| 22 | 144 | |
| 23 | 145 | #ifdef DEBUG_STATE_CHANGE |
| 24 | 146 | #define SET_STATE(desiredState, reason) setState(desiredState, reason) |
| @@ -92,64 +214,70 @@ CharacterController::~CharacterController() { · 67 unmodified lines | ||
| 92 | 214 | } |
| 93 | 215 | |
| 94 | 216 | bool CharacterController::needsRemoval() const { |
| 95 | − return ((_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION); | |
| 217 | + return (_physicsEngine && (_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION); | |
| 96 | 218 | } |
| 97 | 219 | |
| 98 | 220 | bool CharacterController::needsAddition() const { |
| 99 | − return ((_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION) == PENDING_FLAG_ADD_TO_SIMULATION); | |
| 221 | + return (_physicsEngine && (_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION) == PENDING_FLAG_ADD_TO_SIMULATION); | |
| 100 | 222 | } |
| 101 | 223 | |
| 102 | −void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { | |
| 103 | − if (_dynamicsWorld != world) { | |
| 104 | − // remove from old world | |
| 105 | − if (_dynamicsWorld) { | |
| 106 | − if (_rigidBody) { | |
| 107 | − _dynamicsWorld->removeRigidBody(_rigidBody); | |
| 108 | − _dynamicsWorld->removeAction(this); | |
| 109 | − } | |
| 110 | − _dynamicsWorld = nullptr; | |
| 111 | − } | |
| 112 | − int32_t collisionMask = computeCollisionMask(); | |
| 113 | − int32_t collisionGroup = BULLET_COLLISION_GROUP_MY_AVATAR; | |
| 114 | − if (_rigidBody) { | |
| 115 | − updateMassProperties(); | |
| 116 | − } | |
| 117 | − if (world && _rigidBody) { | |
| 118 | − // add to new world | |
| 119 | − _dynamicsWorld = world; | |
| 120 | − _pendingFlags &= ~PENDING_FLAG_JUMP; | |
| 121 | − _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, collisionMask); | |
| 122 | − _dynamicsWorld->addAction(this); | |
| 123 | − // restore gravity settings because adding an object to the world overwrites its gravity setting | |
| 124 | − _rigidBody->setGravity(_currentGravity * _currentUp); | |
| 125 | − // set flag to enable custom contactAddedCallback | |
| 126 | − _rigidBody->setCollisionFlags(btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK); | |
| 127 | − | |
| 128 | − // enable CCD | |
| 129 | − _rigidBody->setCcdSweptSphereRadius(_radius); | |
| 130 | − _rigidBody->setCcdMotionThreshold(_radius); | |
| 131 | − | |
| 132 | − btCollisionShape* shape = _rigidBody->getCollisionShape(); | |
| 133 | − assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE); | |
| 134 | − _ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape)); | |
| 135 | − } | |
| 136 | − _ghost.setCollisionGroupAndMask(collisionGroup, collisionMask & (~ collisionGroup)); | |
| 137 | − _ghost.setCollisionWorld(_dynamicsWorld); | |
| 138 | − _ghost.setRadiusAndHalfHeight(_radius, _halfHeight); | |
| 224 | +void CharacterController::removeFromWorld() { | |
| 225 | + if (_inWorld) { | |
| 139 | 226 | if (_rigidBody) { |
| 140 | − _ghost.setWorldTransform(_rigidBody->getWorldTransform()); | |
| 227 | + _physicsEngine->getDynamicsWorld()->removeRigidBody(_rigidBody); | |
| 228 | + _physicsEngine->getDynamicsWorld()->removeAction(this); | |
| 141 | 229 | } |
| 230 | + _inWorld = false; | |
| 142 | 231 | } |
| 143 | − if (_dynamicsWorld) { | |
| 144 | − if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { | |
| 145 | − // shouldn't fall in here, but if we do make sure both ADD and REMOVE bits are still set | |
| 146 | − _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_REMOVE_FROM_SIMULATION | | |
| 147 | − PENDING_FLAG_ADD_DETAILED_TO_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION; | |
| 148 | − } else { | |
| 149 | − _pendingFlags &= ~PENDING_FLAG_ADD_TO_SIMULATION; | |
| 150 | − } | |
| 232 | + _pendingFlags &= ~PENDING_FLAG_REMOVE_FROM_SIMULATION; | |
| 233 | +} | |
| 234 | + | |
| 235 | +void CharacterController::addToWorld() { | |
| 236 | + if (!_rigidBody) { | |
| 237 | + return; | |
| 238 | + } | |
| 239 | + if (_inWorld) { | |
| 240 | + _pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION; | |
| 241 | + return; | |
| 242 | + } | |
| 243 | + btDiscreteDynamicsWorld* world = _physicsEngine->getDynamicsWorld(); | |
| 244 | + int32_t collisionMask = computeCollisionMask(); | |
| 245 | + int32_t collisionGroup = BULLET_COLLISION_GROUP_MY_AVATAR; | |
| 246 | + | |
| 247 | + updateMassProperties(); | |
| 248 | + _pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION; | |
| 249 | + | |
| 250 | + // add to new world | |
| 251 | + _pendingFlags &= ~PENDING_FLAG_JUMP; | |
| 252 | + world->addRigidBody(_rigidBody, collisionGroup, collisionMask); | |
| 253 | + world->addAction(this); | |
| 254 | + _inWorld = true; | |
| 255 | + | |
| 256 | + // restore gravity settings because adding an object to the world overwrites its gravity setting | |
| 257 | + _rigidBody->setGravity(_currentGravity * _currentUp); | |
| 258 | + // set flag to enable custom contactAddedCallback | |
| 259 | + _rigidBody->setCollisionFlags(btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK); | |
| 260 | + | |
| 261 | + // enable CCD | |
| 262 | + _rigidBody->setCcdSweptSphereRadius(_radius); | |
| 263 | + _rigidBody->setCcdMotionThreshold(_radius); | |
| 264 | + | |
| 265 | + btCollisionShape* shape = _rigidBody->getCollisionShape(); | |
| 266 | + assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE); | |
| 267 | + _ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape)); | |
| 268 | + | |
| 269 | + _ghost.setCollisionGroupAndMask(collisionGroup, collisionMask & (~ collisionGroup)); | |
| 270 | + _ghost.setCollisionWorld(world); | |
| 271 | + _ghost.setRadiusAndHalfHeight(_radius, _halfHeight); | |
| 272 | + if (_rigidBody) { | |
| 273 | + _ghost.setWorldTransform(_rigidBody->getWorldTransform()); | |
| 274 | + } | |
| 275 | + if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { | |
| 276 | + // shouldn't fall in here, but if we do make sure both ADD and REMOVE bits are still set | |
| 277 | + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_REMOVE_FROM_SIMULATION | | |
| 278 | + PENDING_FLAG_ADD_DETAILED_TO_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION; | |
| 151 | 279 | } else { |
| 152 | − _pendingFlags &= ~PENDING_FLAG_REMOVE_FROM_SIMULATION; | |
| 280 | + _pendingFlags &= ~PENDING_FLAG_ADD_TO_SIMULATION; | |
| 153 | 281 | } |
| 154 | 282 | } |
| 155 | 283 | |
| @@ -159,17 +287,21 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) { · 3 unmodified lines | ||
| 159 | 287 | btDispatcher* dispatcher = collisionWorld->getDispatcher(); |
| 160 | 288 | int numManifolds = dispatcher->getNumManifolds(); |
| 161 | 289 | bool hasFloor = false; |
| 162 | − bool isStuck = false; | |
| 290 | + bool probablyStuck = _isStuck && _flippedThisFrame; | |
| 163 | 291 | |
| 164 | 292 | btTransform rotation = _rigidBody->getWorldTransform(); |
| 165 | 293 | rotation.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); // clear translation part |
| 166 | 294 | |
| 295 | + float deepestDistance = 0.0f; | |
| 296 | + float strongestImpulse = 0.0f; | |
| 297 | + | |
| 167 | 298 | for (int i = 0; i < numManifolds; i++) { |
| 168 | 299 | btPersistentManifold* contactManifold = dispatcher->getManifoldByIndexInternal(i); |
| 169 | 300 | if (_rigidBody == contactManifold->getBody1() || _rigidBody == contactManifold->getBody0()) { |
| 170 | 301 | bool characterIsFirst = _rigidBody == contactManifold->getBody0(); |
| 171 | 302 | int numContacts = contactManifold->getNumContacts(); |
| 172 | 303 | int stepContactIndex = -1; |
| 304 | + bool stepValid = true; | |
| 173 | 305 | float highestStep = _minStepHeight; |
| 174 | 306 | for (int j = 0; j < numContacts; j++) { |
| 175 | 307 | // check for "floor" |
| @@ -177,28 +309,24 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) { · 1 unmodified line | ||
| 177 | 309 | btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame |
| 178 | 310 | btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character |
| 179 | 311 | btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp); |
| 180 | − // If there's non-trivial penetration with a big impulse for several steps, we're probably stuck. | |
| 181 | − // Note it here in the controller, and let MyAvatar figure out what to do about it. | |
| 182 | − const float STUCK_PENETRATION = -0.05f; // always negative into the object. | |
| 183 | − const float STUCK_IMPULSE = 500.0f; | |
| 184 | − const int STUCK_LIFETIME = 3; | |
| 185 | − if ((contact.getDistance() < STUCK_PENETRATION) && (contact.getAppliedImpulse() > STUCK_IMPULSE) && (contact.getLifeTime() > STUCK_LIFETIME)) { | |
| 186 | − isStuck = true; // latch on | |
| 312 | + | |
| 313 | + float distance = contact.getDistance(); | |
| 314 | + if (distance < deepestDistance) { | |
| 315 | + deepestDistance = distance; | |
| 316 | + } | |
| 317 | + float impulse = contact.getAppliedImpulse(); | |
| 318 | + if (impulse > strongestImpulse) { | |
| 319 | + strongestImpulse = impulse; | |
| 187 | 320 | } |
| 321 | + | |
| 188 | 322 | if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > _minFloorNormalDotUp) { |
| 189 | 323 | hasFloor = true; |
| 190 | − if (!pushing && isStuck) { | |
| 191 | − // we're not pushing against anything and we're stuck so we can early exit | |
| 192 | − // (all we need to know is that there is a floor) | |
| 193 | − break; | |
| 194 | − } | |
| 195 | 324 | } |
| 196 | − if (pushing && _targetVelocity.dot(normal) < 0.0f) { | |
| 325 | + if (stepValid && pushing && _targetVelocity.dot(normal) < 0.0f) { | |
| 197 | 326 | // remember highest step obstacle |
| 198 | 327 | if (!_stepUpEnabled || hitHeight > _maxStepHeight) { |
| 199 | 328 | // this manifold is invalidated by point that is too high |
| 200 | − stepContactIndex = -1; | |
| 201 | − break; | |
| 329 | + stepValid = false; | |
| 202 | 330 | } else if (hitHeight > highestStep && normal.dot(_targetVelocity) < 0.0f ) { |
| 203 | 331 | highestStep = hitHeight; |
| 204 | 332 | stepContactIndex = j; |
| @@ -206,7 +334,7 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) { · 1 unmodified line | ||
| 206 | 334 | } |
| 207 | 335 | } |
| 208 | 336 | } |
| 209 | − if (stepContactIndex > -1 && highestStep > _stepHeight) { | |
| 337 | + if (stepValid && stepContactIndex > -1 && highestStep > _stepHeight) { | |
| 210 | 338 | // remember step info for later |
| 211 | 339 | btManifoldPoint& contact = contactManifold->getContactPoint(stepContactIndex); |
| 212 | 340 | btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame |
| @@ -214,13 +342,37 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) { · 1 unmodified line | ||
| 214 | 342 | _stepHeight = highestStep; |
| 215 | 343 | _stepPoint = rotation * pointOnCharacter; // rotate into world-frame |
| 216 | 344 | } |
| 217 | − if (hasFloor && isStuck && !(pushing && _stepUpEnabled)) { | |
| 218 | − // early exit since all we need to know is that we're on a floor | |
| 219 | − break; | |
| 345 | + } | |
| 346 | + } | |
| 347 | + | |
| 348 | + // If there's deep penetration and big impulse we're probably stuck. | |
| 349 | + const float STUCK_PENETRATION = -0.05f; // always negative into the object. | |
| 350 | + const float STUCK_IMPULSE = 500.0f; | |
| 351 | + probablyStuck = probablyStuck || (deepestDistance < STUCK_PENETRATION && strongestImpulse > STUCK_IMPULSE); | |
| 352 | + | |
| 353 | + if (_isStuck != probablyStuck) { | |
| 354 | + ++_stuckTransitionCount; | |
| 355 | + if (_stuckTransitionCount == NUM_FRAMES_FOR_STUCK_TRANSITION) { | |
| 356 | + // we've been in this "probablyStuck" state for several consecutive frames | |
| 357 | + // --> make it official by changing state | |
| 358 | + _isStuck = probablyStuck; | |
| 359 | + // start _numStuckFrames at NUM_FRAMES_FOR_SAFE_LANDING_RETRY so SafeLanding tries to help immediately | |
| 360 | + _numStuckFrames = NUM_FRAMES_FOR_SAFE_LANDING_RETRY; | |
| 361 | + _stuckTransitionCount = 0; | |
| 362 | + if (_isStuck) { | |
| 363 | + _physicsEngine->addContactAddedCallback(flipBackfaceTriangleNormals); | |
| 364 | + } else { | |
| 365 | + _physicsEngine->removeContactAddedCallback(flipBackfaceTriangleNormals); | |
| 366 | + _flippedThisFrame = false; | |
| 220 | 367 | } |
| 221 | 368 | } |
| 369 | + } else { | |
| 370 | + _stuckTransitionCount = 0; | |
| 371 | + if (_isStuck) { | |
| 372 | + ++_numStuckFrames; | |
| 373 | + _flippedThisFrame = false; | |
| 374 | + } | |
| 222 | 375 | } |
| 223 | − _isStuck = isStuck; | |
| 224 | 376 | return hasFloor; |
| 225 | 377 | } |
| 226 | 378 | |
| @@ -452,7 +604,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const · 225 unmodified lines | ||
| 452 | 604 | _minStepHeight = DEFAULT_MIN_STEP_HEIGHT_FACTOR * (_halfHeight + _radius); |
| 453 | 605 | _maxStepHeight = DEFAULT_MAX_STEP_HEIGHT_FACTOR * (_halfHeight + _radius); |
| 454 | 606 | |
| 455 | − if (_dynamicsWorld) { | |
| 607 | + if (_physicsEngine) { | |
| 456 | 608 | // must REMOVE from world prior to shape update |
| 457 | 609 | _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION; |
| 458 | 610 | } |
| @@ -470,6 +622,15 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const · 11 unmodified lines | ||
| 470 | 622 | } |
| 471 | 623 | } |
| 472 | 624 | |
| 625 | +void CharacterController::setPhysicsEngine(const PhysicsEnginePointer& engine) { | |
| 626 | + if (!_physicsEngine && engine) { | |
| 627 | + // ATM there is only one PhysicsEngine: it is a singleton, and we are taking advantage | |
| 628 | + // of that assumption here. If we ever introduce more and allow for this backpointer | |
| 629 | + // to change then we'll have to overhaul this method. | |
| 630 | + _physicsEngine = engine; | |
| 631 | + } | |
| 632 | +} | |
| 633 | + | |
| 473 | 634 | void CharacterController::setCollisionless(bool collisionless) { |
| 474 | 635 | if (collisionless != _collisionless) { |
| 475 | 636 | _collisionless = collisionless; |
| @@ -681,7 +842,7 @@ void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) { · 205 unmodified lines | ||
| 681 | 842 | } |
| 682 | 843 | |
| 683 | 844 | void CharacterController::updateState() { |
| 684 | − if (!_dynamicsWorld) { | |
| 845 | + if (!_physicsEngine) { | |
| 685 | 846 | return; |
| 686 | 847 | } |
| 687 | 848 | if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) { |
| @@ -712,7 +873,7 @@ void CharacterController::updateState() { · 24 unmodified lines | ||
| 712 | 873 | |
| 713 | 874 | ClosestNotMe rayCallback(_rigidBody); |
| 714 | 875 | rayCallback.m_closestHitFraction = 1.0f; |
| 715 | − _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); | |
| 876 | + _physicsEngine->getDynamicsWorld()->rayTest(rayStart, rayEnd, rayCallback); | |
| 716 | 877 | bool rayHasHit = rayCallback.hasHit(); |
| 717 | 878 | quint64 now = usecTimestampNow(); |
| 718 | 879 | if (rayHasHit) { |
| @@ -829,6 +990,21 @@ void CharacterController::updateState() { · 110 unmodified lines | ||
| 829 | 990 | } |
| 830 | 991 | |
| 831 | 992 | void CharacterController::preSimulation() { |
| 993 | + if (needsRemoval()) { | |
| 994 | + removeFromWorld(); | |
| 995 | + | |
| 996 | + // We must remove any existing contacts for the avatar so that any new contacts will have | |
| 997 | + // valid data. MyAvatar's RigidBody is the ONLY one in the simulation that does not yet | |
| 998 | + // have a MotionState so we pass nullptr to removeContacts(). | |
| 999 | + if (_physicsEngine) { | |
| 1000 | + _physicsEngine->removeContacts(nullptr); | |
| 1001 | + } | |
| 1002 | + } | |
| 1003 | + updateShapeIfNecessary(); | |
| 1004 | + if (needsAddition()) { | |
| 1005 | + addToWorld(); | |
| 1006 | + } | |
| 1007 | + | |
| 832 | 1008 | if (_rigidBody) { |
| 833 | 1009 | // slam body transform and remember velocity |
| 834 | 1010 | _rigidBody->setWorldTransform(btTransform(btTransform(_rotation, _position))); |