Move Y_180 flip rotation out of Rig

This Y_180 flip is defined in skeletonModel not in the rig.
This is important if we wish to use the Rig for both Avatars (180 flip) and Entity models (no 180 flip).

We can hide this 180 flip from script, if we wish, by including it in all the accessors to and from
MyAvatar -> skeletalModel -> Rig.

Added Quaternions::Y_180 to GLMHelpers.
This commit is contained in:
Anthony J. Thibault 2015-11-23 19:26:30 -08:00
parent 8f46b8a765
commit 14189ac909
7 changed files with 48 additions and 76 deletions

View file

@ -1319,26 +1319,22 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
auto animSkeleton = _rig->getAnimSkeleton();
// the rig is in the skeletonModel frame
AnimPose xform(glm::vec3(1), _skeletonModel.getRotation(), _skeletonModel.getTranslation());
if (_enableDebugDrawDefaultPose && animSkeleton) {
AnimPose xform(glm::vec3(1), getOrientation(), getPosition());
glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f);
AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarDefaultPoses", animSkeleton, _rig->getAbsoluteDefaultPoses(), xform, gray);
}
if (_enableDebugDrawAnimPose && animSkeleton) {
glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f);
// getJointTransforms are aligned such that z is forward, not -z.
// so they need a 180 flip
glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f));
AnimPose xform(glm::vec3(1), getOrientation() * rotY180, getPosition());
// build absolute AnimPoseVec from rig
AnimPoseVec absPoses;
absPoses.reserve(_rig->getJointStateCount());
for (int i = 0; i < _rig->getJointStateCount(); i++) {
absPoses.push_back(AnimPose(_rig->getJointTransform(i)));
}
glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f);
AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, cyan);
}
}
@ -1791,42 +1787,33 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
const glm::quat hmdOrientation = getHMDSensorOrientation();
const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation);
/*
const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f);
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f);
const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f);
const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.0f, 0.0f);
*/
// 2 meter tall dude
const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.9f, 0.0f);
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.9f, 0.0f);
const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 0.0f);
const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.05f, 0.0f);
const glm::vec3 DEFAULT_RIG_MIDDLE_EYE_POS(0.0f, 1.9f, 0.0f);
const glm::vec3 DEFAULT_RIG_NECK_POS(0.0f, 1.70f, 0.0f);
const glm::vec3 DEFAULT_RIG_HIPS_POS(0.0f, 1.05f, 0.0f);
int rightEyeIndex = _rig->indexOfJoint("RightEye");
int leftEyeIndex = _rig->indexOfJoint("LeftEye");
int neckIndex = _rig->indexOfJoint("Neck");
int hipsIndex = _rig->indexOfJoint("Hips");
glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS;
glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS;
glm::vec3 absNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans : DEFAULT_NECK_POS;
glm::vec3 absHipsPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans : DEFAULT_HIPS_POS;
glm::vec3 rigMiddleEyePos = leftEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(leftEyeIndex).trans : DEFAULT_RIG_MIDDLE_EYE_POS;
glm::vec3 rigNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans : DEFAULT_RIG_NECK_POS;
glm::vec3 rigHipsPos = hipsIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans : DEFAULT_RIG_HIPS_POS;
glm::vec3 localEyes = (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos);
glm::vec3 localNeck = (absNeckPos - absHipsPos);
glm::vec3 localEyes = (rigMiddleEyePos - rigHipsPos);
glm::vec3 localNeck = (rigNeckPos - rigHipsPos);
// apply simplistic head/neck model
// figure out where the avatar body should be by applying offsets from the avatar's neck & head joints.
// eyeToNeck offset is relative full HMD orientation.
// while neckToRoot offset is only relative to HMDs yaw.
glm::vec3 eyeToNeck = hmdOrientation * (localNeck - localEyes);
glm::vec3 neckToRoot = hmdOrientationYawOnly * -localNeck;
// Y_180 is necessary because rig is z forward and hmdOrientation is -z forward
glm::vec3 eyeToNeck = hmdOrientation * Quaternions::Y_180 * (localNeck - localEyes);
glm::vec3 neckToRoot = hmdOrientationYawOnly * Quaternions::Y_180 * -localNeck;
glm::vec3 bodyPos = hmdPosition + eyeToNeck + neckToRoot;
// avatar facing is determined solely by hmd orientation.
return createMatFromQuatAndPos(hmdOrientationYawOnly, bodyPos);
}
#endif

View file

