History

improved isStuck detection and MyAvatar::safeLanding() trigger

Andrew Meadows committed 7 years ago

Commit 32400a61 parent 9382fb88 files · +311 −189Browse files

Showing the changes to libraries/physics/src/CharacterController.cpp (1 of 8 files). Show all

Unified · Split

libraries/physics/src/CharacterController.cpp+250−74
@@ -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
21const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f);22const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f);
23static 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.
28bool 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_CHANGE145#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
94bool CharacterController::needsRemoval() const {216bool 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
98bool CharacterController::needsAddition() const {220bool 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
102void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {224void 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 set234
146 _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_REMOVE_FROM_SIMULATION | 235void 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 part293 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-frame309 btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame
178 btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character310 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 on318 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 obstacle326 // remember highest step obstacle
198 if (!_stepUpEnabled || hitHeight > _maxStepHeight) {327 if (!_stepUpEnabled || hitHeight > _maxStepHeight) {
199 // this manifold is invalidated by point that is too high328 // 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 later338 // 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-frame340 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-frame343 _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 floor346 }
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 update608 // 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
625void 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
473void CharacterController::setCollisionless(bool collisionless) {634void 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
683void CharacterController::updateState() {844void 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
831void CharacterController::preSimulation() {992void 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 velocity1009 // slam body transform and remember velocity
834 _rigidBody->setWorldTransform(btTransform(btTransform(_rotation, _position)));1010 _rigidBody->setWorldTransform(btTransform(btTransform(_rotation, _position)));