diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ecd7be2b7b..f33a16f0b8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -103,6 +103,7 @@ class MyAvatar : public Avatar { * @property useAdvancedMovementControls {bool} Stores the user preference only, does not change user mappings, this is done in the defaultScript * "scripts/system/controllers/toggleAdvancedMovementForHandControllers.js". * @property userHeight {number} The height of the user in sensor space. (meters). + * @property userEyeHeight {number} Estimated height of the users eyes in sensor space. (meters) */ // FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type @@ -145,6 +146,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate) Q_PROPERTY(float userHeight READ getUserHeight WRITE setUserHeight) + Q_PROPERTY(float userEyeHeight READ getUserEyeHeight) const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_RIGHT_HAND = "right"; @@ -525,8 +527,8 @@ public: Q_INVOKABLE bool isUp(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) > 0.0f; }; // true iff direction points up wrt avatar's definition of up. Q_INVOKABLE bool isDown(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) < 0.0f; }; - float getUserHeight() const; void setUserHeight(float value); + float getUserHeight() const; float getUserEyeHeight() const; public slots: diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 0222a4c604..614c16228e 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1579,6 +1579,8 @@ float Avatar::getEyeHeight() const { } // TODO: if performance becomes a concern we can cache this value rather then computing it everytime. + // Makes assumption that the y = 0 plane in geometry is the ground plane. + // We also make that assumption in Rig::computeAvatarBoundingCapsule() float avatarScale = getUniformScale(); if (_skeletonModel) { auto& rig = _skeletonModel->getRig(); @@ -1592,7 +1594,8 @@ float Avatar::getEyeHeight() const { return eyeHeight; } else if (eyeJoint >= 0) { // measure eyes to y = 0 plane. - float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y; + float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y; + float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y - groundHeight; return eyeHeight; } else if (headTopJoint >= 0 && toeJoint >= 0) { // measure toe to top of head. Note: default poses already include avatar scale factor @@ -1601,12 +1604,14 @@ float Avatar::getEyeHeight() const { 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; + float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y; + float headHeight = rig.getAbsoluteDefaultPose(headTopJoint).trans().y - groundHeight; + return headHeight - headHeight * ratio; } else if (headJoint >= 0) { + float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y; 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; + float neckHeight = rig.getAbsoluteDefaultPose(headJoint).trans().y - groundHeight; return neckHeight + neckHeight * ratio; } else { return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT;