AnimVars are now in avatar/rig space

This makes it much simpler for code out side of the rig to manipulate AnimVars

* Removed mat4 type from AnimVars
* AnimVariantMap now has a _rigToGeometryTransform matrix
  used to transform positions and rotations into the correct coordinate frame.
* Moved AnimPose code to extract a quat from a scaled matrix into GLMHelpers
This commit is contained in:
Anthony J. Thibault 2015-11-20 18:29:17 -08:00
parent 4f8cd6930d
commit 54408a9c87
10 changed files with 102 additions and 101 deletions

View file

@ -1749,12 +1749,10 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 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_HIPS_POS(0.0f, 1.05f, 0.0f);
AnimPose geometryOffset = _rig->getGeometryOffset();
vec3 localEyes, localNeck; vec3 localEyes, localNeck;
if (!_debugDrawSkeleton) { if (!_debugDrawSkeleton) {
const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); localEyes = (((DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) / 2.0f) - DEFAULT_HIPS_POS);
localEyes = rotY180 * (((DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) / 2.0f) - DEFAULT_HIPS_POS); localNeck = (DEFAULT_NECK_POS - DEFAULT_HIPS_POS);
localNeck = rotY180 * (DEFAULT_NECK_POS - DEFAULT_HIPS_POS);
} else { } else {
// TODO: At the moment MyAvatar does not have access to the rig, which has the skeleton, which has the bind poses. // TODO: At the moment MyAvatar does not have access to the rig, which has the skeleton, which has the bind poses.
// for now use the _debugDrawSkeleton, which is initialized with the same FBX model as the rig. // for now use the _debugDrawSkeleton, which is initialized with the same FBX model as the rig.
@ -1765,17 +1763,13 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck"); int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck");
int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips"); int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips");
// AJT: TODO: perhaps expose default gets from rig? glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS;
// so this can become _rig->getAbsoluteDefaultPose(rightEyeIndex)... 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 absRightEyePos = rightEyeIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; localEyes = (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos);
glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; localNeck = (absNeckPos - absHipsPos);
glm::vec3 absNeckPos = neckIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS;
glm::vec3 absHipsPos = neckIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(hipsIndex).trans : DEFAULT_HIPS_POS;
const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f));
localEyes = rotY180 * (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos);
localNeck = rotY180 * (absNeckPos - absHipsPos);
} }
// apply simplistic head/neck model // apply simplistic head/neck model

View file

