mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 03:50:40 +02:00
VR fixes for: couldn't sit on the floor, wrong walk directions.
- Divided the option "Avatar leaning behavior" into two options that work more usefully: "Allow my avatar to stand" and "Allow my avatar to lean" (PreferencesDialog.cpp). Made the necessary fixes so that the avatar can be set to stand only when the user is standing (more details below). - The logic controlling the direction of MyAvatar's action motor is now centralised in calculateScaledDirection (was previously split between there and updateMotors). calculateScaledDirection now returns a velocity in world space. - CharacterController::FollowHelper now uses separate follow timers for rotation, horizontal and vertical (previously followed all three based on the longest of their follow times). Where appropriate, FollowHelper can now snap immediately to the desired rotation/horizontal/vertical independently (see FOLLOW_TIME_IMMEDIATE_SNAP). - FollowHelper::FollowType has therefore moved to CharacterController::FollowType. - MyAvatar::FollowHelper::postPhysicsUpdate: If MyAvatar is not allowed to stand when the user is sitting, this now avoids recentring the body based on the head height. - Removed Q_PROPERTY(MyAvatar::SitStandModelType, as the sitting/standing/leaning model uses different enums now (see setAllowAvatarStandingPreference, setAllowAvatarLeaningPreference). - Removed Q_PROPERTY(bool isSitStandStateLocked which is no longer used, because we now always track the user's real-world sit/stand state, regardless of what we're doing with it. - MyAvatar::FollowHelper::shouldActivateHorizontal: If MyAvatar is not allowed to lean, this now returns true to recentre the footing if the head is outside the base of support. - MyAvatar::FollowHelper::shouldActivateHorizontalCG: If MyAvatar is not allowed to lean, this now always returns true to recentre the footing. Rearranged to avoid computing values that weren't used depending on the conditions. Resolved some duplicated code. - MyAvatar::setUserRecenterModel previously set HMDLeanRecenterEnabled based on the chosen mode, but it got reset when getting out of a sit. Now HMDLeanRecenterEnabled is only controlled by the scripts. - Added Rig::getUnscaledHipsHeight (like getUnscaledEyeHeight). Refactored a little to avoid duplicated code. Added DEFAULT_AVATAR_HIPS_HEIGHT which is the value that Rig::getUnscaledHipsHeight returns when using the default avatar. - Fix for recentring not behaving as requested by the user after getting up from click-to-sit (always behaving like 'Auto') : MyAvatar::endSit now passes false to centerBody for 'forceFollowYPos'. - Fix for incorrect vertical position of the avatar and viewpoint after changing lean recentre mode while not standing in the real world: MyAvatar::setAllowAvatarStandingPreference now calls centerBody with false for 'forceFollowYPos'. - computeHipsInSensorFrame: The code now matches the comments in that it only skips the dampening of the hips rotation if the centre-of-gravity model is being used.
This commit is contained in:
parent
f1576aba78
commit
2179c153de
12 changed files with 691 additions and 441 deletions
|
@ -96,10 +96,8 @@ const float CENTIMETERS_PER_METER = 100.0f;
|
|||
|
||||
const QString AVATAR_SETTINGS_GROUP_NAME { "Avatar" };
|
||||
|
||||
static const QString USER_RECENTER_MODEL_FORCE_SIT = QStringLiteral("ForceSit");
|
||||
static const QString USER_RECENTER_MODEL_FORCE_STAND = QStringLiteral("ForceStand");
|
||||
static const QString USER_RECENTER_MODEL_AUTO = QStringLiteral("Auto");
|
||||
static const QString USER_RECENTER_MODEL_DISABLE_HMD_LEAN = QStringLiteral("DisableHMDLean");
|
||||
static const QString ALLOW_AVATAR_STANDING_ALWAYS = QStringLiteral("Always");
|
||||
static const QString ALLOW_AVATAR_STANDING_WHEN_USER_IS_STANDING = QStringLiteral("UserStanding");
|
||||
|
||||
const QString HEAD_BLEND_DIRECTIONAL_ALPHA_NAME = "lookAroundAlpha";
|
||||
const QString HEAD_BLEND_LINEAR_ALPHA_NAME = "lookBlendAlpha";
|
||||
|
@ -111,30 +109,38 @@ const QString POINT_BLEND_LINEAR_ALPHA_NAME = "pointBlendAlpha";
|
|||
const QString POINT_REF_JOINT_NAME = "RightShoulder";
|
||||
const float POINT_ALPHA_BLENDING = 1.0f;
|
||||
|
||||
MyAvatar::SitStandModelType stringToUserRecenterModel(const QString& str) {
|
||||
if (str == USER_RECENTER_MODEL_FORCE_SIT) {
|
||||
return MyAvatar::ForceSit;
|
||||
} else if (str == USER_RECENTER_MODEL_FORCE_STAND) {
|
||||
return MyAvatar::ForceStand;
|
||||
} else if (str == USER_RECENTER_MODEL_DISABLE_HMD_LEAN) {
|
||||
return MyAvatar::DisableHMDLean;
|
||||
} else {
|
||||
return MyAvatar::Auto;
|
||||
const std::array<QString, static_cast<uint>(MyAvatar::AllowAvatarStandingPreference::Count)>
|
||||
MyAvatar::allowAvatarStandingPreferenceStrings = {
|
||||
QStringLiteral("WhenUserIsStanding"),
|
||||
QStringLiteral("Always")
|
||||
};
|
||||
|
||||
const std::array<QString, static_cast<uint>(MyAvatar::AllowAvatarLeaningPreference::Count)>
|
||||
MyAvatar::allowAvatarLeaningPreferenceStrings = {
|
||||
QStringLiteral("WhenUserIsStanding"),
|
||||
QStringLiteral("Always"),
|
||||
QStringLiteral("Never"),
|
||||
QStringLiteral("AlwaysNoRecenter")
|
||||
};
|
||||
|
||||
MyAvatar::AllowAvatarStandingPreference stringToAllowAvatarStandingPreference(const QString& str) {
|
||||
for (uint stringIndex = 0; stringIndex < static_cast<uint>(MyAvatar::AllowAvatarStandingPreference::Count); stringIndex++) {
|
||||
if (MyAvatar::allowAvatarStandingPreferenceStrings[stringIndex] == str) {
|
||||
return static_cast<MyAvatar::AllowAvatarStandingPreference>(stringIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return MyAvatar::AllowAvatarStandingPreference::Default;
|
||||
}
|
||||
|
||||
QString userRecenterModelToString(MyAvatar::SitStandModelType model) {
|
||||
switch (model) {
|
||||
case MyAvatar::ForceSit:
|
||||
return USER_RECENTER_MODEL_FORCE_SIT;
|
||||
case MyAvatar::ForceStand:
|
||||
return USER_RECENTER_MODEL_FORCE_STAND;
|
||||
case MyAvatar::DisableHMDLean:
|
||||
return USER_RECENTER_MODEL_DISABLE_HMD_LEAN;
|
||||
case MyAvatar::Auto:
|
||||
default:
|
||||
return USER_RECENTER_MODEL_AUTO;
|
||||
MyAvatar::AllowAvatarLeaningPreference stringToAllowAvatarLeaningPreference(const QString& str) {
|
||||
for (uint stringIndex = 0; stringIndex < static_cast<uint>(MyAvatar::AllowAvatarLeaningPreference::Count); stringIndex++) {
|
||||
if (MyAvatar::allowAvatarLeaningPreferenceStrings[stringIndex] == str) {
|
||||
return static_cast<MyAvatar::AllowAvatarLeaningPreference>(stringIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return MyAvatar::AllowAvatarLeaningPreference::Default;
|
||||
}
|
||||
|
||||
static const QStringList TRIGGER_REACTION_NAMES = {
|
||||
|
@ -166,7 +172,7 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
|
||||
_scriptedMotorMode(SCRIPTED_MOTOR_SIMPLE_MODE),
|
||||
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
|
||||
_characterController(std::shared_ptr<MyAvatar>(this)),
|
||||
_characterController(std::shared_ptr<MyAvatar>(this), _follow._timeRemaining),
|
||||
_eyeContactTarget(LEFT_EYE),
|
||||
_realWorldFieldOfView("realWorldFieldOfView",
|
||||
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
|
||||
|
@ -214,8 +220,12 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_analogWalkSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "analogWalkSpeed", _analogWalkSpeed.get()),
|
||||
_analogPlusWalkSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "analogPlusWalkSpeed", _analogPlusWalkSpeed.get()),
|
||||
_controlSchemeIndexSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "controlSchemeIndex", _controlSchemeIndex),
|
||||
_userRecenterModelSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userRecenterModel", USER_RECENTER_MODEL_AUTO)
|
||||
{
|
||||
_allowAvatarStandingPreferenceSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "allowAvatarStandingPreference",
|
||||
allowAvatarStandingPreferenceStrings[static_cast<uint>(
|
||||
AllowAvatarStandingPreference::Default)]),
|
||||
_allowAvatarLeaningPreferenceSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "allowAvatarLeaningPreference",
|
||||
allowAvatarLeaningPreferenceStrings[static_cast<uint>(
|
||||
AllowAvatarLeaningPreference::Default)]) {
|
||||
_clientTraitsHandler.reset(new ClientTraitsHandler(this));
|
||||
|
||||
// give the pointer to our head to inherited _headData variable from AvatarData
|
||||
|
@ -487,14 +497,17 @@ void MyAvatar::resetSensorsAndBody() {
|
|||
reset(true, false, true);
|
||||
}
|
||||
|
||||
void MyAvatar::centerBody() {
|
||||
// forceFollowYPos: true to force the body matrix to be affected by the HMD's
|
||||
// vertical position, even if crouch recentering is disabled.
|
||||
void MyAvatar::centerBody(const bool forceFollowYPos) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "centerBody");
|
||||
return;
|
||||
}
|
||||
|
||||
// derive the desired body orientation from the current hmd orientation, before the sensor reset.
|
||||
auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
|
||||
auto newBodySensorMatrix =
|
||||
deriveBodyFromHMDSensor(forceFollowYPos); // Based on current cached HMD position/rotation..
|
||||
|
||||
// transform this body into world space
|
||||
auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix;
|
||||
|
@ -571,13 +584,13 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) {
|
|||
}
|
||||
}
|
||||
|
||||
// Determine if the user is sitting or standing in the real world.
|
||||
void MyAvatar::updateSitStandState(float newHeightReading, float dt) {
|
||||
const float STANDING_HEIGHT_MULTIPLE = 1.2f;
|
||||
const float SITTING_HEIGHT_MULTIPLE = 0.833f;
|
||||
const float SITTING_TIMEOUT = 4.0f; // 4 seconds
|
||||
const float STANDING_TIMEOUT = 0.3333f; // 1/3 second
|
||||
const float SITTING_UPPER_BOUND = 1.52f;
|
||||
if (!getIsSitStandStateLocked()) {
|
||||
if (!getIsAway() && getControllerPoseInAvatarFrame(controller::Action::HEAD).isValid()) {
|
||||
if (getIsInSittingState()) {
|
||||
if (newHeightReading > (STANDING_HEIGHT_MULTIPLE * _tippingPoint)) {
|
||||
|
@ -629,7 +642,6 @@ void MyAvatar::updateSitStandState(float newHeightReading, float dt) {
|
|||
_tippingPoint = _userHeight.get();
|
||||
setIsInSittingState(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::update(float deltaTime) {
|
||||
|
@ -692,9 +704,9 @@ void MyAvatar::update(float deltaTime) {
|
|||
}
|
||||
float angleSpine2 = glm::dot(upSpine2, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
if (getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y < (headDefaultPositionAvatarSpace.y - SQUAT_THRESHOLD) &&
|
||||
(angleSpine2 > COSINE_THIRTY_DEGREES) &&
|
||||
(getUserRecenterModel() != MyAvatar::SitStandModelType::ForceStand)) {
|
||||
if (getHMDCrouchRecenterEnabled() &&
|
||||
getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y < (headDefaultPositionAvatarSpace.y - SQUAT_THRESHOLD) &&
|
||||
(angleSpine2 > COSINE_THIRTY_DEGREES)) {
|
||||
|
||||
_squatTimer += deltaTime;
|
||||
if (_squatTimer > SQUATTY_TIMEOUT) {
|
||||
|
@ -832,7 +844,7 @@ void MyAvatar::recalculateChildCauterization() const {
|
|||
_cauterizationNeedsUpdate = true;
|
||||
}
|
||||
|
||||
bool MyAvatar::isFollowActive(FollowHelper::FollowType followType) const {
|
||||
bool MyAvatar::isFollowActive(CharacterController::FollowType followType) const {
|
||||
return _follow.isActive(followType);
|
||||
}
|
||||
|
||||
|
@ -1277,6 +1289,10 @@ void MyAvatar::resizeAvatarEntitySettingHandles(uint32_t maxIndex) {
|
|||
|
||||
void MyAvatar::saveData() {
|
||||
_dominantHandSetting.set(getDominantHand());
|
||||
_allowAvatarStandingPreferenceSetting.set(
|
||||
allowAvatarStandingPreferenceStrings[static_cast<uint>(getAllowAvatarStandingPreference())]);
|
||||
_allowAvatarLeaningPreferenceSetting.set(
|
||||
allowAvatarLeaningPreferenceStrings[static_cast<uint>(getAllowAvatarLeaningPreference())]);
|
||||
_strafeEnabledSetting.set(getStrafeEnabled());
|
||||
_hmdAvatarAlignmentTypeSetting.set(getHmdAvatarAlignmentType());
|
||||
_headPitchSetting.set(getHead()->getBasePitch());
|
||||
|
@ -1311,7 +1327,10 @@ void MyAvatar::saveData() {
|
|||
_analogWalkSpeedSetting.set(getAnalogWalkSpeed());
|
||||
_analogPlusWalkSpeedSetting.set(getAnalogPlusWalkSpeed());
|
||||
_controlSchemeIndexSetting.set(getControlSchemeIndex());
|
||||
_userRecenterModelSetting.set(userRecenterModelToString(getUserRecenterModel()));
|
||||
_allowAvatarStandingPreferenceSetting.set(
|
||||
allowAvatarStandingPreferenceStrings[static_cast<uint>(getAllowAvatarStandingPreference())]);
|
||||
_allowAvatarLeaningPreferenceSetting.set(
|
||||
allowAvatarLeaningPreferenceStrings[static_cast<uint>(getAllowAvatarLeaningPreference())]);
|
||||
|
||||
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
|
||||
saveAvatarEntityDataToSettings();
|
||||
|
@ -2004,7 +2023,10 @@ void MyAvatar::loadData() {
|
|||
setUserHeight(_userHeightSetting.get(DEFAULT_AVATAR_HEIGHT));
|
||||
setTargetScale(_scaleSetting.get());
|
||||
|
||||
setUserRecenterModel(stringToUserRecenterModel(_userRecenterModelSetting.get(USER_RECENTER_MODEL_AUTO)));
|
||||
setAllowAvatarStandingPreference(stringToAllowAvatarStandingPreference(_allowAvatarStandingPreferenceSetting.get(
|
||||
allowAvatarStandingPreferenceStrings[static_cast<uint>(AllowAvatarStandingPreference::Default)])));
|
||||
setAllowAvatarLeaningPreference(stringToAllowAvatarLeaningPreference(_allowAvatarLeaningPreferenceSetting.get(
|
||||
allowAvatarLeaningPreferenceStrings[static_cast<uint>(AllowAvatarLeaningPreference::Default)])));
|
||||
|
||||
setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible));
|
||||
_follow.setToggleHipsFollowing (Menu::getInstance()->isOptionChecked(MenuOption::ToggleHipsFollowing));
|
||||
|
@ -2666,15 +2688,8 @@ controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action act
|
|||
}
|
||||
}
|
||||
|
||||
glm::quat MyAvatar::getOffHandRotation() const {
|
||||
auto hand = (getDominantHand() == DOMINANT_RIGHT_HAND) ? controller::Action::LEFT_HAND : controller::Action::RIGHT_HAND;
|
||||
auto pose = getControllerPoseInAvatarFrame(hand);
|
||||
return pose.rotation;
|
||||
}
|
||||
|
||||
void MyAvatar::updateMotors() {
|
||||
_characterController.clearMotors();
|
||||
glm::quat motorRotation;
|
||||
|
||||
const float FLYING_MOTOR_TIMESCALE = 0.05f;
|
||||
const float WALKING_MOTOR_TIMESCALE = 0.2f;
|
||||
|
@ -2693,35 +2708,17 @@ void MyAvatar::updateMotors() {
|
|||
}
|
||||
|
||||
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
|
||||
if (_characterController.getState() == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
if (!qApp->isHMDMode() && (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE)) {
|
||||
motorRotation = getLookAtRotation();
|
||||
} else {
|
||||
motorRotation = getMyHead()->getHeadOrientation();
|
||||
}
|
||||
} else {
|
||||
// non-hovering = walking: follow camera twist about vertical but not lift
|
||||
// we decompose camera's rotation and store the twist part in motorRotation
|
||||
// however, we need to perform the decomposition in the avatar-frame
|
||||
// using the local UP axis and then transform back into world-frame
|
||||
glm::quat orientation = getWorldOrientation();
|
||||
glm::quat headOrientation = glm::inverse(orientation) * getMyHead()->getHeadOrientation(); // avatar-frame
|
||||
glm::quat liftRotation;
|
||||
swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation);
|
||||
motorRotation = orientation * motorRotation;
|
||||
}
|
||||
|
||||
if (_isPushing || _isBraking || !_isBeingPushed) {
|
||||
_characterController.addMotor(_actionMotorVelocity, motorRotation, horizontalMotorTimescale, verticalMotorTimescale);
|
||||
_characterController.addMotor(_actionMotorVelocity, Quaternions::IDENTITY, horizontalMotorTimescale,
|
||||
verticalMotorTimescale);
|
||||
} else {
|
||||
// _isBeingPushed must be true --> disable action motor by giving it a long timescale,
|
||||
// otherwise it's attempt to "stand in in place" could defeat scripted motor/thrusts
|
||||
_characterController.addMotor(_actionMotorVelocity, motorRotation, INVALID_MOTOR_TIMESCALE);
|
||||
_characterController.addMotor(_actionMotorVelocity, Quaternions::IDENTITY, INVALID_MOTOR_TIMESCALE);
|
||||
}
|
||||
}
|
||||
if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) {
|
||||
glm::quat motorRotation;
|
||||
if (_scriptedMotorFrame == SCRIPTED_MOTOR_CAMERA_FRAME) {
|
||||
motorRotation = getMyHead()->getHeadOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y);
|
||||
} else if (_scriptedMotorFrame == SCRIPTED_MOTOR_AVATAR_FRAME) {
|
||||
|
@ -3793,54 +3790,115 @@ glm::vec3 MyAvatar::scaleMotorSpeed(const glm::vec3 forward, const glm::vec3 rig
|
|||
}
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::calculateScaledDirection(){
|
||||
// Calculate the world-space motor velocity for the avatar.
|
||||
glm::vec3 MyAvatar::calculateScaledDirection() {
|
||||
CharacterController::State state = _characterController.getState();
|
||||
|
||||
// compute action input
|
||||
// Determine if we're head or controller relative...
|
||||
glm::vec3 forward, right;
|
||||
glm::vec3 forward;
|
||||
glm::vec3 right;
|
||||
|
||||
const int movementReference = getMovementReference();
|
||||
CameraMode cameraMode = qApp->getCamera().getMode();
|
||||
|
||||
bool vectorsAreInAvatarFrame = true;
|
||||
bool removeLocalYComponent = false;
|
||||
|
||||
const bool HMDHandRelativeMovement =
|
||||
qApp->isHMDMode() && (movementReference == LocomotionRelativeMovementMode::MOVEMENT_HAND_RELATIVE ||
|
||||
movementReference == LocomotionRelativeMovementMode::MOVEMENT_HAND_RELATIVE_LEVELED);
|
||||
|
||||
const bool desktopLookatOrSelfieMode =
|
||||
!qApp->isHMDMode() && (cameraMode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || cameraMode == CAMERA_MODE_LOOK_AT ||
|
||||
cameraMode == CAMERA_MODE_SELFIE);
|
||||
|
||||
const bool hoveringOrCollisionless = _characterController.getState() == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS;
|
||||
|
||||
if (HMDHandRelativeMovement) {
|
||||
const controller::Action directionHand =
|
||||
(getDominantHand() == DOMINANT_RIGHT_HAND) ? controller::Action::LEFT_HAND : controller::Action::RIGHT_HAND;
|
||||
const controller::Pose handPoseInAvatarFrame = getControllerPoseInAvatarFrame(directionHand);
|
||||
|
||||
if (handPoseInAvatarFrame.isValid()) {
|
||||
const glm::vec3 controllerForward(0.0f, 1.0f, 0.0f);
|
||||
const glm::vec3 controllerRight(0.0f, 0.0f, (directionHand == controller::Action::LEFT_HAND) ? 1.0f : -1.0f);
|
||||
|
||||
forward = (handPoseInAvatarFrame.rotation * controllerForward);
|
||||
right = (handPoseInAvatarFrame.rotation * controllerRight);
|
||||
|
||||
removeLocalYComponent = (movementReference == LocomotionRelativeMovementMode::MOVEMENT_HAND_RELATIVE_LEVELED);
|
||||
}
|
||||
} else { // MOVEMENT_HMD_RELATIVE or desktop mode
|
||||
if (qApp->isHMDMode()) {
|
||||
auto handRotation = getOffHandRotation();
|
||||
glm::vec3 controllerForward(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 controllerRight(0.0f, 0.0f, (getDominantHand() == DOMINANT_RIGHT_HAND ? 1.0f : -1.0f));
|
||||
glm::vec3 transform;
|
||||
switch (getMovementReference()) {
|
||||
case LocomotionRelativeMovementMode::MOVEMENT_HAND_RELATIVE:
|
||||
forward = (handRotation * controllerForward);
|
||||
right = (handRotation * controllerRight);
|
||||
break;
|
||||
case LocomotionRelativeMovementMode::MOVEMENT_HAND_RELATIVE_LEVELED:
|
||||
forward = (handRotation * controllerForward);
|
||||
transform = forward - (glm::dot(forward, Vectors::UNIT_Y) * Vectors::UNIT_Y);
|
||||
if (glm::length(transform) > EPSILON) {
|
||||
forward = glm::normalize(transform);
|
||||
} else {
|
||||
forward = Vectors::ZERO;
|
||||
}
|
||||
right = (handRotation * controllerRight);
|
||||
transform = right - (glm::dot(right, Vectors::UNIT_Y) * Vectors::UNIT_Y);
|
||||
if (glm::length(transform) > EPSILON) {
|
||||
right = glm::normalize(transform);
|
||||
} else {
|
||||
right = Vectors::ZERO;
|
||||
}
|
||||
break;
|
||||
case LocomotionRelativeMovementMode::MOVEMENT_HMD_RELATIVE:
|
||||
default:
|
||||
forward = IDENTITY_FORWARD;
|
||||
right = IDENTITY_RIGHT;
|
||||
}
|
||||
forward = -IDENTITY_FORWARD;
|
||||
right = -IDENTITY_RIGHT;
|
||||
} else {
|
||||
forward = IDENTITY_FORWARD;
|
||||
right = IDENTITY_RIGHT;
|
||||
}
|
||||
|
||||
glm::quat rotation = Quaternions::IDENTITY;
|
||||
|
||||
if (hoveringOrCollisionless && desktopLookatOrSelfieMode) {
|
||||
rotation = getLookAtRotation();
|
||||
removeLocalYComponent = false;
|
||||
vectorsAreInAvatarFrame = false;
|
||||
} else {
|
||||
controller::Pose headPoseLocal = getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
if (headPoseLocal.isValid()) {
|
||||
rotation = headPoseLocal.rotation;
|
||||
}
|
||||
removeLocalYComponent = !hoveringOrCollisionless;
|
||||
}
|
||||
|
||||
forward = rotation * forward;
|
||||
right = rotation * right;
|
||||
}
|
||||
|
||||
if (removeLocalYComponent) {
|
||||
assert(vectorsAreInAvatarFrame);
|
||||
|
||||
auto removeYAndNormalize = [](glm::vec3& vector) {
|
||||
vector.y = 0.f;
|
||||
// Normalize if the remaining components are large enough to get a reliable direction.
|
||||
const float length = glm::length(vector);
|
||||
const float MIN_LENGTH_FOR_NORMALIZE = 0.061f; // sin(3.5 degrees)
|
||||
if (length > MIN_LENGTH_FOR_NORMALIZE) {
|
||||
vector /= length;
|
||||
} else {
|
||||
vector = Vectors::ZERO;
|
||||
}
|
||||
};
|
||||
|
||||
removeYAndNormalize(forward);
|
||||
removeYAndNormalize(right);
|
||||
}
|
||||
|
||||
// In HMD, we combine the head pitch into the flying direction even when using hand-relative movement.
|
||||
// Todo: Option to ignore head pitch in hand-relative flying (MOVEMENT_HAND_RELATIVE_LEVELED would then act like MOVEMENT_HAND_RELATIVE when flying).
|
||||
if (HMDHandRelativeMovement && hoveringOrCollisionless) {
|
||||
controller::Pose headPoseLocal = getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
|
||||
if (headPoseLocal.isValid()) {
|
||||
glm::quat headLocalPitchRotation;
|
||||
glm::quat headLocalYawRotation_unused;
|
||||
swingTwistDecomposition(headPoseLocal.rotation, Vectors::UP, headLocalPitchRotation, headLocalYawRotation_unused);
|
||||
|
||||
forward = headLocalPitchRotation * forward;
|
||||
right = headLocalPitchRotation * right;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 direction = scaleMotorSpeed(forward, right);
|
||||
|
||||
if (state == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
|
||||
if (vectorsAreInAvatarFrame) {
|
||||
direction = getWorldOrientation() * direction;
|
||||
}
|
||||
|
||||
if (hoveringOrCollisionless) {
|
||||
glm::vec3 up = getDriveKey(TRANSLATE_Y) * IDENTITY_UP;
|
||||
direction += up;
|
||||
}
|
||||
|
||||
|
@ -4724,7 +4782,9 @@ void MyAvatar::triggerRotationRecenter() {
|
|||
}
|
||||
|
||||
// old school meat hook style
|
||||
glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
||||
// forceFollowYPos (default false): true to force the body matrix to be affected by the HMD's
|
||||
// vertical position, even if crouch recentering is disabled.
|
||||
glm::mat4 MyAvatar::deriveBodyFromHMDSensor(const bool forceFollowYPos) const {
|
||||
glm::vec3 headPosition(0.0f, _userHeight.get(), 0.0f);
|
||||
glm::quat headOrientation;
|
||||
auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
|
||||
|
@ -4760,8 +4820,30 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
|||
|
||||
float invSensorToWorldScale = getUserEyeHeight() / getEyeHeight();
|
||||
glm::vec3 bodyPos = headPosition + invSensorToWorldScale * (headToNeck + neckToRoot);
|
||||
glm::quat bodyQuat;
|
||||
|
||||
return createMatFromQuatAndPos(headOrientationYawOnly, bodyPos);
|
||||
const controller::Pose hipsControllerPose = getControllerPoseInSensorFrame(controller::Action::HIPS);
|
||||
if (hipsControllerPose.isValid()) {
|
||||
const glm::quat hipsOrientation = hipsControllerPose.rotation * Quaternions::Y_180;
|
||||
const glm::quat hipsOrientationYawOnly = cancelOutRollAndPitch(hipsOrientation);
|
||||
|
||||
const glm::vec3 hipsPos = hipsControllerPose.getTranslation();
|
||||
bodyPos.x = hipsPos.x;
|
||||
bodyPos.z = hipsPos.z;
|
||||
|
||||
bodyQuat = hipsOrientationYawOnly;
|
||||
} else {
|
||||
bodyQuat = headOrientationYawOnly;
|
||||
}
|
||||
|
||||
if (!forceFollowYPos && !getHMDCrouchRecenterEnabled()) {
|
||||
// Set the body's vertical position as if it were standing in its T-pose.
|
||||
bodyPos.y = rig.getUnscaledHipsHeight();
|
||||
}
|
||||
|
||||
glm::mat4 bodyMat = createMatFromQuatAndPos(bodyQuat, bodyPos);
|
||||
|
||||
return bodyMat;
|
||||
}
|
||||
|
||||
glm::mat4 MyAvatar::getSpine2RotationRigSpace() const {
|
||||
|
@ -5159,16 +5241,19 @@ bool MyAvatar::getIsInWalkingState() const {
|
|||
return _isInWalkingState;
|
||||
}
|
||||
|
||||
// Determine if the user is sitting in the real world.
|
||||
bool MyAvatar::getIsInSittingState() const {
|
||||
return _isInSittingState.get();
|
||||
}
|
||||
|
||||
MyAvatar::SitStandModelType MyAvatar::getUserRecenterModel() const {
|
||||
return _userRecenterModel.get();
|
||||
// Get the user preference of when MyAvatar may stand.
|
||||
MyAvatar::AllowAvatarStandingPreference MyAvatar::getAllowAvatarStandingPreference() const {
|
||||
return _allowAvatarStandingPreference.get();
|
||||
}
|
||||
|
||||
bool MyAvatar::getIsSitStandStateLocked() const {
|
||||
return _lockSitStandState.get();
|
||||
// Get the user preference of when MyAvatar may lean.
|
||||
MyAvatar::AllowAvatarLeaningPreference MyAvatar::getAllowAvatarLeaningPreference() const {
|
||||
return _allowAvatarLeaningPreference.get();
|
||||
}
|
||||
|
||||
float MyAvatar::getWalkSpeed() const {
|
||||
|
@ -5221,59 +5306,29 @@ void MyAvatar::setIsInWalkingState(bool isWalking) {
|
|||
_isInWalkingState = isWalking;
|
||||
}
|
||||
|
||||
// Specify whether the user is sitting or standing in the real world.
|
||||
void MyAvatar::setIsInSittingState(bool isSitting) {
|
||||
_sitStandStateTimer = 0.0f;
|
||||
_squatTimer = 0.0f;
|
||||
// on reset height we need the count to be more than one in case the user sits and stands up quickly.
|
||||
_isInSittingState.set(isSitting);
|
||||
setResetMode(true);
|
||||
if (isSitting) {
|
||||
setCenterOfGravityModelEnabled(false);
|
||||
} else {
|
||||
setCenterOfGravityModelEnabled(true);
|
||||
}
|
||||
setCenterOfGravityModelEnabled(!isSitting);
|
||||
setSitStandStateChange(true);
|
||||
}
|
||||
|
||||
void MyAvatar::setUserRecenterModel(MyAvatar::SitStandModelType modelName) {
|
||||
// Set the user preference of when the avatar may stand.
|
||||
void MyAvatar::setAllowAvatarStandingPreference(const MyAvatar::AllowAvatarStandingPreference preference) {
|
||||
_allowAvatarStandingPreference.set(preference);
|
||||
|
||||
_userRecenterModel.set(modelName);
|
||||
|
||||
switch (modelName) {
|
||||
case MyAvatar::SitStandModelType::ForceSit:
|
||||
setHMDLeanRecenterEnabled(true);
|
||||
setIsInSittingState(true);
|
||||
setIsSitStandStateLocked(true);
|
||||
break;
|
||||
case MyAvatar::SitStandModelType::ForceStand:
|
||||
setHMDLeanRecenterEnabled(true);
|
||||
setIsInSittingState(false);
|
||||
setIsSitStandStateLocked(true);
|
||||
break;
|
||||
case MyAvatar::SitStandModelType::Auto:
|
||||
default:
|
||||
setHMDLeanRecenterEnabled(true);
|
||||
setIsInSittingState(false);
|
||||
setIsSitStandStateLocked(false);
|
||||
break;
|
||||
case MyAvatar::SitStandModelType::DisableHMDLean:
|
||||
setHMDLeanRecenterEnabled(false);
|
||||
setIsInSittingState(false);
|
||||
setIsSitStandStateLocked(false);
|
||||
break;
|
||||
}
|
||||
// Set the correct vertical position for the avatar body relative to the HMD,
|
||||
// according to the newly-selected avatar standing preference.
|
||||
centerBody(false);
|
||||
}
|
||||
|
||||
void MyAvatar::setIsSitStandStateLocked(bool isLocked) {
|
||||
_lockSitStandState.set(isLocked);
|
||||
_sitStandStateTimer = 0.0f;
|
||||
_squatTimer = 0.0f;
|
||||
_averageUserHeightSensorSpace = _userHeight.get();
|
||||
_tippingPoint = _userHeight.get();
|
||||
if (!isLocked) {
|
||||
// always start the auto transition mode in standing state.
|
||||
setIsInSittingState(false);
|
||||
}
|
||||
// Set the user preference of when the avatar may lean.
|
||||
void MyAvatar::setAllowAvatarLeaningPreference(const MyAvatar::AllowAvatarLeaningPreference preference) {
|
||||
_allowAvatarLeaningPreference.set(preference);
|
||||
}
|
||||
|
||||
void MyAvatar::setWalkSpeed(float value) {
|
||||
|
@ -5402,10 +5457,12 @@ float MyAvatar::getAnalogPlusSprintSpeed() const {
|
|||
return _analogPlusSprintSpeed.get();
|
||||
}
|
||||
|
||||
// Indicate whether the user's real-world sit/stand state has changed or not.
|
||||
void MyAvatar::setSitStandStateChange(bool stateChanged) {
|
||||
_sitStandStateChange = stateChanged;
|
||||
}
|
||||
|
||||
// Determine if the user's real-world sit/stand state has changed.
|
||||
float MyAvatar::getSitStandStateChange() const {
|
||||
return _sitStandStateChange;
|
||||
}
|
||||
|
@ -5499,65 +5556,82 @@ MyAvatar::FollowHelper::FollowHelper() {
|
|||
}
|
||||
|
||||
void MyAvatar::FollowHelper::deactivate() {
|
||||
for (int i = 0; i < NumFollowTypes; i++) {
|
||||
deactivate((FollowType)i);
|
||||
for (uint i = 0, e = static_cast<uint>(CharacterController::FollowType::Count); i < e; ++i) {
|
||||
deactivate((CharacterController::FollowType)i);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::FollowHelper::deactivate(FollowType type) {
|
||||
assert(type >= 0 && type < NumFollowTypes);
|
||||
void MyAvatar::FollowHelper::deactivate(CharacterController::FollowType type) {
|
||||
assert(static_cast<int>(type) >= 0 && type < CharacterController::FollowType::Count);
|
||||
_timeRemaining[(int)type] = 0.0f;
|
||||
}
|
||||
|
||||
void MyAvatar::FollowHelper::activate(FollowType type) {
|
||||
assert(type >= 0 && type < NumFollowTypes);
|
||||
// snapFollow: true to snap immediately to the desired transform with regard to 'type',
|
||||
// eg. activate(FollowType::Rotation, true) snaps the FollowHelper's rotation immediately
|
||||
// to the rotation of its _followDesiredBodyTransform.
|
||||
void MyAvatar::FollowHelper::activate(CharacterController::FollowType type, const bool snapFollow) {
|
||||
assert(static_cast<int>(type) >= 0 && type < CharacterController::FollowType::Count);
|
||||
|
||||
// TODO: Perhaps, the follow time should be proportional to the displacement.
|
||||
_timeRemaining[(int)type] = FOLLOW_TIME;
|
||||
_timeRemaining[(int)type] = snapFollow ? CharacterController::FOLLOW_TIME_IMMEDIATE_SNAP : FOLLOW_TIME;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::isActive(FollowType type) const {
|
||||
assert(type >= 0 && type < NumFollowTypes);
|
||||
bool MyAvatar::FollowHelper::isActive(CharacterController::FollowType type) const {
|
||||
assert(static_cast<int>(type) >= 0 && type < CharacterController::FollowType::Count);
|
||||
return _timeRemaining[(int)type] > 0.0f;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::isActive() const {
|
||||
for (int i = 0; i < NumFollowTypes; i++) {
|
||||
if (isActive((FollowType)i)) {
|
||||
for (uint i = 0, e = static_cast<uint>(CharacterController::FollowType::Count); i < e; ++i) {
|
||||
if (isActive((CharacterController::FollowType)i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float MyAvatar::FollowHelper::getMaxTimeRemaining() const {
|
||||
float max = 0.0f;
|
||||
for (int i = 0; i < NumFollowTypes; i++) {
|
||||
if (_timeRemaining[i] > max) {
|
||||
max = _timeRemaining[i];
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
|
||||
for (int i = 0; i < NumFollowTypes; i++) {
|
||||
for (uint i = 0, e = static_cast<uint>(CharacterController::FollowType::Count); i < e; ++i) {
|
||||
if (_timeRemaining[i] == CharacterController::FOLLOW_TIME_IMMEDIATE_SNAP) {
|
||||
_timeRemaining[i] = 0.f;
|
||||
}
|
||||
else {
|
||||
_timeRemaining[i] -= dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
// shouldSnapOut: (out) true if the FollowHelper should snap immediately to its desired rotation.
|
||||
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar,
|
||||
const glm::mat4& desiredBodyMatrix,
|
||||
const glm::mat4& currentBodyMatrix,
|
||||
bool& shouldSnapOut) const {
|
||||
shouldSnapOut = false;
|
||||
|
||||
// If hips are under direct control (tracked), they give our desired body rotation and we snap to it every frame.
|
||||
if (myAvatar.areHipsTracked()) {
|
||||
shouldSnapOut = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
const float FOLLOW_ROTATION_THRESHOLD = cosf(myAvatar.getRotationThreshold());
|
||||
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
|
||||
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
if (!myAvatar.isAllowedToLean()) {
|
||||
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
if (!withinBaseOfSupport(currentHeadPose)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// -z axis of currentBodyMatrix in world space.
|
||||
glm::vec3 forward = glm::normalize(glm::vec3(-currentBodyMatrix[0][2], -currentBodyMatrix[1][2], -currentBodyMatrix[2][2]));
|
||||
// x axis of currentBodyMatrix in world space.
|
||||
glm::vec3 right = glm::normalize(glm::vec3(currentBodyMatrix[0][0], currentBodyMatrix[1][0], currentBodyMatrix[2][0]));
|
||||
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
|
||||
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
|
||||
float forwardLeanAmount = glm::dot(forward, offset);
|
||||
float lateralLeanAmount = glm::dot(right, offset);
|
||||
|
@ -5567,11 +5641,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
|
|||
const float MAX_BACKWARD_LEAN = 0.1f;
|
||||
|
||||
bool stepDetected = false;
|
||||
if (myAvatar.getIsInSittingState()) {
|
||||
if (!withinBaseOfSupport(currentHeadPose)) {
|
||||
stepDetected = true;
|
||||
}
|
||||
} else if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
|
||||
if (forwardLeanAmount > MAX_FORWARD_LEAN) {
|
||||
stepDetected = true;
|
||||
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
|
||||
stepDetected = true;
|
||||
|
@ -5583,20 +5653,25 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
|
|||
|
||||
bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) const {
|
||||
|
||||
// get the current readings
|
||||
if (myAvatar.getIsInWalkingState()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
bool stepDetected = false;
|
||||
|
||||
if (!withinBaseOfSupport(currentHeadPose)) {
|
||||
if (!myAvatar.isAllowedToLean()) {
|
||||
stepDetected = true;
|
||||
} else {
|
||||
float myScale = myAvatar.getAvatarScale();
|
||||
|
||||
// get the current readings
|
||||
controller::Pose currentLeftHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
|
||||
controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
|
||||
controller::Pose currentHeadSensorPose = myAvatar.getControllerPoseInSensorFrame(controller::Action::HEAD);
|
||||
|
||||
bool stepDetected = false;
|
||||
float myScale = myAvatar.getAvatarScale();
|
||||
|
||||
if (myAvatar.getIsInWalkingState()) {
|
||||
stepDetected = true;
|
||||
} else {
|
||||
if (!withinBaseOfSupport(currentHeadPose) &&
|
||||
headAngularVelocityBelowThreshold(currentHeadPose) &&
|
||||
if (headAngularVelocityBelowThreshold(currentHeadPose) &&
|
||||
isWithinThresholdHeightMode(currentHeadSensorPose, myAvatar.getCurrentStandingHeight(), myScale) &&
|
||||
handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) &&
|
||||
handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) &&
|
||||
|
@ -5604,29 +5679,34 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
|
|||
isHeadLevel(currentHeadPose, myAvatar.getAverageHeadRotation())) {
|
||||
// a step is detected
|
||||
stepDetected = true;
|
||||
if (glm::length(currentHeadPose.velocity) > DEFAULT_AVATAR_WALK_SPEED_THRESHOLD) {
|
||||
myAvatar.setIsInWalkingState(true);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
if (!stepDetected) {
|
||||
glm::vec3 defaultHipsPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Hips"));
|
||||
glm::vec3 defaultHeadPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Head"));
|
||||
glm::vec3 currentHeadPosition = currentHeadPose.getTranslation();
|
||||
float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition);
|
||||
if (!isActive(Horizontal) &&
|
||||
(!isActive(Vertical)) &&
|
||||
if (!isActive(CharacterController::FollowType::Horizontal) && (!isActive(CharacterController::FollowType::Vertical)) &&
|
||||
(glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) {
|
||||
myAvatar.setResetMode(true);
|
||||
stepDetected = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (stepDetected) {
|
||||
if (glm::length(currentHeadPose.velocity) > DEFAULT_AVATAR_WALK_SPEED_THRESHOLD) {
|
||||
myAvatar.setIsInWalkingState(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stepDetected;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar,
|
||||
const glm::mat4& desiredBodyMatrix,
|
||||
const glm::mat4& currentBodyMatrix) const {
|
||||
const float CYLINDER_TOP = 2.0f;
|
||||
const float CYLINDER_BOTTOM = -1.5f;
|
||||
const float SITTING_BOTTOM = -0.02f;
|
||||
|
@ -5638,9 +5718,6 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co
|
|||
returnValue = true;
|
||||
} else {
|
||||
if (myAvatar.getIsInSittingState()) {
|
||||
if (myAvatar.getIsSitStandStateLocked()) {
|
||||
returnValue = (offset.y > CYLINDER_TOP);
|
||||
}
|
||||
if (offset.y < SITTING_BOTTOM) {
|
||||
// we recenter more easily when in sitting state.
|
||||
returnValue = true;
|
||||
|
@ -5657,51 +5734,80 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co
|
|||
return returnValue;
|
||||
}
|
||||
|
||||
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix,
|
||||
const glm::mat4& currentBodyMatrix, bool hasDriveInput) {
|
||||
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar,
|
||||
const glm::mat4& desiredBodyMatrix,
|
||||
const glm::mat4& currentBodyMatrix,
|
||||
bool hasDriveInput) {
|
||||
const bool feetAreTracked = myAvatar.areFeetTracked();
|
||||
|
||||
if (myAvatar.getHMDLeanRecenterEnabled() &&
|
||||
qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
|
||||
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Rotation);
|
||||
if (myAvatar.getHMDLeanRecenterEnabled()) {
|
||||
|
||||
// Rotation recenter
|
||||
|
||||
{
|
||||
bool snapFollow = false;
|
||||
if (!isActive(CharacterController::FollowType::Rotation) &&
|
||||
(shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix, snapFollow) || hasDriveInput)) {
|
||||
activate(CharacterController::FollowType::Rotation, snapFollow);
|
||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontal and rotation recenter
|
||||
|
||||
if ((feetAreTracked || getForceActivateHorizontal()) && !isActive(CharacterController::FollowType::Horizontal)) {
|
||||
activate(CharacterController::FollowType::Horizontal, feetAreTracked);
|
||||
setForceActivateHorizontal(false);
|
||||
} else {
|
||||
if ((myAvatar.getAllowAvatarLeaningPreference() != MyAvatar::AllowAvatarLeaningPreference::AlwaysNoRecenter) &&
|
||||
qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
|
||||
if (myAvatar.getCenterOfGravityModelEnabled()) {
|
||||
if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) {
|
||||
activate(Horizontal);
|
||||
if (!isActive(CharacterController::FollowType::Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) {
|
||||
activate(CharacterController::FollowType::Horizontal, false);
|
||||
if (myAvatar.getEnableStepResetRotation()) {
|
||||
activate(Rotation);
|
||||
activate(CharacterController::FollowType::Rotation, false);
|
||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// center of gravity model is not enabled
|
||||
if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Horizontal);
|
||||
if (!isActive(CharacterController::FollowType::Horizontal) &&
|
||||
(shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(CharacterController::FollowType::Horizontal, false);
|
||||
if (myAvatar.getEnableStepResetRotation() && !myAvatar.getIsInSittingState()) {
|
||||
activate(Rotation);
|
||||
activate(CharacterController::FollowType::Rotation, false);
|
||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Vertical);
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical recenter
|
||||
|
||||
if (myAvatar.getHMDCrouchRecenterEnabled() && qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
|
||||
if (!isActive(CharacterController::FollowType::Vertical) &&
|
||||
(shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(CharacterController::FollowType::Vertical, false);
|
||||
if (_squatDetected) {
|
||||
_squatDetected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!isActive(Rotation) && getForceActivateRotation()) {
|
||||
activate(Rotation);
|
||||
// Forced activations can be requested by MyAvatar::triggerVerticalRecenter, callable from scripts.
|
||||
|
||||
if (!isActive(CharacterController::FollowType::Rotation) && getForceActivateRotation()) {
|
||||
activate(CharacterController::FollowType::Rotation, true);
|
||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
|
||||
setForceActivateRotation(false);
|
||||
}
|
||||
if (!isActive(Horizontal) && getForceActivateHorizontal()) {
|
||||
activate(Horizontal);
|
||||
if (!isActive(CharacterController::FollowType::Horizontal) && getForceActivateHorizontal()) {
|
||||
activate(CharacterController::FollowType::Horizontal, true);
|
||||
setForceActivateHorizontal(false);
|
||||
}
|
||||
if (!isActive(Vertical) && getForceActivateVertical()) {
|
||||
activate(Vertical);
|
||||
if (!isActive(CharacterController::FollowType::Vertical) && getForceActivateVertical()) {
|
||||
activate(CharacterController::FollowType::Vertical, true);
|
||||
setForceActivateVertical(false);
|
||||
}
|
||||
}
|
||||
|
@ -5721,21 +5827,21 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
|||
// remove scale present from sensorToWorldMatrix
|
||||
followWorldPose.scale() = glm::vec3(1.0f);
|
||||
|
||||
if (isActive(Rotation)) {
|
||||
if (isActive(CharacterController::FollowType::Rotation)) {
|
||||
//use the hmd reading for the hips follow
|
||||
followWorldPose.rot() = glmExtractRotation(desiredWorldMatrix);
|
||||
}
|
||||
if (isActive(Horizontal)) {
|
||||
if (isActive(CharacterController::FollowType::Horizontal)) {
|
||||
glm::vec3 desiredTranslation = extractTranslation(desiredWorldMatrix);
|
||||
followWorldPose.trans().x = desiredTranslation.x;
|
||||
followWorldPose.trans().z = desiredTranslation.z;
|
||||
}
|
||||
if (isActive(Vertical)) {
|
||||
if (isActive(CharacterController::FollowType::Vertical)) {
|
||||
glm::vec3 desiredTranslation = extractTranslation(desiredWorldMatrix);
|
||||
followWorldPose.trans().y = desiredTranslation.y;
|
||||
}
|
||||
|
||||
myAvatar.getCharacterController()->setFollowParameters(followWorldPose, getMaxTimeRemaining());
|
||||
myAvatar.getCharacterController()->setFollowParameters(followWorldPose);
|
||||
}
|
||||
|
||||
glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) {
|
||||
|
@ -5755,11 +5861,15 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(MyAvatar& myAvatar, const gl
|
|||
|
||||
glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix),
|
||||
sensorLinearDisplacement + extractTranslation(currentBodyMatrix));
|
||||
|
||||
if (myAvatar.getHMDCrouchRecenterEnabled()) {
|
||||
if (myAvatar.getSitStandStateChange()) {
|
||||
myAvatar.setSitStandStateChange(false);
|
||||
deactivate(Vertical);
|
||||
deactivate(CharacterController::FollowType::Vertical);
|
||||
setTranslation(newBodyMat, extractTranslation(myAvatar.deriveBodyFromHMDSensor()));
|
||||
}
|
||||
}
|
||||
|
||||
return newBodyMat;
|
||||
} else {
|
||||
return currentBodyMatrix;
|
||||
|
@ -6127,7 +6237,7 @@ void MyAvatar::updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose&
|
|||
}
|
||||
|
||||
bool MyAvatar::isRecenteringHorizontally() const {
|
||||
return _follow.isActive(FollowHelper::Horizontal);
|
||||
return _follow.isActive(CharacterController::FollowType::Horizontal);
|
||||
}
|
||||
|
||||
const MyHead* MyAvatar::getMyHead() const {
|
||||
|
@ -6583,7 +6693,7 @@ void MyAvatar::beginSit(const glm::vec3& position, const glm::quat& rotation) {
|
|||
setHMDLeanRecenterEnabled(false);
|
||||
// Disable movement
|
||||
setSitDriveKeysStatus(false);
|
||||
centerBody();
|
||||
centerBody(true);
|
||||
int hipIndex = getJointIndex("Hips");
|
||||
clearPinOnJoint(hipIndex);
|
||||
pinJoint(hipIndex, position, rotation);
|
||||
|
@ -6601,7 +6711,7 @@ void MyAvatar::endSit(const glm::vec3& position, const glm::quat& rotation) {
|
|||
_characterController.setSeated(false);
|
||||
setCollisionsEnabled(true);
|
||||
setHMDLeanRecenterEnabled(true);
|
||||
centerBody();
|
||||
centerBody(false);
|
||||
slamPosition(position);
|
||||
setWorldOrientation(rotation);
|
||||
|
||||
|
@ -6906,6 +7016,30 @@ bool MyAvatar::isJumping() {
|
|||
_characterController.getState() == CharacterController::State::Takeoff) && !isFlying();
|
||||
}
|
||||
|
||||
// Determine if the avatar is allowed to lean in its current situation.
|
||||
bool MyAvatar::isAllowedToLean() const {
|
||||
return (getAllowAvatarLeaningPreference() == MyAvatar::AllowAvatarLeaningPreference::Always) ||
|
||||
((getAllowAvatarLeaningPreference() == MyAvatar::AllowAvatarLeaningPreference::WhenUserIsStanding) &&
|
||||
!getIsInSittingState());
|
||||
}
|
||||
|
||||
// Determine if the feet are under direct control (tracked).
|
||||
bool MyAvatar::areFeetTracked() const {
|
||||
// Foot tracking only activates when both feet are tracked, so we only need to test one.
|
||||
return getControllerPoseInSensorFrame(controller::Action::LEFT_FOOT).isValid();
|
||||
}
|
||||
|
||||
// Determine if the hips are under direct control (tracked).
|
||||
bool MyAvatar::areHipsTracked() const {
|
||||
return getControllerPoseInSensorFrame(controller::Action::HIPS).isValid();
|
||||
}
|
||||
|
||||
// Determine if crouch recentering is enabled (making the avatar stand when the user is sitting in the real world).
|
||||
bool MyAvatar::getHMDCrouchRecenterEnabled() const {
|
||||
return (!_characterController.getSeated() &&
|
||||
(_allowAvatarStandingPreference.get() == AllowAvatarStandingPreference::Always) && !areFeetTracked());
|
||||
}
|
||||
|
||||
bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result = false;
|
||||
|
|
|
@ -282,16 +282,11 @@ class MyAvatar : public Avatar {
|
|||
* <p><strong>Warning:</strong> Setting this value also sets the value of <code>analogPlusSprintSpeed</code> to twice
|
||||
* the value.</p>
|
||||
* @property {number} analogPlusSprintSpeed - The sprint (run) speed of your avatar for the "AnalogPlus" control scheme.
|
||||
* @property {MyAvatar.SitStandModelType} userRecenterModel - Controls avatar leaning and recentering behavior.
|
||||
* @property {number} isInSittingState - <code>true</code> if the user wearing the HMD is determined to be sitting
|
||||
* (avatar leaning is disabled, recentering is enabled), <code>false</code> if the user wearing the HMD is
|
||||
* determined to be standing (avatar leaning is enabled, and avatar recenters if it leans too far).
|
||||
* If <code>userRecenterModel == 2</code> (i.e., "auto") the property value automatically updates as the user sits
|
||||
* or stands, unless <code>isSitStandStateLocked == true</code>. Setting the property value overrides the current
|
||||
* sitting / standing state, which is updated when the user next sits or stands unless
|
||||
* <code>isSitStandStateLocked == true</code>.
|
||||
* @property {boolean} isSitStandStateLocked - <code>true</code> to lock the avatar sitting/standing state, i.e., use this
|
||||
* to disable automatically changing state.
|
||||
* @property {number} isInSittingState - <code>true</code> if the user wearing the HMD is determined to be sitting;
|
||||
* <code>false</code> if the user wearing the HMD is determined to be standing. This can affect whether the avatar
|
||||
* is allowed to stand, lean or recenter its footing, depending on user preferences.
|
||||
* The property value automatically updates as the user sits or stands. Setting the property value overrides the current
|
||||
* sitting / standing state, which is updated when the user next sits or stands.
|
||||
* @property {boolean} allowTeleporting - <code>true</code> if teleporting is enabled in the Interface settings,
|
||||
* <code>false</code> if it isn't. <em>Read-only.</em>
|
||||
*
|
||||
|
@ -413,8 +408,6 @@ class MyAvatar : public Avatar {
|
|||
Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed NOTIFY walkBackwardSpeedChanged);
|
||||
Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed NOTIFY sprintSpeedChanged);
|
||||
Q_PROPERTY(bool isInSittingState READ getIsInSittingState WRITE setIsInSittingState);
|
||||
Q_PROPERTY(MyAvatar::SitStandModelType userRecenterModel READ getUserRecenterModel WRITE setUserRecenterModel);
|
||||
Q_PROPERTY(bool isSitStandStateLocked READ getIsSitStandStateLocked WRITE setIsSitStandStateLocked);
|
||||
Q_PROPERTY(bool allowTeleporting READ getAllowTeleporting)
|
||||
|
||||
const QString DOMINANT_LEFT_HAND = "left";
|
||||
|
@ -549,6 +542,31 @@ public:
|
|||
};
|
||||
Q_ENUM(SitStandModelType)
|
||||
|
||||
// Note: The option strings in setupPreferences (PreferencesDialog.cpp) must match this order.
|
||||
enum class AllowAvatarStandingPreference : uint
|
||||
{
|
||||
WhenUserIsStanding,
|
||||
Always,
|
||||
Count,
|
||||
Default = Always,
|
||||
};
|
||||
Q_ENUM(AllowAvatarStandingPreference)
|
||||
|
||||
// Note: The option strings in setupPreferences (PreferencesDialog.cpp) must match this order.
|
||||
enum class AllowAvatarLeaningPreference : uint
|
||||
{
|
||||
WhenUserIsStanding,
|
||||
Always,
|
||||
Never,
|
||||
AlwaysNoRecenter, // experimental
|
||||
Count,
|
||||
Default = WhenUserIsStanding,
|
||||
};
|
||||
Q_ENUM(AllowAvatarLeaningPreference)
|
||||
|
||||
static const std::array<QString, (uint)AllowAvatarStandingPreference::Count> allowAvatarStandingPreferenceStrings;
|
||||
static const std::array<QString, (uint)AllowAvatarLeaningPreference::Count> allowAvatarLeaningPreferenceStrings;
|
||||
|
||||
explicit MyAvatar(QThread* thread);
|
||||
virtual ~MyAvatar();
|
||||
|
||||
|
@ -576,7 +594,7 @@ public:
|
|||
* the HMD.
|
||||
* @function MyAvatar.centerBody
|
||||
*/
|
||||
Q_INVOKABLE void centerBody(); // thread-safe
|
||||
Q_INVOKABLE void centerBody(const bool forceFollowYPos); // thread-safe
|
||||
|
||||
|
||||
/**jsdoc
|
||||
|
@ -1417,7 +1435,6 @@ public:
|
|||
controller::Pose getControllerPoseInSensorFrame(controller::Action action) const;
|
||||
controller::Pose getControllerPoseInWorldFrame(controller::Action action) const;
|
||||
controller::Pose getControllerPoseInAvatarFrame(controller::Action action) const;
|
||||
glm::quat getOffHandRotation() const;
|
||||
|
||||
bool hasDriveInput() const;
|
||||
|
||||
|
@ -1709,7 +1726,7 @@ public:
|
|||
|
||||
// derive avatar body position and orientation from the current HMD Sensor location.
|
||||
// results are in sensor frame (-z forward)
|
||||
glm::mat4 deriveBodyFromHMDSensor() const;
|
||||
glm::mat4 deriveBodyFromHMDSensor(const bool forceFollowYPos = false) const;
|
||||
|
||||
glm::mat4 getSpine2RotationRigSpace() const;
|
||||
|
||||
|
@ -1753,10 +1770,10 @@ public:
|
|||
bool getIsInWalkingState() const;
|
||||
void setIsInSittingState(bool isSitting);
|
||||
bool getIsInSittingState() const;
|
||||
void setUserRecenterModel(MyAvatar::SitStandModelType modelName);
|
||||
MyAvatar::SitStandModelType getUserRecenterModel() const;
|
||||
void setIsSitStandStateLocked(bool isLocked);
|
||||
bool getIsSitStandStateLocked() const;
|
||||
void setAllowAvatarStandingPreference(const AllowAvatarStandingPreference preference);
|
||||
AllowAvatarStandingPreference getAllowAvatarStandingPreference() const;
|
||||
void setAllowAvatarLeaningPreference(const AllowAvatarLeaningPreference preference);
|
||||
AllowAvatarLeaningPreference getAllowAvatarLeaningPreference() const;
|
||||
void setWalkSpeed(float value);
|
||||
float getWalkSpeed() const;
|
||||
void setWalkBackwardSpeed(float value);
|
||||
|
@ -1989,6 +2006,10 @@ public:
|
|||
glm::vec3 getLookAtPivotPoint();
|
||||
glm::vec3 getCameraEyesPosition(float deltaTime);
|
||||
bool isJumping();
|
||||
bool getHMDCrouchRecenterEnabled() const;
|
||||
bool isAllowedToLean() const;
|
||||
bool areFeetTracked() const;
|
||||
bool areHipsTracked() const;
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -2841,23 +2862,15 @@ private:
|
|||
struct FollowHelper {
|
||||
FollowHelper();
|
||||
|
||||
enum FollowType {
|
||||
Rotation = 0,
|
||||
Horizontal,
|
||||
Vertical,
|
||||
NumFollowTypes
|
||||
};
|
||||
float _timeRemaining[NumFollowTypes];
|
||||
CharacterController::FollowTimePerType _timeRemaining;
|
||||
|
||||
void deactivate();
|
||||
void deactivate(FollowType type);
|
||||
void activate();
|
||||
void activate(FollowType type);
|
||||
void deactivate(CharacterController::FollowType type);
|
||||
void activate(CharacterController::FollowType type, const bool snapFollow);
|
||||
bool isActive() const;
|
||||
bool isActive(FollowType followType) const;
|
||||
float getMaxTimeRemaining() const;
|
||||
bool isActive(CharacterController::FollowType followType) const;
|
||||
void decrementTimeRemaining(float dt);
|
||||
bool shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||
bool shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool& shouldSnapOut) const;
|
||||
bool shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||
bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||
bool shouldActivateHorizontalCG(MyAvatar& myAvatar) const;
|
||||
|
@ -2880,7 +2893,7 @@ private:
|
|||
|
||||
FollowHelper _follow;
|
||||
|
||||
bool isFollowActive(FollowHelper::FollowType followType) const;
|
||||
bool isFollowActive(CharacterController::FollowType followType) const;
|
||||
|
||||
bool _goToPending { false };
|
||||
bool _physicsSafetyPending { false };
|
||||
|
@ -2922,6 +2935,9 @@ private:
|
|||
|
||||
bool _centerOfGravityModelEnabled { true };
|
||||
bool _hmdLeanRecenterEnabled { true };
|
||||
bool _hmdCrouchRecenterEnabled{
|
||||
true
|
||||
}; // Is MyAvatar allowed to recenter vertically (stand) when the user is sitting in the real world.
|
||||
bool _sprint { false };
|
||||
|
||||
AnimPose _prePhysicsRoomPose;
|
||||
|
@ -2953,7 +2969,6 @@ private:
|
|||
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
|
||||
float _averageUserHeightSensorSpace { _userHeight.get() };
|
||||
bool _sitStandStateChange { false };
|
||||
ThreadSafeValueCache<bool> _lockSitStandState { false };
|
||||
|
||||
// max unscaled forward movement speed
|
||||
ThreadSafeValueCache<float> _defaultWalkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
|
||||
|
@ -2969,7 +2984,12 @@ private:
|
|||
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
|
||||
bool _isInWalkingState { false };
|
||||
ThreadSafeValueCache<bool> _isInSittingState { false };
|
||||
ThreadSafeValueCache<MyAvatar::SitStandModelType> _userRecenterModel { MyAvatar::SitStandModelType::Auto };
|
||||
ThreadSafeValueCache<MyAvatar::AllowAvatarStandingPreference> _allowAvatarStandingPreference{
|
||||
MyAvatar::AllowAvatarStandingPreference::Default
|
||||
}; // The user preference of when MyAvatar may stand.
|
||||
ThreadSafeValueCache<MyAvatar::AllowAvatarLeaningPreference> _allowAvatarLeaningPreference{
|
||||
MyAvatar::AllowAvatarLeaningPreference::Default
|
||||
}; // The user preference of when MyAvatar may lean.
|
||||
float _sitStandStateTimer { 0.0f };
|
||||
float _squatTimer { 0.0f };
|
||||
float _tippingPoint { _userHeight.get() };
|
||||
|
@ -3012,7 +3032,8 @@ private:
|
|||
Setting::Handle<int> _controlSchemeIndexSetting;
|
||||
std::vector<Setting::Handle<QUuid>> _avatarEntityIDSettings;
|
||||
std::vector<Setting::Handle<QByteArray>> _avatarEntityDataSettings;
|
||||
Setting::Handle<QString> _userRecenterModelSetting;
|
||||
Setting::Handle<QString> _allowAvatarStandingPreferenceSetting;
|
||||
Setting::Handle<QString> _allowAvatarLeaningPreferenceSetting;
|
||||
|
||||
// AvatarEntities stuff:
|
||||
// We cache the "map of unfortunately-formatted-binary-blobs" because they are expensive to compute
|
||||
|
|
|
@ -26,7 +26,9 @@ void MyCharacterController::RayShotgunResult::reset() {
|
|||
walkable = true;
|
||||
}
|
||||
|
||||
MyCharacterController::MyCharacterController(std::shared_ptr<MyAvatar> avatar) {
|
||||
MyCharacterController::MyCharacterController(std::shared_ptr<MyAvatar> avatar,
|
||||
const FollowTimePerType& followTimeRemainingPerType) :
|
||||
CharacterController(followTimeRemainingPerType) {
|
||||
|
||||
assert(avatar);
|
||||
_avatar = avatar;
|
||||
|
|
|
@ -23,7 +23,7 @@ class DetailedMotionState;
|
|||
|
||||
class MyCharacterController : public CharacterController {
|
||||
public:
|
||||
explicit MyCharacterController(std::shared_ptr<MyAvatar> avatar);
|
||||
explicit MyCharacterController(std::shared_ptr<MyAvatar> avatar, const FollowTimePerType& followTimeRemainingPerType);
|
||||
~MyCharacterController ();
|
||||
|
||||
void addToWorld() override;
|
||||
|
|
|
@ -65,13 +65,20 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
|||
return result;
|
||||
}
|
||||
|
||||
const bool useCenterOfGravityModel =
|
||||
myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !myAvatar->getIsInWalkingState() &&
|
||||
!myAvatar->getIsInSittingState() && myAvatar->getHMDLeanRecenterEnabled() &&
|
||||
(myAvatar->getAllowAvatarLeaningPreference() != MyAvatar::AllowAvatarLeaningPreference::AlwaysNoRecenter) &&
|
||||
myAvatar->getHMDCrouchRecenterEnabled();
|
||||
|
||||
glm::mat4 hipsMat;
|
||||
if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && !(myAvatar->getIsInSittingState()) && myAvatar->getHMDLeanRecenterEnabled()) {
|
||||
if (useCenterOfGravityModel) {
|
||||
// then we use center of gravity model
|
||||
hipsMat = myAvatar->deriveBodyUsingCgModel();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// otherwise use the default of putting the hips under the head
|
||||
hipsMat = myAvatar->deriveBodyFromHMDSensor();
|
||||
hipsMat = myAvatar->deriveBodyFromHMDSensor(true);
|
||||
}
|
||||
glm::vec3 hipsPos = extractTranslation(hipsMat);
|
||||
glm::quat hipsRot = glmExtractRotation(hipsMat);
|
||||
|
@ -82,7 +89,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
|||
|
||||
// dampen hips rotation, by mixing it with the avatar orientation in sensor space
|
||||
// turning this off for center of gravity model because it is already mixed in there
|
||||
if (!(myAvatar->getCenterOfGravityModelEnabled())) {
|
||||
if (!useCenterOfGravityModel) {
|
||||
const float MIX_RATIO = 0.5f;
|
||||
hipsRot = safeLerp(glmExtractRotation(avatarToSensorMat), hipsRot, MIX_RATIO);
|
||||
}
|
||||
|
|
|
@ -67,13 +67,13 @@ void AnimStats::updateStats(bool force) {
|
|||
|
||||
// print if we are recentering or not.
|
||||
_recenterText = "Recenter: ";
|
||||
if (myAvatar->isFollowActive(MyAvatar::FollowHelper::Rotation)) {
|
||||
if (myAvatar->isFollowActive(CharacterController::FollowType::Rotation)) {
|
||||
_recenterText += "Rotation ";
|
||||
}
|
||||
if (myAvatar->isFollowActive(MyAvatar::FollowHelper::Horizontal)) {
|
||||
if (myAvatar->isFollowActive(CharacterController::FollowType::Horizontal)) {
|
||||
_recenterText += "Horizontal ";
|
||||
}
|
||||
if (myAvatar->isFollowActive(MyAvatar::FollowHelper::Vertical)) {
|
||||
if (myAvatar->isFollowActive(CharacterController::FollowType::Vertical)) {
|
||||
_recenterText += "Vertical ";
|
||||
}
|
||||
emit recenterTextChanged();
|
||||
|
|
|
@ -422,40 +422,40 @@ void setupPreferences() {
|
|||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->int {
|
||||
switch (myAvatar->getUserRecenterModel()) {
|
||||
case MyAvatar::SitStandModelType::Auto:
|
||||
default:
|
||||
return 0;
|
||||
case MyAvatar::SitStandModelType::ForceSit:
|
||||
return 1;
|
||||
case MyAvatar::SitStandModelType::ForceStand:
|
||||
return 2;
|
||||
case MyAvatar::SitStandModelType::DisableHMDLean:
|
||||
return 3;
|
||||
}
|
||||
const IntPreference::Getter getter = [myAvatar]() -> int {
|
||||
return static_cast<int>(myAvatar->getAllowAvatarStandingPreference());
|
||||
};
|
||||
auto setter = [myAvatar](int value) {
|
||||
switch (value) {
|
||||
case 0:
|
||||
default:
|
||||
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::Auto);
|
||||
break;
|
||||
case 1:
|
||||
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::ForceSit);
|
||||
break;
|
||||
case 2:
|
||||
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::ForceStand);
|
||||
break;
|
||||
case 3:
|
||||
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::DisableHMDLean);
|
||||
break;
|
||||
}
|
||||
|
||||
const IntPreference::Setter setter = [myAvatar](const int& value) {
|
||||
myAvatar->setAllowAvatarStandingPreference(static_cast<MyAvatar::AllowAvatarStandingPreference>(value));
|
||||
};
|
||||
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Auto / Force Sit / Force Stand / Disable Recenter", getter, setter);
|
||||
|
||||
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Allow my avatar to stand", getter, setter);
|
||||
QStringList items;
|
||||
items << "Auto - turns on avatar leaning when standing in real world" << "Seated - disables all avatar leaning while sitting in real world" << "Standing - enables avatar leaning while sitting in real world" << "Disabled - allows avatar sitting on the floor [Experimental]";
|
||||
preference->setHeading("Avatar leaning behavior");
|
||||
items << "When I'm standing"
|
||||
<< "Always"; // Must match the order in MyAvatar::AllowAvatarStandingPreference.
|
||||
assert(items.size() == static_cast<uint>(MyAvatar::AllowAvatarStandingPreference::Count));
|
||||
preference->setHeading("Allow my avatar to stand:");
|
||||
preference->setItems(items);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
const IntPreference::Getter getter = [myAvatar]() -> int {
|
||||
return static_cast<int>(myAvatar->getAllowAvatarLeaningPreference());
|
||||
};
|
||||
|
||||
const IntPreference::Setter setter = [myAvatar](const int& value) {
|
||||
myAvatar->setAllowAvatarLeaningPreference(static_cast<MyAvatar::AllowAvatarLeaningPreference>(value));
|
||||
};
|
||||
|
||||
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Allow my avatar to lean", getter, setter);
|
||||
QStringList items;
|
||||
items << "When I'm standing"
|
||||
<< "Always"
|
||||
<< "Never"
|
||||
<< "Always, no recenter (Experimental)"; // Must match the order in MyAvatar::AllowAvatarLeaningPreference.
|
||||
assert(items.size() == static_cast<uint>(MyAvatar::AllowAvatarLeaningPreference::Count));
|
||||
preference->setHeading("Allow my avatar to lean:");
|
||||
preference->setItems(items);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
|
|
@ -1855,6 +1855,16 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJoin
|
|||
return position;
|
||||
}
|
||||
|
||||
// Get the scale factor to convert distances in the geometry frame into the unscaled rig frame.
|
||||
// Typically it will be the unit conversion from cm to m.
|
||||
float Rig::GetScaleFactorGeometryToUnscaledRig() const {
|
||||
// Normally the model offset transform will contain the avatar scale factor; we explicitly remove it here.
|
||||
AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), getModelOffsetPose().rot(), getModelOffsetPose().trans());
|
||||
AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * getGeometryOffsetPose();
|
||||
|
||||
return geomToRigWithoutAvatarScale.scale().x; // in practice this is always a uniform scale factor.
|
||||
}
|
||||
|
||||
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
|
||||
bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
|
@ -2703,10 +2713,10 @@ void Rig::computeAvatarBoundingCapsule(
|
|||
Extents totalExtents;
|
||||
totalExtents.reset();
|
||||
|
||||
// HACK by convention our Avatars are always modeled such that y=0 is the ground plane.
|
||||
// add the zero point so that our avatars will always have bounding volumes that are flush with the ground
|
||||
// HACK by convention our Avatars are always modeled such that y=0 (GEOMETRY_GROUND_Y) is the ground plane.
|
||||
// add the ground point so that our avatars will always have bounding volumes that are flush with the ground
|
||||
// even if they do not have legs (default robot)
|
||||
totalExtents.addPoint(glm::vec3(0.0f));
|
||||
totalExtents.addPoint(glm::vec3(0.f, GEOMETRY_GROUND_Y, 0.f));
|
||||
|
||||
// To reduce the radius of the bounding capsule to be tight with the torso, we only consider joints
|
||||
// from the head to the hips when computing the rest of the bounding capsule.
|
||||
|
@ -2747,24 +2757,20 @@ void Rig::initFlow(bool isActive) {
|
|||
}
|
||||
}
|
||||
|
||||
// Get the vertical position of eye joints, in the rig coordinate frame, ignoring the avatar scale.
|
||||
float Rig::getUnscaledEyeHeight() const {
|
||||
// Normally the model offset transform will contain the avatar scale factor, we explicitly remove it here.
|
||||
AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), getModelOffsetPose().rot(), getModelOffsetPose().trans());
|
||||
AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * getGeometryOffsetPose();
|
||||
|
||||
// This factor can be used to scale distances in the geometry frame into the unscaled rig frame.
|
||||
// Typically it will be the unit conversion from cm to m.
|
||||
float scaleFactor = geomToRigWithoutAvatarScale.scale().x; // in practice this always a uniform scale factor.
|
||||
// Factor to scale distances in the geometry frame into the unscaled rig frame.
|
||||
float scaleFactor = GetScaleFactorGeometryToUnscaledRig();
|
||||
|
||||
int headTopJoint = indexOfJoint("HeadTop_End");
|
||||
int headJoint = indexOfJoint("Head");
|
||||
int eyeJoint = indexOfJoint("LeftEye") != -1 ? indexOfJoint("LeftEye") : indexOfJoint("RightEye");
|
||||
int toeJoint = indexOfJoint("LeftToeBase") != -1 ? indexOfJoint("LeftToeBase") : indexOfJoint("RightToeBase");
|
||||
|
||||
// Makes assumption that the y = 0 plane in geometry is the ground plane.
|
||||
// We also make that assumption in Rig::computeAvatarBoundingCapsule()
|
||||
const float GROUND_Y = 0.0f;
|
||||
|
||||
// Values from the skeleton are in the geometry coordinate frame.
|
||||
auto skeleton = getAnimSkeleton();
|
||||
if (eyeJoint >= 0 && toeJoint >= 0) {
|
||||
|
@ -2772,8 +2778,8 @@ float Rig::getUnscaledEyeHeight() const {
|
|||
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y;
|
||||
return scaleFactor * eyeHeight;
|
||||
} else if (eyeJoint >= 0) {
|
||||
// Measure Eye joint to y = 0 plane.
|
||||
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - GROUND_Y;
|
||||
// Measure Eye joint to ground plane.
|
||||
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - GEOMETRY_GROUND_Y;
|
||||
return scaleFactor * eyeHeight;
|
||||
} else if (headTopJoint >= 0 && toeJoint >= 0) {
|
||||
// Measure from ToeBase joint to HeadTop_End joint, then remove forehead distance.
|
||||
|
@ -2783,19 +2789,36 @@ float Rig::getUnscaledEyeHeight() const {
|
|||
} else if (headTopJoint >= 0) {
|
||||
// Measure from HeadTop_End joint to the ground, then remove forehead distance.
|
||||
const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT;
|
||||
float headHeight = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GROUND_Y;
|
||||
float headHeight = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GEOMETRY_GROUND_Y;
|
||||
return scaleFactor * (headHeight - headHeight * ratio);
|
||||
} else if (headJoint >= 0) {
|
||||
// Measure Head joint to the ground, then add in distance from neck to eye.
|
||||
const float DEFAULT_AVATAR_NECK_TO_EYE = DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD - DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
|
||||
const float ratio = DEFAULT_AVATAR_NECK_TO_EYE / DEFAULT_AVATAR_NECK_HEIGHT;
|
||||
float neckHeight = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GROUND_Y;
|
||||
float neckHeight = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GEOMETRY_GROUND_Y;
|
||||
return scaleFactor * (neckHeight + neckHeight * ratio);
|
||||
} else {
|
||||
return DEFAULT_AVATAR_EYE_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the vertical position of the hips joint, in the rig coordinate frame, ignoring the avatar scale.
|
||||
float Rig::getUnscaledHipsHeight() const {
|
||||
// This factor can be used to scale distances in the geometry frame into the unscaled rig frame.
|
||||
const float scaleFactor = GetScaleFactorGeometryToUnscaledRig();
|
||||
|
||||
const int hipsJoint = indexOfJoint("Hips");
|
||||
|
||||
// Values from the skeleton are in the geometry coordinate frame.
|
||||
if (hipsJoint >= 0) {
|
||||
// Measure hip joint to ground plane.
|
||||
float hipsHeight = getAnimSkeleton()->getAbsoluteDefaultPose(hipsJoint).trans().y - GEOMETRY_GROUND_Y;
|
||||
return scaleFactor * hipsHeight;
|
||||
} else {
|
||||
return DEFAULT_AVATAR_HIPS_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::setDirectionalBlending(const QString& targetName, const glm::vec3& blendingTarget, const QString& alphaName, float alpha) {
|
||||
_animVars.set(targetName, blendingTarget);
|
||||
_animVars.set(alphaName, alpha);
|
||||
|
|
|
@ -251,6 +251,7 @@ public:
|
|||
Flow& getFlow() { return _internalFlow; }
|
||||
|
||||
float getUnscaledEyeHeight() const;
|
||||
float getUnscaledHipsHeight() const;
|
||||
void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) const;
|
||||
|
||||
int getOverrideJointCount() const;
|
||||
|
@ -287,6 +288,11 @@ protected:
|
|||
glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo,
|
||||
const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const;
|
||||
|
||||
// Get the scale factor to convert distances in the geometry frame into the unscaled rig frame.
|
||||
float GetScaleFactorGeometryToUnscaledRig() const;
|
||||
|
||||
// The ground plane Y position in geometry space.
|
||||
static constexpr float GEOMETRY_GROUND_Y = 0.0f;
|
||||
|
||||
AnimPose _modelOffset; // model to rig space
|
||||
AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets)
|
||||
|
|
|
@ -107,12 +107,12 @@ CharacterController::CharacterMotor::CharacterMotor(const glm::vec3& vel, const
|
|||
|
||||
static uint32_t _numCharacterControllers { 0 };
|
||||
|
||||
CharacterController::CharacterController() {
|
||||
CharacterController::CharacterController(const FollowTimePerType& followTimeRemainingPerType) :
|
||||
_followTimeRemainingPerType(followTimeRemainingPerType) {
|
||||
_floorDistance = _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT;
|
||||
|
||||
_targetVelocity.setValue(0.0f, 0.0f, 0.0f);
|
||||
_followDesiredBodyTransform.setIdentity();
|
||||
_followTimeRemaining = 0.0f;
|
||||
_state = State::Hover;
|
||||
_isPushingUp = false;
|
||||
_rayHitStartTime = 0;
|
||||
|
@ -351,27 +351,63 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar
|
|||
computeNewVelocity(dt, velocity);
|
||||
|
||||
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();
|
||||
static_assert(FOLLOW_TIME_IMMEDIATE_SNAP > MINIMUM_TIME_REMAINING, "The code below assumes this condition is true.");
|
||||
|
||||
bool hasFollowTimeRemaining = false;
|
||||
for (float followTime : _followTimeRemainingPerType) {
|
||||
if (followTime > MINIMUM_TIME_REMAINING) {
|
||||
hasFollowTimeRemaining = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFollowTimeRemaining) {
|
||||
const float MAX_DISPLACEMENT = 0.5f * _radius;
|
||||
|
||||
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 linearDisplacement(0, 0, 0);
|
||||
{
|
||||
linearDisplacement.setZero();
|
||||
|
||||
const float horizontalTime = _followTimeRemainingPerType[static_cast<uint>(FollowType::Horizontal)];
|
||||
const float verticalTime = _followTimeRemainingPerType[static_cast<uint>(FollowType::Vertical)];
|
||||
|
||||
if (horizontalTime == FOLLOW_TIME_IMMEDIATE_SNAP) {
|
||||
linearDisplacement.setX(deltaPos.x());
|
||||
linearDisplacement.setZ(deltaPos.z());
|
||||
} else if (horizontalTime > MINIMUM_TIME_REMAINING) {
|
||||
linearDisplacement.setX((deltaPos.x() * dt) / horizontalTime);
|
||||
linearDisplacement.setZ((deltaPos.z() * dt) / horizontalTime);
|
||||
}
|
||||
|
||||
if (verticalTime == FOLLOW_TIME_IMMEDIATE_SNAP) {
|
||||
linearDisplacement.setY(deltaPos.y());
|
||||
} else if (verticalTime > MINIMUM_TIME_REMAINING) {
|
||||
linearDisplacement.setY((deltaPos.y() * dt) / verticalTime);
|
||||
}
|
||||
|
||||
linearDisplacement = clampLength(linearDisplacement, MAX_DISPLACEMENT); // clamp displacement to prevent tunneling.
|
||||
}
|
||||
|
||||
btVector3 endPos = startPos + linearDisplacement;
|
||||
|
||||
// resolve the simple linearDisplacement
|
||||
_followLinearDisplacement += linearDisplacement;
|
||||
|
||||
// now for the rotational part...
|
||||
|
||||
btQuaternion startRot = bodyTransform.getRotation();
|
||||
btQuaternion desiredRot = _followDesiredBodyTransform.getRotation();
|
||||
|
||||
// startRot as default rotation
|
||||
btQuaternion endRot = startRot;
|
||||
|
||||
const float rotationTime = _followTimeRemainingPerType[static_cast<uint>(FollowType::Rotation)];
|
||||
if (rotationTime > MINIMUM_TIME_REMAINING) {
|
||||
btQuaternion desiredRot = _followDesiredBodyTransform.getRotation();
|
||||
|
||||
// the dot product between two quaternions is equal to +/- cos(angle/2)
|
||||
// where 'angle' is that of the rotation between them
|
||||
float qDot = desiredRot.dot(startRot);
|
||||
|
@ -394,8 +430,11 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar
|
|||
|
||||
// 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;
|
||||
|
||||
if (rotationTime != FOLLOW_TIME_IMMEDIATE_SNAP) {
|
||||
if (dt < rotationTime) {
|
||||
angle *= dt / rotationTime;
|
||||
}
|
||||
}
|
||||
|
||||
// accumulate rotation
|
||||
|
@ -406,11 +445,14 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar
|
|||
btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset);
|
||||
|
||||
endRot = deltaRot * startRot;
|
||||
btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset);
|
||||
btVector3 swingDisplacement =
|
||||
rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset);
|
||||
_followLinearDisplacement += swingDisplacement;
|
||||
}
|
||||
}
|
||||
_rigidBody->setWorldTransform(btTransform(endRot, endPos));
|
||||
}
|
||||
|
||||
_followTime += dt;
|
||||
|
||||
if (_steppingUp) {
|
||||
|
@ -606,8 +648,7 @@ void CharacterController::setParentVelocity(const glm::vec3& velocity) {
|
|||
_parentVelocity = glmToBullet(velocity);
|
||||
}
|
||||
|
||||
void CharacterController::setFollowParameters(const glm::mat4& desiredWorldBodyMatrix, float timeRemaining) {
|
||||
_followTimeRemaining = timeRemaining;
|
||||
void CharacterController::setFollowParameters(const glm::mat4& desiredWorldBodyMatrix) {
|
||||
_followDesiredBodyTransform = glmToBullet(desiredWorldBodyMatrix) * btTransform(btQuaternion::getIdentity(), glmToBullet(_shapeLocalOffset));
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,21 @@ const btScalar MIN_CHARACTER_MOTOR_TIMESCALE = 0.05f;
|
|||
class CharacterController : public btCharacterControllerInterface {
|
||||
|
||||
public:
|
||||
CharacterController();
|
||||
enum class FollowType : uint8_t
|
||||
{
|
||||
Rotation,
|
||||
Horizontal,
|
||||
Vertical,
|
||||
Count
|
||||
};
|
||||
|
||||
// Remaining follow time for each FollowType
|
||||
typedef std::array<float, static_cast<size_t>(FollowType::Count)> FollowTimePerType;
|
||||
|
||||
// Follow time value meaning that we should snap immediately to the target.
|
||||
static constexpr float FOLLOW_TIME_IMMEDIATE_SNAP = FLT_MAX;
|
||||
|
||||
CharacterController(const FollowTimePerType& followTimeRemainingPerType);
|
||||
virtual ~CharacterController();
|
||||
bool needsRemoval() const;
|
||||
bool needsAddition() const;
|
||||
|
@ -99,7 +113,8 @@ public:
|
|||
void getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const;
|
||||
|
||||
void setParentVelocity(const glm::vec3& parentVelocity);
|
||||
void setFollowParameters(const glm::mat4& desiredWorldMatrix, float timeRemaining);
|
||||
|
||||
void setFollowParameters(const glm::mat4& desiredWorldMatrix);
|
||||
float getFollowTime() const { return _followTime; }
|
||||
glm::vec3 getFollowLinearDisplacement() const;
|
||||
glm::quat getFollowAngularDisplacement() const;
|
||||
|
@ -144,7 +159,7 @@ public:
|
|||
|
||||
void setPendingFlagsUpdateCollisionMask(){ _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; }
|
||||
void setSeated(bool isSeated) { _isSeated = isSeated; }
|
||||
bool getSeated() { return _isSeated; }
|
||||
bool getSeated() const { return _isSeated; }
|
||||
|
||||
void resetStuckCounter() { _numStuckSubsteps = 0; }
|
||||
|
||||
|
@ -178,7 +193,7 @@ protected:
|
|||
btVector3 _preSimulationVelocity;
|
||||
btVector3 _velocityChange;
|
||||
btTransform _followDesiredBodyTransform;
|
||||
btScalar _followTimeRemaining;
|
||||
const FollowTimePerType& _followTimeRemainingPerType;
|
||||
btTransform _characterBodyTransform;
|
||||
btVector3 _position;
|
||||
btQuaternion _rotation;
|
||||
|
|
|
@ -20,6 +20,7 @@ const float DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD = 0.11f; // meters
|
|||
const float DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD = 0.185f; // meters
|
||||
const float DEFAULT_AVATAR_NECK_HEIGHT = DEFAULT_AVATAR_HEIGHT - DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD;
|
||||
const float DEFAULT_AVATAR_EYE_HEIGHT = DEFAULT_AVATAR_HEIGHT - DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
|
||||
const float DEFAULT_AVATAR_HIPS_HEIGHT = 1.01327407f; // meters
|
||||
const float DEFAULT_SPINE2_SPLINE_PROPORTION = 0.71f;
|
||||
const float DEFAULT_AVATAR_SUPPORT_BASE_LEFT = -0.25f;
|
||||
const float DEFAULT_AVATAR_SUPPORT_BASE_RIGHT = 0.25f;
|
||||
|
|
Loading…
Reference in a new issue