mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 19:59:28 +02:00
prevent avatars from walking up walls
This commit is contained in:
parent
d29386c43f
commit
4fb6d5023b
3 changed files with 72 additions and 21 deletions
|
@ -128,6 +128,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
||||||
_ghost.setMaxStepHeight(0.75f * (_radius + _halfHeight)); // HACK
|
_ghost.setMaxStepHeight(0.75f * (_radius + _halfHeight)); // HACK
|
||||||
_ghost.setMinWallAngle(PI / 4.0f); // HACK
|
_ghost.setMinWallAngle(PI / 4.0f); // HACK
|
||||||
_ghost.setUpDirection(_currentUp);
|
_ghost.setUpDirection(_currentUp);
|
||||||
|
_ghost.setMotorOnly(!_moveKinematically);
|
||||||
_ghost.setWorldTransform(_rigidBody->getWorldTransform());
|
_ghost.setWorldTransform(_rigidBody->getWorldTransform());
|
||||||
}
|
}
|
||||||
if (_dynamicsWorld) {
|
if (_dynamicsWorld) {
|
||||||
|
@ -143,7 +144,24 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld, btScalar dt) {
|
bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld, btScalar dt) {
|
||||||
|
if (_moveKinematically) {
|
||||||
|
// kinematic motion will move() the _ghost later
|
||||||
|
return _ghost.hasSupport();
|
||||||
|
}
|
||||||
_stepHeight = _minStepHeight; // clears last step obstacle
|
_stepHeight = _minStepHeight; // clears last step obstacle
|
||||||
|
|
||||||
|
btScalar targetSpeed = _targetVelocity.length();
|
||||||
|
if (targetSpeed > FLT_EPSILON) {
|
||||||
|
// move the _ghost forward to test for step
|
||||||
|
btTransform transform = _rigidBody->getWorldTransform();
|
||||||
|
transform.setOrigin(transform.getOrigin());
|
||||||
|
_ghost.setWorldTransform(transform);
|
||||||
|
_ghost.setMotorVelocity(_targetVelocity);
|
||||||
|
float overshoot = _radius;
|
||||||
|
_ghost.setHovering(_state == State::Hover);
|
||||||
|
_ghost.move(dt, overshoot, _gravity);
|
||||||
|
}
|
||||||
|
|
||||||
btDispatcher* dispatcher = collisionWorld->getDispatcher();
|
btDispatcher* dispatcher = collisionWorld->getDispatcher();
|
||||||
int numManifolds = dispatcher->getNumManifolds();
|
int numManifolds = dispatcher->getNumManifolds();
|
||||||
bool hasFloor = false;
|
bool hasFloor = false;
|
||||||
|
@ -165,17 +183,26 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld, btSc
|
||||||
btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame
|
btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame
|
||||||
btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character
|
btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character
|
||||||
btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp);
|
btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp);
|
||||||
if (hitHeight < _radius && normal.dot(_currentUp) > COS_PI_OVER_THREE) {
|
if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > COS_PI_OVER_THREE) {
|
||||||
hasFloor = true;
|
hasFloor = true;
|
||||||
|
if (!_ghost.isSteppingUp()) {
|
||||||
|
// early exit since all we need to know is that we're on a floor
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// remember highest step obstacle
|
// analysis of the step info using manifold data is unreliable, so we only proceed
|
||||||
if (hitHeight > _maxStepHeight) {
|
// when the _ghost has detected a steppable obstacle
|
||||||
// this manifold is invalidated by point that is too high
|
if (_ghost.isSteppingUp()) {
|
||||||
stepContactIndex = -1;
|
// remember highest step obstacle
|
||||||
break;
|
if (hitHeight > _maxStepHeight) {
|
||||||
} else if (hitHeight > highestStep && normal.dot(_targetVelocity) < 0.0f ) {
|
// this manifold is invalidated by point that is too high
|
||||||
highestStep = hitHeight;
|
stepContactIndex = -1;
|
||||||
stepContactIndex = j;
|
break;
|
||||||
|
} else if (hitHeight > highestStep && normal.dot(_targetVelocity) < 0.0f ) {
|
||||||
|
highestStep = hitHeight;
|
||||||
|
stepContactIndex = j;
|
||||||
|
hasFloor = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stepContactIndex > -1 && highestStep > _stepHeight) {
|
if (stepContactIndex > -1 && highestStep > _stepHeight) {
|
||||||
|
@ -187,6 +214,10 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld, btSc
|
||||||
_stepPoint = rotation * pointOnCharacter; // rotate into world-frame
|
_stepPoint = rotation * pointOnCharacter; // rotate into world-frame
|
||||||
_stepNormal = normal;
|
_stepNormal = normal;
|
||||||
}
|
}
|
||||||
|
if (hasFloor && !_ghost.isSteppingUp()) {
|
||||||
|
// early exit since all we need to know is that we're on a floor
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hasFloor;
|
return hasFloor;
|
||||||
|
@ -532,9 +563,13 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
||||||
} else {
|
} else {
|
||||||
// compute local UP
|
// compute local UP
|
||||||
btVector3 up = _currentUp.rotate(axis, -angle);
|
btVector3 up = _currentUp.rotate(axis, -angle);
|
||||||
|
|
||||||
// add sky hook when encountering a step obstacle
|
|
||||||
btVector3 motorVelocity = motor.velocity;
|
btVector3 motorVelocity = motor.velocity;
|
||||||
|
|
||||||
|
// save these non-adjusted components for later
|
||||||
|
btVector3 vTargetVelocity = motorVelocity.dot(up) * up;
|
||||||
|
btVector3 hTargetVelocity = motorVelocity - vTargetVelocity;
|
||||||
|
|
||||||
|
// adjust motorVelocity uphill when encountering a step obstacle
|
||||||
btScalar vTimescale = motor.vTimescale;
|
btScalar vTimescale = motor.vTimescale;
|
||||||
if (_stepHeight > _minStepHeight) {
|
if (_stepHeight > _minStepHeight) {
|
||||||
// there is a step
|
// there is a step
|
||||||
|
@ -557,8 +592,8 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
||||||
// split velocity into horizontal and vertical components
|
// split velocity into horizontal and vertical components
|
||||||
btVector3 vVelocity = velocity.dot(up) * up;
|
btVector3 vVelocity = velocity.dot(up) * up;
|
||||||
btVector3 hVelocity = velocity - vVelocity;
|
btVector3 hVelocity = velocity - vVelocity;
|
||||||
btVector3 vTargetVelocity = motorVelocity.dot(up) * up;
|
btVector3 vMotorVelocity = motorVelocity.dot(up) * up;
|
||||||
btVector3 hTargetVelocity = motorVelocity - vTargetVelocity;
|
btVector3 hMotorVelocity = motorVelocity - vMotorVelocity;
|
||||||
|
|
||||||
// modify each component separately
|
// modify each component separately
|
||||||
btScalar maxTau = 0.0f;
|
btScalar maxTau = 0.0f;
|
||||||
|
@ -568,7 +603,7 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
||||||
tau = 1.0f;
|
tau = 1.0f;
|
||||||
}
|
}
|
||||||
maxTau = tau;
|
maxTau = tau;
|
||||||
hVelocity += (hTargetVelocity - hVelocity) * tau;
|
hVelocity += (hMotorVelocity - hVelocity) * tau;
|
||||||
}
|
}
|
||||||
if (vTimescale < MAX_CHARACTER_MOTOR_TIMESCALE) {
|
if (vTimescale < MAX_CHARACTER_MOTOR_TIMESCALE) {
|
||||||
btScalar tau = dt / vTimescale;
|
btScalar tau = dt / vTimescale;
|
||||||
|
@ -578,7 +613,7 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
||||||
if (tau > maxTau) {
|
if (tau > maxTau) {
|
||||||
maxTau = tau;
|
maxTau = tau;
|
||||||
}
|
}
|
||||||
vVelocity += (vTargetVelocity - vVelocity) * tau;
|
vVelocity += (vMotorVelocity - vVelocity) * tau;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add components back together and rotate into world-frame
|
// add components back together and rotate into world-frame
|
||||||
|
@ -819,5 +854,6 @@ void CharacterController::setMoveKinematically(bool kinematic) {
|
||||||
if (kinematic != _moveKinematically) {
|
if (kinematic != _moveKinematically) {
|
||||||
_moveKinematically = kinematic;
|
_moveKinematically = kinematic;
|
||||||
_pendingFlags |= PENDING_FLAG_UPDATE_SHAPE;
|
_pendingFlags |= PENDING_FLAG_UPDATE_SHAPE;
|
||||||
|
_ghost.setMotorOnly(!_moveKinematically);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,13 @@ void CharacterGhostObject::setUpDirection(const btVector3& up) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CharacterGhostObject::setMotorVelocity(const btVector3& velocity) {
|
||||||
|
_motorVelocity = velocity;
|
||||||
|
if (_motorOnly) {
|
||||||
|
_linearVelocity = _motorVelocity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// override of btCollisionObject::setCollisionShape()
|
// override of btCollisionObject::setCollisionShape()
|
||||||
void CharacterGhostObject::setCharacterCapsule(btCapsuleShape* capsule) {
|
void CharacterGhostObject::setCharacterCapsule(btCapsuleShape* capsule) {
|
||||||
assert(capsule);
|
assert(capsule);
|
||||||
|
@ -74,6 +81,7 @@ void CharacterGhostObject::setCollisionWorld(btCollisionWorld* world) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravity) {
|
void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravity) {
|
||||||
|
bool oldOnFloor = _onFloor;
|
||||||
_onFloor = false;
|
_onFloor = false;
|
||||||
_steppingUp = false;
|
_steppingUp = false;
|
||||||
assert(_world && _inWorld);
|
assert(_world && _inWorld);
|
||||||
|
@ -172,6 +180,7 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravit
|
||||||
}
|
}
|
||||||
nextTransform.setOrigin(startPosition + forwardTranslation);
|
nextTransform.setOrigin(startPosition + forwardTranslation);
|
||||||
setWorldTransform(nextTransform);
|
setWorldTransform(nextTransform);
|
||||||
|
_onFloor = _onFloor || oldOnFloor;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if we get here then we hit something that might be steppable
|
// if we get here then we hit something that might be steppable
|
||||||
|
@ -213,6 +222,7 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravit
|
||||||
// either there is no future landing spot, or there is but we can't stand on it
|
// either there is no future landing spot, or there is but we can't stand on it
|
||||||
// in any case: we go forward as much as possible
|
// in any case: we go forward as much as possible
|
||||||
nextTransform.setOrigin(startPosition + forwardSweepHitFraction * (stepDistance / longSweepDistance) * forwardSweep);
|
nextTransform.setOrigin(startPosition + forwardSweepHitFraction * (stepDistance / longSweepDistance) * forwardSweep);
|
||||||
|
_onFloor = _onFloor || oldOnFloor;
|
||||||
updateTraction(nextTransform.getOrigin());
|
updateTraction(nextTransform.getOrigin());
|
||||||
}
|
}
|
||||||
setWorldTransform(nextTransform);
|
setWorldTransform(nextTransform);
|
||||||
|
@ -352,10 +362,12 @@ void CharacterGhostObject::refreshOverlappingPairCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterGhostObject::updateVelocity(btScalar dt, btScalar gravity) {
|
void CharacterGhostObject::updateVelocity(btScalar dt, btScalar gravity) {
|
||||||
if (_hovering) {
|
if (!_motorOnly) {
|
||||||
_linearVelocity *= 0.999f; // HACK damping
|
if (_hovering) {
|
||||||
} else {
|
_linearVelocity *= 0.999f; // HACK damping
|
||||||
_linearVelocity += (dt * gravity) * _upDirection;
|
} else {
|
||||||
|
_linearVelocity += (dt * gravity) * _upDirection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +389,7 @@ void CharacterGhostObject::updateHoverState(const btVector3& position) {
|
||||||
|
|
||||||
void CharacterGhostObject::updateTraction(const btVector3& position) {
|
void CharacterGhostObject::updateTraction(const btVector3& position) {
|
||||||
updateHoverState(position);
|
updateHoverState(position);
|
||||||
if (_hovering) {
|
if (_hovering || _motorOnly) {
|
||||||
_linearVelocity = _motorVelocity;
|
_linearVelocity = _motorVelocity;
|
||||||
} else if (_onFloor) {
|
} else if (_onFloor) {
|
||||||
// compute a velocity that swings the capsule around the _floorContact
|
// compute a velocity that swings the capsule around the _floorContact
|
||||||
|
|
|
@ -33,7 +33,7 @@ public:
|
||||||
|
|
||||||
void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight);
|
void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight);
|
||||||
void setUpDirection(const btVector3& up);
|
void setUpDirection(const btVector3& up);
|
||||||
void setMotorVelocity(const btVector3& velocity) { _motorVelocity = velocity; }
|
void setMotorVelocity(const btVector3& velocity);
|
||||||
void setMinWallAngle(btScalar angle) { _maxWallNormalUpComponent = cosf(angle); }
|
void setMinWallAngle(btScalar angle) { _maxWallNormalUpComponent = cosf(angle); }
|
||||||
void setMaxStepHeight(btScalar height) { _maxStepHeight = height; }
|
void setMaxStepHeight(btScalar height) { _maxStepHeight = height; }
|
||||||
|
|
||||||
|
@ -53,9 +53,11 @@ public:
|
||||||
|
|
||||||
bool isHovering() const { return _hovering; }
|
bool isHovering() const { return _hovering; }
|
||||||
void setHovering(bool hovering) { _hovering = hovering; }
|
void setHovering(bool hovering) { _hovering = hovering; }
|
||||||
|
void setMotorOnly(bool motorOnly) { _motorOnly = motorOnly; }
|
||||||
|
|
||||||
bool hasSupport() const { return _onFloor; }
|
bool hasSupport() const { return _onFloor; }
|
||||||
bool isSteppingUp() const { return _steppingUp; }
|
bool isSteppingUp() const { return _steppingUp; }
|
||||||
|
const btVector3& getFloorNormal() const { return _floorNormal; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void removeFromWorld();
|
void removeFromWorld();
|
||||||
|
@ -93,6 +95,7 @@ protected:
|
||||||
bool _onFloor { false }; // output, is actually standing on floor
|
bool _onFloor { false }; // output, is actually standing on floor
|
||||||
bool _steppingUp { false }; // output, future sweep hit a steppable ledge
|
bool _steppingUp { false }; // output, future sweep hit a steppable ledge
|
||||||
bool _hasFloor { false }; // output, has floor underneath to fall on
|
bool _hasFloor { false }; // output, has floor underneath to fall on
|
||||||
|
bool _motorOnly { false }; // input, _linearVelocity slaves to _motorVelocity
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_CharacterGhostObject_h
|
#endif // hifi_CharacterGhostObject_h
|
||||||
|
|
Loading…
Reference in a new issue