Merge pull request from hyperlogic/tony/lean-threshold

Better Head IK when in HMD mode
This commit is contained in:
Howard Stearns 2015-09-23 16:57:02 -07:00
commit 14bfdebdca
14 changed files with 265 additions and 49 deletions

View file

@ -23,6 +23,11 @@
"positionVar": "leftHandPosition",
"rotationVar": "leftHandRotation"
},
{
"jointName": "Neck",
"positionVar": "neckPosition",
"rotationVar": "neckRotation"
},
{
"jointName": "Head",
"positionVar": "headPosition",

View file

@ -794,6 +794,8 @@ void Application::cleanupBeforeQuit() {
DependencyManager::get<EyeTracker>()->setEnabled(false, true);
#endif
AnimDebugDraw::getInstance().shutdown();
if (_keyboardFocusHighlightID > 0) {
getOverlays().deleteOverlay(_keyboardFocusHighlightID);
_keyboardFocusHighlightID = -1;

View file

@ -47,6 +47,7 @@
#include "Recorder.h"
#include "Util.h"
#include "InterfaceLogging.h"
#include "DebugDraw.h"
using namespace std;
@ -1333,7 +1334,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
// bones are aligned such that z is forward, not -z.
glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f));
AnimPose xform(glm::vec3(1), rotY180 * getOrientation(), getPosition());
AnimPose xform(glm::vec3(1), getOrientation() * rotY180, getPosition());
if (_enableDebugDrawBindPose && _debugDrawSkeleton) {
glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f);
@ -1358,6 +1359,9 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
}
}
DebugDraw::getInstance().updateMyAvatarPos(getPosition());
DebugDraw::getInstance().updateMyAvatarRot(getOrientation());
if (shouldDrawHead != _prevShouldDrawHead) {
_skeletonModel.setCauterizeBones(!shouldDrawHead);
}

View file

@ -21,6 +21,7 @@
#include "SkeletonModel.h"
#include "Util.h"
#include "InterfaceLogging.h"
#include "AnimDebugDraw.h"
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) :
Model(rig, parent),
@ -126,19 +127,35 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
headParams.leanSideways = head->getFinalLeanSideways();
headParams.leanForward = head->getFinalLeanForward();
headParams.torsoTwist = head->getTorsoTwist();
headParams.localHeadOrientation = head->getFinalOrientationInLocalFrame();
headParams.localHeadPitch = head->getFinalPitch();
headParams.localHeadYaw = head->getFinalYaw();
headParams.localHeadRoll = head->getFinalRoll();
headParams.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());
headParams.localHeadPosition = hmdPosition;
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), myAvatar->getOrientation(), myAvatar->getPosition());
glm::mat4 worldToAvatar = glm::inverse((glm::mat4)avatarToWorld);
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;
headParams.worldHeadOrientation = extractRotation(worldHMDMat);
} else {
headParams.isInHMD = false;
// We don't have a valid localHeadPosition.
headParams.localHeadOrientation = head->getFinalOrientationInLocalFrame();
headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame();
}
headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame();
headParams.eyeLookAt = head->getLookAtPosition();
headParams.eyeSaccade = head->getSaccade();
headParams.leanJointIndex = geometry.leanJointIndex;

View file

