more correct steepest walkable slope detection

This commit is contained in:
Andrew Meadows 2016-10-18 16:48:33 -07:00
parent 5a04c7ceee
commit 6a445a0a98

View file

@ -112,11 +112,29 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm:
rayDirection /= stepLength; rayDirection /= stepLength;
const btScalar backSlop = 0.04f; const btScalar backSlop = 0.04f;
// compute rotation that will orient local ray start points to face step direction
btVector3 forward = rotation * btVector3(0.0f, 0.0f, -1.0f);
btVector3 adjustedDirection = rayDirection - rayDirection.dot(_currentUp) * _currentUp;
btVector3 axis = forward.cross(adjustedDirection);
btScalar lengthAxis = axis.length();
if (lengthAxis > FLT_EPSILON) {
// we're walking sideways
btScalar angle = acosf(lengthAxis / adjustedDirection.length());
if (rayDirection.dot(forward) < 0.0f) {
angle = PI - angle;
}
axis /= lengthAxis;
rotation = btMatrix3x3(btQuaternion(axis, angle)) * rotation;
} else if (rayDirection.dot(forward) < 0.0f) {
// we're walking backwards
rotation = btMatrix3x3(btQuaternion(_currentUp, PI)) * rotation;
}
// scan the top // scan the top
// NOTE: if we scan an extra distance forward we can detect flat surfaces that are too steep to walk on. // NOTE: if we scan an extra distance forward we can detect flat surfaces that are too steep to walk on.
// The approximate extra distance can be derived with trigonometry. // The approximate extra distance can be derived with trigonometry.
// //
// minimumForward = [ (maxStepHeight + radius / sinTheta - radius) * (sinTheta / cosTheta) - radius ] // minimumForward = [ (maxStepHeight + radius / cosTheta - radius) * (cosTheta / sinTheta) - radius ]
// //
// where: theta = max angle between floor normal and vertical // where: theta = max angle between floor normal and vertical
// //
@ -124,11 +142,16 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm:
// //
btScalar cosTheta = _minFloorNormalDotUp; btScalar cosTheta = _minFloorNormalDotUp;
btScalar sinTheta = sqrtf(1.0f - cosTheta * cosTheta); btScalar sinTheta = sqrtf(1.0f - cosTheta * cosTheta);
btScalar forwardSlop = (_maxStepHeight + _radius / sinTheta - _radius) * (sinTheta / cosTheta) - (_radius + stepLength); const btScalar MIN_FORWARD_SLOP = 0.12f; // HACK: not sure why this is necessary to detect steepest walkable slope
bool needToRemoveForwardSlop = true; btScalar forwardSlop = (_maxStepHeight + _radius / cosTheta - _radius) * (cosTheta / sinTheta) - (_radius + stepLength) + MIN_FORWARD_SLOP;
btScalar adjacent = (_radius + stepLength + forwardSlop);
btScalar opposite = (_topPoints[0].dot(_currentUp) + (_radius + _halfHeight)) + _radius / cosTheta - _radius;
btScalar angle = atanf(opposite/adjacent);
if (forwardSlop < 0.0f) { if (forwardSlop < 0.0f) {
// BIG step, no slop necessary
forwardSlop = 0.0f; forwardSlop = 0.0f;
needToRemoveForwardSlop = false;
} }
for (int32_t i = 0; i < _topPoints.size(); ++i) { for (int32_t i = 0; i < _topPoints.size(); ++i) {
@ -151,10 +174,9 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm:
// remove both forwardSlop and backSlop // remove both forwardSlop and backSlop
result.hitFraction = glm::min(1.0f, (closestRayResult.m_closestHitFraction * (backSlop + stepLength + forwardSlop) - backSlop) / stepLength); result.hitFraction = glm::min(1.0f, (closestRayResult.m_closestHitFraction * (backSlop + stepLength + forwardSlop) - backSlop) / stepLength);
} else { } else {
if (needToRemoveForwardSlop && result.hitFraction < 1.0f) { // remove forwardSlop
// remove forwardSlop result.hitFraction = (closestRayResult.m_closestHitFraction * (backSlop + stepLength + forwardSlop)) / (backSlop + stepLength);
result.hitFraction = (closestRayResult.m_closestHitFraction * (backSlop + stepLength + forwardSlop)) / (backSlop + stepLength);
}
// scan the bottom // scan the bottom
for (int32_t i = 0; i < _bottomPoints.size(); ++i) { for (int32_t i = 0; i < _bottomPoints.size(); ++i) {
rayStart = newPosition + rotation * _bottomPoints[i] - backSlop * rayDirection; rayStart = newPosition + rotation * _bottomPoints[i] - backSlop * rayDirection;