improved shape prevents avatar walking up walls

This commit is contained in:
Andrew Meadows 2016-10-15 16:00:02 -07:00
parent 6125357104
commit 72ad974b3e
6 changed files with 54 additions and 36 deletions

View file

@ -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<btScalar*>(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();

View file

@ -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<btCapsuleShape*>(shape)); // KINEMATIC_CONTROLLER_HACK
assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE);
_ghost.setCharacterShape(static_cast<btConvexHullShape*>(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

View file

@ -16,8 +16,8 @@
#include <PhysicsHelpers.h>
#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<const btConvexHullShape*>(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<const btConvexShape*>(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<const btConvexShape*>(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;
}

View file

@ -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

View file

@ -10,9 +10,16 @@
//
#include "CharacterGhostShape.h"
CharacterGhostShape::CharacterGhostShape(const btConvexHullShape* shape) :
btConvexHullShape(reinterpret_cast<const btScalar*>(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;

View file

@ -12,13 +12,12 @@
#ifndef hifi_CharacterGhostShape_h
#define hifi_CharacterGhostShape_h
#include <BulletCollision/CollisionShapes/btCapsuleShape.h>
#include <BulletCollision/CollisionShapes/btConvexHullShape.h>
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;
};