From 6454149536921dee807cfce03388ba3a1bd85262 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 20 Mar 2015 12:45:29 -0700 Subject: [PATCH 1/9] formatting and comments --- libraries/physics/src/CharacterController.cpp | 25 +++++++++++++------ libraries/physics/src/CharacterController.h | 2 -- libraries/physics/src/PhysicsEngine.cpp | 4 ++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index fb200b2c57..256c83357a 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -231,7 +231,6 @@ CharacterController::CharacterController(AvatarData* avatarData) { m_addedMargin = 0.02f; m_walkDirection.setValue(0.0f,0.0f,0.0f); - m_turnAngle = btScalar(0.0f); m_useWalkDirection = true; // use walk direction by default, legacy behavior m_velocityTimeInterval = 0.0f; m_verticalVelocity = 0.0f; @@ -797,19 +796,31 @@ void CharacterController::updateShape() { } void CharacterController::preSimulation(btScalar timeStep) { + bool wasEnabled = m_enabled; + + // lock avatarData, get everything we need from it ASAP, then unlock m_avatarData->lockForRead(); - - // cache the "PhysicsEnabled" state of m_avatarData here - // and use the cached value for the rest of the simulation step m_enabled = m_avatarData->isPhysicsEnabled(); - glm::quat rotation = m_avatarData->getOrientation(); glm::vec3 position = m_avatarData->getPosition() + rotation * m_shapeLocalOffset; - m_ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); + // TODO: Andrew to implement: harvest jump event here btVector3 walkVelocity = glmToBullet(m_avatarData->getVelocity()); - setVelocityForTimeInterval(walkVelocity, timeStep); m_avatarData->unlock(); + + if (wasEnabled != m_enabled) { + if (m_enabled) { + // TODO: Andrew to implement: add collision shape back into world + } else { + // TODO: Andrew to implement: remove collision shape from world, + // otherwise things will continue to collide with it + } + } + + if (m_enabled) { + m_ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); + setVelocityForTimeInterval(walkVelocity, timeStep); + } } void CharacterController::postSimulation() { diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 7969fffd9d..b84662a389 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -58,8 +58,6 @@ protected: btScalar m_maxSlopeCosine; // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization) btScalar m_gravity; - btScalar m_turnAngle; - btScalar m_stepHeight; // height of stepUp prior to stepForward btScalar m_addedMargin;//@todo: remove this and fix the code diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index a46ba9f819..447a3ab6f1 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -294,16 +294,18 @@ void PhysicsEngine::stepSimulation() { _clock.reset(); float timeStep = btMin(dt, MAX_TIMESTEP); - // This is step (2). + // TODO: move character->preSimulation() into relayIncomingChanges if (_characterController) { _characterController->preSimulation(timeStep); } + // This is step (2). int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); _numSubsteps += (uint32_t)numSubsteps; stepNonPhysicalKinematics(usecTimestampNow()); unlock(); + // TODO: make all of this harvest stuff into one function: relayOutgoingChanges() if (numSubsteps > 0) { // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. // From 0164de80fe5d235d5a44d05489841b2df194dabc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 20 Mar 2015 13:19:24 -0700 Subject: [PATCH 2/9] apply coding standard to CharacterController --- libraries/physics/src/CharacterController.cpp | 514 +++++++++--------- libraries/physics/src/CharacterController.h | 65 ++- 2 files changed, 285 insertions(+), 294 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 256c83357a..f7f8ab4d53 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -48,18 +48,18 @@ class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::Closes public: btKinematicClosestNotMeRayResultCallback(btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) { -m_me = me; +_me = me; } virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) { -if (rayResult.m_collisionObject == m_me) +if (rayResult.m_collisionObject == _me) return 1.0; return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); } protected: -btCollisionObject* m_me; +btCollisionObject* _me; }; */ @@ -67,14 +67,14 @@ class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::Clo public: btKinematicClosestNotMeConvexResultCallback(btCollisionObject* me, const btVector3& up, btScalar minSlopeDot) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) - , m_me(me) - , m_up(up) - , m_minSlopeDot(minSlopeDot) + , _me(me) + , _up(up) + , _minSlopeDot(minSlopeDot) { } virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace) { - if (convexResult.m_hitCollisionObject == m_me) { + if (convexResult.m_hitCollisionObject == _me) { return btScalar(1.0); } @@ -91,10 +91,10 @@ class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::Clo } // Note: hitNormalWorld points into character, away from object - // and m_up points opposite to movement + // and _up points opposite to movement - btScalar dotUp = m_up.dot(hitNormalWorld); - if (dotUp < m_minSlopeDot) { + btScalar dotUp = _up.dot(hitNormalWorld); + if (dotUp < _minSlopeDot) { return btScalar(1.0); } @@ -102,9 +102,9 @@ class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::Clo } protected: - btCollisionObject* m_me; - const btVector3 m_up; - btScalar m_minSlopeDot; + btCollisionObject* _me; + const btVector3 _up; + btScalar _minSlopeDot; }; class StepDownConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { @@ -119,19 +119,19 @@ class StepDownConvexResultCallback : public btCollisionWorld::ClosestConvexResul btScalar radius, btScalar halfHeight) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) - , m_me(me) - , m_up(up) - , m_start(start) - , m_step(step) - , m_pushDirection(pushDirection) - , m_minSlopeDot(minSlopeDot) - , m_radius(radius) - , m_halfHeight(halfHeight) + , _me(me) + , _up(up) + , _start(start) + , _step(step) + , _pushDirection(pushDirection) + , _minSlopeDot(minSlopeDot) + , _radius(radius) + , _halfHeight(halfHeight) { } virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace) { - if (convexResult.m_hitCollisionObject == m_me) { + if (convexResult.m_hitCollisionObject == _me) { return btScalar(1.0); } @@ -148,24 +148,24 @@ class StepDownConvexResultCallback : public btCollisionWorld::ClosestConvexResul } // Note: hitNormalWorld points into character, away from object - // and m_up points opposite to movement + // and _up points opposite to movement - btScalar dotUp = m_up.dot(hitNormalWorld); - if (dotUp < m_minSlopeDot) { - if (hitNormalWorld.dot(m_pushDirection) > 0.0f) { + btScalar dotUp = _up.dot(hitNormalWorld); + if (dotUp < _minSlopeDot) { + if (hitNormalWorld.dot(_pushDirection) > 0.0f) { // ignore hits that push in same direction as character is moving // which helps character NOT snag when stepping off ledges return btScalar(1.0f); } // compute the angle between "down" and the line from character center to "hit" point - btVector3 fractionalStep = convexResult.m_hitFraction * m_step; - btVector3 localHit = convexResult.m_hitPointLocal - m_start + fractionalStep; - btScalar angle = localHit.angle(-m_up); + btVector3 fractionalStep = convexResult.m_hitFraction * _step; + btVector3 localHit = convexResult.m_hitPointLocal - _start + fractionalStep; + btScalar angle = localHit.angle(-_up); - // compute a maxAngle based on size of m_step - btVector3 side(m_radius, - (m_halfHeight - m_step.length() + fractionalStep.dot(m_up)), 0.0f); - btScalar maxAngle = side.angle(-m_up); + // compute a maxAngle based on size of _step + btVector3 side(_radius, - (_halfHeight - _step.length() + fractionalStep.dot(_up)), 0.0f); + btScalar maxAngle = side.angle(-_up); // Ignore hits that are larger than maxAngle. Effectively what is happening here is: // we're ignoring hits at contacts that have non-vertical normals... if they hit higher @@ -182,14 +182,14 @@ class StepDownConvexResultCallback : public btCollisionWorld::ClosestConvexResul } protected: - btCollisionObject* m_me; - const btVector3 m_up; - btVector3 m_start; - btVector3 m_step; - btVector3 m_pushDirection; - btScalar m_minSlopeDot; - btScalar m_radius; - btScalar m_halfHeight; + btCollisionObject* _me; + const btVector3 _up; + btVector3 _start; + btVector3 _step; + btVector3 _pushDirection; + btScalar _minSlopeDot; + btScalar _radius; + btScalar _halfHeight; }; /* @@ -218,42 +218,37 @@ btVector3 CharacterController::perpindicularComponent(const btVector3& direction CharacterController::CharacterController(AvatarData* avatarData) { assert(avatarData); - m_avatarData = avatarData; + _avatarData = avatarData; - // cache the "PhysicsEnabled" state of m_avatarData - m_avatarData->lockForRead(); - m_enabled = m_avatarData->isPhysicsEnabled(); - m_avatarData->unlock(); + // cache the "PhysicsEnabled" state of _avatarData + _avatarData->lockForRead(); + _enabled = _avatarData->isPhysicsEnabled(); + _avatarData->unlock(); createShapeAndGhost(); - m_upAxis = 1; // HACK: hard coded to be 1 for now (yAxis) + _upAxis = 1; // HACK: hard coded to be 1 for now (yAxis) - m_addedMargin = 0.02f; - m_walkDirection.setValue(0.0f,0.0f,0.0f); - m_useWalkDirection = true; // use walk direction by default, legacy behavior - m_velocityTimeInterval = 0.0f; - m_verticalVelocity = 0.0f; - m_verticalOffset = 0.0f; - m_gravity = 9.8f; - m_maxFallSpeed = 55.0f; // Terminal velocity of a sky diver in m/s. - m_jumpSpeed = 10.0f; // ? - m_wasOnGround = false; - m_wasJumping = false; - m_interpolateUp = true; + _addedMargin = 0.02f; + _walkDirection.setValue(0.0f,0.0f,0.0f); + _useWalkDirection = true; // use walk direction by default, legacy behavior + _velocityTimeInterval = 0.0f; + _verticalVelocity = 0.0f; + _verticalOffset = 0.0f; + _gravity = 9.8f; + _maxFallSpeed = 55.0f; // Terminal velocity of a sky diver in m/s. + _jumpSpeed = 10.0f; // ? + _wasOnGround = false; + _wasJumping = false; setMaxSlope(btRadians(45.0f)); - m_lastStepUp = 0.0f; - - // internal state data members - full_drop = false; - bounce_fix = false; + _lastStepUp = 0.0f; } CharacterController::~CharacterController() { } btPairCachingGhostObject* CharacterController::getGhostObject() { - return m_ghostObject; + return _ghostObject; } bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorld) { @@ -266,26 +261,26 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl // paircache and the ghostobject's internal paircache at the same time. /BW btVector3 minAabb, maxAabb; - m_convexShape->getAabb(m_ghostObject->getWorldTransform(), minAabb, maxAabb); - collisionWorld->getBroadphase()->setAabb(m_ghostObject->getBroadphaseHandle(), + _convexShape->getAabb(_ghostObject->getWorldTransform(), minAabb, maxAabb); + collisionWorld->getBroadphase()->setAabb(_ghostObject->getBroadphaseHandle(), minAabb, maxAabb, collisionWorld->getDispatcher()); bool penetration = false; - collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher()); + collisionWorld->getDispatcher()->dispatchAllCollisionPairs(_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher()); - m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); - btVector3 up = getUpAxisDirections()[m_upAxis]; + _currentPosition = _ghostObject->getWorldTransform().getOrigin(); + btVector3 up = getUpAxisDirections()[_upAxis]; - btVector3 currentPosition = m_currentPosition; + btVector3 currentPosition = _currentPosition; btScalar maxPen = btScalar(0.0); - for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++) { - m_manifoldArray.resize(0); + for (int i = 0; i < _ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++) { + _manifoldArray.resize(0); - btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; + btBroadphasePair* collisionPair = &_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; btCollisionObject* obj0 = static_cast(collisionPair->m_pProxy0->m_clientObject); btCollisionObject* obj1 = static_cast(collisionPair->m_pProxy1->m_clientObject); @@ -295,12 +290,12 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl } if (collisionPair->m_algorithm) { - collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray); + collisionPair->m_algorithm->getAllContactManifolds(_manifoldArray); } - for (int j = 0;j < m_manifoldArray.size(); j++) { - btPersistentManifold* manifold = m_manifoldArray[j]; - btScalar directionSign = (manifold->getBody0() == m_ghostObject) ? btScalar(1.0) : btScalar(-1.0); + for (int j = 0;j < _manifoldArray.size(); j++) { + btPersistentManifold* manifold = _manifoldArray[j]; + btScalar directionSign = (manifold->getBody0() == _ghostObject) ? btScalar(1.0) : btScalar(-1.0); for (int p = 0;p < manifold->getNumContacts(); p++) { const btManifoldPoint&pt = manifold->getContactPoint(p); @@ -312,7 +307,7 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl normal *= directionSign; // always points from object to character btScalar normalDotUp = normal.dot(up); - if (normalDotUp < m_maxSlopeCosine) { + if (normalDotUp < _maxSlopeCosine) { // this contact has a non-vertical normal... might need to ignored btVector3 collisionPoint; if (directionSign > 0.0) { @@ -322,11 +317,11 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl } // we do math in frame where character base is origin - btVector3 characterBase = currentPosition - (m_radius + m_halfHeight) * up; + btVector3 characterBase = currentPosition - (_radius + _halfHeight) * up; collisionPoint -= characterBase; btScalar collisionHeight = collisionPoint.dot(up); - if (collisionHeight < m_lastStepUp) { + if (collisionHeight < _lastStepUp) { // This contact is below the lastStepUp, so we ignore it for penetration resolution, // otherwise it may prevent the character from getting close enough to find any available // horizontal foothold that would allow it to climbe the ledge. In other words, we're @@ -338,19 +333,19 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl if (dist < maxPen) { maxPen = dist; - m_floorNormal = normal; + _floorNormal = normal; } const btScalar INCREMENTAL_RESOLUTION_FACTOR = 0.2f; - m_currentPosition += normal * (fabsf(dist) * INCREMENTAL_RESOLUTION_FACTOR); + _currentPosition += normal * (fabsf(dist) * INCREMENTAL_RESOLUTION_FACTOR); penetration = true; } } } } } - btTransform newTrans = m_ghostObject->getWorldTransform(); - newTrans.setOrigin(m_currentPosition); - m_ghostObject->setWorldTransform(newTrans); + btTransform newTrans = _ghostObject->getWorldTransform(); + newTrans.setOrigin(_currentPosition); + _ghostObject->setWorldTransform(newTrans); return penetration; } @@ -360,45 +355,40 @@ void CharacterController::stepUp( btCollisionWorld* world) { // compute start and end btTransform start, end; start.setIdentity(); - start.setOrigin(m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_convexShape->getMargin() + m_addedMargin)); + start.setOrigin(_currentPosition + getUpAxisDirections()[_upAxis] * (_convexShape->getMargin() + _addedMargin)); - //m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_stepHeight + (m_verticalOffset > 0.0f ? m_verticalOffset : 0.0f)); - m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * m_stepHeight; + _targetPosition = _currentPosition + getUpAxisDirections()[_upAxis] * _stepHeight; end.setIdentity(); - end.setOrigin(m_targetPosition); + end.setOrigin(_targetPosition); // sweep up - btVector3 sweepDirNegative = -getUpAxisDirections()[m_upAxis]; - btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, sweepDirNegative, btScalar(0.7071)); + btVector3 sweepDirNegative = -getUpAxisDirections()[_upAxis]; + btKinematicClosestNotMeConvexResultCallback callback(_ghostObject, sweepDirNegative, btScalar(0.7071)); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; - m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration); + _ghostObject->convexSweepTest(_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration); if (callback.hasHit()) { // we hit something, so zero our vertical velocity - m_verticalVelocity = 0.0; - m_verticalOffset = 0.0; + _verticalVelocity = 0.0; + _verticalOffset = 0.0; // Only modify the position if the hit was a slope and not a wall or ceiling. - if (callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > 0.0) { - m_lastStepUp = m_stepHeight * callback.m_closestHitFraction; - if (m_interpolateUp == true) { - m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); - } else { - m_currentPosition = m_targetPosition; - } + if (callback.m_hitNormalWorld.dot(getUpAxisDirections()[_upAxis]) > 0.0) { + _lastStepUp = _stepHeight * callback.m_closestHitFraction; + _currentPosition.setInterpolate3(_currentPosition, _targetPosition, callback.m_closestHitFraction); } else { - m_lastStepUp = m_stepHeight; - m_currentPosition = m_targetPosition; + _lastStepUp = _stepHeight; + _currentPosition = _targetPosition; } } else { - m_currentPosition = m_targetPosition; - m_lastStepUp = m_stepHeight; + _currentPosition = _targetPosition; + _lastStepUp = _stepHeight; } } void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag) { - btVector3 movementDirection = m_targetPosition - m_currentPosition; + btVector3 movementDirection = _targetPosition - _currentPosition; btScalar movementLength = movementDirection.length(); if (movementLength > SIMD_EPSILON) { movementDirection.normalize(); @@ -411,74 +401,74 @@ void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& parallelDir = parallelComponent(reflectDir, hitNormal); perpindicularDir = perpindicularComponent(reflectDir, hitNormal); - m_targetPosition = m_currentPosition; + _targetPosition = _currentPosition; //if (tangentMag != 0.0) { if (0) { btVector3 parComponent = parallelDir * btScalar(tangentMag * movementLength); - m_targetPosition += parComponent; + _targetPosition += parComponent; } if (normalMag != 0.0) { btVector3 perpComponent = perpindicularDir * btScalar(normalMag * movementLength); - m_targetPosition += perpComponent; + _targetPosition += perpComponent; } } } void CharacterController::stepForward( btCollisionWorld* collisionWorld, const btVector3& movement) { // phase 2: forward - m_targetPosition = m_currentPosition + movement; + _targetPosition = _currentPosition + movement; btTransform start, end; start.setIdentity(); end.setIdentity(); /* TODO: experiment with this to see if we can use this to help direct motion when a floor is available - if (m_touchingContact) { - if (m_normalizedDirection.dot(m_floorNormal) < btScalar(0.0)) { - updateTargetPositionBasedOnCollision(m_floorNormal, 1.0f, 1.0f); + if (_touchingContact) { + if (_normalizedDirection.dot(_floorNormal) < btScalar(0.0)) { + updateTargetPositionBasedOnCollision(_floorNormal, 1.0f, 1.0f); } }*/ // modify shape's margin for the sweeps - btScalar margin = m_convexShape->getMargin(); - m_convexShape->setMargin(margin + m_addedMargin); + btScalar margin = _convexShape->getMargin(); + _convexShape->setMargin(margin + _addedMargin); const btScalar MIN_STEP_DISTANCE = 0.0001f; - btVector3 step = m_targetPosition - m_currentPosition; + btVector3 step = _targetPosition - _currentPosition; btScalar stepLength2 = step.length2(); int maxIter = 10; while (stepLength2 > MIN_STEP_DISTANCE && maxIter-- > 0) { - start.setOrigin(m_currentPosition); - end.setOrigin(m_targetPosition); + start.setOrigin(_currentPosition); + end.setOrigin(_targetPosition); // sweep forward - btVector3 sweepDirNegative(m_currentPosition - m_targetPosition); - btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, sweepDirNegative, btScalar(0.0)); + btVector3 sweepDirNegative(_currentPosition - _targetPosition); + btKinematicClosestNotMeConvexResultCallback callback(_ghostObject, sweepDirNegative, btScalar(0.0)); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; - m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + _ghostObject->convexSweepTest(_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); if (callback.hasHit()) { // we hit soemthing! // Compute new target position by removing portion cut-off by collision, which will produce a new target // that is the closest approach of the the obstacle plane to the original target. - step = m_targetPosition - m_currentPosition; + step = _targetPosition - _currentPosition; btScalar stepDotNormal = step.dot(callback.m_hitNormalWorld); // we expect this dot to be negative step += (stepDotNormal * (1.0f - callback.m_closestHitFraction)) * callback.m_hitNormalWorld; - m_targetPosition = m_currentPosition + step; + _targetPosition = _currentPosition + step; stepLength2 = step.length2(); } else { // we swept to the end without hitting anything - m_currentPosition = m_targetPosition; + _currentPosition = _targetPosition; break; } } // restore shape's margin - m_convexShape->setMargin(margin); + _convexShape->setMargin(margin); } void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar dt) { @@ -488,20 +478,20 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d // If it hits a ledge then it stops otherwise it makes another sweep down in search of a floor within // reach of the character's feet. - btScalar downSpeed = (m_verticalVelocity < 0.0f) ? -m_verticalVelocity : 0.0f; - if (downSpeed > 0.0f && downSpeed > m_maxFallSpeed && (m_wasOnGround || !m_wasJumping)) { - downSpeed = m_maxFallSpeed; + btScalar downSpeed = (_verticalVelocity < 0.0f) ? -_verticalVelocity : 0.0f; + if (downSpeed > 0.0f && downSpeed > _maxFallSpeed && (_wasOnGround || !_wasJumping)) { + downSpeed = _maxFallSpeed; } // first sweep for ledge - btVector3 step = getUpAxisDirections()[m_upAxis] * (-(m_lastStepUp + downSpeed * dt)); + btVector3 step = getUpAxisDirections()[_upAxis] * (-(_lastStepUp + downSpeed * dt)); - StepDownConvexResultCallback callback(m_ghostObject, - getUpAxisDirections()[m_upAxis], - m_currentPosition, step, - m_walkDirection, - m_maxSlopeCosine, - m_radius, m_halfHeight); + StepDownConvexResultCallback callback(_ghostObject, + getUpAxisDirections()[_upAxis], + _currentPosition, step, + _walkDirection, + _maxSlopeCosine, + _radius, _halfHeight); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; @@ -509,75 +499,77 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d start.setIdentity(); end.setIdentity(); - start.setOrigin(m_currentPosition); - m_targetPosition = m_currentPosition + step; - end.setOrigin(m_targetPosition); - m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + start.setOrigin(_currentPosition); + _targetPosition = _currentPosition + step; + end.setOrigin(_targetPosition); + _ghostObject->convexSweepTest(_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); if (callback.hasHit()) { - m_currentPosition += callback.m_closestHitFraction * step; - m_verticalVelocity = 0.0f; - m_verticalOffset = 0.0f; - m_wasJumping = false; + _currentPosition += callback.m_closestHitFraction * step; + _verticalVelocity = 0.0f; + _verticalOffset = 0.0f; + _wasJumping = false; } else { // sweep again for floor within downStep threshold - StepDownConvexResultCallback callback2 (m_ghostObject, - getUpAxisDirections()[m_upAxis], - m_currentPosition, step, - m_walkDirection, - m_maxSlopeCosine, - m_radius, m_halfHeight); + StepDownConvexResultCallback callback2 (_ghostObject, + getUpAxisDirections()[_upAxis], + _currentPosition, step, + _walkDirection, + _maxSlopeCosine, + _radius, _halfHeight); callback2.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; - m_currentPosition = m_targetPosition; - btVector3 oldPosition = m_currentPosition; - step = (- m_stepHeight) * getUpAxisDirections()[m_upAxis]; - m_targetPosition = m_currentPosition + step; + _currentPosition = _targetPosition; + btVector3 oldPosition = _currentPosition; + step = (- _stepHeight) * getUpAxisDirections()[_upAxis]; + _targetPosition = _currentPosition + step; - start.setOrigin(m_currentPosition); - end.setOrigin(m_targetPosition); - m_ghostObject->convexSweepTest(m_convexShape, start, end, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + start.setOrigin(_currentPosition); + end.setOrigin(_targetPosition); + _ghostObject->convexSweepTest(_convexShape, start, end, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); if (callback2.hasHit()) { - m_currentPosition += callback2.m_closestHitFraction * step; - m_verticalVelocity = 0.0f; - m_verticalOffset = 0.0f; - m_wasJumping = false; + _currentPosition += callback2.m_closestHitFraction * step; + _verticalVelocity = 0.0f; + _verticalOffset = 0.0f; + _wasJumping = false; } else { // nothing to step down on, so remove the stepUp effect - m_currentPosition = oldPosition - m_lastStepUp * getUpAxisDirections()[m_upAxis]; - m_lastStepUp = 0.0f; + _currentPosition = oldPosition - _lastStepUp * getUpAxisDirections()[_upAxis]; + _lastStepUp = 0.0f; } } } void CharacterController::setWalkDirection(const btVector3& walkDirection) { - m_useWalkDirection = true; - m_walkDirection = walkDirection; - m_normalizedDirection = getNormalizedVector(m_walkDirection); + _useWalkDirection = true; + _walkDirection = walkDirection; + _normalizedDirection = getNormalizedVector(_walkDirection); } void CharacterController::setVelocityForTimeInterval(const btVector3& velocity, btScalar timeInterval) { - m_useWalkDirection = false; - m_walkDirection = velocity; - m_normalizedDirection = getNormalizedVector(m_walkDirection); - m_velocityTimeInterval += timeInterval; + _useWalkDirection = false; + _walkDirection = velocity; + _normalizedDirection = getNormalizedVector(_walkDirection); + _velocityTimeInterval += timeInterval; } void CharacterController::reset( btCollisionWorld* collisionWorld ) { - m_verticalVelocity = 0.0; - m_verticalOffset = 0.0; - m_wasOnGround = false; - m_wasJumping = false; - m_walkDirection.setValue(0,0,0); - m_velocityTimeInterval = 0.0; + _verticalVelocity = 0.0; + _verticalOffset = 0.0; + _wasOnGround = false; + _wasJumping = false; + _walkDirection.setValue(0,0,0); + _velocityTimeInterval = 0.0; //clear pair cache - btHashedOverlappingPairCache *cache = m_ghostObject->getOverlappingPairCache(); + btHashedOverlappingPairCache *cache = _ghostObject->getOverlappingPairCache(); while (cache->getOverlappingPairArray().size() > 0) { - cache->removeOverlappingPair(cache->getOverlappingPairArray()[0].m_pProxy0, cache->getOverlappingPairArray()[0].m_pProxy1, collisionWorld->getDispatcher()); + cache->removeOverlappingPair(cache->getOverlappingPairArray()[0].m_pProxy0, + cache->getOverlappingPairArray()[0].m_pProxy1, + collisionWorld->getDispatcher()); } } @@ -585,46 +577,46 @@ void CharacterController::warp(const btVector3& origin) { btTransform xform; xform.setIdentity(); xform.setOrigin(origin); - m_ghostObject->setWorldTransform(xform); + _ghostObject->setWorldTransform(xform); } void CharacterController::preStep( btCollisionWorld* collisionWorld) { - if (!m_enabled) { + if (!_enabled) { return; } int numPenetrationLoops = 0; - m_touchingContact = false; + _touchingContact = false; while (recoverFromPenetration(collisionWorld)) { numPenetrationLoops++; - m_touchingContact = true; + _touchingContact = true; if (numPenetrationLoops > 4) { break; } } - m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); - m_targetPosition = m_currentPosition; + _currentPosition = _ghostObject->getWorldTransform().getOrigin(); + _targetPosition = _currentPosition; } void CharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt) { - if (!m_enabled || (!m_useWalkDirection && m_velocityTimeInterval <= 0.0)) { + if (!_enabled || (!_useWalkDirection && _velocityTimeInterval <= 0.0)) { return; // no motion } - m_wasOnGround = onGround(); + _wasOnGround = onGround(); // Update fall velocity. - m_verticalVelocity -= m_gravity * dt; - if (m_verticalVelocity > m_jumpSpeed) { - m_verticalVelocity = m_jumpSpeed; - } else if (m_verticalVelocity < -m_maxFallSpeed) { - m_verticalVelocity = -m_maxFallSpeed; + _verticalVelocity -= _gravity * dt; + if (_verticalVelocity > _jumpSpeed) { + _verticalVelocity = _jumpSpeed; + } else if (_verticalVelocity < -_maxFallSpeed) { + _verticalVelocity = -_maxFallSpeed; } - m_verticalOffset = m_verticalVelocity * dt; + _verticalOffset = _verticalVelocity * dt; btTransform xform; - xform = m_ghostObject->getWorldTransform(); + xform = _ghostObject->getWorldTransform(); // the algorithm is as follows: // (1) step the character up a little bit so that its forward step doesn't hit the floor @@ -632,33 +624,33 @@ void CharacterController::playerStep( btCollisionWorld* collisionWorld, btScala // (3) step the character down looking for new ledges, the original floor, or a floor one step below where we started stepUp(collisionWorld); - if (m_useWalkDirection) { - stepForward(collisionWorld, m_walkDirection); + if (_useWalkDirection) { + stepForward(collisionWorld, _walkDirection); } else { // compute substep and decrement total interval - btScalar dtMoving = (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval; - m_velocityTimeInterval -= dt; + btScalar dtMoving = (dt < _velocityTimeInterval) ? dt : _velocityTimeInterval; + _velocityTimeInterval -= dt; // stepForward substep - btVector3 move = m_walkDirection * dtMoving; + btVector3 move = _walkDirection * dtMoving; stepForward(collisionWorld, move); } stepDown(collisionWorld, dt); - xform.setOrigin(m_currentPosition); - m_ghostObject->setWorldTransform(xform); + xform.setOrigin(_currentPosition); + _ghostObject->setWorldTransform(xform); } void CharacterController::setMaxFallSpeed(btScalar speed) { - m_maxFallSpeed = speed; + _maxFallSpeed = speed; } void CharacterController::setJumpSpeed(btScalar jumpSpeed) { - m_jumpSpeed = jumpSpeed; + _jumpSpeed = jumpSpeed; } void CharacterController::setMaxJumpHeight(btScalar maxJumpHeight) { - m_maxJumpHeight = maxJumpHeight; + _maxJumpHeight = maxJumpHeight; } bool CharacterController::canJump() const { @@ -670,39 +662,39 @@ void CharacterController::jump() { return; } - m_verticalVelocity = m_jumpSpeed; - m_wasJumping = true; + _verticalVelocity = _jumpSpeed; + _wasJumping = true; #if 0 currently no jumping. btTransform xform; - m_rigidBody->getMotionState()->getWorldTransform(xform); + _rigidBody->getMotionState()->getWorldTransform(xform); btVector3 up = xform.getBasis()[1]; up.normalize(); - btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0); - m_rigidBody->applyCentralImpulse (up * magnitude); + btScalar magnitude = (btScalar(1.0)/_rigidBody->getInvMass()) * btScalar(8.0); + _rigidBody->applyCentralImpulse (up * magnitude); #endif } void CharacterController::setGravity(btScalar gravity) { - m_gravity = gravity; + _gravity = gravity; } btScalar CharacterController::getGravity() const { - return m_gravity; + return _gravity; } void CharacterController::setMaxSlope(btScalar slopeRadians) { - m_maxSlopeRadians = slopeRadians; - m_maxSlopeCosine = btCos(slopeRadians); + _maxSlopeRadians = slopeRadians; + _maxSlopeCosine = btCos(slopeRadians); } btScalar CharacterController::getMaxSlope() const { - return m_maxSlopeRadians; + return _maxSlopeRadians; } bool CharacterController::onGround() const { - return m_enabled && m_verticalVelocity == 0.0 && m_verticalOffset == 0.0; + return _enabled && _verticalVelocity == 0.0 && _verticalOffset == 0.0; } btVector3* CharacterController::getUpAxisDirections() { @@ -715,48 +707,50 @@ void CharacterController::debugDraw(btIDebugDraw* debugDrawer) { } void CharacterController::setUpInterpolate(bool value) { - m_interpolateUp = value; + // This method is required by btCharacterControllerInterface, but it does nothing. + // What it used to do was determine whether stepUp() would: stop where it hit the ceiling + // (interpolate = true, and now default behavior) or happily penetrate objects above the avatar. } // protected void CharacterController::createShapeAndGhost() { // get new dimensions from avatar - m_avatarData->lockForRead(); - AABox box = m_avatarData->getLocalAABox(); + _avatarData->lockForRead(); + AABox box = _avatarData->getLocalAABox(); // create new ghost - m_ghostObject = new btPairCachingGhostObject(); - m_ghostObject->setWorldTransform(btTransform(glmToBullet(m_avatarData->getOrientation()), - glmToBullet(m_avatarData->getPosition()))); - m_avatarData->unlock(); + _ghostObject = new btPairCachingGhostObject(); + _ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), + glmToBullet(_avatarData->getPosition()))); + _avatarData->unlock(); const glm::vec3& diagonal = box.getScale(); - m_radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); - m_halfHeight = 0.5f * diagonal.y - m_radius; + _radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); + _halfHeight = 0.5f * diagonal.y - _radius; float MIN_HALF_HEIGHT = 0.1f; - if (m_halfHeight < MIN_HALF_HEIGHT) { - m_halfHeight = MIN_HALF_HEIGHT; + if (_halfHeight < MIN_HALF_HEIGHT) { + _halfHeight = MIN_HALF_HEIGHT; } glm::vec3 offset = box.getCorner() + 0.5f * diagonal; - m_shapeLocalOffset = offset; + _shapeLocalOffset = offset; // stepHeight affects the heights of ledges that the character can ascend - // however the actual ledge height is some function of m_stepHeight + // however the actual ledge height is some function of _stepHeight // due to character shape and this CharacterController algorithm - // (the function is approximately 2*m_stepHeight) - m_stepHeight = 0.1f * (m_radius + m_halfHeight) + 0.1f; + // (the function is approximately 2*_stepHeight) + _stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f; // create new shape - m_convexShape = new btCapsuleShape(m_radius, 2.0f * m_halfHeight); - m_ghostObject->setCollisionShape(m_convexShape); - m_ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); + _convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight); + _ghostObject->setCollisionShape(_convexShape); + _ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); } bool CharacterController::needsShapeUpdate() { // get new dimensions from avatar - m_avatarData->lockForRead(); - AABox box = m_avatarData->getLocalAABox(); - m_avatarData->unlock(); + _avatarData->lockForRead(); + AABox box = _avatarData->getLocalAABox(); + _avatarData->unlock(); const glm::vec3& diagonal = box.getScale(); float radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); @@ -768,14 +762,14 @@ bool CharacterController::needsShapeUpdate() { glm::vec3 offset = box.getCorner() + 0.5f * diagonal; // compare dimensions (and offset) - float radiusDelta = glm::abs(radius - m_radius); - float heightDelta = glm::abs(halfHeight - m_halfHeight); + float radiusDelta = glm::abs(radius - _radius); + float heightDelta = glm::abs(halfHeight - _halfHeight); if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { // shape hasn't changed --> nothing to do - float offsetDelta = glm::distance(offset, m_shapeLocalOffset); + float offsetDelta = glm::distance(offset, _shapeLocalOffset); if (offsetDelta > FLT_EPSILON) { // if only the offset changes then we can update it --> no need to rebuild shape - m_shapeLocalOffset = offset; + _shapeLocalOffset = offset; } return false; } @@ -787,29 +781,29 @@ void CharacterController::updateShape() { // the PhysicsEngine before calling this. // delete shape and GhostObject - delete m_ghostObject; - m_ghostObject = NULL; - delete m_convexShape; - m_convexShape = NULL; + delete _ghostObject; + _ghostObject = NULL; + delete _convexShape; + _convexShape = NULL; createShapeAndGhost(); } void CharacterController::preSimulation(btScalar timeStep) { - bool wasEnabled = m_enabled; + bool wasEnabled = _enabled; // lock avatarData, get everything we need from it ASAP, then unlock - m_avatarData->lockForRead(); - m_enabled = m_avatarData->isPhysicsEnabled(); - glm::quat rotation = m_avatarData->getOrientation(); - glm::vec3 position = m_avatarData->getPosition() + rotation * m_shapeLocalOffset; + _avatarData->lockForRead(); + _enabled = _avatarData->isPhysicsEnabled(); + glm::quat rotation = _avatarData->getOrientation(); + glm::vec3 position = _avatarData->getPosition() + rotation * _shapeLocalOffset; // TODO: Andrew to implement: harvest jump event here - btVector3 walkVelocity = glmToBullet(m_avatarData->getVelocity()); + btVector3 walkVelocity = glmToBullet(_avatarData->getVelocity()); - m_avatarData->unlock(); + _avatarData->unlock(); - if (wasEnabled != m_enabled) { - if (m_enabled) { + if (wasEnabled != _enabled) { + if (_enabled) { // TODO: Andrew to implement: add collision shape back into world } else { // TODO: Andrew to implement: remove collision shape from world, @@ -817,21 +811,21 @@ void CharacterController::preSimulation(btScalar timeStep) { } } - if (m_enabled) { - m_ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); + if (_enabled) { + _ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); setVelocityForTimeInterval(walkVelocity, timeStep); } } void CharacterController::postSimulation() { - if (m_enabled) { - m_avatarData->lockForWrite(); - const btTransform& avatarTransform = m_ghostObject->getWorldTransform(); + if (_enabled) { + _avatarData->lockForWrite(); + const btTransform& avatarTransform = _ghostObject->getWorldTransform(); glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); - glm::vec3 offset = rotation * m_shapeLocalOffset; - m_avatarData->setOrientation(rotation); - m_avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); - m_avatarData->unlock(); + glm::vec3 offset = rotation * _shapeLocalOffset; + _avatarData->setOrientation(rotation); + _avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); + _avatarData->unlock(); } } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index b84662a389..23c338412c 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -41,53 +41,50 @@ ATTRIBUTE_ALIGNED16(class) CharacterController : public btCharacterControllerInt { protected: - AvatarData* m_avatarData = NULL; - btPairCachingGhostObject* m_ghostObject; - glm::vec3 m_shapeLocalOffset; + AvatarData* _avatarData = NULL; + btPairCachingGhostObject* _ghostObject; + glm::vec3 _shapeLocalOffset; - btConvexShape* m_convexShape;//is also in m_ghostObject, but it needs to be convex, so we store it here to avoid upcast - btScalar m_radius; - btScalar m_halfHeight; + btConvexShape* _convexShape;//is also in _ghostObject, but it needs to be convex, so we store it here to avoid upcast + btScalar _radius; + btScalar _halfHeight; - btScalar m_verticalVelocity; - btScalar m_verticalOffset; // fall distance from velocity this frame - btScalar m_maxFallSpeed; - btScalar m_jumpSpeed; - btScalar m_maxJumpHeight; - btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value) - btScalar m_maxSlopeCosine; // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization) - btScalar m_gravity; + btScalar _verticalVelocity; + btScalar _verticalOffset; // fall distance from velocity this frame + btScalar _maxFallSpeed; + btScalar _jumpSpeed; + btScalar _maxJumpHeight; + btScalar _maxSlopeRadians; // Slope angle that is set (used for returning the exact value) + btScalar _maxSlopeCosine; // Cosine equivalent of _maxSlopeRadians (calculated once when set, for optimization) + btScalar _gravity; - btScalar m_stepHeight; // height of stepUp prior to stepForward + btScalar _stepHeight; // height of stepUp prior to stepForward - btScalar m_addedMargin;//@todo: remove this and fix the code + btScalar _addedMargin;//@todo: remove this and fix the code ///this is the desired walk direction, set by the user - btVector3 m_walkDirection; - btVector3 m_normalizedDirection; + btVector3 _walkDirection; + btVector3 _normalizedDirection; //some internal variables - btVector3 m_currentPosition; - btVector3 m_targetPosition; - btScalar m_lastStepUp; + btVector3 _currentPosition; + btVector3 _targetPosition; + btScalar _lastStepUp; ///keep track of the contact manifolds - btManifoldArray m_manifoldArray; + btManifoldArray _manifoldArray; - bool m_touchingContact; - btVector3 m_floorNormal; // points from object to character + bool _touchingContact; + btVector3 _floorNormal; // points from object to character - bool m_enabled; - bool m_wasOnGround; - bool m_wasJumping; - bool m_useWalkDirection; - btScalar m_velocityTimeInterval; - int m_upAxis; + bool _enabled; + bool _wasOnGround; + bool _wasJumping; + bool _useWalkDirection; + btScalar _velocityTimeInterval; + int _upAxis; static btVector3* getUpAxisDirections(); - bool m_interpolateUp; - bool full_drop; - bool bounce_fix; btVector3 computeReflectionDirection(const btVector3& direction, const btVector3& normal); btVector3 parallelComponent(const btVector3& direction, const btVector3& normal); @@ -121,7 +118,7 @@ public: axis = 0; if (axis > 2) axis = 2; - m_upAxis = axis; + _upAxis = axis; } /// This should probably be called setPositionIncrementPerSimulatorStep. From 49f5de2b944a59556beb53abede24fb8caa49b5c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 20 Mar 2015 16:39:38 -0700 Subject: [PATCH 3/9] make character's "up" axis depend on orientation --- libraries/physics/src/CharacterController.cpp | 40 +++++++++---------- libraries/physics/src/CharacterController.h | 12 +----- 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index f7f8ab4d53..d0b083fc01 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -216,6 +216,8 @@ btVector3 CharacterController::perpindicularComponent(const btVector3& direction return direction - parallelComponent(direction, normal); } +const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); + CharacterController::CharacterController(AvatarData* avatarData) { assert(avatarData); _avatarData = avatarData; @@ -227,8 +229,6 @@ CharacterController::CharacterController(AvatarData* avatarData) { createShapeAndGhost(); - _upAxis = 1; // HACK: hard coded to be 1 for now (yAxis) - _addedMargin = 0.02f; _walkDirection.setValue(0.0f,0.0f,0.0f); _useWalkDirection = true; // use walk direction by default, legacy behavior @@ -272,7 +272,7 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl collisionWorld->getDispatcher()->dispatchAllCollisionPairs(_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher()); _currentPosition = _ghostObject->getWorldTransform().getOrigin(); - btVector3 up = getUpAxisDirections()[_upAxis]; + btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS); btVector3 currentPosition = _currentPosition; @@ -355,14 +355,15 @@ void CharacterController::stepUp( btCollisionWorld* world) { // compute start and end btTransform start, end; start.setIdentity(); - start.setOrigin(_currentPosition + getUpAxisDirections()[_upAxis] * (_convexShape->getMargin() + _addedMargin)); + btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS); + start.setOrigin(_currentPosition + up * (_convexShape->getMargin() + _addedMargin)); - _targetPosition = _currentPosition + getUpAxisDirections()[_upAxis] * _stepHeight; + _targetPosition = _currentPosition + up * _stepHeight; end.setIdentity(); end.setOrigin(_targetPosition); // sweep up - btVector3 sweepDirNegative = -getUpAxisDirections()[_upAxis]; + btVector3 sweepDirNegative = - up; btKinematicClosestNotMeConvexResultCallback callback(_ghostObject, sweepDirNegative, btScalar(0.7071)); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; @@ -370,11 +371,11 @@ void CharacterController::stepUp( btCollisionWorld* world) { if (callback.hasHit()) { // we hit something, so zero our vertical velocity - _verticalVelocity = 0.0; - _verticalOffset = 0.0; + _verticalVelocity = 0.0f; + _verticalOffset = 0.0f; // Only modify the position if the hit was a slope and not a wall or ceiling. - if (callback.m_hitNormalWorld.dot(getUpAxisDirections()[_upAxis]) > 0.0) { + if (callback.m_hitNormalWorld.dot(up) > 0.0f) { _lastStepUp = _stepHeight * callback.m_closestHitFraction; _currentPosition.setInterpolate3(_currentPosition, _targetPosition, callback.m_closestHitFraction); } else { @@ -484,10 +485,11 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d } // first sweep for ledge - btVector3 step = getUpAxisDirections()[_upAxis] * (-(_lastStepUp + downSpeed * dt)); + btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS); + btVector3 step = up * (-(_lastStepUp + downSpeed * dt)); StepDownConvexResultCallback callback(_ghostObject, - getUpAxisDirections()[_upAxis], + up, _currentPosition, step, _walkDirection, _maxSlopeCosine, @@ -512,7 +514,7 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d } else { // sweep again for floor within downStep threshold StepDownConvexResultCallback callback2 (_ghostObject, - getUpAxisDirections()[_upAxis], + up, _currentPosition, step, _walkDirection, _maxSlopeCosine, @@ -523,7 +525,7 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d _currentPosition = _targetPosition; btVector3 oldPosition = _currentPosition; - step = (- _stepHeight) * getUpAxisDirections()[_upAxis]; + step = (- _stepHeight) * up; _targetPosition = _currentPosition + step; start.setOrigin(_currentPosition); @@ -537,7 +539,7 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d _wasJumping = false; } else { // nothing to step down on, so remove the stepUp effect - _currentPosition = oldPosition - _lastStepUp * getUpAxisDirections()[_upAxis]; + _currentPosition = oldPosition - _lastStepUp * up; _lastStepUp = 0.0f; } } @@ -595,7 +597,9 @@ void CharacterController::preStep( btCollisionWorld* collisionWorld) { } } - _currentPosition = _ghostObject->getWorldTransform().getOrigin(); + const btTransform& transform = _ghostObject->getWorldTransform(); + _currentRotation = transform.getRotation(); + _currentPosition = transform.getOrigin(); _targetPosition = _currentPosition; } @@ -697,12 +701,6 @@ bool CharacterController::onGround() const { return _enabled && _verticalVelocity == 0.0 && _verticalOffset == 0.0; } -btVector3* CharacterController::getUpAxisDirections() { - static btVector3 sUpAxisDirection[3] = { btVector3(1.0f, 0.0f, 0.0f), btVector3(0.0f, 1.0f, 0.0f), btVector3(0.0f, 0.0f, 1.0f) }; - - return sUpAxisDirection; -} - void CharacterController::debugDraw(btIDebugDraw* debugDrawer) { } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 23c338412c..b6109eb0ff 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -68,6 +68,7 @@ protected: //some internal variables btVector3 _currentPosition; + btQuaternion _currentRotation; btVector3 _targetPosition; btScalar _lastStepUp; @@ -82,9 +83,6 @@ protected: bool _wasJumping; bool _useWalkDirection; btScalar _velocityTimeInterval; - int _upAxis; - - static btVector3* getUpAxisDirections(); btVector3 computeReflectionDirection(const btVector3& direction, const btVector3& normal); btVector3 parallelComponent(const btVector3& direction, const btVector3& normal); @@ -113,14 +111,6 @@ public: ///btActionInterface interface void debugDraw(btIDebugDraw* debugDrawer); - void setUpAxis(int axis) { - if (axis < 0) - axis = 0; - if (axis > 2) - axis = 2; - _upAxis = axis; - } - /// This should probably be called setPositionIncrementPerSimulatorStep. /// This is neither a direction nor a velocity, but the amount to /// increment the position each simulation iteration, regardless From 44eca08fa488bf653dd06dd10f51ea798323385e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 24 Mar 2015 17:38:35 -0700 Subject: [PATCH 4/9] PhysicsEngine doesn't need AvatarData MyAvatar now owns its CharacterController fix for bug of phantom collision obj when avatar's physics are disabled --- interface/src/Application.cpp | 7 +- interface/src/avatar/MyAvatar.cpp | 14 +- interface/src/avatar/MyAvatar.h | 11 + libraries/avatars/src/AvatarData.h | 13 - libraries/physics/src/CharacterController.cpp | 229 +++++++++++------- libraries/physics/src/CharacterController.h | 16 +- libraries/physics/src/PhysicsEngine.cpp | 43 +--- libraries/physics/src/PhysicsEngine.h | 4 +- 8 files changed, 197 insertions(+), 140 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4ff41e1b6f..418407282c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1903,8 +1903,6 @@ void Application::init() { _physicsEngine.init(&_entityEditSender); - _physicsEngine.setAvatarData(_myAvatar); - auto entityScriptingInterface = DependencyManager::get(); connect(&_physicsEngine, &EntitySimulation::entityCollisionWithEntity, @@ -2191,6 +2189,7 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("physics"); + _myAvatar->preSimulation(); _physicsEngine.stepSimulation(); } @@ -4207,7 +4206,7 @@ void Application::checkSkeleton() { _myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL); _myAvatar->sendIdentityPacket(); } else { - _myAvatar->updateLocalAABox(); - _physicsEngine.setAvatarData(_myAvatar); + _myAvatar->updateCharacterController(); + _physicsEngine.setCharacterController(_myAvatar->getCharacterController()); } } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ab4989a651..e0de24247e 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -82,6 +82,8 @@ MyAvatar::MyAvatar() : _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME), _motionBehaviors(AVATAR_MOTION_DEFAULTS), + _characterController(this), + _enablePhysics(false), _lookAtTargetAvatar(), _shouldRender(true), _billboardValid(false), @@ -954,15 +956,15 @@ glm::vec3 MyAvatar::getSkeletonPosition() const { return Avatar::getPosition(); } -void MyAvatar::updateLocalAABox() { +void MyAvatar::updateCharacterController() { + // compute localAABox const CapsuleShape& capsule = _skeletonModel.getBoundingShape(); float radius = capsule.getRadius(); float height = 2.0f * (capsule.getHalfHeight() + radius); - glm::vec3 offset = _skeletonModel.getBoundingShapeOffset(); glm::vec3 corner(-radius, -0.5f * height, -radius); - corner += offset; + corner += _skeletonModel.getBoundingShapeOffset(); glm::vec3 scale(2.0f * radius, height, 2.0f * radius); - _localAABox.setBox(corner, scale); + _characterController.setLocalBoundingBox(corner, scale); } QString MyAvatar::getScriptedMotorFrame() const { @@ -1580,6 +1582,10 @@ glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { return palm->getPosition(); } +void MyAvatar::preSimulation() { + _characterController.setEnabled(_enablePhysics); +} + void MyAvatar::clearDriveKeys() { for (int i = 0; i < MAX_DRIVE_KEYS; ++i) { _driveKeys[i] = 0.0f; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 08c0228f1e..1b2b2f5e46 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -13,6 +13,7 @@ #define hifi_MyAvatar_h #include +#include #include "Avatar.h" @@ -122,6 +123,8 @@ public: virtual glm::vec3 getSkeletonPosition() const; void updateLocalAABox(); + CharacterController* getCharacterController() { return &_characterController; } + void updateCharacterController(); void clearJointAnimationPriorities(); @@ -145,6 +148,11 @@ public: const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } + + void togglePhysicsEnabled() { _enablePhysics = !_enablePhysics; } + bool isPhysicsEnabled() { return _enablePhysics; } + void setPhysicsEnabled(bool enablePhysics) { _enablePhysics = enablePhysics; } + void preSimulation(); public slots: void increaseSize(); @@ -202,6 +210,9 @@ private: int _scriptedMotorFrame; quint32 _motionBehaviors; + bool _enablePhysics; + CharacterController _characterController; + QWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; bool _shouldRender; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 28123124a0..a2feb98798 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -300,16 +300,6 @@ public: const AABox& getLocalAABox() const { return _localAABox; } const Referential* getReferential() const { return _referential; } - void togglePhysicsEnabled() { _enablePhysics = !_enablePhysics; } - bool isPhysicsEnabled() { return _enablePhysics; } - void setPhysicsEnabled(bool enablePhysics) { _enablePhysics = enablePhysics; } - - void lockForRead() { _lock.lockForRead(); } - bool tryLockForRead() { return _lock.tryLockForRead(); } - void lockForWrite() { _lock.lockForWrite(); } - bool tryLockForWrite() { return _lock.tryLockForWrite(); } - void unlock() { _lock.unlock(); } - void setVelocity(const glm::vec3 velocity) { _velocity = velocity; } Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; } @@ -409,9 +399,6 @@ private: // privatize the copy constructor and assignment operator so they cannot be called AvatarData(const AvatarData&); AvatarData& operator= (const AvatarData&); - - QReadWriteLock _lock; - bool _enablePhysics = false; }; Q_DECLARE_METATYPE(AvatarData*) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index d0b083fc01..be10cd1f54 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -21,6 +21,12 @@ subject to the following restrictions: #include "BulletUtil.h" #include "CharacterController.h" +const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; +const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; +//const uint32_t PENDING_FLAG_ENABLE = 1U << 0; +//const uint32_t PENDING_FLAG_DISABLE = 1U << 1; +const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; +const uint32_t PENDING_FLAG_JUMP = 1U << 4; // static helper method static btVector3 getNormalizedVector(const btVector3& v) { @@ -223,11 +229,10 @@ CharacterController::CharacterController(AvatarData* avatarData) { _avatarData = avatarData; // cache the "PhysicsEnabled" state of _avatarData - _avatarData->lockForRead(); - _enabled = _avatarData->isPhysicsEnabled(); - _avatarData->unlock(); + _enabled = false; - createShapeAndGhost(); + _ghostObject = NULL; + _convexShape = NULL; _addedMargin = 0.02f; _walkDirection.setValue(0.0f,0.0f,0.0f); @@ -242,6 +247,7 @@ CharacterController::CharacterController(AvatarData* avatarData) { _wasJumping = false; setMaxSlope(btRadians(45.0f)); _lastStepUp = 0.0f; + _pendingFlags = 0; } CharacterController::~CharacterController() { @@ -349,7 +355,7 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl return penetration; } -void CharacterController::stepUp( btCollisionWorld* world) { +void CharacterController::stepUp(btCollisionWorld* world) { // phase 1: up // compute start and end @@ -416,7 +422,7 @@ void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& } } -void CharacterController::stepForward( btCollisionWorld* collisionWorld, const btVector3& movement) { +void CharacterController::stepForward(btCollisionWorld* collisionWorld, const btVector3& movement) { // phase 2: forward _targetPosition = _currentPosition + movement; @@ -472,7 +478,7 @@ void CharacterController::stepForward( btCollisionWorld* collisionWorld, const b _convexShape->setMargin(margin); } -void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar dt) { +void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt) { // phase 3: down // // The "stepDown" phase first makes a normal sweep down that cancels the lift from the "stepUp" phase. @@ -558,7 +564,7 @@ void CharacterController::setVelocityForTimeInterval(const btVector3& velocity, _velocityTimeInterval += timeInterval; } -void CharacterController::reset( btCollisionWorld* collisionWorld ) { +void CharacterController::reset(btCollisionWorld* collisionWorld) { _verticalVelocity = 0.0; _verticalOffset = 0.0; _wasOnGround = false; @@ -583,7 +589,7 @@ void CharacterController::warp(const btVector3& origin) { } -void CharacterController::preStep( btCollisionWorld* collisionWorld) { +void CharacterController::preStep(btCollisionWorld* collisionWorld) { if (!_enabled) { return; } @@ -603,7 +609,7 @@ void CharacterController::preStep( btCollisionWorld* collisionWorld) { _targetPosition = _currentPosition; } -void CharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt) { +void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt) { if (!_enabled || (!_useWalkDirection && _velocityTimeInterval <= 0.0)) { return; // no motion } @@ -710,106 +716,163 @@ void CharacterController::setUpInterpolate(bool value) { // (interpolate = true, and now default behavior) or happily penetrate objects above the avatar. } +/* // protected void CharacterController::createShapeAndGhost() { // get new dimensions from avatar - _avatarData->lockForRead(); - AABox box = _avatarData->getLocalAABox(); - - // create new ghost - _ghostObject = new btPairCachingGhostObject(); - _ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), - glmToBullet(_avatarData->getPosition()))); - _avatarData->unlock(); - - const glm::vec3& diagonal = box.getScale(); - _radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); - _halfHeight = 0.5f * diagonal.y - _radius; + float x = _boxScale.x; + float z = _boxScale.z; + _radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); + _halfHeight = 0.5f * _boxScale.y - _radius; float MIN_HALF_HEIGHT = 0.1f; if (_halfHeight < MIN_HALF_HEIGHT) { _halfHeight = MIN_HALF_HEIGHT; } - glm::vec3 offset = box.getCorner() + 0.5f * diagonal; - _shapeLocalOffset = offset; + // NOTE: _shapeLocalOffset is already computed - // stepHeight affects the heights of ledges that the character can ascend - // however the actual ledge height is some function of _stepHeight - // due to character shape and this CharacterController algorithm - // (the function is approximately 2*_stepHeight) - _stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f; - - // create new shape - _convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight); - _ghostObject->setCollisionShape(_convexShape); - _ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); + if (_radius > 0.0f) { + // create new ghost + _ghostObject = new btPairCachingGhostObject(); + _ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), + glmToBullet(_avatarData->getPosition()))); + // stepHeight affects the heights of ledges that the character can ascend + // however the actual ledge height is some function of _stepHeight + // due to character shape and this CharacterController algorithm + // (the function is approximately 2*_stepHeight) + _stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f; + + // create new shape + _convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight); + _ghostObject->setCollisionShape(_convexShape); + _ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); + } else { + // TODO: handle this failure case + } + _pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE; } +*/ -bool CharacterController::needsShapeUpdate() { - // get new dimensions from avatar - _avatarData->lockForRead(); - AABox box = _avatarData->getLocalAABox(); - _avatarData->unlock(); +void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { + _boxScale = scale; - const glm::vec3& diagonal = box.getScale(); - float radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); - float halfHeight = 0.5f * diagonal.y - radius; + float x = _boxScale.x; + float z = _boxScale.z; + float radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); + float halfHeight = 0.5f * _boxScale.y - radius; float MIN_HALF_HEIGHT = 0.1f; if (halfHeight < MIN_HALF_HEIGHT) { halfHeight = MIN_HALF_HEIGHT; } - glm::vec3 offset = box.getCorner() + 0.5f * diagonal; - // compare dimensions (and offset) + // compare dimensions float radiusDelta = glm::abs(radius - _radius); float heightDelta = glm::abs(halfHeight - _halfHeight); if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { // shape hasn't changed --> nothing to do - float offsetDelta = glm::distance(offset, _shapeLocalOffset); - if (offsetDelta > FLT_EPSILON) { - // if only the offset changes then we can update it --> no need to rebuild shape - _shapeLocalOffset = offset; - } - return false; + } else { + // we need to: remove, update, add + _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION + | PENDING_FLAG_UPDATE_SHAPE + | PENDING_FLAG_ADD_TO_SIMULATION; + } + + // it's ok to change offset immediately -- there are no thread safety issues here + _shapeLocalOffset = corner + 0.5f * _boxScale; +} + +bool CharacterController::needsAddition() const { + return (bool)(_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION); +} + +bool CharacterController::needsRemoval() const { + return (bool)(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION); +} + +void CharacterController::setEnabled(bool enabled) { + if (enabled != _enabled) { + if (enabled) { + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; + } else { + _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; + _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; + } + _enabled = enabled; + } +} + +void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { + if (_dynamicsWorld != world) { + if (_dynamicsWorld) { + _dynamicsWorld->removeCollisionObject(getGhostObject()); + _dynamicsWorld->removeAction(this); + } + _dynamicsWorld = world; + if (_dynamicsWorld) { + _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; + _dynamicsWorld->addCollisionObject(getGhostObject(), + btBroadphaseProxy::CharacterFilter, + btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); + _dynamicsWorld->addAction(this); + reset(_dynamicsWorld); + } else { + _pendingFlags &= ~ PENDING_FLAG_REMOVE_FROM_SIMULATION; + } + } else { + _pendingFlags &= ~ (PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_ADD_TO_SIMULATION); } - return true; } void CharacterController::updateShape() { - // DANGER: make sure this CharacterController and its GhostShape have been removed from - // the PhysicsEngine before calling this. - - // delete shape and GhostObject - delete _ghostObject; - _ghostObject = NULL; - delete _convexShape; - _convexShape = NULL; - - createShapeAndGhost(); + if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { + assert(!(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION)); + _pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE; + // make sure there is NO pending removal from simulation at this point + // (don't want to delete _ghostObject out from under the simulation) + // delete shape and GhostObject + delete _ghostObject; + _ghostObject = NULL; + delete _convexShape; + _convexShape = NULL; + + // compute new dimensions from avatar's bounding box + float x = _boxScale.x; + float z = _boxScale.z; + _radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); + _halfHeight = 0.5f * _boxScale.y - _radius; + float MIN_HALF_HEIGHT = 0.1f; + if (_halfHeight < MIN_HALF_HEIGHT) { + _halfHeight = MIN_HALF_HEIGHT; + } + // NOTE: _shapeLocalOffset is already computed + + if (_radius > 0.0f) { + // create new ghost + _ghostObject = new btPairCachingGhostObject(); + _ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), + glmToBullet(_avatarData->getPosition()))); + // stepHeight affects the heights of ledges that the character can ascend + // however the actual ledge height is some function of _stepHeight + // due to character shape and this CharacterController algorithm + // (the function is approximately 2*_stepHeight) + _stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f; + + // create new shape + _convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight); + _ghostObject->setCollisionShape(_convexShape); + _ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); + } else { + // TODO: handle this failure case + } + } } void CharacterController::preSimulation(btScalar timeStep) { - bool wasEnabled = _enabled; + if (_enabled && _dynamicsWorld) { + glm::quat rotation = _avatarData->getOrientation(); + glm::vec3 position = _avatarData->getPosition() + rotation * _shapeLocalOffset; + // TODO: harvest jump event here + btVector3 walkVelocity = glmToBullet(_avatarData->getVelocity()); - // lock avatarData, get everything we need from it ASAP, then unlock - _avatarData->lockForRead(); - _enabled = _avatarData->isPhysicsEnabled(); - glm::quat rotation = _avatarData->getOrientation(); - glm::vec3 position = _avatarData->getPosition() + rotation * _shapeLocalOffset; - // TODO: Andrew to implement: harvest jump event here - btVector3 walkVelocity = glmToBullet(_avatarData->getVelocity()); - - _avatarData->unlock(); - - if (wasEnabled != _enabled) { - if (_enabled) { - // TODO: Andrew to implement: add collision shape back into world - } else { - // TODO: Andrew to implement: remove collision shape from world, - // otherwise things will continue to collide with it - } - } - - if (_enabled) { _ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); setVelocityForTimeInterval(walkVelocity, timeStep); } @@ -817,13 +880,11 @@ void CharacterController::preSimulation(btScalar timeStep) { void CharacterController::postSimulation() { if (_enabled) { - _avatarData->lockForWrite(); const btTransform& avatarTransform = _ghostObject->getWorldTransform(); glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); glm::vec3 offset = rotation * _shapeLocalOffset; _avatarData->setOrientation(rotation); _avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); - _avatarData->unlock(); } } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index b6109eb0ff..9803d0d6ee 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -37,13 +37,13 @@ class btPairCachingGhostObject; ///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations. ///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user. + ATTRIBUTE_ALIGNED16(class) CharacterController : public btCharacterControllerInterface { protected: AvatarData* _avatarData = NULL; btPairCachingGhostObject* _ghostObject; - glm::vec3 _shapeLocalOffset; btConvexShape* _convexShape;//is also in _ghostObject, but it needs to be convex, so we store it here to avoid upcast btScalar _radius; @@ -83,6 +83,12 @@ protected: bool _wasJumping; bool _useWalkDirection; btScalar _velocityTimeInterval; + uint32_t _pendingFlags; + + glm::vec3 _shapeLocalOffset; + glm::vec3 _boxScale; // used to compute capsule shape + + btDynamicsWorld* _dynamicsWorld = NULL; btVector3 computeReflectionDirection(const btVector3& direction, const btVector3& normal); btVector3 parallelComponent(const btVector3& direction, const btVector3& normal); @@ -152,7 +158,13 @@ public: bool onGround() const; void setUpInterpolate(bool value); - bool needsShapeUpdate(); + bool needsRemoval() const; + bool needsAddition() const; + void setEnabled(bool enabled); + void setDynamicsWorld(btDynamicsWorld* world); + + void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); + bool needsShapeUpdate() const; void updateShape(); void preSimulation(btScalar timeStep); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 447a3ab6f1..bcde3318ad 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -280,12 +280,12 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) { void PhysicsEngine::stepSimulation() { lock(); // NOTE: the grand order of operations is: - // (1) relay incoming changes + // (1) pull incoming changes // (2) step simulation // (3) synchronize outgoing motion states // (4) send outgoing packets - // This is step (1). + // This is step (1) pull incoming changes relayIncomingChangesToSimulation(); const int MAX_NUM_SUBSTEPS = 4; @@ -296,10 +296,17 @@ void PhysicsEngine::stepSimulation() { // TODO: move character->preSimulation() into relayIncomingChanges if (_characterController) { + if (_characterController->needsRemoval()) { + _characterController->setDynamicsWorld(NULL); + } + _characterController->updateShape(); + if (_characterController->needsAddition()) { + _characterController->setDynamicsWorld(_dynamicsWorld); + } _characterController->preSimulation(timeStep); } - // This is step (2). + // This is step (2) step simulation int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); _numSubsteps += (uint32_t)numSubsteps; stepNonPhysicalKinematics(usecTimestampNow()); @@ -600,34 +607,10 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio return true; } -void PhysicsEngine::setAvatarData(AvatarData *avatarData) { - if (_characterController) { - bool needsShapeUpdate = _characterController->needsShapeUpdate(); - if (needsShapeUpdate) { - lock(); - // remove old info - _dynamicsWorld->removeCollisionObject(_characterController->getGhostObject()); - _dynamicsWorld->removeAction(_characterController); - // update shape - _characterController->updateShape(); - // insert new info - _dynamicsWorld->addCollisionObject(_characterController->getGhostObject(), - btBroadphaseProxy::CharacterFilter, - btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); - _dynamicsWorld->addAction(_characterController); - _characterController->reset(_dynamicsWorld); - unlock(); - } - } else { - // initialize _characterController - assert(avatarData); // don't pass NULL argument +void PhysicsEngine::setCharacterController(CharacterController* character) { + if (!_characterController) { lock(); - _characterController = new CharacterController(avatarData); - _dynamicsWorld->addCollisionObject(_characterController->getGhostObject(), - btBroadphaseProxy::CharacterFilter, - btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); - _dynamicsWorld->addAction(_characterController); - _characterController->reset(_dynamicsWorld); + _characterController = character; unlock(); } } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index cb637c60b9..0661b47d3a 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -17,9 +17,7 @@ #include #include #include -//#include -#include #include #include @@ -86,7 +84,7 @@ public: /// process queue of changed from external sources void relayIncomingChangesToSimulation(); - void setAvatarData(AvatarData *avatarData); + void setCharacterController(CharacterController* character); private: /// \param motionState pointer to Object's MotionState From d49762097686af6b9400383367d8c66e6df77430 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 24 Mar 2015 23:05:46 -0700 Subject: [PATCH 5/9] avatar can jump with E key when walking --- interface/src/avatar/MyAvatar.cpp | 3 +- interface/src/avatar/MyAvatar.h | 3 +- libraries/physics/src/CharacterController.cpp | 139 ++++-------------- libraries/physics/src/CharacterController.h | 17 +-- 4 files changed, 39 insertions(+), 123 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e0de24247e..2f42544f28 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -70,7 +70,6 @@ MyAvatar::MyAvatar() : Avatar(), _turningKeyPressTime(0.0f), _gravity(0.0f, 0.0f, 0.0f), - _shouldJump(false), _wasPushing(false), _isPushing(false), _isBraking(false), @@ -82,8 +81,8 @@ MyAvatar::MyAvatar() : _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME), _motionBehaviors(AVATAR_MOTION_DEFAULTS), - _characterController(this), _enablePhysics(false), + _characterController(this), _lookAtTargetAvatar(), _shouldRender(true), _billboardValid(false), diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1b2b2f5e46..a37d1c6a30 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -89,7 +89,7 @@ public: void clearDriveKeys(); void setDriveKeys(int key, float val) { _driveKeys[key] = val; }; bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; - void jump() { _shouldJump = true; }; + void jump() { _characterController.jump(); } bool isMyAvatar() { return true; } @@ -194,7 +194,6 @@ private: float _turningKeyPressTime; glm::vec3 _gravity; - bool _shouldJump; float _driveKeys[MAX_DRIVE_KEYS]; bool _wasPushing; bool _isPushing; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index be10cd1f54..fa02f81e34 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -23,10 +23,8 @@ subject to the following restrictions: const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; -//const uint32_t PENDING_FLAG_ENABLE = 1U << 0; -//const uint32_t PENDING_FLAG_DISABLE = 1U << 1; const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; -const uint32_t PENDING_FLAG_JUMP = 1U << 4; +const uint32_t PENDING_FLAG_JUMP = 1U << 3; // static helper method static btVector3 getNormalizedVector(const btVector3& v) { @@ -41,34 +39,6 @@ static btVector3 getNormalizedVector(const btVector3& v) { return n; } -///@todo Interact with dynamic objects, -///Ride kinematicly animated platforms properly -///More realistic (or maybe just a config option) falling -/// -> Should integrate falling velocity manually and use that in stepDown() -///Support jumping -///Support ducking - -/* This callback is unused, but we're keeping it around just in case we figure out how to use it. -class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback -{ -public: -btKinematicClosestNotMeRayResultCallback(btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) -{ -_me = me; -} - -virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) -{ -if (rayResult.m_collisionObject == _me) -return 1.0; - -return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); -} -protected: -btCollisionObject* _me; -}; -*/ - class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: btKinematicClosestNotMeConvexResultCallback(btCollisionObject* me, const btVector3& up, btScalar minSlopeDot) @@ -236,13 +206,12 @@ CharacterController::CharacterController(AvatarData* avatarData) { _addedMargin = 0.02f; _walkDirection.setValue(0.0f,0.0f,0.0f); - _useWalkDirection = true; // use walk direction by default, legacy behavior _velocityTimeInterval = 0.0f; _verticalVelocity = 0.0f; _verticalOffset = 0.0f; _gravity = 9.8f; _maxFallSpeed = 55.0f; // Terminal velocity of a sky diver in m/s. - _jumpSpeed = 10.0f; // ? + _jumpSpeed = 7.0f; _wasOnGround = false; _wasJumping = false; setMaxSlope(btRadians(45.0f)); @@ -485,14 +454,9 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt // If it hits a ledge then it stops otherwise it makes another sweep down in search of a floor within // reach of the character's feet. - btScalar downSpeed = (_verticalVelocity < 0.0f) ? -_verticalVelocity : 0.0f; - if (downSpeed > 0.0f && downSpeed > _maxFallSpeed && (_wasOnGround || !_wasJumping)) { - downSpeed = _maxFallSpeed; - } - // first sweep for ledge btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS); - btVector3 step = up * (-(_lastStepUp + downSpeed * dt)); + btVector3 step = (_verticalVelocity * dt - _lastStepUp) * up; StepDownConvexResultCallback callback(_ghostObject, up, @@ -517,7 +481,7 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt _verticalVelocity = 0.0f; _verticalOffset = 0.0f; _wasJumping = false; - } else { + } else if (!_wasJumping) { // sweep again for floor within downStep threshold StepDownConvexResultCallback callback2 (_ghostObject, up, @@ -548,17 +512,19 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt _currentPosition = oldPosition - _lastStepUp * up; _lastStepUp = 0.0f; } + } else { + // we're jumping, and didn't hit anything, so our target position is where we would have fallen to + _currentPosition = _targetPosition; } } void CharacterController::setWalkDirection(const btVector3& walkDirection) { - _useWalkDirection = true; - _walkDirection = walkDirection; - _normalizedDirection = getNormalizedVector(_walkDirection); + // This must be implemented to satisfy base-class interface but does nothing. + // Use setVelocityForTimeInterval() instead. + assert(false); } void CharacterController::setVelocityForTimeInterval(const btVector3& velocity, btScalar timeInterval) { - _useWalkDirection = false; _walkDirection = velocity; _normalizedDirection = getNormalizedVector(_walkDirection); _velocityTimeInterval += timeInterval; @@ -610,7 +576,7 @@ void CharacterController::preStep(btCollisionWorld* collisionWorld) { } void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt) { - if (!_enabled || (!_useWalkDirection && _velocityTimeInterval <= 0.0)) { + if (!_enabled) { return; // no motion } @@ -634,17 +600,15 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar // (3) step the character down looking for new ledges, the original floor, or a floor one step below where we started stepUp(collisionWorld); - if (_useWalkDirection) { - stepForward(collisionWorld, _walkDirection); - } else { - // compute substep and decrement total interval - btScalar dtMoving = (dt < _velocityTimeInterval) ? dt : _velocityTimeInterval; - _velocityTimeInterval -= dt; - // stepForward substep - btVector3 move = _walkDirection * dtMoving; - stepForward(collisionWorld, move); - } + // compute substep and decrement total interval + btScalar dtMoving = (dt < _velocityTimeInterval) ? dt : _velocityTimeInterval; + _velocityTimeInterval -= dt; + + // stepForward substep + btVector3 move = _walkDirection * dtMoving; + stepForward(collisionWorld, move); + stepDown(collisionWorld, dt); xform.setOrigin(_currentPosition); @@ -668,22 +632,7 @@ bool CharacterController::canJump() const { } void CharacterController::jump() { - if (!canJump()) { - return; - } - - _verticalVelocity = _jumpSpeed; - _wasJumping = true; - -#if 0 - currently no jumping. - btTransform xform; - _rigidBody->getMotionState()->getWorldTransform(xform); - btVector3 up = xform.getBasis()[1]; - up.normalize(); - btScalar magnitude = (btScalar(1.0)/_rigidBody->getInvMass()) * btScalar(8.0); - _rigidBody->applyCentralImpulse (up * magnitude); -#endif + _pendingFlags |= PENDING_FLAG_JUMP; } void CharacterController::setGravity(btScalar gravity) { @@ -704,7 +653,7 @@ btScalar CharacterController::getMaxSlope() const { } bool CharacterController::onGround() const { - return _enabled && _verticalVelocity == 0.0 && _verticalOffset == 0.0; + return _enabled && _verticalVelocity == 0.0f && _verticalOffset == 0.0f; } void CharacterController::debugDraw(btIDebugDraw* debugDrawer) { @@ -716,42 +665,6 @@ void CharacterController::setUpInterpolate(bool value) { // (interpolate = true, and now default behavior) or happily penetrate objects above the avatar. } -/* -// protected -void CharacterController::createShapeAndGhost() { - // get new dimensions from avatar - float x = _boxScale.x; - float z = _boxScale.z; - _radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); - _halfHeight = 0.5f * _boxScale.y - _radius; - float MIN_HALF_HEIGHT = 0.1f; - if (_halfHeight < MIN_HALF_HEIGHT) { - _halfHeight = MIN_HALF_HEIGHT; - } - // NOTE: _shapeLocalOffset is already computed - - if (_radius > 0.0f) { - // create new ghost - _ghostObject = new btPairCachingGhostObject(); - _ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), - glmToBullet(_avatarData->getPosition()))); - // stepHeight affects the heights of ledges that the character can ascend - // however the actual ledge height is some function of _stepHeight - // due to character shape and this CharacterController algorithm - // (the function is approximately 2*_stepHeight) - _stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f; - - // create new shape - _convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight); - _ghostObject->setCollisionShape(_convexShape); - _ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); - } else { - // TODO: handle this failure case - } - _pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE; -} -*/ - void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { _boxScale = scale; @@ -808,7 +721,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } _dynamicsWorld = world; if (_dynamicsWorld) { - _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; + _pendingFlags &= ~ (PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_JUMP); _dynamicsWorld->addCollisionObject(getGhostObject(), btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); @@ -870,11 +783,17 @@ void CharacterController::preSimulation(btScalar timeStep) { if (_enabled && _dynamicsWorld) { glm::quat rotation = _avatarData->getOrientation(); glm::vec3 position = _avatarData->getPosition() + rotation * _shapeLocalOffset; - // TODO: harvest jump event here btVector3 walkVelocity = glmToBullet(_avatarData->getVelocity()); _ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); setVelocityForTimeInterval(walkVelocity, timeStep); + if (_pendingFlags & PENDING_FLAG_JUMP) { + _pendingFlags &= ~ PENDING_FLAG_JUMP; + if (canJump()) { + _verticalVelocity = _jumpSpeed; + _wasJumping = true; + } + } } } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 9803d0d6ee..598bc866d5 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -81,7 +81,6 @@ protected: bool _enabled; bool _wasOnGround; bool _wasJumping; - bool _useWalkDirection; btScalar _velocityTimeInterval; uint32_t _pendingFlags; @@ -132,18 +131,19 @@ public: virtual void setVelocityForTimeInterval(const btVector3& velocity, btScalar timeInterval); - void reset(btCollisionWorld* collisionWorld ); - void warp(const btVector3& origin); + virtual void reset(btCollisionWorld* collisionWorld ); + virtual void warp(const btVector3& origin); - void preStep(btCollisionWorld* collisionWorld); - void playerStep(btCollisionWorld* collisionWorld, btScalar dt); + virtual void preStep(btCollisionWorld* collisionWorld); + virtual void playerStep(btCollisionWorld* collisionWorld, btScalar dt); + + virtual bool canJump() const; + virtual void jump(); + virtual bool onGround() const; void setMaxFallSpeed(btScalar speed); void setJumpSpeed(btScalar jumpSpeed); void setMaxJumpHeight(btScalar maxJumpHeight); - bool canJump() const; - - void jump(); void setGravity(btScalar gravity); btScalar getGravity() const; @@ -155,7 +155,6 @@ public: btPairCachingGhostObject* getGhostObject(); - bool onGround() const; void setUpInterpolate(bool value); bool needsRemoval() const; From 430158c1095ed8c191d3d731bf083ffdbd8d269f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 24 Mar 2015 23:35:09 -0700 Subject: [PATCH 6/9] updateShapeIfNecessary() is a more correct name --- libraries/physics/src/CharacterController.cpp | 2 +- libraries/physics/src/CharacterController.h | 2 +- libraries/physics/src/PhysicsEngine.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index fa02f81e34..c26ffa3339 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -735,7 +735,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } } -void CharacterController::updateShape() { +void CharacterController::updateShapeIfNecessary() { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { assert(!(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION)); _pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 598bc866d5..740940ce53 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -164,7 +164,7 @@ public: void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); bool needsShapeUpdate() const; - void updateShape(); + void updateShapeIfNecessary(); void preSimulation(btScalar timeStep); void postSimulation(); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index bcde3318ad..9ca718e19a 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -299,7 +299,7 @@ void PhysicsEngine::stepSimulation() { if (_characterController->needsRemoval()) { _characterController->setDynamicsWorld(NULL); } - _characterController->updateShape(); + _characterController->updateShapeIfNecessary(); if (_characterController->needsAddition()) { _characterController->setDynamicsWorld(_dynamicsWorld); } From 674b2c97a28ec5d5000a6e597325d9cef152ea36 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 25 Mar 2015 08:53:33 -0700 Subject: [PATCH 7/9] added comments to zlib license --- libraries/physics/src/CharacterController.cpp | 1 + libraries/physics/src/CharacterController.h | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index c26ffa3339..277d9748cd 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -1,6 +1,7 @@ /* Bullet Continuous Collision Detection and Physics Library Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com +2015.03.25 -- modified by Andrew Meadows andrew@highfidelity.io This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 740940ce53..323529b1cd 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -1,6 +1,7 @@ /* Bullet Continuous Collision Detection and Physics Library Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com +2015.03.25 -- modified by Andrew Meadows andrew@highfidelity.io This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. From 2128f213082ccc8e49865d9a9eae069f8be72db5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 25 Mar 2015 11:02:48 -0700 Subject: [PATCH 8/9] only set ADD bit with UPDATE_SHAPE when enabled --- libraries/physics/src/CharacterController.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 277d9748cd..88d26110a7 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -684,10 +684,13 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { // shape hasn't changed --> nothing to do } else { - // we need to: remove, update, add + // we always need to: REMOVE when UPDATE_SHAPE, to avoid deleting shapes out from under the PhysicsEngine _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION - | PENDING_FLAG_UPDATE_SHAPE - | PENDING_FLAG_ADD_TO_SIMULATION; + | PENDING_FLAG_UPDATE_SHAPE; + // but only need to ADD back when we happen to be enabled + if (_enabled) { + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; + } } // it's ok to change offset immediately -- there are no thread safety issues here @@ -705,8 +708,12 @@ bool CharacterController::needsRemoval() const { void CharacterController::setEnabled(bool enabled) { if (enabled != _enabled) { if (enabled) { + // Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit. + // Setting the ADD bit here works for all cases so we don't even bother checking other bits. _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; } else { + // Always set REMOVE bit when going disabled, and we always clear the ADD bit just in case + // it was previously set by something else (e.g. an UPDATE_SHAPE event). _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; } From 2440c47648f355e6d48bf1148c31f1ea9ff7ed37 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 25 Mar 2015 11:06:19 -0700 Subject: [PATCH 9/9] remove extra whitespace --- libraries/physics/src/CharacterController.cpp | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 88d26110a7..e84df7d644 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -5,12 +5,12 @@ Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. - If you use this software in a product, an acknowledgment in the product documentation would be appreciated +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. @@ -29,7 +29,7 @@ const uint32_t PENDING_FLAG_JUMP = 1U << 3; // static helper method static btVector3 getNormalizedVector(const btVector3& v) { - // NOTE: check the length first, then normalize + // NOTE: check the length first, then normalize // --> avoids assert when trying to normalize zero-length vectors btScalar vLength = v.length(); if (vLength < FLT_EPSILON) { @@ -87,8 +87,8 @@ protected: class StepDownConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { // special convex sweep callback for character during the stepDown() phase public: - StepDownConvexResultCallback(btCollisionObject* me, - const btVector3& up, + StepDownConvexResultCallback(btCollisionObject* me, + const btVector3& up, const btVector3& start, const btVector3& step, const btVector3& pushDirection, @@ -144,7 +144,7 @@ class StepDownConvexResultCallback : public btCollisionWorld::ClosestConvexResul btVector3 side(_radius, - (_halfHeight - _step.length() + fractionalStep.dot(_up)), 0.0f); btScalar maxAngle = side.angle(-_up); - // Ignore hits that are larger than maxAngle. Effectively what is happening here is: + // Ignore hits that are larger than maxAngle. Effectively what is happening here is: // we're ignoring hits at contacts that have non-vertical normals... if they hit higher // than the character's "feet". Ignoring the contact allows the character to slide down // for these hits. In other words, vertical walls against the character's torso will @@ -238,9 +238,9 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl btVector3 minAabb, maxAabb; _convexShape->getAabb(_ghostObject->getWorldTransform(), minAabb, maxAabb); - collisionWorld->getBroadphase()->setAabb(_ghostObject->getBroadphaseHandle(), - minAabb, - maxAabb, + collisionWorld->getBroadphase()->setAabb(_ghostObject->getBroadphaseHandle(), + minAabb, + maxAabb, collisionWorld->getDispatcher()); bool penetration = false; @@ -299,14 +299,14 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl if (collisionHeight < _lastStepUp) { // This contact is below the lastStepUp, so we ignore it for penetration resolution, - // otherwise it may prevent the character from getting close enough to find any available + // otherwise it may prevent the character from getting close enough to find any available // horizontal foothold that would allow it to climbe the ledge. In other words, we're // making the character's "feet" soft for collisions against steps, but not floors. useContact = false; - } + } } if (useContact) { - + if (dist < maxPen) { maxPen = dist; _floorNormal = normal; @@ -382,7 +382,7 @@ void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& //if (tangentMag != 0.0) { if (0) { btVector3 parComponent = parallelDir * btScalar(tangentMag * movementLength); - _targetPosition += parComponent; + _targetPosition += parComponent; } if (normalMag != 0.0) { @@ -451,7 +451,7 @@ void CharacterController::stepForward(btCollisionWorld* collisionWorld, const bt void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt) { // phase 3: down // - // The "stepDown" phase first makes a normal sweep down that cancels the lift from the "stepUp" phase. + // The "stepDown" phase first makes a normal sweep down that cancels the lift from the "stepUp" phase. // If it hits a ledge then it stops otherwise it makes another sweep down in search of a floor within // reach of the character's feet. @@ -459,11 +459,11 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS); btVector3 step = (_verticalVelocity * dt - _lastStepUp) * up; - StepDownConvexResultCallback callback(_ghostObject, - up, - _currentPosition, step, + StepDownConvexResultCallback callback(_ghostObject, + up, + _currentPosition, step, _walkDirection, - _maxSlopeCosine, + _maxSlopeCosine, _radius, _halfHeight); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; @@ -484,11 +484,11 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt _wasJumping = false; } else if (!_wasJumping) { // sweep again for floor within downStep threshold - StepDownConvexResultCallback callback2 (_ghostObject, - up, + StepDownConvexResultCallback callback2 (_ghostObject, + up, _currentPosition, step, _walkDirection, - _maxSlopeCosine, + _maxSlopeCosine, _radius, _halfHeight); callback2.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; @@ -542,8 +542,8 @@ void CharacterController::reset(btCollisionWorld* collisionWorld) { //clear pair cache btHashedOverlappingPairCache *cache = _ghostObject->getOverlappingPairCache(); while (cache->getOverlappingPairArray().size() > 0) { - cache->removeOverlappingPair(cache->getOverlappingPairArray()[0].m_pProxy0, - cache->getOverlappingPairArray()[0].m_pProxy1, + cache->removeOverlappingPair(cache->getOverlappingPairArray()[0].m_pProxy0, + cache->getOverlappingPairArray()[0].m_pProxy1, collisionWorld->getDispatcher()); } } @@ -712,7 +712,7 @@ void CharacterController::setEnabled(bool enabled) { // Setting the ADD bit here works for all cases so we don't even bother checking other bits. _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; } else { - // Always set REMOVE bit when going disabled, and we always clear the ADD bit just in case + // Always set REMOVE bit when going disabled, and we always clear the ADD bit just in case // it was previously set by something else (e.g. an UPDATE_SHAPE event). _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; @@ -754,7 +754,7 @@ void CharacterController::updateShapeIfNecessary() { _ghostObject = NULL; delete _convexShape; _convexShape = NULL; - + // compute new dimensions from avatar's bounding box float x = _boxScale.x; float z = _boxScale.z; @@ -765,18 +765,18 @@ void CharacterController::updateShapeIfNecessary() { _halfHeight = MIN_HALF_HEIGHT; } // NOTE: _shapeLocalOffset is already computed - + if (_radius > 0.0f) { // create new ghost _ghostObject = new btPairCachingGhostObject(); _ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), glmToBullet(_avatarData->getPosition()))); // stepHeight affects the heights of ledges that the character can ascend - // however the actual ledge height is some function of _stepHeight - // due to character shape and this CharacterController algorithm + // however the actual ledge height is some function of _stepHeight + // due to character shape and this CharacterController algorithm // (the function is approximately 2*_stepHeight) _stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f; - + // create new shape _convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight); _ghostObject->setCollisionShape(_convexShape); @@ -807,11 +807,11 @@ void CharacterController::preSimulation(btScalar timeStep) { void CharacterController::postSimulation() { if (_enabled) { - const btTransform& avatarTransform = _ghostObject->getWorldTransform(); + const btTransform& avatarTransform = _ghostObject->getWorldTransform(); glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); glm::vec3 offset = rotation * _shapeLocalOffset; _avatarData->setOrientation(rotation); - _avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); + _avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); } }