@ -98,8 +98,8 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition)); target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition));
if (target.getType() != IKTarget::Type::Unknown) { if (target.getType() != IKTarget::Type::Unknown) {
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses);
glm::quat rotation = animVars.lookup(targetVar.rotationVar, defaultPose.rot); glm::quat rotation = animVars.lookupRigToGeometry(targetVar.rotationVar, defaultPose.rot);
glm::vec3 translation = animVars.lookup(targetVar.positionVar, defaultPose.trans); glm::vec3 translation = animVars.lookupRigToGeometry(targetVar.positionVar, defaultPose.trans);
if (target.getType() == IKTarget::Type::HipsRelativeRotationAndPosition) { if (target.getType() == IKTarget::Type::HipsRelativeRotationAndPosition) {
translation += _hipsOffset; translation += _hipsOffset;
} }

View file

@ -103,9 +103,9 @@ AnimPose AnimManipulator::computeRelativePoseFromJointVar(const AnimVariantMap&
if (jointVar.type == JointVar::Type::AbsoluteRotation || jointVar.type == JointVar::Type::AbsolutePosition) { if (jointVar.type == JointVar::Type::AbsoluteRotation || jointVar.type == JointVar::Type::AbsolutePosition) {
if (jointVar.type == JointVar::Type::AbsoluteRotation) { if (jointVar.type == JointVar::Type::AbsoluteRotation) {
defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); defaultAbsPose.rot = animVars.lookupRigToGeometry(jointVar.var, defaultAbsPose.rot);
} else if (jointVar.type == JointVar::Type::AbsolutePosition) { } else if (jointVar.type == JointVar::Type::AbsolutePosition) {
defaultAbsPose.trans = animVars.lookup(jointVar.var, defaultAbsPose.trans); defaultAbsPose.trans = animVars.lookupRigToGeometry(jointVar.var, defaultAbsPose.trans);
} }
// because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose. // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose.
@ -123,9 +123,9 @@ AnimPose AnimManipulator::computeRelativePoseFromJointVar(const AnimVariantMap&
// override the default rel pose // override the default rel pose
AnimPose relPose = defaultRelPose; AnimPose relPose = defaultRelPose;
if (jointVar.type == JointVar::Type::RelativeRotation) { if (jointVar.type == JointVar::Type::RelativeRotation) {
relPose.rot = animVars.lookup(jointVar.var, defaultRelPose.rot); relPose.rot = animVars.lookupRigToGeometry(jointVar.var, defaultRelPose.rot);
} else if (jointVar.type == JointVar::Type::RelativePosition) { } else if (jointVar.type == JointVar::Type::RelativePosition) {
relPose.trans = animVars.lookup(jointVar.var, defaultRelPose.trans); relPose.trans = animVars.lookupRigToGeometry(jointVar.var, defaultRelPose.trans);
} }
return relPose; return relPose;

View file

@ -10,7 +10,6 @@
#include "AnimPose.h" #include "AnimPose.h"
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <glm/gtc/matrix_transform.hpp>
#include <algorithm> #include <algorithm>
const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
@ -19,14 +18,7 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
AnimPose::AnimPose(const glm::mat4& mat) { AnimPose::AnimPose(const glm::mat4& mat) {
scale = extractScale(mat); scale = extractScale(mat);
float maxScale = std::max(std::max(scale.x, scale.y), scale.z); rot = glmExtractRotation(mat);
if (maxScale > 1.01f || maxScale <= 0.99f) {
// quat_cast doesn't work so well with scaled matrices, so cancel it out.
mat4 tmp = glm::scale(mat, 1.0f / scale);
rot = glm::normalize(glm::quat_cast(tmp));
} else {
rot = glm::normalize(glm::quat_cast(mat));
}
trans = extractTranslation(mat); trans = extractTranslation(mat);
} }

View file

@ -43,7 +43,7 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine,
target.setProperty(name, quatToScriptValue(engine, value.getQuat())); target.setProperty(name, quatToScriptValue(engine, value.getQuat()));
break; break;
default: default:
// Note that we don't do mat4 in Javascript currently, and there's not yet a reason to start now. // Unknown type
assert(QString("AnimVariant::Type") == QString("valid")); assert(QString("AnimVariant::Type") == QString("valid"));
} }
}; };

View file