@ -59,7 +59,6 @@ void SkeletonModel::initJointStates() {
getJointPosition(rootJointIndex, rootModelPosition);
_defaultEyeModelPosition = midEyePosition - rootModelPosition;
_defaultEyeModelPosition.z = -_defaultEyeModelPosition.z;
// Skeleton may have already been scaled so unscale it
_defaultEyeModelPosition = _defaultEyeModelPosition / _scale;
@ -91,25 +90,20 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (qApp->getAvatarUpdater()->isHMDMode()) {
headParams.isInHMD = true;
// get HMD position from sensor space into world space, and back into model space
AnimPose avatarToWorld(glm::vec3(1.0f), myAvatar->getOrientation(), myAvatar->getPosition());
glm::mat4 worldToAvatar = glm::inverse((glm::mat4)avatarToWorld);
// get HMD position from sensor space into world space, and back into rig space
glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
glm::mat4 hmdMat = worldToAvatar * worldHMDMat;
// in local avatar space (i.e. relative to avatar pos/orientation.)
glm::vec3 hmdPosition = extractTranslation(hmdMat);
glm::quat hmdOrientation = extractRotation(hmdMat);
headParams.localHeadPosition = hmdPosition;
headParams.localHeadOrientation = hmdOrientation;
glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation());
glm::mat4 worldToRig = glm::inverse(rigToWorld);
glm::mat4 rigHMDMat = worldToRig * worldHMDMat;
headParams.rigHeadPosition = extractTranslation(rigHMDMat);
headParams.rigHeadOrientation = extractRotation(rigHMDMat);
headParams.worldHeadOrientation = extractRotation(worldHMDMat);
} else {
headParams.isInHMD = false;
// We don't have a valid localHeadPosition.
headParams.localHeadOrientation = head->getFinalOrientationInLocalFrame();
headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame();
headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame();
}
@ -124,8 +118,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
auto leftPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand);
if (leftPalm.isValid() && leftPalm.isActive()) {
handParams.isLeftEnabled = true;
handParams.leftPosition = leftPalm.getRawPosition();
handParams.leftOrientation = leftPalm.getRawRotation();
handParams.leftPosition = Quaternions::Y_180 * leftPalm.getRawPosition();
handParams.leftOrientation = Quaternions::Y_180 * leftPalm.getRawRotation();
handParams.leftTrigger = leftPalm.getTrigger();
} else {
handParams.isLeftEnabled = false;
@ -134,8 +128,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
auto rightPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::RightHand);
if (rightPalm.isValid() && rightPalm.isActive()) {
handParams.isRightEnabled = true;
handParams.rightPosition = rightPalm.getRawPosition();
handParams.rightOrientation = rightPalm.getRawRotation();
handParams.rightPosition = Quaternions::Y_180 * rightPalm.getRawPosition();
handParams.rightOrientation = Quaternions::Y_180 * rightPalm.getRawRotation();
handParams.rightTrigger = rightPalm.getTrigger();
} else {
handParams.isRightEnabled = false;
@ -144,6 +138,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig->updateFromHandParameters(handParams, deltaTime);
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
// evaluate AnimGraph animation and update jointStates.
Model::updateRig(deltaTime, parentTransform);
@ -198,8 +193,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
void SkeletonModel::updateAttitude() {
setTranslation(_owningAvatar->getSkeletonPosition());
static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
setRotation(_owningAvatar->getOrientation() * refOrientation);
setRotation(_owningAvatar->getOrientation() * Quaternions::Y_180);
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale());
}

View file

@ -129,7 +129,7 @@ void AnimClip::copyFromNetworkAnim() {
// so it looks wrong if we enable this code.
glm::quat preRotation = animSkeleton.getPreRotation(animJoint);
#else
// TODO: This is legacy approach, this does not work when animations and models do not
// TODO: This is the legacy approach, this does not work when animations and models do not
// have the same set of pre rotations. For example when mixing maya models with blender animations.
glm::quat preRotation = _skeleton->getRelativeBindPose(skeletonJoint).rot;
#endif

View file

@ -182,7 +182,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff
_relativePoses.clear();
_relativePoses = _animSkeleton->getRelativeDefaultPoses();
buildAbsoluteRigPoses(_relativePoses, _absolutePoses, true);
buildAbsoluteRigPoses(_relativePoses, _absolutePoses);
_overridePoses.clear();
_overridePoses = _animSkeleton->getRelativeDefaultPoses();
@ -210,7 +210,7 @@ void Rig::reset(const FBXGeometry& geometry) {
_relativePoses.clear();
_relativePoses = _animSkeleton->getRelativeDefaultPoses();
buildAbsoluteRigPoses(_relativePoses, _absolutePoses, true);
buildAbsoluteRigPoses(_relativePoses, _absolutePoses);
_overridePoses.clear();
_overridePoses = _animSkeleton->getRelativeDefaultPoses();
@ -258,8 +258,7 @@ void Rig::setModelOffset(const glm::mat4& modelOffsetMat) {
_modelOffset = newModelOffset;
// compute geometryToAvatarTransforms
glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
_geometryToRigTransform = AnimPose(glm::vec3(1), yFlip180, glm::vec3()) * _modelOffset * _geometryOffset;
_geometryToRigTransform = _modelOffset * _geometryOffset;
_rigToGeometryTransform = glm::inverse(_geometryToRigTransform);
// rebuild cached default poses
@ -343,22 +342,18 @@ void Rig::restoreJointTranslation(int index, float fraction, float priority) {
ASSERT(false);
}
// AJT: NOTE old code did not have 180 flip!
bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const {
if (isValid(jointIndex)) {
glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
position = (rotation * yFlip180 * _absolutePoses[jointIndex].trans) + translation;
position = (rotation * _absolutePoses[jointIndex].trans) + translation;
return true;
} else {
return false;
}
}
// AJT: NOTE old code did not have 180 flip!
bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const {
if (isValid(jointIndex)) {
glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
position = yFlip180 * _absolutePoses[jointIndex].trans;
position = _absolutePoses[jointIndex].trans;
return true;
} else {
return false;
@ -737,7 +732,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
}
applyOverridePoses();
buildAbsoluteRigPoses(_relativePoses, _absolutePoses, true);
buildAbsoluteRigPoses(_relativePoses, _absolutePoses);
}
void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
@ -859,7 +854,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
glm::vec3 headPos, neckPos;
glm::quat headRot, neckRot;
AnimPose hmdPose(glm::vec3(1.0f), params.localHeadOrientation * yFlip180, params.localHeadPosition);
AnimPose hmdPose(glm::vec3(1.0f), params.rigHeadOrientation * yFlip180, params.rigHeadPosition);
computeHeadNeckAnimVars(hmdPose, headPos, headRot, neckPos, neckRot);
// debug rendering
@ -885,7 +880,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
} else {
_animVars.unset("headPosition");
_animVars.set("headRotation", params.localHeadOrientation * yFlip180);
_animVars.set("headRotation", params.rigHeadOrientation * yFlip180);
_animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly);
_animVars.set("headType", (int)IKTarget::Type::RotationOnly);
_animVars.unset("neckPosition");
@ -1023,11 +1018,7 @@ void Rig::applyOverridePoses() {
}
}
// AJT: TODO: we should ALWAYS have the 180 flip.
// However currently matrices used for rendering require it.
// we need to find were the 180 is applied down the pipe and remove it,
// cluster matrices? render parts?
void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut, bool omitY180Flip) {
void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) {
if (!_animSkeleton) {
return;
}
@ -1047,9 +1038,7 @@ void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& a
// transform all absolute poses into rig space.
AnimPose geometryToRigTransform(_geometryToRigTransform);
if (omitY180Flip) {
geometryToRigTransform = _modelOffset * _geometryOffset;
}
geometryToRigTransform = _modelOffset * _geometryOffset;
for (int i = 0; i < (int)_absolutePoses.size(); i++) {
absolutePosesOut[i] = geometryToRigTransform * absolutePosesOut[i];
}

View file

@ -42,8 +42,8 @@ public:
float torsoTwist = 0.0f; // degrees
bool enableLean = false;
glm::quat worldHeadOrientation = glm::quat(); // world space (-z forward)
glm::quat localHeadOrientation = glm::quat(); // rig space (-z forward)
glm::vec3 localHeadPosition = glm::vec3(); // rig space
glm::quat rigHeadOrientation = glm::quat(); // rig space (-z forward)
glm::vec3 rigHeadPosition = glm::vec3(); // rig space
bool isInHMD = false;
int leanJointIndex = -1;
int neckJointIndex = -1;
@ -132,7 +132,7 @@ public:
// legacy
bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
// rig space (without y 180 flip)
// rig space
glm::mat4 getJointTransform(int jointIndex) const;
// Start or stop animations as needed.
@ -183,7 +183,7 @@ public:
void removeAnimationStateHandler(QScriptValue handler);
void animationStateHandlerResult(int identifier, QScriptValue result);
// rig space (without y 180 flip)
// rig space
bool getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const;
const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; }
@ -201,7 +201,7 @@ public:
bool isValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
void updateAnimationStateHandlers();
void applyOverridePoses();
void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut, bool omitY180Flip = false);
void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut);
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
void updateNeckJoint(int index, const HeadParameters& params);
@ -212,11 +212,11 @@ public:
void computeEyesInRootFrame(const AnimPoseVec& poses);
AnimPose _modelOffset; // model to rig space (without 180 flip)
AnimPose _modelOffset; // model to rig space
AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets)
AnimPoseVec _relativePoses; // geometry space relative to parent.
AnimPoseVec _absolutePoses; // rig space, not relative to parent. (without 180 flip)
AnimPoseVec _absolutePoses; // rig space, not relative to parent.
AnimPoseVec _overridePoses; // geometry space relative to parent.
std::vector<bool> _overrideFlags;

View file

@ -34,6 +34,7 @@ const vec3& Vectors::UP = Vectors::UNIT_Y;
const vec3& Vectors::FRONT = Vectors::UNIT_NEG_Z;
const quat Quaternions::IDENTITY{ 1.0f, 0.0f, 0.0f, 0.0f };
const quat Quaternions::Y_180{ 0.0f, 0.0f, 1.0f, 0.0f };
// Safe version of glm::mix; based on the code in Nick Bobick's article,
// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde,

View file

@ -58,6 +58,7 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
class Quaternions {
public:
static const quat IDENTITY;
static const quat Y_180;
};
class Vectors {