@ -29,6 +29,12 @@ const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, flo
const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
_alpha = animVars.lookup(_alphaVar, _alpha);
_poses = underPoses;
if (underPoses.size() == 0) {
return _poses;
}
for (auto& jointVar : _jointVars) {
if (!jointVar.hasPerformedJointLookup) {
jointVar.jointIndex = _skeleton->nameToJointIndex(jointVar.jointName);

View file

@ -61,7 +61,7 @@ public:
// hierarchy accessors
void addChild(Pointer child) { _children.push_back(child); }
void removeChild(Pointer child);
Pointer getChild(int i) const;
int getChildCount() const { return (int)_children.size(); }

View file

@ -22,7 +22,7 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
AnimPose::AnimPose(const glm::mat4& mat) {
scale = extractScale(mat);
rot = extractRotation(mat);
rot = glm::normalize(glm::quat_cast(mat));
trans = extractTranslation(mat);
}

View file

@ -52,7 +52,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl
if (_duringInterp) {
_alpha += _alphaVel * dt;
if (_alpha < 1.0f) {
if (_poses.size() > 0) {
if (_poses.size() > 0 && _nextPoses.size() > 0 && _prevPoses.size() > 0) {
::blend(_poses.size(), &_prevPoses[0], &_nextPoses[0], _alpha, &_poses[0]);
}
} else {
@ -77,8 +77,6 @@ void AnimStateMachine::addState(State::Pointer state) {
void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointer desiredState) {
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID();
const float FRAMES_PER_SECOND = 30.0f;
auto prevStateNode = _currentState->getNode();
@ -96,6 +94,8 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe
Triggers triggers;
_nextPoses = nextStateNode->evaluate(animVars, dt, triggers);
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget;
_currentState = desiredState;
}

View file

@ -14,9 +14,11 @@
#include <glm/gtx/vector_angle.hpp>
#include <queue>
#include "NumericalConstants.h"
#include "AnimationHandle.h"
#include "AnimationLogging.h"
#include "AnimSkeleton.h"
#include "DebugDraw.h"
#include "Rig.h"
@ -984,21 +986,97 @@ void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, floa
}
}
static AnimPose avatarToBonePose(AnimPose pose, AnimSkeleton::ConstPointer skeleton) {
AnimPose rootPose = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Hips"));
AnimPose rotY180(glm::vec3(1), glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3(0));
return rootPose * rotY180 * pose;
}
static AnimPose boneToAvatarPose(AnimPose pose, AnimSkeleton::ConstPointer skeleton) {
AnimPose rootPose = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Hips"));
AnimPose rotY180(glm::vec3(1), glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3(0));
return (rootPose * rotY180).inverse() * pose;
}
static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const AnimPose& avatarHMDPose,
glm::vec3& headPositionOut, glm::quat& headOrientationOut,
glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) {
// the input hmd values are in avatar space
// we need to transform them into bone space.
AnimPose hmdPose = avatarToBonePose(avatarHMDPose, skeleton);
const glm::vec3 hmdPosition = hmdPose.trans;
const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f));
const glm::quat hmdOrientation = hmdPose.rot * rotY180; // rotY180 will make z forward not -z
// TODO: cache jointIndices
// Use absolute bindPose positions just in case the relBindPose have rotations we don't expect.
glm::vec3 absRightEyePos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("RightEye")).trans;
glm::vec3 absLeftEyePos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("LeftEye")).trans;
glm::vec3 absHeadPos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Head")).trans;
glm::vec3 absNeckPos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Neck")).trans;
glm::vec3 absCenterEyePos = (absRightEyePos + absLeftEyePos) / 2.0f;
glm::vec3 eyeOffset = absCenterEyePos - absHeadPos;
glm::vec3 headOffset = absHeadPos - absNeckPos;
// apply simplistic head/neck model
// head
headPositionOut = hmdPosition - hmdOrientation * eyeOffset;
headOrientationOut = hmdOrientation;
// neck
neckPositionOut = hmdPosition - hmdOrientation * (headOffset + eyeOffset);
// slerp between bind orientation and hmdOrientation
neckOrientationOut = safeMix(hmdOrientation, skeleton->getRelativeBindPose(skeleton->nameToJointIndex("Neck")).rot, 0.5f);
}
void Rig::updateNeckJoint(int index, const HeadParameters& params) {
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
if (_enableAnimGraph && _animSkeleton && _animNode) {
// 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);
if (_enableAnimGraph && _animSkeleton) {
if (params.isInHMD) {
int headIndex = _animSkeleton->nameToJointIndex("Head");
AnimPose rootPose = _animNode->getRootPose(headIndex);
_animVars.set("headPosition", rootPose.trans + params.localHeadPosition); // rootPose.rot is handled in params?d
glm::vec3 headPos, neckPos;
glm::quat headRot, neckRot;
AnimPose avatarHMDPose(glm::vec3(1), params.localHeadOrientation, params.localHeadPosition);
computeHeadNeckAnimVars(_animSkeleton, avatarHMDPose, headPos, headRot, neckPos, neckRot);
// debug rendering
/*
const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f);
const glm::vec4 green(0.0f, 1.0f, 0.0f, 1.0f);
// transform from bone into avatar space
AnimPose headPose(glm::vec3(1), headRot, headPos);
headPose = boneToAvatarPose(headPose, _animSkeleton);
DebugDraw::getInstance().addMyAvatarMarker("headTarget", headPose.rot, headPose.trans, red);
// transform from bone into avatar space
AnimPose neckPose(glm::vec3(1), neckRot, neckPos);
neckPose = boneToAvatarPose(neckPose, _animSkeleton);
DebugDraw::getInstance().addMyAvatarMarker("neckTarget", neckPose.rot, neckPose.trans, green);
*/
_animVars.set("headPosition", headPos);
_animVars.set("headRotation", headRot);
_animVars.set("neckPosition", neckPos);
_animVars.set("neckRotation", neckRot);
} else {
// 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.unset("headPosition");
_animVars.set("headRotation", realLocalHeadOrientation);
_animVars.unset("neckPosition");
_animVars.unset("neckRotation");
}
} else if (!_enableAnimGraph) {

View file

@ -13,6 +13,7 @@
#include "AbstractViewStateInterface.h"
#include "RenderUtilsLogging.h"
#include "GLMHelpers.h"
#include "DebugDraw.h"
#include "AnimDebugDraw.h"
@ -67,13 +68,9 @@ namespace render {
}
}
static AnimDebugDraw* instance = nullptr;
AnimDebugDraw& AnimDebugDraw::getInstance() {
if (!instance) {
instance = new AnimDebugDraw();
}
return *instance;
static AnimDebugDraw instance;
return instance;
}
static uint32_t toRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
@ -144,6 +141,10 @@ AnimDebugDraw::AnimDebugDraw() :
}
AnimDebugDraw::~AnimDebugDraw() {
}
void AnimDebugDraw::shutdown() {
// remove renderItem from main 3d scene.
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
if (scene && _itemID) {
render::PendingChanges pendingChanges;
@ -176,14 +177,6 @@ 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);
@ -380,7 +373,11 @@ void AnimDebugDraw::update() {
}
}
numVerts += _singlePoses.size() * VERTICES_PER_BONE;
// count marker verts from shared DebugDraw singleton
auto markerMap = DebugDraw::getInstance().getMarkerMap();
numVerts += markerMap.size() * VERTICES_PER_BONE;
auto myAvatarMarkerMap = DebugDraw::getInstance().getMyAvatarMarkerMap();
numVerts += myAvatarMarkerMap.size() * VERTICES_PER_BONE;
data._vertexBuffer->resize(sizeof(Vertex) * numVerts);
Vertex* verts = (Vertex*)data._vertexBuffer->editData();
@ -486,11 +483,22 @@ 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);
// draw markers from shared DebugDraw singleton
for (auto& iter : markerMap) {
glm::quat rot = std::get<0>(iter.second);
glm::vec3 pos = std::get<1>(iter.second);
glm::vec4 color = std::get<2>(iter.second); // TODO: currently ignored.
const float radius = POSE_RADIUS;
addBone(AnimPose::identity, AnimPose(glm::vec3(1), rot, pos), radius, v);
}
AnimPose myAvatarPose(glm::vec3(1), DebugDraw::getInstance().getMyAvatarRot(), DebugDraw::getInstance().getMyAvatarPos());
for (auto& iter : myAvatarMarkerMap) {
glm::quat rot = std::get<0>(iter.second);
glm::vec3 pos = std::get<1>(iter.second);
glm::vec4 color = std::get<2>(iter.second); // TODO: currently ignored.
const float radius = POSE_RADIUS;
addBone(myAvatarPose, AnimPose(glm::vec3(1), rot, pos), radius, v);
}
assert(numVerts == (v - verts));

View file

@ -27,6 +27,8 @@ public:
AnimDebugDraw();
~AnimDebugDraw();
void shutdown();
// 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);
@ -39,10 +41,6 @@ public:
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:
@ -55,12 +53,10 @@ 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

@ -0,0 +1,40 @@
//
// DebugDraw.cpp
//
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DebugDraw.h"
DebugDraw& DebugDraw::getInstance() {
static DebugDraw instance;
return instance;
}
DebugDraw::DebugDraw() {
}
DebugDraw::~DebugDraw() {
}
void DebugDraw::addMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color) {
_markers[key] = MarkerInfo(rotation, position, color);
}
void DebugDraw::removeMarker(const std::string& key) {
_markers.erase(key);
}
void DebugDraw::addMyAvatarMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color) {
_myAvatarMarkers[key] = MarkerInfo(rotation, position, color);
}
void DebugDraw::removeMyAvatarMarker(const std::string& key) {
_myAvatarMarkers.erase(key);
}

View file

@ -0,0 +1,55 @@
//
// DebugDraw.h
//
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_DebugDraw_h
#define hifi_DebugDraw_h
#include <unordered_map>
#include <tuple>
#include <string>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
class DebugDraw {
public:
static DebugDraw& getInstance();
DebugDraw();
~DebugDraw();
// world space maker
void addMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color);
void removeMarker(const std::string& key);
// myAvatar relative marker
void addMyAvatarMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color);
void removeMyAvatarMarker(const std::string& key);
using MarkerInfo = std::tuple<glm::quat, glm::vec3, glm::vec4>;
using MarkerMap = std::unordered_map<std::string, MarkerInfo>;
//
// accessors used by renderer
//
const MarkerMap& getMarkerMap() const { return _markers; }
const MarkerMap& getMyAvatarMarkerMap() const { return _myAvatarMarkers; }
void updateMyAvatarPos(const glm::vec3& pos) { _myAvatarPos = pos; }
const glm::vec3& getMyAvatarPos() const { return _myAvatarPos; }
void updateMyAvatarRot(const glm::quat& rot) { _myAvatarRot = rot; }
const glm::quat& getMyAvatarRot() const { return _myAvatarRot; }
protected:
MarkerMap _markers;
MarkerMap _myAvatarMarkers;
glm::quat _myAvatarRot;
glm::vec3 _myAvatarPos;
};
#endif // hifi_DebugDraw_h

View file

@ -23,6 +23,11 @@
"positionVar": "leftHandPosition",
"rotationVar": "leftHandRotation"
},
{
"jointName": "Neck",
"positionVar": "neckPosition",
"rotationVar": "neckRotation"
},
{
"jointName": "Head",
"positionVar": "headPosition",