@ -18,8 +18,9 @@
#include <map> #include <map>
#include <set> #include <set>
#include <QScriptValue> #include <QScriptValue>
#include <StreamUtils.h>
#include <GLMHelpers.h>
#include "AnimationLogging.h" #include "AnimationLogging.h"
#include "StreamUtils.h"
class AnimVariant { class AnimVariant {
public: public:
@ -29,7 +30,6 @@ public:
Float, Float,
Vec3, Vec3,
Quat, Quat,
Mat4,
String, String,
NumTypes NumTypes
}; };
@ -40,7 +40,6 @@ public:
AnimVariant(float value) : _type(Type::Float) { _val.floats[0] = value; } AnimVariant(float value) : _type(Type::Float) { _val.floats[0] = value; }
AnimVariant(const glm::vec3& value) : _type(Type::Vec3) { *reinterpret_cast<glm::vec3*>(&_val) = value; } AnimVariant(const glm::vec3& value) : _type(Type::Vec3) { *reinterpret_cast<glm::vec3*>(&_val) = value; }
AnimVariant(const glm::quat& value) : _type(Type::Quat) { *reinterpret_cast<glm::quat*>(&_val) = value; } AnimVariant(const glm::quat& value) : _type(Type::Quat) { *reinterpret_cast<glm::quat*>(&_val) = value; }
AnimVariant(const glm::mat4& value) : _type(Type::Mat4) { *reinterpret_cast<glm::mat4*>(&_val) = value; }
AnimVariant(const QString& value) : _type(Type::String) { _stringVal = value; } AnimVariant(const QString& value) : _type(Type::String) { _stringVal = value; }
bool isBool() const { return _type == Type::Bool; } bool isBool() const { return _type == Type::Bool; }
@ -48,7 +47,6 @@ public:
bool isFloat() const { return _type == Type::Float; } bool isFloat() const { return _type == Type::Float; }
bool isVec3() const { return _type == Type::Vec3; } bool isVec3() const { return _type == Type::Vec3; }
bool isQuat() const { return _type == Type::Quat; } bool isQuat() const { return _type == Type::Quat; }
bool isMat4() const { return _type == Type::Mat4; }
bool isString() const { return _type == Type::String; } bool isString() const { return _type == Type::String; }
Type getType() const { return _type; } Type getType() const { return _type; }
@ -57,7 +55,6 @@ public:
void setFloat(float value) { assert(_type == Type::Float); _val.floats[0] = value; } void setFloat(float value) { assert(_type == Type::Float); _val.floats[0] = value; }
void setVec3(const glm::vec3& value) { assert(_type == Type::Vec3); *reinterpret_cast<glm::vec3*>(&_val) = value; } void setVec3(const glm::vec3& value) { assert(_type == Type::Vec3); *reinterpret_cast<glm::vec3*>(&_val) = value; }
void setQuat(const glm::quat& value) { assert(_type == Type::Quat); *reinterpret_cast<glm::quat*>(&_val) = value; } void setQuat(const glm::quat& value) { assert(_type == Type::Quat); *reinterpret_cast<glm::quat*>(&_val) = value; }
void setMat4(const glm::mat4& value) { assert(_type == Type::Mat4); *reinterpret_cast<glm::mat4*>(&_val) = value; }
void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; } void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; }
bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; }
@ -66,7 +63,6 @@ public:
const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); } const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); }
const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); } const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); }
const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast<const glm::mat4*>(&_val); }
const QString& getString() const { assert(_type == Type::String); return _stringVal; } const QString& getString() const { assert(_type == Type::String); return _stringVal; }
protected: protected:
@ -112,7 +108,7 @@ public:
} }
} }
const glm::vec3& lookup(const QString& key, const glm::vec3& defaultValue) const { const glm::vec3& lookupRaw(const QString& key, const glm::vec3& defaultValue) const {
if (key.isEmpty()) { if (key.isEmpty()) {
return defaultValue; return defaultValue;
} else { } else {
@ -121,7 +117,16 @@ public:
} }
} }
const glm::quat& lookup(const QString& key, const glm::quat& defaultValue) const { glm::vec3 lookupRigToGeometry(const QString& key, const glm::vec3& defaultValue) const {
if (key.isEmpty()) {
return defaultValue;
} else {
auto iter = _map.find(key);
return iter != _map.end() ? transformPoint(_rigToGeometryMat, iter->second.getVec3()) : defaultValue;
}
}
const glm::quat& lookupRaw(const QString& key, const glm::quat& defaultValue) const {
if (key.isEmpty()) { if (key.isEmpty()) {
return defaultValue; return defaultValue;
} else { } else {
@ -130,12 +135,12 @@ public:
} }
} }
const glm::mat4& lookup(const QString& key, const glm::mat4& defaultValue) const { glm::quat lookupRigToGeometry(const QString& key, const glm::quat& defaultValue) const {
if (key.isEmpty()) { if (key.isEmpty()) {
return defaultValue; return defaultValue;
} else { } else {
auto iter = _map.find(key); auto iter = _map.find(key);
return iter != _map.end() ? iter->second.getMat4() : defaultValue; return iter != _map.end() ? _rigToGeometryRot * iter->second.getQuat() : defaultValue;
} }
} }
@ -153,13 +158,17 @@ public:
void set(const QString& key, float value) { _map[key] = AnimVariant(value); } void set(const QString& key, float value) { _map[key] = AnimVariant(value); }
void set(const QString& key, const glm::vec3& value) { _map[key] = AnimVariant(value); } void set(const QString& key, const glm::vec3& value) { _map[key] = AnimVariant(value); }
void set(const QString& key, const glm::quat& value) { _map[key] = AnimVariant(value); } void set(const QString& key, const glm::quat& value) { _map[key] = AnimVariant(value); }
void set(const QString& key, const glm::mat4& value) { _map[key] = AnimVariant(value); }
void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); } void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); }
void unset(const QString& key) { _map.erase(key); } void unset(const QString& key) { _map.erase(key); }
void setTrigger(const QString& key) { _triggers.insert(key); } void setTrigger(const QString& key) { _triggers.insert(key); }
void clearTriggers() { _triggers.clear(); } void clearTriggers() { _triggers.clear(); }
void setRigToGeometryTransform(const glm::mat4& rigToGeometry) {
_rigToGeometryMat = rigToGeometry;
_rigToGeometryRot = glmExtractRotation(rigToGeometry);
}
void clearMap() { _map.clear(); } void clearMap() { _map.clear(); }
bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
@ -189,9 +198,6 @@ public:
case AnimVariant::Type::Quat: case AnimVariant::Type::Quat:
qCDebug(animation) << " " << pair.first << "=" << pair.second.getQuat(); qCDebug(animation) << " " << pair.first << "=" << pair.second.getQuat();
break; break;
case AnimVariant::Type::Mat4:
qCDebug(animation) << " " << pair.first << "=" << pair.second.getMat4();
break;
case AnimVariant::Type::String: case AnimVariant::Type::String:
qCDebug(animation) << " " << pair.first << "=" << pair.second.getString(); qCDebug(animation) << " " << pair.first << "=" << pair.second.getString();
break; break;
@ -205,6 +211,8 @@ public:
protected: protected:
std::map<QString, AnimVariant> _map; std::map<QString, AnimVariant> _map;
std::set<QString> _triggers; std::set<QString> _triggers;
glm::mat4 _rigToGeometryMat;
glm::quat _rigToGeometryRot;
}; };
typedef std::function<void(QScriptValue)> AnimVariantResultHandler; typedef std::function<void(QScriptValue)> AnimVariantResultHandler;

