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 | #include "CharacterController.h" | 12 | #include "CharacterController.h" |
| 13 | 13 | ||
| 14 | #include <NumericalConstants.h> | ||
| 15 | #include <AvatarConstants.h> | 14 | #include <AvatarConstants.h> |
| 15 | #include <NumericalConstants.h> | ||
| 16 | #include <PhysicsCollisionGroups.h> | ||
| 16 | 17 | ||
| 17 | #include "ObjectMotionState.h" | 18 | #include "ObjectMotionState.h" |
| 18 | #include "PhysicsHelpers.h" | 19 | #include "PhysicsHelpers.h" |
| 19 | #include "PhysicsLogging.h" | 20 | #include "PhysicsLogging.h" |
| 20 | 21 | ||
| 21 | const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); | 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 | #ifdef DEBUG_STATE_CHANGE | 145 | #ifdef DEBUG_STATE_CHANGE |
| 24 | #define SET_STATE(desiredState, reason) setState(desiredState, reason) | 146 | #define SET_STATE(desiredState, reason) setState(desiredState, reason) |
| @@ -92,64 +214,70 @@ CharacterController::~CharacterController() { · 67 unmodified lines | |||
| 92 | } | 214 | } |
| 93 | 215 | ||
| 94 | bool CharacterController::needsRemoval() const { | 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 | bool CharacterController::needsAddition() const { | 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) { | 224 | void CharacterController::removeFromWorld() { |
| 103 | if (_dynamicsWorld != world) { | 225 | if (_inWorld) { |
| 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); | ||
| 139 | if (_rigidBody) { | 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) { | 232 | _pendingFlags &= ~PENDING_FLAG_REMOVE_FROM_SIMULATION; |
| 144 | if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { | 233 | } |
| 145 | // shouldn't fall in here, but if we do make sure both ADD and REMOVE bits are still set | 234 | |
| 146 | _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_REMOVE_FROM_SIMULATION | | 235 | void CharacterController::addToWorld() { |
| 147 | PENDING_FLAG_ADD_DETAILED_TO_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION; | 236 | if (!_rigidBody) { |
| 148 | } else { | 237 | return; |
| 149 | _pendingFlags &= ~PENDING_FLAG_ADD_TO_SIMULATION; | 238 | } |
| 150 | } | 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 | } else { | 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 | btDispatcher* dispatcher = collisionWorld->getDispatcher(); | 287 | btDispatcher* dispatcher = collisionWorld->getDispatcher(); |
| 160 | int numManifolds = dispatcher->getNumManifolds(); | 288 | int numManifolds = dispatcher->getNumManifolds(); |
| 161 | bool hasFloor = false; | 289 | bool hasFloor = false; |
| 162 | bool isStuck = false; | 290 | bool probablyStuck = _isStuck && _flippedThisFrame; |
| 163 | 291 | ||
| 164 | btTransform rotation = _rigidBody->getWorldTransform(); | 292 | btTransform rotation = _rigidBody->getWorldTransform(); |
| 165 | rotation.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); // clear translation part | 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 | for (int i = 0; i < numManifolds; i++) { | 298 | for (int i = 0; i < numManifolds; i++) { |
| 168 | btPersistentManifold* contactManifold = dispatcher->getManifoldByIndexInternal(i); | 299 | btPersistentManifold* contactManifold = dispatcher->getManifoldByIndexInternal(i); |
| 169 | if (_rigidBody == contactManifold->getBody1() || _rigidBody == contactManifold->getBody0()) { | 300 | if (_rigidBody == contactManifold->getBody1() || _rigidBody == contactManifold->getBody0()) { |
| 170 | bool characterIsFirst = _rigidBody == contactManifold->getBody0(); | 301 | bool characterIsFirst = _rigidBody == contactManifold->getBody0(); |
| 171 | int numContacts = contactManifold->getNumContacts(); | 302 | int numContacts = contactManifold->getNumContacts(); |
| 172 | int stepContactIndex = -1; | 303 | int stepContactIndex = -1; |
| 304 | bool stepValid = true; | ||
| 173 | float highestStep = _minStepHeight; | 305 | float highestStep = _minStepHeight; |
| 174 | for (int j = 0; j < numContacts; j++) { | 306 | for (int j = 0; j < numContacts; j++) { |
| 175 | // check for "floor" | 307 | // check for "floor" |
| @@ -177,28 +309,24 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) { · 1 unmodified line | |||
| 177 | btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame | 309 | btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame |
| 178 | btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character | 310 | btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character |
| 179 | btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp); | 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. | 312 | |
| 181 | // Note it here in the controller, and let MyAvatar figure out what to do about it. | 313 | float distance = contact.getDistance(); |
| 182 | const float STUCK_PENETRATION = -0.05f; // always negative into the object. | 314 | if (distance < deepestDistance) { |
| 183 | const float STUCK_IMPULSE = 500.0f; | 315 | deepestDistance = distance; |
| 184 | const int STUCK_LIFETIME = 3; | 316 | } |
| 185 | if ((contact.getDistance() < STUCK_PENETRATION) && (contact.getAppliedImpulse() > STUCK_IMPULSE) && (contact.getLifeTime() > STUCK_LIFETIME)) { | 317 | float impulse = contact.getAppliedImpulse(); |
| 186 | isStuck = true; // latch on | 318 | if (impulse > strongestImpulse) { |
| 319 | strongestImpulse = impulse; | ||
| 187 | } | 320 | } |
| 321 | |||
| 188 | if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > _minFloorNormalDotUp) { | 322 | if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > _minFloorNormalDotUp) { |
| 189 | hasFloor = true; | 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 | // remember highest step obstacle | 326 | // remember highest step obstacle |
| 198 | if (!_stepUpEnabled || hitHeight > _maxStepHeight) { | 327 | if (!_stepUpEnabled || hitHeight > _maxStepHeight) { |
| 199 | // this manifold is invalidated by point that is too high | 328 | // this manifold is invalidated by point that is too high |
| 200 | stepContactIndex = -1; | 329 | stepValid = false; |
| 201 | break; | ||
| 202 | } else if (hitHeight > highestStep && normal.dot(_targetVelocity) < 0.0f ) { | 330 | } else if (hitHeight > highestStep && normal.dot(_targetVelocity) < 0.0f ) { |
| 203 | highestStep = hitHeight; | 331 | highestStep = hitHeight; |
| 204 | stepContactIndex = j; | 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 | // remember step info for later | 338 | // remember step info for later |
| 211 | btManifoldPoint& contact = contactManifold->getContactPoint(stepContactIndex); | 339 | btManifoldPoint& contact = contactManifold->getContactPoint(stepContactIndex); |
| 212 | btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame | 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 | _stepHeight = highestStep; | 342 | _stepHeight = highestStep; |
| 215 | _stepPoint = rotation * pointOnCharacter; // rotate into world-frame | 343 | _stepPoint = rotation * pointOnCharacter; // rotate into world-frame |
| 216 | } | 344 | } |
| 217 | if (hasFloor && isStuck && !(pushing && _stepUpEnabled)) { | 345 | } |
| 218 | // early exit since all we need to know is that we're on a floor | 346 | } |
| 219 | break; | 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 | return hasFloor; | 376 | return hasFloor; |
| 225 | } | 377 | } |
| 226 | 378 | ||
| @@ -452,7 +604,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const · 225 unmodified lines | |||
| 452 | _minStepHeight = DEFAULT_MIN_STEP_HEIGHT_FACTOR * (_halfHeight + _radius); | 604 | _minStepHeight = DEFAULT_MIN_STEP_HEIGHT_FACTOR * (_halfHeight + _radius); |
| 453 | _maxStepHeight = DEFAULT_MAX_STEP_HEIGHT_FACTOR * (_halfHeight + _radius); | 605 | _maxStepHeight = DEFAULT_MAX_STEP_HEIGHT_FACTOR * (_halfHeight + _radius); |
| 454 | 606 | ||
| 455 | if (_dynamicsWorld) { | 607 | if (_physicsEngine) { |
| 456 | // must REMOVE from world prior to shape update | 608 | // must REMOVE from world prior to shape update |
| 457 | _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION; | 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 | void CharacterController::setCollisionless(bool collisionless) { | 634 | void CharacterController::setCollisionless(bool collisionless) { |
| 474 | if (collisionless != _collisionless) { | 635 | if (collisionless != _collisionless) { |
| 475 | _collisionless = collisionless; | 636 | _collisionless = collisionless; |
| @@ -681,7 +842,7 @@ void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) { · 205 unmodified lines | |||
| 681 | } | 842 | } |
| 682 | 843 | ||
| 683 | void CharacterController::updateState() { | 844 | void CharacterController::updateState() { |
| 684 | if (!_dynamicsWorld) { | 845 | if (!_physicsEngine) { |
| 685 | return; | 846 | return; |
| 686 | } | 847 | } |
| 687 | if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) { | 848 | if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) { |
| @@ -712,7 +873,7 @@ void CharacterController::updateState() { · 24 unmodified lines | |||
| 712 | 873 | ||
| 713 | ClosestNotMe rayCallback(_rigidBody); | 874 | ClosestNotMe rayCallback(_rigidBody); |
| 714 | rayCallback.m_closestHitFraction = 1.0f; | 875 | rayCallback.m_closestHitFraction = 1.0f; |
| 715 | _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); | 876 | _physicsEngine->getDynamicsWorld()->rayTest(rayStart, rayEnd, rayCallback); |
| 716 | bool rayHasHit = rayCallback.hasHit(); | 877 | bool rayHasHit = rayCallback.hasHit(); |
| 717 | quint64 now = usecTimestampNow(); | 878 | quint64 now = usecTimestampNow(); |
| 718 | if (rayHasHit) { | 879 | if (rayHasHit) { |
| @@ -829,6 +990,21 @@ void CharacterController::updateState() { · 110 unmodified lines | |||
| 829 | } | 990 | } |
| 830 | 991 | ||
| 831 | void CharacterController::preSimulation() { | 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 | if (_rigidBody) { | 1008 | if (_rigidBody) { |
| 833 | // slam body transform and remember velocity | 1009 | // slam body transform and remember velocity |
| 834 | _rigidBody->setWorldTransform(btTransform(btTransform(_rotation, _position))); | 1010 | _rigidBody->setWorldTransform(btTransform(btTransform(_rotation, _position))); |