btScalar totalWeight = 0.0f; for (size_t i = 0; i < weights.size(); ++i) { totalWeight += weights[i]; } if (totalWeight > 0.0f) { velocity = btVector3(0.0f, 0.0f, 0.0f); for (size_t i = 0; i < velocities.size(); ++i) { velocity += (weights[i] / totalWeight) * velocities[i]; } } if (velocity.length2() < MIN_TARGET_SPEED_SQUARED) { velocity = btVector3(0.0f, 0.0f, 0.0f); } // 'thrust' is applied at the very end velocity += dt * _linearAcceleration; _targetVelocity = velocity;} void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) { btVector3 btVelocity = glmToBullet(velocity); computeNewVelocity(dt, btVelocity); velocity = bulletToGLM(btVelocity);} void CharacterController::preSimulation() { if (_enabled && _dynamicsWorld) { quint64 now = usecTimestampNow(); // slam body to where it is supposed to be _rigidBody->setWorldTransform(_characterBodyTransform); btVector3 velocity = _rigidBody->getLinearVelocity(); _preSimulationVelocity = velocity; // scan for distant floor // rayStart is at center of bottom sphere btVector3 rayStart = _characterBodyTransform.getOrigin() - _halfHeight * _currentUp; // rayEnd is straight down MAX_FALL_HEIGHT btScalar rayLength = _radius + MAX_FALL_HEIGHT; btVector3 rayEnd = rayStart - rayLength * _currentUp; const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; const btScalar MIN_HOVER_HEIGHT = 2.5f; const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; const btScalar MAX_WALKING_SPEED = 2.5f; const quint64 RAY_HIT_START_PERIOD = 500 * MSECS_PER_SECOND; ClosestNotMe rayCallback(_rigidBody); rayCallback.m_closestHitFraction = 1.0f; _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); bool rayHasHit = rayCallback.hasHit(); if (rayHasHit) { _rayHitStartTime = now; _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; } else if ((now - _rayHitStartTime) < RAY_HIT_START_PERIOD) { rayHasHit = true; } else { _floorDistance = FLT_MAX; } // record a time stamp when the jump button was first pressed. if ((_previousFlags & PENDING_FLAG_JUMP) != (_pendingFlags & PENDING_FLAG_JUMP)) { if (_pendingFlags & PENDING_FLAG_JUMP) { _jumpButtonDownStartTime = now; _jumpButtonDownCount++; } } bool jumpButtonHeld = _pendingFlags & PENDING_FLAG_JUMP; btVector3 actualHorizVelocity = velocity - velocity.dot(_currentUp) * _currentUp; bool flyingFast = _state == State::Hover && actualHorizVelocity.length() > (MAX_WALKING_SPEED * 0.75f); switch (_state) { case State::Ground: if (!rayHasHit && !_hasSupport) { SET_STATE(State::Hover, "no ground detected"); } else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) { _takeoffJumpButtonID = _jumpButtonDownCount; _takeoffToInAirStartTime = now; SET_STATE(State::Takeoff, "jump pressed"); } else if (rayHasHit && !_hasSupport && _floorDistance > JUMP_PROXIMITY_THRESHOLD) { SET_STATE(State::InAir, "falling"); } break; case State::Takeoff: if (!rayHasHit && !_hasSupport) { SET_STATE(State::Hover, "no ground"); } else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) { SET_STATE(State::InAir, "takeoff done"); velocity += _jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); } break; case State::InAir: { if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { SET_STATE(State::Ground, "hit ground"); } else { btVector3 desiredVelocity = _targetVelocity; if (desiredVelocity.length2() < MIN_TARGET_SPEED_SQUARED) { desiredVelocity = btVector3(0.0f, 0.0f, 0.0f); } bool vertTargetSpeedIsNonZero = desiredVelocity.dot(_currentUp) > MIN_TARGET_SPEED; if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (_takeoffJumpButtonID != _jumpButtonDownCount)) { SET_STATE(State::Hover, "double jump button"); } else if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) { SET_STATE(State::Hover, "jump button held"); } } break; } case State::Hover: if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) { SET_STATE(State::InAir, "near ground"); } else if (((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport) && !flyingFast) { SET_STATE(State::Ground, "touching ground"); } break; } } _previousFlags = _pendingFlags; _pendingFlags &= ~PENDING_FLAG_JUMP; _followTime = 0.0f; _followLinearDisplacement = btVector3(0, 0, 0); _followAngularDisplacement = btQuaternion::getIdentity();} void CharacterController::postSimulation() { // postSimulation() exists for symmetry and just in case we need to do something here later btVector3 velocity = _rigidBody->getLinearVelocity(); _velocityChange = velocity - _preSimulationVelocity;} bool CharacterController::getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) { if (!_rigidBody) { return false; } const btTransform& worldTrans = _rigidBody->getCenterOfMassTransform(); avatarRigidBodyPosition = bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(); avatarRigidBodyRotation = bulletToGLM(worldTrans.getRotation()); return true;} void CharacterController::setFlyingAllowed(bool value) { if (_flyingAllowed != value) { _flyingAllowed = value; if (!_flyingAllowed && _state == State::Hover) { SET_STATE(State::InAir, "flying not allowed"); } }}Lines 501–659 of 659. Previous lines
btScalar totalWeight = 0.0f; for (size_t i = 0; i < weights.size(); ++i) { totalWeight += weights[i]; } if (totalWeight > 0.0f) { velocity = btVector3(0.0f, 0.0f, 0.0f); for (size_t i = 0; i < velocities.size(); ++i) { velocity += (weights[i] / totalWeight) * velocities[i]; } } if (velocity.length2() < MIN_TARGET_SPEED_SQUARED) { velocity = btVector3(0.0f, 0.0f, 0.0f); } // 'thrust' is applied at the very end velocity += dt * _linearAcceleration; _targetVelocity = velocity; } void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) { btVector3 btVelocity = glmToBullet(velocity); computeNewVelocity(dt, btVelocity); velocity = bulletToGLM(btVelocity); } void CharacterController::preSimulation() { if (_enabled && _dynamicsWorld) { quint64 now = usecTimestampNow(); // slam body to where it is supposed to be _rigidBody->setWorldTransform(_characterBodyTransform); btVector3 velocity = _rigidBody->getLinearVelocity(); _preSimulationVelocity = velocity; // scan for distant floor // rayStart is at center of bottom sphere btVector3 rayStart = _characterBodyTransform.getOrigin() - _halfHeight * _currentUp; // rayEnd is straight down MAX_FALL_HEIGHT btScalar rayLength = _radius + MAX_FALL_HEIGHT; btVector3 rayEnd = rayStart - rayLength * _currentUp; const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; const btScalar MIN_HOVER_HEIGHT = 2.5f; const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; const btScalar MAX_WALKING_SPEED = 2.5f; const quint64 RAY_HIT_START_PERIOD = 500 * MSECS_PER_SECOND; ClosestNotMe rayCallback(_rigidBody); rayCallback.m_closestHitFraction = 1.0f; _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); bool rayHasHit = rayCallback.hasHit(); if (rayHasHit) { _rayHitStartTime = now; _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; } else if ((now - _rayHitStartTime) < RAY_HIT_START_PERIOD) { rayHasHit = true; } else { _floorDistance = FLT_MAX; } // record a time stamp when the jump button was first pressed. if ((_previousFlags & PENDING_FLAG_JUMP) != (_pendingFlags & PENDING_FLAG_JUMP)) { if (_pendingFlags & PENDING_FLAG_JUMP) { _jumpButtonDownStartTime = now; _jumpButtonDownCount++; } } bool jumpButtonHeld = _pendingFlags & PENDING_FLAG_JUMP; btVector3 actualHorizVelocity = velocity - velocity.dot(_currentUp) * _currentUp; bool flyingFast = _state == State::Hover && actualHorizVelocity.length() > (MAX_WALKING_SPEED * 0.75f); switch (_state) { case State::Ground: if (!rayHasHit && !_hasSupport) { SET_STATE(State::Hover, "no ground detected"); } else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) { _takeoffJumpButtonID = _jumpButtonDownCount; _takeoffToInAirStartTime = now; SET_STATE(State::Takeoff, "jump pressed"); } else if (rayHasHit && !_hasSupport && _floorDistance > JUMP_PROXIMITY_THRESHOLD) { SET_STATE(State::InAir, "falling"); } break; case State::Takeoff: if (!rayHasHit && !_hasSupport) { SET_STATE(State::Hover, "no ground"); } else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) { SET_STATE(State::InAir, "takeoff done"); velocity += _jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); } break; case State::InAir: { if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { SET_STATE(State::Ground, "hit ground"); } else { btVector3 desiredVelocity = _targetVelocity; if (desiredVelocity.length2() < MIN_TARGET_SPEED_SQUARED) { desiredVelocity = btVector3(0.0f, 0.0f, 0.0f); } bool vertTargetSpeedIsNonZero = desiredVelocity.dot(_currentUp) > MIN_TARGET_SPEED; if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (_takeoffJumpButtonID != _jumpButtonDownCount)) { SET_STATE(State::Hover, "double jump button"); } else if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) { SET_STATE(State::Hover, "jump button held"); } } break; } case State::Hover: if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) { SET_STATE(State::InAir, "near ground"); } else if (((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport) && !flyingFast) { SET_STATE(State::Ground, "touching ground"); } break; } } _previousFlags = _pendingFlags; _pendingFlags &= ~PENDING_FLAG_JUMP; _followTime = 0.0f; _followLinearDisplacement = btVector3(0, 0, 0); _followAngularDisplacement = btQuaternion::getIdentity(); } void CharacterController::postSimulation() { // postSimulation() exists for symmetry and just in case we need to do something here later btVector3 velocity = _rigidBody->getLinearVelocity(); _velocityChange = velocity - _preSimulationVelocity; } bool CharacterController::getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) { if (!_rigidBody) { return false; } const btTransform& worldTrans = _rigidBody->getCenterOfMassTransform(); avatarRigidBodyPosition = bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(); avatarRigidBodyRotation = bulletToGLM(worldTrans.getRotation()); return true; } void CharacterController::setFlyingAllowed(bool value) { if (_flyingAllowed != value) { _flyingAllowed = value; if (!_flyingAllowed && _state == State::Hover) { SET_STATE(State::InAir, "flying not allowed"); } } }