diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index d98c66c8c6..9b46aae84d 100644 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -37,12 +37,32 @@ void MyCharacterController::updateShapeIfNecessary() { if (_radius > 0.0f) { // create RigidBody if it doesn't exist if (!_rigidBody) { + // HACK: the avatar collides using convex hull with a collision margin equal to + // the old capsule radius. Two points define a capsule and additional points are + // spread out at chest level to produce a slight taper toward the feet. This + // makes the avatar more likely to collide with vertical walls at a higher point + // and thus less likely to produce a single-point collision manifold below the + // _maxStepHeight when walking into against vertical surfaces --> fixes a bug + // where the "walk up steps" feature would allow the avatar to walk up vertical + // walls. + const int32_t NUM_POINTS = 6; + btVector3 points[NUM_POINTS]; + btVector3 xAxis = btVector3(1.0f, 0.0f, 0.0f); + btVector3 yAxis = btVector3(0.0f, 1.0f, 0.0f); + btVector3 zAxis = btVector3(0.0f, 0.0f, 1.0f); + points[0] = _halfHeight * yAxis; + points[1] = -_halfHeight * yAxis; + points[2] = (0.75f * _halfHeight) * yAxis - (0.1f * _radius) * zAxis; + points[3] = (0.75f * _halfHeight) * yAxis + (0.1f * _radius) * zAxis; + points[4] = (0.75f * _halfHeight) * yAxis - (0.1f * _radius) * xAxis; + points[5] = (0.75f * _halfHeight) * yAxis + (0.1f * _radius) * xAxis; + btCollisionShape* shape = new btConvexHullShape(reinterpret_cast(points), NUM_POINTS); + shape->setMargin(_radius); // HACK: use some simple mass property defaults for now const float DEFAULT_AVATAR_MASS = 100.0f; const btVector3 DEFAULT_AVATAR_INERTIA_TENSOR(30.0f, 8.0f, 30.0f); - btCollisionShape* shape = new btCapsuleShape(_radius, 2.0f * _halfHeight); _rigidBody = new btRigidBody(DEFAULT_AVATAR_MASS, nullptr, shape, DEFAULT_AVATAR_INERTIA_TENSOR); } else { btCollisionShape* shape = _rigidBody->getCollisionShape(); diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 07442b4f45..64025449dd 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -116,8 +116,8 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { // restore gravity settings because adding an object to the world overwrites its gravity setting _rigidBody->setGravity(_gravity * _currentUp); btCollisionShape* shape = _rigidBody->getCollisionShape(); - assert(shape && shape->getShapeType() == CAPSULE_SHAPE_PROXYTYPE); - _ghost.setCharacterCapsule(static_cast(shape)); // KINEMATIC_CONTROLLER_HACK + assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE); + _ghost.setCharacterShape(static_cast(shape)); } // KINEMATIC_CONTROLLER_HACK _ghost.setCollisionGroupAndMask(_collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ _collisionGroup)); @@ -171,7 +171,6 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld, btSc btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp); if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > COS_PI_OVER_THREE) { - //std::cout << "adebug manifoldIndex = " << i << " contactIndex = " << j << " hitOnCharacter*up = " << pointOnCharacter.dot(_currentUp) << std::endl; // adebug hasFloor = true; if (!pushing) { // we're not pushing against anything so we can early exit diff --git a/libraries/physics/src/CharacterGhostObject.cpp b/libraries/physics/src/CharacterGhostObject.cpp index 328d869f01..f897711622 100755 --- a/libraries/physics/src/CharacterGhostObject.cpp +++ b/libraries/physics/src/CharacterGhostObject.cpp @@ -16,8 +16,8 @@ #include -#include "CharacterGhostShape.h" #include "CharacterRayResult.h" +#include "CharacterGhostShape.h" CharacterGhostObject::~CharacterGhostObject() { @@ -62,13 +62,14 @@ void CharacterGhostObject::setMotorVelocity(const btVector3& velocity) { } // override of btCollisionObject::setCollisionShape() -void CharacterGhostObject::setCharacterCapsule(btCapsuleShape* capsule) { - assert(capsule); - // we create our own CharacterGhostShape which has a larger Aabb for more reliable sweep tests +void CharacterGhostObject::setCharacterShape(btConvexHullShape* shape) { + assert(shape); + // we create our own shape with an expanded Aabb for more reliable sweep tests if (_ghostShape) { delete _ghostShape; } - _ghostShape = new CharacterGhostShape(capsule->getRadius(), 2.0f * capsule->getHalfHeight()); + + _ghostShape = new CharacterGhostShape(static_cast(shape)); setCollisionShape(_ghostShape); } @@ -127,14 +128,10 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravit return; } - const btCollisionShape* shape = getCollisionShape(); - assert(shape->isConvex()); - const btConvexShape* convexShape= static_cast(shape); - // augment forwardSweep to help slow moving sweeps get over steppable ledges - btScalar margin = shape->getMargin(); - if (overshoot < margin) { - overshoot = margin; + const btScalar MIN_OVERSHOOT = 0.04f; // default margin + if (overshoot < MIN_OVERSHOOT) { + overshoot = MIN_OVERSHOOT; } btScalar longSweepDistance = stepDistance + overshoot; forwardSweep *= longSweepDistance / stepDistance; @@ -143,7 +140,7 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravit CharacterSweepResult result(this); btTransform nextTransform = startTransform; nextTransform.setOrigin(startPosition + forwardSweep); - sweepTest(convexShape, startTransform, nextTransform, result); // forward + sweepTest(_characterShape, startTransform, nextTransform, result); // forward if (!result.hasHit()) { nextTransform.setOrigin(startPosition + (stepDistance / longSweepDistance) * forwardSweep); @@ -151,7 +148,7 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravit updateTraction(nextTransform.getOrigin()); return; } - bool verticalOnly = btFabs(btFabs(_linearVelocity.dot(_upDirection)) - speed) < margin; + bool verticalOnly = btFabs(btFabs(_linearVelocity.dot(_upDirection)) - speed) < MIN_OVERSHOOT; if (verticalOnly) { // no need to step nextTransform.setOrigin(startPosition + (result.m_closestHitFraction * stepDistance / longSweepDistance) * forwardSweep); @@ -172,7 +169,7 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravit btVector3 hitFromBase = result.m_hitPointWorld - (startPosition - ((_radius + _halfHeight) * _upDirection)); btScalar hitHeight = hitFromBase.dot(_upDirection); if (hitHeight > _maxStepHeight) { - // capsule can't step over the obstacle so move forward as much as possible before we bail + // shape can't step over the obstacle so move forward as much as possible before we bail btVector3 forwardTranslation = result.m_closestHitFraction * forwardSweep; btScalar forwardDistance = forwardTranslation.length(); if (forwardDistance > stepDistance) { @@ -195,7 +192,7 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravit result.resetHitHistory(); startTransform.setOrigin(startPosition + availableStepHeight * _upDirection); nextTransform.setOrigin(startTransform.getOrigin() + forwardSweep); - sweepTest(convexShape, startTransform, nextTransform, result); + sweepTest(_characterShape, startTransform, nextTransform, result); if (result.hasHit()) { startTransform.setOrigin(startTransform.getOrigin() + result.m_closestHitFraction * forwardSweep); } else { @@ -206,7 +203,7 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravit result.resetHitHistory(); btVector3 downSweep = (- availableStepHeight) * _upDirection; nextTransform.setOrigin(startTransform.getOrigin() + downSweep); - sweepTest(convexShape, startTransform, nextTransform, result); + sweepTest(_characterShape, startTransform, nextTransform, result); if (result.hasHit() && result.m_hitNormalWorld.dot(_upDirection) > _maxWallNormalUpComponent) { // can stand on future landing spot, so we interpolate toward it _floorNormal = result.m_hitNormalWorld; @@ -395,7 +392,7 @@ void CharacterGhostObject::updateTraction(const btVector3& position) { if (_hovering || _motorOnly) { _linearVelocity = _motorVelocity; } else if (_onFloor) { - // compute a velocity that swings the capsule around the _floorContact + // compute a velocity that swings the shape around the _floorContact btVector3 leverArm = _floorContact - position; btVector3 pathDirection = leverArm.cross(_motorVelocity.cross(leverArm)); btScalar pathLength = pathDirection.length(); @@ -408,15 +405,11 @@ void CharacterGhostObject::updateTraction(const btVector3& position) { } btScalar CharacterGhostObject::measureAvailableStepHeight() const { - const btCollisionShape* shape = getCollisionShape(); - assert(shape->isConvex()); - const btConvexShape* convexShape= static_cast(shape); - CharacterSweepResult result(this); btTransform transform = getWorldTransform(); btTransform nextTransform = transform; nextTransform.setOrigin(transform.getOrigin() + _maxStepHeight * _upDirection); - sweepTest(convexShape, transform, nextTransform, result); + sweepTest(_characterShape, transform, nextTransform, result); return result.m_closestHitFraction * _maxStepHeight; } diff --git a/libraries/physics/src/CharacterGhostObject.h b/libraries/physics/src/CharacterGhostObject.h index 87310714c8..2567cae204 100755 --- a/libraries/physics/src/CharacterGhostObject.h +++ b/libraries/physics/src/CharacterGhostObject.h @@ -40,7 +40,7 @@ public: void setLinearVelocity(const btVector3& velocity) { _linearVelocity = velocity; } const btVector3& getLinearVelocity() const { return _linearVelocity; } - void setCharacterCapsule(btCapsuleShape* capsule); + void setCharacterShape(btConvexHullShape* shape); void setCollisionWorld(btCollisionWorld* world); @@ -88,8 +88,8 @@ protected: btScalar _radius { 0.0f }; btScalar _maxWallNormalUpComponent { 0.0f }; // input: max vertical component of wall normal btScalar _maxStepHeight { 0.0f }; // input, max step height the character can climb - btCapsuleShape* _characterShape { nullptr }; // input, shape of character - CharacterGhostShape* _ghostShape{ nullptr }; // internal, shape whose Aabb is used for overlap cache + btConvexHullShape* _characterShape { nullptr }; // input, shape of character + CharacterGhostShape* _ghostShape { nullptr }; // internal, shape whose Aabb is used for overlap cache int16_t _collisionFilterGroup { 0 }; int16_t _collisionFilterMask { 0 }; bool _inWorld { false }; // internal, was added to world diff --git a/libraries/physics/src/CharacterGhostShape.cpp b/libraries/physics/src/CharacterGhostShape.cpp index bf031ec569..156d18a22b 100644 --- a/libraries/physics/src/CharacterGhostShape.cpp +++ b/libraries/physics/src/CharacterGhostShape.cpp @@ -10,9 +10,16 @@ // #include "CharacterGhostShape.h" +CharacterGhostShape::CharacterGhostShape(const btConvexHullShape* shape) : + btConvexHullShape(reinterpret_cast(shape->getUnscaledPoints()), shape->getNumPoints(), sizeof(btVector3)) { + assert(shape); + assert(shape->getUnscaledPoints()); + assert(shape->getNumPoints() > 0); + setMargin(shape->getMargin()); +} void CharacterGhostShape::getAabb (const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const { - btCapsuleShape::getAabb(t, aabbMin, aabbMax); + btConvexHullShape::getAabb(t, aabbMin, aabbMax); // double the size of the Aabb by expanding both corners by half the extent btVector3 expansion = 0.5f * (aabbMax - aabbMin); aabbMin -= expansion; diff --git a/libraries/physics/src/CharacterGhostShape.h b/libraries/physics/src/CharacterGhostShape.h index 9c9efded34..614bc168af 100644 --- a/libraries/physics/src/CharacterGhostShape.h +++ b/libraries/physics/src/CharacterGhostShape.h @@ -12,13 +12,12 @@ #ifndef hifi_CharacterGhostShape_h #define hifi_CharacterGhostShape_h -#include +#include -class CharacterGhostShape : public btCapsuleShape { - // Same as btCapsuleShape but reports an expanded Aabb for larger ghost overlap cache +class CharacterGhostShape : public btConvexHullShape { + // Same as btConvexHullShape but reports an expanded Aabb for larger ghost overlap cache public: - CharacterGhostShape(btScalar radius, btScalar height) : btCapsuleShape(radius, height) { - } + CharacterGhostShape(const btConvexHullShape* shape); virtual void getAabb (const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const override; };