Proper fix

This commit is contained in:
luiscuenca 2018-07-12 14:00:55 -07:00
parent ced4e1e0e0
commit 1ab6c7a8ac

View file

@ -262,29 +262,54 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar
btVector3 linearDisplacement = clampLength(vel * dt, MAX_DISPLACEMENT); // clamp displacement to prevent tunneling. btVector3 linearDisplacement = clampLength(vel * dt, MAX_DISPLACEMENT); // clamp displacement to prevent tunneling.
btVector3 endPos = startPos + linearDisplacement; btVector3 endPos = startPos + linearDisplacement;
// resolve the simple linearDisplacement
_followLinearDisplacement += linearDisplacement;
// now for the rotational part...
btQuaternion startRot = bodyTransform.getRotation(); btQuaternion startRot = bodyTransform.getRotation();
btQuaternion desiredRot = _followDesiredBodyTransform.getRotation(); btQuaternion desiredRot = _followDesiredBodyTransform.getRotation();
if (desiredRot.dot(startRot) < 0.0f) {
desiredRot = -desiredRot; // startRot as default rotation
} btQuaternion endRot = startRot;
btQuaternion deltaRot = desiredRot * startRot.inverse();
float angularSpeed = deltaRot.getAngle() / _followTimeRemaining; // the dot product between two quaternions is equal to +/- cos(angle/2)
glm::vec3 rotationAxis = glm::normalize(glm::axis(bulletToGLM(deltaRot))); // deltaRot.getAxis() is inaccurate // where 'angle' is that of the rotation between them
btQuaternion angularDisplacement = btQuaternion(glmToBullet(rotationAxis), angularSpeed * dt); float qDot = desiredRot.dot(startRot);
btQuaternion endRot = angularDisplacement * startRot;
// when the abs() value of the dot product is approximately 1.0
// in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account. // then the two rotations are effectively adjacent
btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset); const float MIN_DOT_PRODUCT_OF_ADJACENT_QUATERNIONS = 0.99999f; // corresponds to approx 0.5 degrees
btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset); if (fabsf(qDot) < MIN_DOT_PRODUCT_OF_ADJACENT_QUATERNIONS) {
if (qDot < 0.0f) {
if (!isNaN(bulletToGLM(endPos)) && !isNaN(bulletToGLM(endRot))) { // the quaternions are actually on opposite hyperhemispheres
_followLinearDisplacement = linearDisplacement + swingDisplacement + _followLinearDisplacement; // so we move one to agree with the other and negate qDot
_followAngularDisplacement = angularDisplacement * _followAngularDisplacement; desiredRot = -desiredRot;
qDot = -qDot;
_rigidBody->setWorldTransform(btTransform(endRot, endPos)); }
} else { btQuaternion deltaRot = desiredRot * startRot.inverse();
qCWarning(physics) << "CharacterController::playerStep produced NaN.";
// the axis is the imaginary part, but scaled by sin(angle/2)
btVector3 axis(deltaRot.getX(), deltaRot.getY(), deltaRot.getZ());
axis /= sqrtf(1.0f - qDot*qDot);
// compute the angle we will resolve for this dt, but don't overshoot
float angle = (2.0f * acosf(qDot));
if ( dt < _followTimeRemaining) {
angle *= dt / _followTimeRemaining;
}
// accumulate rotation
deltaRot = btQuaternion(axis, angle);
_followAngularDisplacement = (deltaRot * _followAngularDisplacement).normalize();
// in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account.
btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset);
endRot = deltaRot * startRot;
btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset);
_followLinearDisplacement += swingDisplacement;
} }
_rigidBody->setWorldTransform(btTransform(endRot, endPos));
} }
_followTime += dt; _followTime += dt;