From 963ddce7bcf45abe53522b63b70554e0376891f7 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 17 Aug 2017 11:08:14 -0700 Subject: [PATCH] Use eye-height to match user and avatar scales, follow fixes Fix postPhysicsUpdate to properly transform follow displacement into sensor frame. --- interface/src/Application.cpp | 8 +++- interface/src/avatar/MyAvatar.cpp | 33 +++++++++++---- interface/src/avatar/MyAvatar.h | 1 + .../src/scripting/HMDScriptingInterface.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 41 ++++++++++--------- .../src/avatars-renderer/Avatar.h | 8 ++-- libraries/shared/src/AvatarConstants.h | 2 + 7 files changed, 59 insertions(+), 36 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 398b2dbdb4..c07bf81785 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2385,7 +2385,7 @@ void Application::paintGL() { if (isHMDMode()) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); _myCamera.setPosition(extractTranslation(camMat)); - _myCamera.setOrientation(glm::quat_cast(camMat)); + _myCamera.setOrientation(glmExtractRotation(camMat)); } else { _myCamera.setPosition(myAvatar->getDefaultEyePosition()); _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); @@ -2393,7 +2393,7 @@ void Application::paintGL() { } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { if (isHMDMode()) { auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - _myCamera.setOrientation(glm::normalize(glm::quat_cast(hmdWorldMat))); + _myCamera.setOrientation(glm::normalize(glmExtractRotation(hmdWorldMat))); _myCamera.setPosition(extractTranslation(hmdWorldMat) + myAvatar->getOrientation() * boomOffset); } else { @@ -2501,6 +2501,10 @@ void Application::paintGL() { auto hmdInterface = DependencyManager::get(); float IPDScale = hmdInterface->getIPDScale(); + // scale IPD by height ratio, to make the world seem larger or smaller accordingly. + float heightRatio = getMyAvatar()->getEyeHeight() / getMyAvatar()->getUserEyeHeight(); + IPDScale *= heightRatio; + // FIXME we probably don't need to set the projection matrix every frame, // only when the display plugin changes (or in non-HMD modes when the user // changes the FOV manually, which right now I don't think they can. diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e574cb78ec..dc9c55243b 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -321,7 +321,7 @@ void MyAvatar::centerBody() { // transform this body into world space auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; auto worldBodyPos = extractTranslation(worldBodyMatrix); - auto worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); + auto worldBodyRot = glmExtractRotation(worldBodyMatrix); if (_characterController.getState() == CharacterController::State::Ground) { // the avatar's physical aspect thinks it is standing on something @@ -374,7 +374,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { // transform this body into world space auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; auto worldBodyPos = extractTranslation(worldBodyMatrix); - auto worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); + auto worldBodyRot = glmExtractRotation(worldBodyMatrix); // this will become our new position. setPosition(worldBodyPos); @@ -640,7 +640,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } _hmdSensorPosition = newHmdSensorPosition; - _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); + _hmdSensorOrientation = glmExtractRotation(hmdSensorMatrix); auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD); if (headPose.isValid()) { _headControllerFacing = getFacingDir2D(headPose.rotation); @@ -664,9 +664,11 @@ void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeV // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. void MyAvatar::updateSensorToWorldMatrix() { + // update the sensor mat so that the body position will end up in the desired // position when driven from the head. - glm::mat4 desiredMat = createMatFromQuatAndPos(getOrientation(), getPosition()); + float heightRatio = getEyeHeight() / getUserEyeHeight(); + glm::mat4 desiredMat = createMatFromScaleQuatAndPos(glm::vec3(heightRatio), getOrientation(), getPosition()); _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); lateUpdatePalms(); @@ -2615,7 +2617,9 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { // Y_180 is necessary because rig is z forward and hmdOrientation is -z forward glm::vec3 headToNeck = headOrientation * Quaternions::Y_180 * (localNeck - localHead); glm::vec3 neckToRoot = headOrientationYawOnly * Quaternions::Y_180 * -localNeck; - glm::vec3 bodyPos = headPosition + headToNeck + neckToRoot; + + float invHeightRatio = getUserEyeHeight() / getEyeHeight(); + glm::vec3 bodyPos = headPosition + invHeightRatio * (headToNeck + neckToRoot); return createMatFromQuatAndPos(headOrientationYawOnly, bodyPos); } @@ -2628,6 +2632,12 @@ void MyAvatar::setUserHeight(float value) { _userHeight.set(value); } +float MyAvatar::getUserEyeHeight() const { + float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; + float userHeight = _userHeight.get(); + return userHeight - userHeight * ratio; +} + glm::vec3 MyAvatar::getPositionForAudio() { switch (_audioListenerMode) { case AudioListenerMode::FROM_HEAD: @@ -2798,6 +2808,10 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix; AnimPose followWorldPose(currentWorldMatrix); + + // remove scale present from sensorToWorldMatrix + followWorldPose.scale() = glm::vec3(1.0f); + if (isActive(Rotation)) { followWorldPose.rot() = glmExtractRotation(desiredWorldMatrix); } @@ -2822,11 +2836,12 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co // apply follow displacement to the body matrix. glm::vec3 worldLinearDisplacement = myAvatar.getCharacterController()->getFollowLinearDisplacement(); glm::quat worldAngularDisplacement = myAvatar.getCharacterController()->getFollowAngularDisplacement(); - glm::quat sensorToWorld = glmExtractRotation(myAvatar.getSensorToWorldMatrix()); - glm::quat worldToSensor = glm::inverse(sensorToWorld); - glm::vec3 sensorLinearDisplacement = worldToSensor * worldLinearDisplacement; - glm::quat sensorAngularDisplacement = worldToSensor * worldAngularDisplacement * sensorToWorld; + glm::mat4 sensorToWorldMatrix = myAvatar.getSensorToWorldMatrix(); + glm::mat4 worldToSensorMatrix = glm::inverse(sensorToWorldMatrix); + + glm::vec3 sensorLinearDisplacement = transformVectorFast(worldToSensorMatrix, worldLinearDisplacement); + glm::quat sensorAngularDisplacement = glmExtractRotation(worldToSensorMatrix) * worldAngularDisplacement * glmExtractRotation(sensorToWorldMatrix); glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix), sensorLinearDisplacement + extractTranslation(currentBodyMatrix)); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 70385cbaf2..3b87980018 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -527,6 +527,7 @@ public: float getUserHeight() const; void setUserHeight(float value); + float getUserEyeHeight() const; public slots: void increaseSize(); diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index 93c3a7652e..39d3164f1f 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -151,7 +151,7 @@ glm::vec3 HMDScriptingInterface::getPosition() const { glm::quat HMDScriptingInterface::getOrientation() const { if (qApp->getActiveDisplayPlugin()->isHmd()) { - return glm::normalize(glm::quat_cast(getWorldHMDMatrix())); + return glmExtractRotation(getWorldHMDMatrix()); } return glm::quat(); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 7d577a1687..17e3cd82bb 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1550,47 +1550,48 @@ void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& } } -// returns the avatar height, in meters, includes avatar scale factor. -float Avatar::getHeight() const { +float Avatar::getEyeHeight() const { if (QThread::currentThread() != thread()) { - float result = DEFAULT_AVATAR_HEIGHT; + float result = DEFAULT_AVATAR_EYE_HEIGHT; BLOCKING_INVOKE_METHOD(const_cast(this), "getHeight", Q_RETURN_ARG(float, result)); return result; } // TODO: if performance becomes a concern we can cache this value rather then computing it everytime. - // AJT: TODO: I don't know what scale is to use here... getDomainLimitedScale? - float avatarScale = getTargetScale(); + float avatarScale = getUniformScale(); if (_skeletonModel) { auto& rig = _skeletonModel->getRig(); int headTopJoint = rig.indexOfJoint("HeadTop_End"); int headJoint = rig.indexOfJoint("Head"); int eyeJoint = rig.indexOfJoint("LeftEye") != -1 ? rig.indexOfJoint("LeftEye") : rig.indexOfJoint("RightEye"); int toeJoint = rig.indexOfJoint("LeftToeBase") != -1 ? rig.indexOfJoint("LeftToeBase") : rig.indexOfJoint("RightToeBase"); - if (headTopJoint >= 0 && toeJoint >= 0) { - // measure toe to top of head. Note: default poses already include avatar scale factor - float height = rig.getAbsoluteDefaultPose(headTopJoint).trans().y - rig.getAbsoluteDefaultPose(toeJoint).trans().y; - return height; - } else if (eyeJoint >= 0 && toeJoint >= 0) { - // measure from eyes to toes + the average eye to top of head distance. - const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; + if (eyeJoint >= 0 && toeJoint >= 0) { + // measure from eyes to toes. float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y - rig.getAbsoluteDefaultPose(toeJoint).trans().y; - return eyeHeight + eyeHeight * ratio; - } else if (headTopJoint >= 0) { - return rig.getAbsoluteDefaultPose(headTopJoint).trans().y; + return eyeHeight; } else if (eyeJoint >= 0) { - const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; + // measure eyes to y = 0 plane. float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y; - return eyeHeight + eyeHeight * ratio; + return eyeHeight; + } else if (headTopJoint >= 0 && toeJoint >= 0) { + // measure toe to top of head. Note: default poses already include avatar scale factor + const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; + float height = rig.getAbsoluteDefaultPose(headTopJoint).trans().y - rig.getAbsoluteDefaultPose(toeJoint).trans().y; + return height - height * ratio; + } else if (headTopJoint >= 0) { + const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; + float height = rig.getAbsoluteDefaultPose(headTopJoint).trans().y; + return height - height * ratio; } else if (headJoint >= 0) { - const float ratio = DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; + 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 = rig.getAbsoluteDefaultPose(headJoint).trans().y; return neckHeight + neckHeight * ratio; } else { - return avatarScale * DEFAULT_AVATAR_HEIGHT; + return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT; } } else { - return avatarScale * DEFAULT_AVATAR_HEIGHT; + return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT; } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index e070b82e00..a7e3076f10 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -252,11 +252,11 @@ public: void updateFadingStatus(render::ScenePointer scene); /**jsdoc - * Provides read only access to the current height of the avatar in world space. - * @function Avatar.getHeight - * @returns {number} height of avatar in meters + * Provides read only access to the current eye height of the avatar. + * @function Avatar.getEyeHeight + * @returns {number} eye height of avatar in meters */ - Q_INVOKABLE float getHeight() const; + Q_INVOKABLE float getEyeHeight() const; public slots: diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index b1ff1872e7..5849897dde 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -16,6 +16,8 @@ const float DEFAULT_AVATAR_HEIGHT = 1.755f; // meters 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; // Used when avatar is missing joints... (avatar space) static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };