Added head target to AnimGraph IK node.

* In HMD mode head orientation and position is set.
* When not in HMD only orientation is set, position should
  default to the underlying pose position.
This commit is contained in:
Anthony J. Thibault 2015-09-11 09:48:48 -07:00
parent 9a9838fd0d
commit 7996a02bd8
9 changed files with 101 additions and 33 deletions

View file

@ -1276,7 +1276,8 @@ void MyAvatar::initAnimGraph() {
//
// or run a local web-server
// python -m SimpleHTTPServer&
auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/e58e0a24cc341ad5d060/raw/2a994bef7726ce8e9efcee7622b8b1a1b6b67490/ik-avatar.json");
// auto graphUrl = QUrl("http://localhost:8000/avatar.json");
auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/e58e0a24cc341ad5d060/raw/8f824da2908fd89ad1befadd1d8f5d7b3b6efa66/ik-avatar.json");
_rig->initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry());
}

View file

@ -116,6 +116,17 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
params.leanForward = head->getFinalLeanForward();
params.torsoTwist = head->getTorsoTwist();
params.localHeadOrientation = head->getFinalOrientationInLocalFrame();
params.localHeadPitch = head->getFinalPitch();
params.localHeadYaw = head->getFinalYaw();
params.localHeadRoll = head->getFinalRoll();
params.isInHMD = qApp->getAvatarUpdater()->isHMDMode();
// get HMD position from sensor space into world space, and back into model space
glm::mat4 worldToModel = glm::inverse(createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()));
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
glm::vec3 hmdPosition = glm::angleAxis((float)M_PI, yAxis) * transformPoint(worldToModel * myAvatar->getSensorToWorldMatrix(), myAvatar->getHMDSensorPosition());
params.localHeadPosition = hmdPosition;
params.worldHeadOrientation = head->getFinalOrientationInWorldFrame();
params.eyeLookAt = head->getLookAtPosition();
params.eyeSaccade = head->getSaccade();

View file

@ -40,7 +40,7 @@ void AnimInverseKinematics::loadPoses(const AnimPoseVec& poses) {
void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) const {
int numJoints = (int)_relativePoses.size();
absolutePoses.clear();
absolutePoses.reserve(numJoints);
absolutePoses.resize(numJoints);
assert(numJoints <= _skeleton->getNumJoints());
for (int i = 0; i < numJoints; ++i) {
int parentIndex = _skeleton->getParentIndex(i);

View file

@ -149,6 +149,7 @@ public:
void set(const std::string& key, const glm::quat& value) { _map[key] = AnimVariant(value); }
void set(const std::string& key, const glm::mat4& value) { _map[key] = AnimVariant(value); }
void set(const std::string& key, const std::string& value) { _map[key] = AnimVariant(value); }
void unset(const std::string& key) { _map.erase(key); }
void setTrigger(const std::string& key) { _triggers.insert(key); }
void clearTriggers() { _triggers.clear(); }

View file

@ -30,6 +30,9 @@ void Rig::HeadParameters::dump() const {
qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta);
axis = glm::axis(worldHeadOrientation);
theta = glm::angle(worldHeadOrientation);
qCDebug(animation, " localHead pitch = %.5f, yaw = %.5f, roll = %.5f", (double)localHeadPitch, (double)localHeadYaw, (double)localHeadRoll);
qCDebug(animation, " localHeadPosition = (%.5f, %.5f, %.5f)", (double)localHeadPosition.x, (double)localHeadPosition.y, (double)localHeadPosition.z);
qCDebug(animation, " isInHMD = %s", isInHMD ? "true" : "false");
qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta);
axis = glm::axis(modelRotation);
theta = glm::angle(modelRotation);
@ -953,56 +956,71 @@ void Rig::updateFromHeadParameters(const HeadParameters& params) {
if (params.enableLean) {
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
}
updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist);
updateNeckJoint(params.neckJointIndex, params);
updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation,
params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
}
static const glm::vec3 X_AXIS(1.0f, 0.0f, 0.0f);
static const glm::vec3 Y_AXIS(0.0f, 1.0f, 0.0f);
static const glm::vec3 Z_AXIS(0.0f, 0.0f, 1.0f);
void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) {
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
if (_enableAnimGraph && _animSkeleton) {
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, zAxis) *
glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, xAxis) *
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, yAxis));
glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, Z_AXIS) *
glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, X_AXIS) *
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, Y_AXIS));
_animVars.set("lean", absRot);
} else if (!_enableAnimGraph) {
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
// get the rotation axes in joint space and use them to adjust the rotation
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
glm::quat inverse = glm::inverse(parentState.getRotation() * getJointDefaultRotationInParentFrame(index));
setJointRotationInConstrainedFrame(index,
glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * zAxis) *
glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * xAxis) *
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * yAxis) *
glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * Z_AXIS) *
glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * X_AXIS) *
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * Y_AXIS) *
getJointState(index).getDefaultRotation(), DEFAULT_PRIORITY);
}
}
}
void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist) {
void Rig::updateNeckJoint(int index, const HeadParameters& params) {
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
auto& state = _jointStates[index];
auto& parentState = _jointStates[state.getParentIndex()];
if (_enableAnimGraph && _animSkeleton) {
// the params.localHeadOrientation is composed incorrectly, so re-compose it correctly from pitch, yaw and roll.
glm::quat realLocalHeadOrientation = (glm::angleAxis(glm::radians(-params.localHeadRoll), Z_AXIS) *
glm::angleAxis(glm::radians(params.localHeadYaw), Y_AXIS) *
glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS));
_animVars.set("headRotation", realLocalHeadOrientation);
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(glm::quat());
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) *
state.getPreTransform() * glm::mat4_cast(state.getPreRotation())));
glm::vec3 pitchYawRoll = safeEulerAngles(localHeadOrientation);
glm::vec3 lean = glm::radians(glm::vec3(leanForward, torsoTwist, leanSideways));
pitchYawRoll -= lean;
setJointRotationInConstrainedFrame(index,
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) *
glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) *
glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) *
state.getDefaultRotation(), DEFAULT_PRIORITY);
auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans;
if (params.isInHMD) {
_animVars.set("headPosition", params.localHeadPosition + rootTrans);
} else {
_animVars.unset("headPosition");
}
} else if (!_enableAnimGraph) {
auto& state = _jointStates[index];
auto& parentState = _jointStates[state.getParentIndex()];
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(glm::quat());
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) *
state.getPreTransform() * glm::mat4_cast(state.getPreRotation())));
glm::vec3 pitchYawRoll = safeEulerAngles(params.localHeadOrientation);
glm::vec3 lean = glm::radians(glm::vec3(params.leanForward, params.torsoTwist, params.leanSideways));
pitchYawRoll -= lean;
setJointRotationInConstrainedFrame(index,
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * Z_AXIS)) *
glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * Y_AXIS)) *
glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * X_AXIS)) *
state.getDefaultRotation(), DEFAULT_PRIORITY);
}
}
}

View file

@ -59,10 +59,15 @@ public:
bool enableLean = false;
glm::quat modelRotation = glm::quat();
glm::quat localHeadOrientation = glm::quat();
float localHeadPitch = 0.0f; // degrees
float localHeadYaw = 0.0f; // degrees
float localHeadRoll = 0.0f; // degrees
glm::vec3 localHeadPosition = glm::vec3(0);
bool isInHMD = false;
glm::quat worldHeadOrientation = glm::quat();
glm::vec3 eyeLookAt = glm::vec3(); // world space
glm::vec3 eyeSaccade = glm::vec3(); // world space
glm::vec3 modelTranslation = glm::vec3();
glm::vec3 modelTranslation = glm::vec3(0);
int leanJointIndex = -1;
int neckJointIndex = -1;
int leftEyeJointIndex = -1;
@ -177,7 +182,7 @@ public:
protected:
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
void updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist);
void updateNeckJoint(int index, const HeadParameters& params);
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
QVector<JointState> _jointStates;

View file

@ -176,6 +176,14 @@ void AnimDebugDraw::removePoses(const std::string& key) {
_poses.erase(key);
}
void AnimDebugDraw::addPose(const std::string& key, const AnimPose& pose, const AnimPose& rootPose) {
_singlePoses[key] = SinglePoseInfo(pose, rootPose);
}
void AnimDebugDraw::removePose(const std::string& key) {
_singlePoses.erase(key);
}
static const uint32_t red = toRGBA(255, 0, 0, 255);
static const uint32_t green = toRGBA(0, 255, 0, 255);
static const uint32_t blue = toRGBA(0, 0, 255, 255);
@ -333,6 +341,7 @@ void AnimDebugDraw::update() {
const size_t VERTICES_PER_LINK = 8 * 2;
const float BONE_RADIUS = 0.01f; // 1 cm
const float POSE_RADIUS = 0.1f; // 10 cm
// figure out how many verts we will need.
int numVerts = 0;
@ -371,6 +380,8 @@ void AnimDebugDraw::update() {
}
}
numVerts += _singlePoses.size() * VERTICES_PER_BONE;
data._vertexBuffer->resize(sizeof(Vertex) * numVerts);
Vertex* verts = (Vertex*)data._vertexBuffer->editData();
Vertex* v = verts;
@ -475,6 +486,13 @@ void AnimDebugDraw::update() {
}
}
for (auto& iter : _singlePoses) {
AnimPose pose = std::get<0>(iter.second);
AnimPose rootPose = std::get<1>(iter.second);
const float radius = POSE_RADIUS / (pose.scale.x * rootPose.scale.x);
addBone(rootPose, pose, radius, v);
}
assert(numVerts == (v - verts));
data._indexBuffer->resize(sizeof(uint16_t) * numVerts);

View file

@ -27,15 +27,22 @@ public:
AnimDebugDraw();
~AnimDebugDraw();
// draw a skeleton bind pose
void addSkeleton(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color);
void removeSkeleton(const std::string& key);
// draw the interal poses for a specific animNode
void addAnimNode(const std::string& key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color);
void removeAnimNode(const std::string& key);
// draw a set of poses with a skeleton
void addPoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color);
void removePoses(const std::string& key);
// draw a single pose
void addPose(const std::string& key, const AnimPose& rootPose, const AnimPose& pose);
void removePose(const std::string& key);
void update();
protected:
@ -48,10 +55,12 @@ protected:
typedef std::tuple<AnimSkeleton::ConstPointer, AnimPose, glm::vec4> SkeletonInfo;
typedef std::tuple<AnimNode::ConstPointer, AnimPose, glm::vec4> AnimNodeInfo;
typedef std::tuple<AnimSkeleton::ConstPointer, AnimPoseVec, AnimPose, glm::vec4> PosesInfo;
typedef std::tuple<AnimPose, AnimPose> SinglePoseInfo;
std::unordered_map<std::string, SkeletonInfo> _skeletons;
std::unordered_map<std::string, AnimNodeInfo> _animNodes;
std::unordered_map<std::string, PosesInfo> _poses;
std::unordered_map<std::string, SinglePoseInfo> _singlePoses;
// no copies
AnimDebugDraw(const AnimDebugDraw&) = delete;

View file

@ -22,6 +22,11 @@
"jointName": "LeftHand",
"positionVar": "leftHandPosition",
"rotationVar": "leftHandRotation"
},
{
"jointName": "Head",
"positionVar": "headPosition",
"rotationVar": "headRotation"
}
]
},