void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { btVector3 velocity = _rigidBody->getLinearVelocity() - _parentVelocity; computeNewVelocity(dt, velocity); _rigidBody->setLinearVelocity(velocity + _parentVelocity); // Dynamicaly compute a follow velocity to move this body toward the _followDesiredBodyTransform. // Rather than add this velocity to velocity the RigidBody, we explicitly teleport the RigidBody towards its goal. // This mirrors the computation done in MyAvatar::FollowHelper::postPhysicsUpdate(). const float MINIMUM_TIME_REMAINING = 0.005f; const float MAX_DISPLACEMENT = 0.5f * _radius; _followTimeRemaining -= dt; if (_followTimeRemaining >= MINIMUM_TIME_REMAINING) { btTransform bodyTransform = _rigidBody->getWorldTransform(); btVector3 startPos = bodyTransform.getOrigin(); btVector3 deltaPos = _followDesiredBodyTransform.getOrigin() - startPos; btVector3 vel = deltaPos / _followTimeRemaining; btVector3 linearDisplacement = clampLength(vel * dt, MAX_DISPLACEMENT); // clamp displacement to prevent tunneling. btVector3 endPos = startPos + linearDisplacement; btQuaternion startRot = bodyTransform.getRotation(); glm::vec2 currentFacing = getFacingDir2D(bulletToGLM(startRot)); glm::vec2 currentRight(currentFacing.y, -currentFacing.x); glm::vec2 desiredFacing = getFacingDir2D(bulletToGLM(_followDesiredBodyTransform.getRotation())); float deltaAngle = acosf(glm::clamp(glm::dot(currentFacing, desiredFacing), -1.0f, 1.0f)); float angularSpeed = deltaAngle / _followTimeRemaining; float sign = copysignf(1.0f, glm::dot(desiredFacing, currentRight)); btQuaternion angularDisplacement = btQuaternion(btVector3(0.0f, 1.0f, 0.0f), sign * angularSpeed * dt); btQuaternion endRot = angularDisplacement * startRot; // in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account. btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset); btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset); _followLinearDisplacement = linearDisplacement + swingDisplacement + _followLinearDisplacement; _followAngularDisplacement = angularDisplacement * _followAngularDisplacement; _rigidBody->setWorldTransform(btTransform(endRot, endPos)); } _followTime += dt; }
void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { const btScalar MIN_SPEED = 0.001f; btVector3 actualVelocity = _rigidBody->getLinearVelocity(); if (actualVelocity.length() < MIN_SPEED) { actualVelocity = btVector3(0.0f, 0.0f, 0.0f); } btVector3 desiredVelocity = _walkVelocity; if (desiredVelocity.length() < MIN_SPEED) { desiredVelocity = btVector3(0.0f, 0.0f, 0.0f); } // decompose into horizontal and vertical components. btVector3 actualVertVelocity = actualVelocity.dot(_currentUp) * _currentUp; btVector3 actualHorizVelocity = actualVelocity - actualVertVelocity; btVector3 desiredVertVelocity = desiredVelocity.dot(_currentUp) * _currentUp; btVector3 desiredHorizVelocity = desiredVelocity - desiredVertVelocity; btVector3 finalVelocity; switch (_state) { case State::Ground: case State::Takeoff: { // horizontal ground control const btScalar WALK_ACCELERATION_TIMESCALE = 0.1f; btScalar tau = dt / WALK_ACCELERATION_TIMESCALE; finalVelocity = tau * desiredHorizVelocity + (1.0f - tau) * actualHorizVelocity + actualVertVelocity; } break; case State::InAir: { // horizontal air control const btScalar IN_AIR_ACCELERATION_TIMESCALE = 2.0f; btScalar tau = dt / IN_AIR_ACCELERATION_TIMESCALE; finalVelocity = tau * desiredHorizVelocity + (1.0f - tau) * actualHorizVelocity + actualVertVelocity; } break; case State::Hover: { // vertical and horizontal air control const btScalar FLY_ACCELERATION_TIMESCALE = 0.2f; btScalar tau = dt / FLY_ACCELERATION_TIMESCALE; finalVelocity = tau * desiredVelocity + (1.0f - tau) * actualVelocity; } break; } _rigidBody->setLinearVelocity(finalVelocity); // Dynamicaly compute a follow velocity to move this body toward the _followDesiredBodyTransform. // Rather then add this velocity to velocity the RigidBody, we explicitly teleport the RigidBody towards its goal. // This mirrors the computation done in MyAvatar::FollowHelper::postPhysicsUpdate(). const float MINIMUM_TIME_REMAINING = 0.005f; const float MAX_DISPLACEMENT = 0.5f * _radius; _followTimeRemaining -= dt; if (_followTimeRemaining >= MINIMUM_TIME_REMAINING) { btTransform bodyTransform = _rigidBody->getWorldTransform(); btVector3 startPos = bodyTransform.getOrigin(); btVector3 deltaPos = _followDesiredBodyTransform.getOrigin() - startPos; btVector3 vel = deltaPos / _followTimeRemaining; btVector3 linearDisplacement = clampLength(vel * dt, MAX_DISPLACEMENT); // clamp displacement to prevent tunneling. btVector3 endPos = startPos + linearDisplacement; btQuaternion startRot = bodyTransform.getRotation(); glm::vec2 currentFacing = getFacingDir2D(bulletToGLM(startRot)); glm::vec2 currentRight(currentFacing.y, -currentFacing.x); glm::vec2 desiredFacing = getFacingDir2D(bulletToGLM(_followDesiredBodyTransform.getRotation())); float deltaAngle = acosf(glm::clamp(glm::dot(currentFacing, desiredFacing), -1.0f, 1.0f)); float angularSpeed = deltaAngle / _followTimeRemaining; float sign = copysignf(1.0f, glm::dot(desiredFacing, currentRight)); btQuaternion angularDisplacement = btQuaternion(btVector3(0.0f, 1.0f, 0.0f), sign * angularSpeed * dt); btQuaternion endRot = angularDisplacement * startRot; // in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account. btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset); btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset); _followLinearDisplacement = linearDisplacement + swingDisplacement + _followLinearDisplacement; _followAngularDisplacement = angularDisplacement * _followAngularDisplacement; _rigidBody->setWorldTransform(btTransform(endRot, endPos)); } _followTime += dt; }