Use eye-height to match user and avatar scales, follow fixes

Fix postPhysicsUpdate to properly transform follow displacement into sensor frame.
This commit is contained in:
Anthony J. Thibault 2017-08-17 11:08:14 -07:00
parent 80b660b258
commit 963ddce7bc
7 changed files with 59 additions and 36 deletions

View file

@ -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<HMDScriptingInterface>();
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.

View file

@ -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));

View file

@ -527,6 +527,7 @@ public:
float getUserHeight() const;
void setUserHeight(float value);
float getUserEyeHeight() const;
public slots:
void increaseSize();

View file

@ -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();
}

View file

@ -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<Avatar*>(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;
}
}

View file

@ -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:

View file

@ -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 };