View file

@ -236,12 +236,17 @@ int Rig::getJointStateCount() const {
return _relativePoses.size(); return _relativePoses.size();
} }
int Rig::indexOfJoint(const QString& jointName) { int Rig::indexOfJoint(const QString& jointName) const {
return _animSkeleton->nameToJointIndex(jointName); return _animSkeleton->nameToJointIndex(jointName);
} }
void Rig::setModelOffset(const glm::mat4& modelOffset) { void Rig::setModelOffset(const glm::mat4& modelOffset) {
_modelOffset = AnimPose(modelOffset); _modelOffset = AnimPose(modelOffset);
// 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;
_rigToGeometryTransform = glm::inverse(_geometryToRigTransform);
} }
bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { bool Rig::getJointStateRotation(int index, glm::quat& rotation) const {
@ -322,9 +327,9 @@ void Rig::restoreJointTranslation(int index, float fraction, float priority) {
// AJT: NOTE old code did not have 180 flip! // AJT: NOTE old code did not have 180 flip!
bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const { bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const {
glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) {
position = (rotation * yFlip * _absolutePoses[jointIndex].trans) + translation; glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
position = (rotation * yFlip180 * _absolutePoses[jointIndex].trans) + translation;
return true; return true;
} else { } else {
return false; return false;
@ -333,9 +338,9 @@ bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm:
// AJT: NOTE old code did not have 180 flip! // AJT: NOTE old code did not have 180 flip!
bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const {
glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) {
position = yFlip * _absolutePoses[jointIndex].trans; glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
position = yFlip180 * _absolutePoses[jointIndex].trans;
return true; return true;
} else { } else {
return false; return false;
@ -419,6 +424,14 @@ void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) {
} }
} }
AnimPose Rig::getAbsoluteDefaultPose(int index) const {
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
return AnimPose(_geometryToRigTransform) * _animSkeleton->getAbsoluteDefaultPose(index);
} else {
return AnimPose::identity;
}
}
// animation reference speeds. // animation reference speeds.
static const std::vector<float> FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s static const std::vector<float> FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s
static const std::vector<float> BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s static const std::vector<float> BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s
@ -682,13 +695,14 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
if (_animNode) { if (_animNode) {
updateAnimationStateHandlers(); updateAnimationStateHandlers();
_animVars.setRigToGeometryTransform(_rigToGeometryTransform);
// evaluate the animation // evaluate the animation
AnimNode::Triggers triggersOut; AnimNode::Triggers triggersOut;
_relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut); _relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut);
if ((int)_relativePoses.size() != _animSkeleton->getNumJoints()) { if ((int)_relativePoses.size() != _animSkeleton->getNumJoints()) {
// animations haven't fully loaded yet. // animations haven't fully loaded yet.
_relativePoses = _animSkeleton->getRelativeBindPoses(); _relativePoses = _animSkeleton->getRelativeDefaultPoses();
} }
_animVars.clearTriggers(); _animVars.clearTriggers();
for (auto& trigger : triggersOut) { for (auto& trigger : triggersOut) {
@ -781,27 +795,23 @@ void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, floa
} }
} }
static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const AnimPose& geometryHMDPose, void Rig::computeHeadNeckAnimVars(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut,
glm::vec3& headPositionOut, glm::quat& headOrientationOut, glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) const {
glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) {
// the input hmd values are in avatar space // the input hmd values are in avatar/rig space
// we need to transform them into bone space.
AnimPose hmdPose = geometryHMDPose;
const glm::vec3 hmdPosition = hmdPose.trans; const glm::vec3 hmdPosition = hmdPose.trans;
const glm::quat hmdOrientation = hmdPose.rot; const glm::quat hmdOrientation = hmdPose.rot;
// TODO: cache jointIndices // TODO: cache jointIndices
int rightEyeIndex = skeleton->nameToJointIndex("RightEye"); int rightEyeIndex = indexOfJoint("RightEye");
int leftEyeIndex = skeleton->nameToJointIndex("LeftEye"); int leftEyeIndex = indexOfJoint("LeftEye");
int headIndex = skeleton->nameToJointIndex("Head"); int headIndex = indexOfJoint("Head");
int neckIndex = skeleton->nameToJointIndex("Neck"); int neckIndex = indexOfJoint("Neck");
// Use absolute bindPose positions just in case the relBindPose have rotations we don't expect. glm::vec3 absRightEyePos = rightEyeIndex != -1 ? getAbsoluteDefaultPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS;
glm::vec3 absRightEyePos = rightEyeIndex != -1 ? skeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? getAbsoluteDefaultPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS;
glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? skeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; glm::vec3 absHeadPos = headIndex != -1 ? getAbsoluteDefaultPose(headIndex).trans : DEFAULT_HEAD_POS;
glm::vec3 absHeadPos = headIndex != -1 ? skeleton->getAbsoluteBindPose(headIndex).trans : DEFAULT_HEAD_POS; glm::vec3 absNeckPos = neckIndex != -1 ? getAbsoluteDefaultPose(neckIndex).trans : DEFAULT_NECK_POS;
glm::vec3 absNeckPos = neckIndex != -1 ? skeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS;
glm::vec3 absCenterEyePos = (absRightEyePos + absLeftEyePos) / 2.0f; glm::vec3 absCenterEyePos = (absRightEyePos + absLeftEyePos) / 2.0f;
glm::vec3 eyeOffset = absCenterEyePos - absHeadPos; glm::vec3 eyeOffset = absCenterEyePos - absHeadPos;
@ -816,18 +826,19 @@ static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const A
// neck // neck
neckPositionOut = hmdPosition - hmdOrientation * (headOffset + eyeOffset); neckPositionOut = hmdPosition - hmdOrientation * (headOffset + eyeOffset);
// slerp between bind orientation and hmdOrientation // slerp between default orientation and hmdOrientation
neckOrientationOut = safeMix(hmdOrientation, skeleton->getRelativeBindPose(skeleton->nameToJointIndex("Neck")).rot, 0.5f); neckOrientationOut = safeMix(hmdOrientation, _animSkeleton->getRelativeDefaultPose(neckIndex).rot, 0.5f);
} }
void Rig::updateNeckJoint(int index, const HeadParameters& params) { void Rig::updateNeckJoint(int index, const HeadParameters& params) {
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
if (params.isInHMD) { if (params.isInHMD) {
glm::vec3 headPos, neckPos; glm::vec3 headPos, neckPos;
glm::quat headRot, neckRot; glm::quat headRot, neckRot;
AnimPose hmdPose(glm::vec3(1.0f), avatarToGeometryNegZForward(params.localHeadOrientation), avatarToGeometry(params.localHeadPosition)); AnimPose hmdPose(glm::vec3(1.0f), params.localHeadOrientation * yFlip180, params.localHeadPosition);
computeHeadNeckAnimVars(_animSkeleton, hmdPose, headPos, headRot, neckPos, neckRot); computeHeadNeckAnimVars(hmdPose, headPos, headRot, neckPos, neckRot);
// debug rendering // debug rendering
#ifdef DEBUG_RENDERING #ifdef DEBUG_RENDERING
@ -852,7 +863,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
} else { } else {
_animVars.unset("headPosition"); _animVars.unset("headPosition");
_animVars.set("headRotation", avatarToGeometryNegZForward(params.localHeadOrientation)); _animVars.set("headRotation", params.localHeadOrientation * yFlip180);
_animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly); _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly);
_animVars.set("headType", (int)IKTarget::Type::RotationOnly); _animVars.set("headType", (int)IKTarget::Type::RotationOnly);
_animVars.unset("neckPosition"); _animVars.unset("neckPosition");
@ -886,31 +897,12 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
*/ */
} }
glm::vec3 Rig::avatarToGeometry(const glm::vec3& pos) const {
glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
AnimPose geometryToAvatarTransform = AnimPose(glm::vec3(1), yFlip, glm::vec3()) * _modelOffset * _geometryOffset;
glm::vec3 result = geometryToAvatarTransform.inverse() * pos;
return result;
}
glm::quat Rig::avatarToGeometryNegZForward(const glm::quat& quat) const {
AnimPose yFlip(glm::vec3(1), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3());
AnimPose geometryToAvatarTransform = yFlip * _modelOffset * _geometryOffset;
return (geometryToAvatarTransform.inverse() * AnimPose(glm::vec3(1), quat, glm::vec3()) * yFlip).rot;
}
glm::quat Rig::avatarToGeometryZForward(const glm::quat& quat) const {
AnimPose yFlip(glm::vec3(1), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3());
AnimPose geometryToAvatarTransform = yFlip * _modelOffset * _geometryOffset;
return (geometryToAvatarTransform.inverse() * AnimPose(glm::vec3(1), quat, glm::vec3())).rot;
}
void Rig::updateFromHandParameters(const HandParameters& params, float dt) { void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
if (_animSkeleton && _animNode) { if (_animSkeleton && _animNode) {
if (params.isLeftEnabled) { if (params.isLeftEnabled) {
_animVars.set("leftHandPosition", avatarToGeometry(params.leftPosition)); _animVars.set("leftHandPosition", params.leftPosition);
_animVars.set("leftHandRotation", avatarToGeometryZForward(params.leftOrientation)); _animVars.set("leftHandRotation", params.leftOrientation);
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
} else { } else {
_animVars.unset("leftHandPosition"); _animVars.unset("leftHandPosition");
@ -918,8 +910,8 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition); _animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
} }
if (params.isRightEnabled) { if (params.isRightEnabled) {
_animVars.set("rightHandPosition", avatarToGeometry(params.rightPosition)); _animVars.set("rightHandPosition", params.rightPosition);
_animVars.set("rightHandRotation", avatarToGeometryZForward(params.rightOrientation)); _animVars.set("rightHandRotation", params.rightOrientation);
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
} else { } else {
_animVars.unset("rightHandPosition"); _animVars.unset("rightHandPosition");
@ -986,7 +978,7 @@ void Rig::initAnimGraph(const QUrl& url) {
bool Rig::getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const { bool Rig::getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const {
if (_animSkeleton && _rootJointIndex >= 0) { if (_animSkeleton && _rootJointIndex >= 0) {
modelRegistrationPointOut = _geometryOffset * -_animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; modelRegistrationPointOut = _geometryOffset * -_animSkeleton->getAbsoluteDefaultPose(_rootJointIndex).trans;
return true; return true;
} else { } else {
return false; return false;

View file

@ -85,10 +85,9 @@ public:
void reset(const FBXGeometry& geometry); void reset(const FBXGeometry& geometry);
bool jointStatesEmpty(); bool jointStatesEmpty();
int getJointStateCount() const; int getJointStateCount() const;
int indexOfJoint(const QString& jointName); int indexOfJoint(const QString& jointName) const;
void setModelOffset(const glm::mat4& modelOffset); void setModelOffset(const glm::mat4& modelOffset);
const AnimPose& getGeometryOffset() const { return _geometryOffset; }
bool getJointStateRotation(int index, glm::quat& rotation) const; bool getJointStateRotation(int index, glm::quat& rotation) const;
bool getJointStateTranslation(int index, glm::vec3& translation) const; bool getJointStateTranslation(int index, glm::vec3& translation) const;
@ -146,6 +145,8 @@ public:
const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; } const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; }
AnimPose getAbsoluteDefaultPose(int index) const; // avatar space
void copyJointsIntoJointData(QVector<JointData>& jointDataVec) const; void copyJointsIntoJointData(QVector<JointData>& jointDataVec) const;
void copyJointsFromJointData(const QVector<JointData>& jointDataVec); void copyJointsFromJointData(const QVector<JointData>& jointDataVec);
@ -156,15 +157,13 @@ public:
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
void updateNeckJoint(int index, const HeadParameters& params); void updateNeckJoint(int index, const HeadParameters& params);
void computeHeadNeckAnimVars(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut,
glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) const;
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
void calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const; void calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const;
void computeEyesInRootFrame(const AnimPoseVec& poses); void computeEyesInRootFrame(const AnimPoseVec& poses);
glm::vec3 avatarToGeometry(const glm::vec3& pos) const;
glm::quat avatarToGeometryZForward(const glm::quat& quat) const;
glm::quat avatarToGeometryNegZForward(const glm::quat& quat) const;
AnimPose _modelOffset; // model to avatar space (without 180 flip) AnimPose _modelOffset; // model to avatar space (without 180 flip)
AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets) AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets)
AnimPoseVec _relativePoses; // geometry space relative to parent. AnimPoseVec _relativePoses; // geometry space relative to parent.
@ -172,6 +171,9 @@ public:
AnimPoseVec _overridePoses; // geometry space relative to parent. AnimPoseVec _overridePoses; // geometry space relative to parent.
std::vector<bool> _overrideFlags; std::vector<bool> _overrideFlags;
glm::mat4 _geometryToRigTransform;
glm::mat4 _rigToGeometryTransform;
int _rootJointIndex { -1 }; int _rootJointIndex { -1 };
int _leftHandJointIndex { -1 }; int _leftHandJointIndex { -1 };

View file

@ -10,7 +10,7 @@
// //
#include "GLMHelpers.h" #include "GLMHelpers.h"
#include <glm/gtc/matrix_transform.hpp>
#include "NumericalConstants.h" #include "NumericalConstants.h"
const vec3 Vectors::UNIT_X{ 1.0f, 0.0f, 0.0f }; const vec3 Vectors::UNIT_X{ 1.0f, 0.0f, 0.0f };
@ -276,6 +276,18 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
0.5f * sqrtf(z2) * (upper[0][1] >= upper[1][0] ? 1.0f : -1.0f))); 0.5f * sqrtf(z2) * (upper[0][1] >= upper[1][0] ? 1.0f : -1.0f)));
} }
glm::quat glmExtractRotation(const glm::mat4& matrix) {
glm::vec3 scale = extractScale(matrix);
float maxScale = std::max(std::max(scale.x, scale.y), scale.z);
if (maxScale > 1.01f || maxScale <= 0.99f) {
// quat_cast doesn't work so well with scaled matrices, so cancel it out.
glm::mat4 tmp = glm::scale(matrix, 1.0f / scale);
return glm::normalize(glm::quat_cast(tmp));
} else {
return glm::normalize(glm::quat_cast(matrix));
}
}
glm::vec3 extractScale(const glm::mat4& matrix) { glm::vec3 extractScale(const glm::mat4& matrix) {
return glm::vec3(glm::length(matrix[0]), glm::length(matrix[1]), glm::length(matrix[2])); return glm::vec3(glm::length(matrix[0]), glm::length(matrix[1]), glm::length(matrix[2]));
} }

View file

@ -131,6 +131,7 @@ glm::vec3 extractTranslation(const glm::mat4& matrix);
void setTranslation(glm::mat4& matrix, const glm::vec3& translation); void setTranslation(glm::mat4& matrix, const glm::vec3& translation);
glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal = false); glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal = false);
glm::quat glmExtractRotation(const glm::mat4& matrix);
glm::vec3 extractScale(const glm::mat4& matrix); glm::vec3 extractScale(const glm::mat4& matrix);