mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Leg IK fixes
Fixes for raising legs and sometimes squatting while in HMD mode.
This commit is contained in:
parent
8201997a16
commit
401995fb06
34 changed files with 3524 additions and 1205 deletions
File diff suppressed because it is too large
Load diff
1228
interface/resources/avatar/old-avatar-animation.json
Normal file
1228
interface/resources/avatar/old-avatar-animation.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -24,7 +24,7 @@ AnimBlendLinear::~AnimBlendLinear() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||
|
||||
|
@ -43,6 +43,9 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con
|
|||
|
||||
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt);
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
@ -51,7 +54,7 @@ const AnimPoseVec& AnimBlendLinear::getPosesInternal() const {
|
|||
return _poses;
|
||||
}
|
||||
|
||||
void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
||||
void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||
size_t prevPoseIndex, size_t nextPoseIndex, float dt) {
|
||||
if (prevPoseIndex == nextPoseIndex) {
|
||||
// this can happen if alpha is on an integer boundary
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
AnimBlendLinear(const QString& id, float alpha);
|
||||
virtual ~AnimBlendLinear() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||
|
||||
|
@ -38,7 +38,7 @@ protected:
|
|||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
||||
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
||||
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||
size_t prevPoseIndex, size_t nextPoseIndex, float dt);
|
||||
|
||||
AnimPoseVec _poses;
|
||||
|
|
|
@ -26,7 +26,7 @@ AnimBlendLinearMove::~AnimBlendLinearMove() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
assert(_children.size() == _characteristicSpeeds.size());
|
||||
|
||||
|
@ -54,6 +54,9 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
|
|||
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
||||
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
@ -62,7 +65,7 @@ const AnimPoseVec& AnimBlendLinearMove::getPosesInternal() const {
|
|||
return _poses;
|
||||
}
|
||||
|
||||
void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
||||
void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||
size_t prevPoseIndex, size_t nextPoseIndex,
|
||||
float prevDeltaTime, float nextDeltaTime) {
|
||||
if (prevPoseIndex == nextPoseIndex) {
|
||||
|
@ -82,7 +85,7 @@ void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVar
|
|||
}
|
||||
|
||||
void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
|
||||
float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut) {
|
||||
float* prevDeltaTimeOut, float* nextDeltaTimeOut, AnimVariantMap& triggersOut) {
|
||||
|
||||
const float FRAMES_PER_SECOND = 30.0f;
|
||||
auto prevClipNode = std::dynamic_pointer_cast<AnimClip>(_children[prevPoseIndex]);
|
||||
|
@ -109,7 +112,7 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn
|
|||
|
||||
// detect loop trigger events
|
||||
if (_phase >= 1.0f) {
|
||||
triggersOut.push_back(_id + "Loop");
|
||||
triggersOut.setTrigger(_id + "Loop");
|
||||
_phase = glm::fract(_phase);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ public:
|
|||
AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector<float>& characteristicSpeeds);
|
||||
virtual ~AnimBlendLinearMove() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||
void setDesiredSpeedVar(const QString& desiredSpeedVar) { _desiredSpeedVar = desiredSpeedVar; }
|
||||
|
@ -48,12 +48,12 @@ protected:
|
|||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
||||
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
||||
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||
size_t prevPoseIndex, size_t nextPoseIndex,
|
||||
float prevDeltaTime, float nextDeltaTime);
|
||||
|
||||
void setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
|
||||
float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut);
|
||||
float* prevDeltaTimeOut, float* nextDeltaTimeOut, AnimVariantMap& triggersOut);
|
||||
|
||||
virtual void setCurrentFrameInternal(float frame) override;
|
||||
|
||||
|
|
159
libraries/animation/src/AnimChain.h
Normal file
159
libraries/animation/src/AnimChain.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
//
|
||||
// AnimChain.h
|
||||
//
|
||||
// Created by Anthony J. Thibault on 7/16/2018.
|
||||
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// 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_AnimChain
|
||||
#define hifi_AnimChain
|
||||
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <DebugDraw.h>
|
||||
|
||||
template <int N>
|
||||
class AnimChainT {
|
||||
|
||||
public:
|
||||
AnimChainT() {}
|
||||
|
||||
AnimChainT(const AnimChainT& orig) {
|
||||
_top = orig._top;
|
||||
for (int i = 0; i < _top; i++) {
|
||||
_chain[i] = orig._chain[i];
|
||||
}
|
||||
}
|
||||
|
||||
AnimChainT& operator=(const AnimChainT& orig) {
|
||||
_top = orig._top;
|
||||
for (int i = 0; i < _top; i++) {
|
||||
_chain[i] = orig._chain[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool buildFromRelativePoses(const AnimSkeleton::ConstPointer& skeleton, const AnimPoseVec& relativePoses, int tipIndex) {
|
||||
_top = 0;
|
||||
// iterate through the skeleton parents, from the tip to the base, copying over relativePoses into the chain.
|
||||
for (int jointIndex = tipIndex; jointIndex != -1; jointIndex = skeleton->getParentIndex(jointIndex)) {
|
||||
if (_top >= N) {
|
||||
assert(chainTop < N);
|
||||
// stack overflow
|
||||
return false;
|
||||
}
|
||||
_chain[_top].relativePose = relativePoses[jointIndex];
|
||||
_chain[_top].jointIndex = jointIndex;
|
||||
_chain[_top].dirty = true;
|
||||
_top++;
|
||||
}
|
||||
|
||||
buildDirtyAbsolutePoses();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const AnimPose& getAbsolutePoseFromJointIndex(int jointIndex) const {
|
||||
for (int i = 0; i < _top; i++) {
|
||||
if (_chain[i].jointIndex == jointIndex) {
|
||||
return _chain[i].absolutePose;
|
||||
}
|
||||
}
|
||||
return AnimPose::identity;
|
||||
}
|
||||
|
||||
bool setRelativePoseAtJointIndex(int jointIndex, const AnimPose& relativePose) {
|
||||
bool foundIndex = false;
|
||||
for (int i = _top - 1; i >= 0; i--) {
|
||||
if (_chain[i].jointIndex == jointIndex) {
|
||||
_chain[i].relativePose = relativePose;
|
||||
foundIndex = true;
|
||||
}
|
||||
// all child absolute poses are now dirty
|
||||
if (foundIndex) {
|
||||
_chain[i].dirty = true;
|
||||
}
|
||||
}
|
||||
return foundIndex;
|
||||
}
|
||||
|
||||
void buildDirtyAbsolutePoses() {
|
||||
// the relative and absolute pose is the same for the base of the chain.
|
||||
_chain[_top - 1].absolutePose = _chain[_top - 1].relativePose;
|
||||
_chain[_top - 1].dirty = false;
|
||||
|
||||
// iterate chain from base to tip, concatinating the relative poses to build the absolute poses.
|
||||
for (int i = _top - 1; i > 0; i--) {
|
||||
AnimChainElem& parent = _chain[i];
|
||||
AnimChainElem& child = _chain[i - 1];
|
||||
|
||||
if (child.dirty) {
|
||||
child.absolutePose = parent.absolutePose * child.relativePose;
|
||||
child.dirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void blend(const AnimChainT& srcChain, float alpha) {
|
||||
// make sure chains have same lengths
|
||||
assert(srcChain._top == _top);
|
||||
if (srcChain._top != _top) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only blend the relative poses
|
||||
for (int i = 0; i < _top; i++) {
|
||||
_chain[i].relativePose.blend(srcChain._chain[i].relativePose, alpha);
|
||||
_chain[i].dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
int size() const {
|
||||
return _top;
|
||||
}
|
||||
|
||||
void outputRelativePoses(AnimPoseVec& relativePoses) {
|
||||
for (int i = 0; i < _top; i++) {
|
||||
relativePoses[_chain[i].jointIndex] = _chain[i].relativePose;
|
||||
}
|
||||
}
|
||||
|
||||
void debugDraw(const glm::mat4& geomToWorldMat, const glm::vec4& color) const {
|
||||
for (int i = 1; i < _top; i++) {
|
||||
glm::vec3 start = transformPoint(geomToWorldMat, _chain[i - 1].absolutePose.trans());
|
||||
glm::vec3 end = transformPoint(geomToWorldMat, _chain[i].absolutePose.trans());
|
||||
DebugDraw::getInstance().drawRay(start, end, color);
|
||||
}
|
||||
}
|
||||
|
||||
void dump() const {
|
||||
for (int i = 0; i < _top; i++) {
|
||||
qWarning() << "AJT: AnimPoseElem[" << i << "]";
|
||||
qWarning() << "AJT: relPose =" << _chain[i].relativePose;
|
||||
qWarning() << "AJT: absPose =" << _chain[i].absolutePose;
|
||||
qWarning() << "AJT: jointIndex =" << _chain[i].jointIndex;
|
||||
qWarning() << "AJT: dirty =" << _chain[i].dirty;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
struct AnimChainElem {
|
||||
AnimPose relativePose;
|
||||
AnimPose absolutePose;
|
||||
int jointIndex { -1 };
|
||||
bool dirty { true };
|
||||
};
|
||||
|
||||
AnimChainElem _chain[N];
|
||||
int _top { 0 };
|
||||
};
|
||||
|
||||
using AnimChain = AnimChainT<10>;
|
||||
|
||||
#endif
|
|
@ -30,7 +30,7 @@ AnimClip::~AnimClip() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
// lookup parameters from animVars, using current instance variables as defaults.
|
||||
_startFrame = animVars.lookup(_startFrameVar, _startFrame);
|
||||
|
@ -77,6 +77,8 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const Anim
|
|||
::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]);
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
@ -89,7 +91,7 @@ void AnimClip::loadURL(const QString& url) {
|
|||
void AnimClip::setCurrentFrameInternal(float frame) {
|
||||
// because dt is 0, we should not encounter any triggers
|
||||
const float dt = 0.0f;
|
||||
Triggers triggers;
|
||||
AnimVariantMap triggers;
|
||||
_frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag);
|
||||
virtual ~AnimClip() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setStartFrameVar(const QString& startFrameVar) { _startFrameVar = startFrameVar; }
|
||||
void setEndFrameVar(const QString& endFrameVar) { _endFrameVar = endFrameVar; }
|
||||
|
|
|
@ -20,12 +20,15 @@ AnimDefaultPose::~AnimDefaultPose() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimDefaultPose::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimDefaultPose::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
if (_skeleton) {
|
||||
_poses = _skeleton->getRelativeDefaultPoses();
|
||||
} else {
|
||||
_poses.clear();
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
AnimDefaultPose(const QString& id);
|
||||
virtual ~AnimDefaultPose() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
protected:
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
|
|
@ -259,14 +259,6 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
|
|||
jointChainInfoVec[i].jointInfoVec[j].rot = safeMix(_prevJointChainInfoVec[i].jointInfoVec[j].rot, jointChainInfoVec[i].jointInfoVec[j].rot, alpha);
|
||||
jointChainInfoVec[i].jointInfoVec[j].trans = lerp(_prevJointChainInfoVec[i].jointInfoVec[j].trans, jointChainInfoVec[i].jointInfoVec[j].trans, alpha);
|
||||
}
|
||||
|
||||
// if joint chain was just disabled, ramp the weight toward zero.
|
||||
if (_prevJointChainInfoVec[i].target.getType() != IKTarget::Type::Unknown &&
|
||||
jointChainInfoVec[i].target.getType() == IKTarget::Type::Unknown) {
|
||||
IKTarget newTarget = _prevJointChainInfoVec[i].target;
|
||||
newTarget.setWeight((1.0f - alpha) * _prevJointChainInfoVec[i].target.getWeight());
|
||||
jointChainInfoVec[i].target = newTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -874,14 +866,14 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
|
|||
}
|
||||
|
||||
//virtual
|
||||
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
// don't call this function, call overlay() instead
|
||||
assert(false);
|
||||
return _relativePoses;
|
||||
}
|
||||
|
||||
//virtual
|
||||
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
||||
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) {
|
||||
#ifdef Q_OS_ANDROID
|
||||
// disable IK on android
|
||||
return underPoses;
|
||||
|
@ -961,6 +953,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
PROFILE_RANGE_EX(simulation_animation, "ik/shiftHips", 0xffff00ff, 0);
|
||||
|
||||
if (_hipsTargetIndex >= 0) {
|
||||
|
||||
assert(_hipsTargetIndex < (int)targets.size());
|
||||
|
||||
// slam the hips to match the _hipsTarget
|
||||
|
@ -1045,6 +1038,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0);
|
||||
|
||||
setSecondaryTargets(context);
|
||||
|
||||
preconditionRelativePosesToAvoidLimbLock(context, targets);
|
||||
|
||||
solve(context, targets, dt, jointChainInfoVec);
|
||||
|
@ -1056,6 +1050,8 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
}
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _relativePoses;
|
||||
}
|
||||
|
||||
|
@ -1750,7 +1746,7 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC
|
|||
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
||||
|
||||
for (auto& target : targets) {
|
||||
if (target.getIndex() != -1) {
|
||||
if (target.getIndex() != -1 && target.getType() == IKTarget::Type::RotationAndPosition) {
|
||||
for (int i = 0; i < NUM_LIMBS; i++) {
|
||||
if (limbs[i].first == target.getIndex()) {
|
||||
int tipIndex = limbs[i].first;
|
||||
|
@ -1843,6 +1839,10 @@ void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource s
|
|||
default:
|
||||
case SolutionSource::RelaxToUnderPoses:
|
||||
blendToPoses(underPoses, underPoses, RELAX_BLEND_FACTOR);
|
||||
// special case for hips: don't dampen hip motion from underposes
|
||||
if (_hipsIndex >= 0 && _hipsIndex < (int)_relativePoses.size()) {
|
||||
_relativePoses[_hipsIndex] = underPoses[_hipsIndex];
|
||||
}
|
||||
break;
|
||||
case SolutionSource::RelaxToLimitCenterPoses:
|
||||
blendToPoses(_limitCenterPoses, underPoses, RELAX_BLEND_FACTOR);
|
||||
|
|
|
@ -52,8 +52,8 @@ public:
|
|||
const QString& typeVar, const QString& weightVar, float weight, const std::vector<float>& flexCoefficients,
|
||||
const QString& poleVectorEnabledVar, const QString& poleReferenceVectorVar, const QString& poleVectorVar);
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) override;
|
||||
|
||||
void clearIKJointLimitHistory();
|
||||
|
||||
|
|
|
@ -32,11 +32,11 @@ AnimManipulator::~AnimManipulator() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
return overlay(animVars, context, dt, triggersOut, _skeleton->getRelativeDefaultPoses());
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
||||
const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) {
|
||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||
|
||||
_poses = underPoses;
|
||||
|
@ -74,6 +74,8 @@ const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, cons
|
|||
}
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ public:
|
|||
AnimManipulator(const QString& id, float alpha);
|
||||
virtual ~AnimManipulator() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) override;
|
||||
|
||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||
|
||||
|
|
|
@ -59,3 +59,19 @@ void AnimNode::setCurrentFrame(float frame) {
|
|||
child->setCurrentFrameInternal(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimNode::processOutputJoints(AnimVariantMap& triggersOut) const {
|
||||
if (!_skeleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto&& jointName : _outputJointNames) {
|
||||
// TODO: cache the jointIndices
|
||||
int jointIndex = _skeleton->nameToJointIndex(jointName);
|
||||
if (jointIndex >= 0) {
|
||||
AnimPose pose = _skeleton->getAbsolutePose(jointIndex, getPosesInternal());
|
||||
triggersOut.set(_id + jointName + "Rotation", pose.rot());
|
||||
triggersOut.set(_id + jointName + "Position", pose.trans());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,11 +45,12 @@ public:
|
|||
Manipulator,
|
||||
InverseKinematics,
|
||||
DefaultPose,
|
||||
TwoBoneIK,
|
||||
PoleVectorConstraint,
|
||||
NumTypes
|
||||
};
|
||||
using Pointer = std::shared_ptr<AnimNode>;
|
||||
using ConstPointer = std::shared_ptr<const AnimNode>;
|
||||
using Triggers = std::vector<QString>;
|
||||
|
||||
friend class AnimDebugDraw;
|
||||
friend void buildChildMap(std::map<QString, Pointer>& map, Pointer node);
|
||||
|
@ -61,6 +62,8 @@ public:
|
|||
const QString& getID() const { return _id; }
|
||||
Type getType() const { return _type; }
|
||||
|
||||
void addOutputJoint(const QString& outputJointName) { _outputJointNames.push_back(outputJointName); }
|
||||
|
||||
// hierarchy accessors
|
||||
Pointer getParent();
|
||||
void addChild(Pointer child);
|
||||
|
@ -74,8 +77,8 @@ public:
|
|||
|
||||
AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; }
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) = 0;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut,
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) = 0;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut,
|
||||
const AnimPoseVec& underPoses) {
|
||||
return evaluate(animVars, context, dt, triggersOut);
|
||||
}
|
||||
|
@ -114,11 +117,14 @@ protected:
|
|||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const = 0;
|
||||
|
||||
void processOutputJoints(AnimVariantMap& triggersOut) const;
|
||||
|
||||
Type _type;
|
||||
QString _id;
|
||||
std::vector<AnimNode::Pointer> _children;
|
||||
AnimSkeleton::ConstPointer _skeleton;
|
||||
std::weak_ptr<AnimNode> _parent;
|
||||
std::vector<QString> _outputJointNames;
|
||||
|
||||
// no copies
|
||||
AnimNode(const AnimNode&) = delete;
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include "AnimManipulator.h"
|
||||
#include "AnimInverseKinematics.h"
|
||||
#include "AnimDefaultPose.h"
|
||||
#include "AnimTwoBoneIK.h"
|
||||
#include "AnimPoleVectorConstraint.h"
|
||||
|
||||
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
@ -38,6 +40,8 @@ static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const
|
|||
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadTwoBoneIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
||||
static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
|
||||
|
||||
|
@ -56,6 +60,8 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
|
|||
case AnimNode::Type::Manipulator: return "manipulator";
|
||||
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
||||
case AnimNode::Type::DefaultPose: return "defaultPose";
|
||||
case AnimNode::Type::TwoBoneIK: return "twoBoneIK";
|
||||
case AnimNode::Type::PoleVectorConstraint: return "poleVectorConstraint";
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -116,6 +122,8 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
|||
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
||||
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
||||
case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;
|
||||
case AnimNode::Type::TwoBoneIK: return loadTwoBoneIKNode;
|
||||
case AnimNode::Type::PoleVectorConstraint: return loadPoleVectorConstraintNode;
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -131,6 +139,8 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
|||
case AnimNode::Type::Manipulator: return processDoNothing;
|
||||
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
||||
case AnimNode::Type::DefaultPose: return processDoNothing;
|
||||
case AnimNode::Type::TwoBoneIK: return processDoNothing;
|
||||
case AnimNode::Type::PoleVectorConstraint: return processDoNothing;
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -189,6 +199,25 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
|||
} \
|
||||
do {} while (0)
|
||||
|
||||
#define READ_VEC3(NAME, JSON_OBJ, ID, URL, ERROR_RETURN) \
|
||||
auto NAME##_VAL = JSON_OBJ.value(#NAME); \
|
||||
if (!NAME##_VAL.isArray()) { \
|
||||
qCCritical(animation) << "AnimNodeLoader, error reading vector" \
|
||||
<< #NAME << "id =" << ID \
|
||||
<< ", url =" << URL.toDisplayString(); \
|
||||
return ERROR_RETURN; \
|
||||
} \
|
||||
QJsonArray NAME##_ARRAY = NAME##_VAL.toArray(); \
|
||||
if (NAME##_ARRAY.size() != 3) { \
|
||||
qCCritical(animation) << "AnimNodeLoader, vector size != 3" \
|
||||
<< #NAME << "id =" << ID \
|
||||
<< ", url =" << URL.toDisplayString(); \
|
||||
return ERROR_RETURN; \
|
||||
} \
|
||||
glm::vec3 NAME((float)NAME##_ARRAY.at(0).toDouble(), \
|
||||
(float)NAME##_ARRAY.at(1).toDouble(), \
|
||||
(float)NAME##_ARRAY.at(2).toDouble())
|
||||
|
||||
static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) {
|
||||
auto idVal = jsonObj.value("id");
|
||||
if (!idVal.isString()) {
|
||||
|
@ -216,6 +245,16 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr
|
|||
}
|
||||
auto dataObj = dataValue.toObject();
|
||||
|
||||
std::vector<QString> outputJoints;
|
||||
|
||||
auto outputJoints_VAL = dataObj.value("outputJoints");
|
||||
if (outputJoints_VAL.isArray()) {
|
||||
QJsonArray outputJoints_ARRAY = outputJoints_VAL.toArray();
|
||||
for (int i = 0; i < outputJoints_ARRAY.size(); i++) {
|
||||
outputJoints.push_back(outputJoints_ARRAY.at(i).toString());
|
||||
}
|
||||
}
|
||||
|
||||
assert((int)type >= 0 && type < AnimNode::Type::NumTypes);
|
||||
auto node = (animNodeTypeToLoaderFunc(type))(dataObj, id, jsonUrl);
|
||||
if (!node) {
|
||||
|
@ -242,6 +281,9 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr
|
|||
}
|
||||
|
||||
if ((animNodeTypeToProcessFunc(type))(node, dataObj, id, jsonUrl)) {
|
||||
for (auto&& outputJoint : outputJoints) {
|
||||
node->addOutputJoint(outputJoint);
|
||||
}
|
||||
return node;
|
||||
} else {
|
||||
return nullptr;
|
||||
|
@ -531,6 +573,41 @@ static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const Q
|
|||
return node;
|
||||
}
|
||||
|
||||
static AnimNode::Pointer loadTwoBoneIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_BOOL(enabled, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_FLOAT(interpDuration, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(baseJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(midJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(tipJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_VEC3(midHingeAxis, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(alphaVar, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(enabledVar, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(endEffectorRotationVarVar, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(endEffectorPositionVarVar, jsonObj, id, jsonUrl, nullptr);
|
||||
|
||||
auto node = std::make_shared<AnimTwoBoneIK>(id, alpha, enabled, interpDuration,
|
||||
baseJointName, midJointName, tipJointName, midHingeAxis,
|
||||
alphaVar, enabledVar,
|
||||
endEffectorRotationVarVar, endEffectorPositionVarVar);
|
||||
return node;
|
||||
}
|
||||
|
||||
static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||
READ_VEC3(referenceVector, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_BOOL(enabled, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(baseJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(midJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(tipJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(enabledVar, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(poleVectorVar, jsonObj, id, jsonUrl, nullptr);
|
||||
|
||||
auto node = std::make_shared<AnimPoleVectorConstraint>(id, enabled, referenceVector,
|
||||
baseJointName, midJointName, tipJointName,
|
||||
enabledVar, poleVectorVar);
|
||||
return node;
|
||||
}
|
||||
|
||||
void buildChildMap(std::map<QString, int>& map, AnimNode::Pointer node) {
|
||||
for (int i = 0; i < (int)node->getChildCount(); ++i) {
|
||||
map.insert(std::pair<QString, int>(node->getChild(i)->getID(), i));
|
||||
|
@ -682,7 +759,8 @@ AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& j
|
|||
QString version = versionVal.toString();
|
||||
|
||||
// check version
|
||||
if (version != "1.0") {
|
||||
// AJT: TODO version check
|
||||
if (version != "1.0" && version != "1.1") {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad version number" << version << "expected \"1.0\", url =" << jsonUrl.toDisplayString();
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ void AnimOverlay::buildBoneSet(BoneSet boneSet) {
|
|||
}
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
// lookup parameters from animVars, using current instance variables as defaults.
|
||||
// NOTE: switching bonesets can be an expensive operation, let's try to avoid it.
|
||||
|
@ -66,6 +66,9 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const A
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
AnimOverlay(const QString& id, BoneSet boneSet, float alpha);
|
||||
virtual ~AnimOverlay() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setBoneSetVar(const QString& boneSetVar) { _boneSetVar = boneSetVar; }
|
||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||
|
|
245
libraries/animation/src/AnimPoleVectorConstraint.cpp
Normal file
245
libraries/animation/src/AnimPoleVectorConstraint.cpp
Normal file
|
@ -0,0 +1,245 @@
|
|||
//
|
||||
// AnimPoleVectorConstraint.cpp
|
||||
//
|
||||
// Created by Anthony J. Thibault on 5/12/18.
|
||||
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimPoleVectorConstraint.h"
|
||||
#include "AnimationLogging.h"
|
||||
#include "AnimUtil.h"
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
const float FRAMES_PER_SECOND = 30.0f;
|
||||
const float INTERP_DURATION = 6.0f;
|
||||
|
||||
AnimPoleVectorConstraint::AnimPoleVectorConstraint(const QString& id, bool enabled, glm::vec3 referenceVector,
|
||||
const QString& baseJointName, const QString& midJointName, const QString& tipJointName,
|
||||
const QString& enabledVar, const QString& poleVectorVar) :
|
||||
AnimNode(AnimNode::Type::PoleVectorConstraint, id),
|
||||
_enabled(enabled),
|
||||
_referenceVector(referenceVector),
|
||||
_baseJointName(baseJointName),
|
||||
_midJointName(midJointName),
|
||||
_tipJointName(tipJointName),
|
||||
_enabledVar(enabledVar),
|
||||
_poleVectorVar(poleVectorVar) {
|
||||
|
||||
}
|
||||
|
||||
AnimPoleVectorConstraint::~AnimPoleVectorConstraint() {
|
||||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimPoleVectorConstraint::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
assert(_children.size() == 1);
|
||||
if (_children.size() != 1) {
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// evalute underPoses
|
||||
AnimPoseVec underPoses = _children[0]->evaluate(animVars, context, dt, triggersOut);
|
||||
|
||||
// if we don't have a skeleton, or jointName lookup failed.
|
||||
if (!_skeleton || _baseJointIndex == -1 || _midJointIndex == -1 || _tipJointIndex == -1 || underPoses.size() == 0) {
|
||||
// pass underPoses through unmodified.
|
||||
_poses = underPoses;
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// guard against size changes
|
||||
if (underPoses.size() != _poses.size()) {
|
||||
_poses = underPoses;
|
||||
}
|
||||
|
||||
// Look up poleVector from animVars, make sure to convert into geom space.
|
||||
glm::vec3 poleVector = animVars.lookupRigToGeometryVector(_poleVectorVar, Vectors::UNIT_Z);
|
||||
float poleVectorLength = glm::length(poleVector);
|
||||
|
||||
// determine if we should interpolate
|
||||
bool enabled = animVars.lookup(_enabledVar, _enabled);
|
||||
|
||||
const float MIN_LENGTH = 1.0e-4f;
|
||||
if (glm::length(poleVector) < MIN_LENGTH) {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
if (enabled != _enabled) {
|
||||
AnimChain poseChain;
|
||||
poseChain.buildFromRelativePoses(_skeleton, _poses, _tipJointIndex);
|
||||
if (enabled) {
|
||||
beginInterp(InterpType::SnapshotToSolve, poseChain);
|
||||
} else {
|
||||
beginInterp(InterpType::SnapshotToUnderPoses, poseChain);
|
||||
}
|
||||
}
|
||||
_enabled = enabled;
|
||||
|
||||
// don't build chains or do IK if we are disbled & not interping.
|
||||
if (_interpType == InterpType::None && !enabled) {
|
||||
_poses = underPoses;
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// compute chain
|
||||
AnimChain underChain;
|
||||
underChain.buildFromRelativePoses(_skeleton, underPoses, _tipJointIndex);
|
||||
AnimChain ikChain = underChain;
|
||||
|
||||
AnimPose baseParentPose = ikChain.getAbsolutePoseFromJointIndex(_baseParentJointIndex);
|
||||
AnimPose basePose = ikChain.getAbsolutePoseFromJointIndex(_baseJointIndex);
|
||||
AnimPose midPose = ikChain.getAbsolutePoseFromJointIndex(_midJointIndex);
|
||||
AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||
|
||||
// Look up refVector from animVars, make sure to convert into geom space.
|
||||
glm::vec3 refVector = midPose.xformVectorFast(_referenceVector);
|
||||
float refVectorLength = glm::length(refVector);
|
||||
|
||||
glm::vec3 axis = basePose.trans() - tipPose.trans();
|
||||
float axisLength = glm::length(axis);
|
||||
glm::vec3 unitAxis = axis / axisLength;
|
||||
|
||||
glm::vec3 sideVector = glm::cross(unitAxis, refVector);
|
||||
float sideVectorLength = glm::length(sideVector);
|
||||
|
||||
// project refVector onto axis plane
|
||||
glm::vec3 refVectorProj = refVector - glm::dot(refVector, unitAxis) * unitAxis;
|
||||
float refVectorProjLength = glm::length(refVectorProj);
|
||||
|
||||
// project poleVector on plane formed by axis.
|
||||
glm::vec3 poleVectorProj = poleVector - glm::dot(poleVector, unitAxis) * unitAxis;
|
||||
float poleVectorProjLength = glm::length(poleVectorProj);
|
||||
|
||||
// double check for zero length vectors or vectors parallel to rotaiton axis.
|
||||
if (axisLength > MIN_LENGTH && refVectorLength > MIN_LENGTH && sideVectorLength > MIN_LENGTH &&
|
||||
refVectorProjLength > MIN_LENGTH && poleVectorProjLength > MIN_LENGTH) {
|
||||
|
||||
float dot = glm::clamp(glm::dot(refVectorProj / refVectorProjLength, poleVectorProj / poleVectorProjLength), 0.0f, 1.0f);
|
||||
float sideDot = glm::dot(poleVector, sideVector);
|
||||
float theta = copysignf(1.0f, sideDot) * acosf(dot);
|
||||
|
||||
glm::quat deltaRot = glm::angleAxis(theta, unitAxis);
|
||||
|
||||
// transform result back into parent relative frame.
|
||||
glm::quat relBaseRot = glm::inverse(baseParentPose.rot()) * deltaRot * basePose.rot();
|
||||
ikChain.setRelativePoseAtJointIndex(_baseJointIndex, AnimPose(relBaseRot, underPoses[_baseJointIndex].trans()));
|
||||
|
||||
glm::quat relTipRot = glm::inverse(midPose.rot()) * glm::inverse(deltaRot) * tipPose.rot();
|
||||
ikChain.setRelativePoseAtJointIndex(_tipJointIndex, AnimPose(relTipRot, underPoses[_tipJointIndex].trans()));
|
||||
}
|
||||
|
||||
// start off by initializing output poses with the underPoses
|
||||
_poses = underPoses;
|
||||
|
||||
// apply smooth interpolation
|
||||
if (_interpType != InterpType::None) {
|
||||
_interpAlpha += _interpAlphaVel * dt;
|
||||
|
||||
if (_interpAlpha < 1.0f) {
|
||||
AnimChain interpChain;
|
||||
if (_interpType == InterpType::SnapshotToUnderPoses) {
|
||||
interpChain = underChain;
|
||||
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||
} else if (_interpType == InterpType::SnapshotToSolve) {
|
||||
interpChain = ikChain;
|
||||
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||
}
|
||||
// copy interpChain into _poses
|
||||
interpChain.outputRelativePoses(_poses);
|
||||
} else {
|
||||
// interpolation complete
|
||||
_interpType = InterpType::None;
|
||||
}
|
||||
}
|
||||
|
||||
if (_interpType == InterpType::None) {
|
||||
if (enabled) {
|
||||
// copy chain into _poses
|
||||
ikChain.outputRelativePoses(_poses);
|
||||
} else {
|
||||
// copy under chain into _poses
|
||||
underChain.outputRelativePoses(_poses);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.getEnableDebugDrawIKChains()) {
|
||||
if (_interpType == InterpType::None && enabled) {
|
||||
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
ikChain.debugDraw(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix(), BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.getEnableDebugDrawIKChains()) {
|
||||
if (enabled) {
|
||||
const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
const glm::vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
const glm::vec4 CYAN(0.0f, 1.0f, 1.0f, 1.0f);
|
||||
const glm::vec4 YELLOW(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
const float VECTOR_LENGTH = 0.5f;
|
||||
|
||||
glm::mat4 geomToWorld = context.getRigToWorldMatrix() * context.getGeometryToRigMatrix();
|
||||
|
||||
// draw the pole
|
||||
glm::vec3 start = transformPoint(geomToWorld, basePose.trans());
|
||||
glm::vec3 end = transformPoint(geomToWorld, tipPose.trans());
|
||||
DebugDraw::getInstance().drawRay(start, end, CYAN);
|
||||
|
||||
// draw the poleVector
|
||||
glm::vec3 midPoint = 0.5f * (start + end);
|
||||
glm::vec3 poleVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, poleVector));
|
||||
DebugDraw::getInstance().drawRay(midPoint, poleVectorEnd, GREEN);
|
||||
|
||||
// draw the refVector
|
||||
glm::vec3 refVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, refVector));
|
||||
DebugDraw::getInstance().drawRay(midPoint, refVectorEnd, RED);
|
||||
|
||||
// draw the sideVector
|
||||
glm::vec3 sideVector = glm::cross(poleVector, refVector);
|
||||
glm::vec3 sideVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, sideVector));
|
||||
DebugDraw::getInstance().drawRay(midPoint, sideVectorEnd, YELLOW);
|
||||
}
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
const AnimPoseVec& AnimPoleVectorConstraint::getPosesInternal() const {
|
||||
return _poses;
|
||||
}
|
||||
|
||||
void AnimPoleVectorConstraint::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
|
||||
AnimNode::setSkeletonInternal(skeleton);
|
||||
lookUpIndices();
|
||||
}
|
||||
|
||||
void AnimPoleVectorConstraint::lookUpIndices() {
|
||||
assert(_skeleton);
|
||||
|
||||
// look up bone indices by name
|
||||
std::vector<int> indices = _skeleton->lookUpJointIndices({_baseJointName, _midJointName, _tipJointName});
|
||||
|
||||
// cache the results
|
||||
_baseJointIndex = indices[0];
|
||||
_midJointIndex = indices[1];
|
||||
_tipJointIndex = indices[2];
|
||||
|
||||
if (_baseJointIndex != -1) {
|
||||
_baseParentJointIndex = _skeleton->getParentIndex(_baseJointIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimPoleVectorConstraint::beginInterp(InterpType interpType, const AnimChain& chain) {
|
||||
// capture the current poses in a snapshot.
|
||||
_snapshotChain = chain;
|
||||
|
||||
_interpType = interpType;
|
||||
_interpAlphaVel = FRAMES_PER_SECOND / INTERP_DURATION;
|
||||
_interpAlpha = 0.0f;
|
||||
}
|
74
libraries/animation/src/AnimPoleVectorConstraint.h
Normal file
74
libraries/animation/src/AnimPoleVectorConstraint.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// AnimPoleVectorConstraint.h
|
||||
//
|
||||
// Created by Anthony J. Thibault on 5/25/18.
|
||||
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// 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_AnimPoleVectorConstraint_h
|
||||
#define hifi_AnimPoleVectorConstraint_h
|
||||
|
||||
#include "AnimNode.h"
|
||||
#include "AnimChain.h"
|
||||
|
||||
// Three bone IK chain
|
||||
|
||||
class AnimPoleVectorConstraint : public AnimNode {
|
||||
public:
|
||||
friend class AnimTests;
|
||||
|
||||
AnimPoleVectorConstraint(const QString& id, bool enabled, glm::vec3 referenceVector,
|
||||
const QString& baseJointName, const QString& midJointName, const QString& tipJointName,
|
||||
const QString& enabledVar, const QString& poleVectorVar);
|
||||
virtual ~AnimPoleVectorConstraint() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
protected:
|
||||
|
||||
enum class InterpType {
|
||||
None = 0,
|
||||
SnapshotToUnderPoses,
|
||||
SnapshotToSolve,
|
||||
NumTypes
|
||||
};
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
|
||||
|
||||
void lookUpIndices();
|
||||
void beginInterp(InterpType interpType, const AnimChain& chain);
|
||||
|
||||
AnimPoseVec _poses;
|
||||
|
||||
bool _enabled;
|
||||
glm::vec3 _referenceVector;
|
||||
|
||||
QString _baseJointName;
|
||||
QString _midJointName;
|
||||
QString _tipJointName;
|
||||
|
||||
QString _enabledVar;
|
||||
QString _poleVectorVar;
|
||||
|
||||
int _baseParentJointIndex { -1 };
|
||||
int _baseJointIndex { -1 };
|
||||
int _midJointIndex { -1 };
|
||||
int _tipJointIndex { -1 };
|
||||
|
||||
InterpType _interpType { InterpType::None };
|
||||
float _interpAlphaVel { 0.0f };
|
||||
float _interpAlpha { 0.0f };
|
||||
|
||||
AnimChain _snapshotChain;
|
||||
|
||||
// no copies
|
||||
AnimPoleVectorConstraint(const AnimPoleVectorConstraint&) = delete;
|
||||
AnimPoleVectorConstraint& operator=(const AnimPoleVectorConstraint&) = delete;
|
||||
};
|
||||
|
||||
#endif // hifi_AnimPoleVectorConstraint_h
|
|
@ -12,6 +12,7 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "AnimUtil.h"
|
||||
|
||||
const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
|
||||
glm::quat(),
|
||||
|
@ -77,4 +78,16 @@ AnimPose::operator glm::mat4() const {
|
|||
glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f));
|
||||
}
|
||||
|
||||
void AnimPose::blend(const AnimPose& srcPose, float alpha) {
|
||||
// adjust signs if necessary
|
||||
const glm::quat& q1 = srcPose._rot;
|
||||
glm::quat q2 = _rot;
|
||||
float dot = glm::dot(q1, q2);
|
||||
if (dot < 0.0f) {
|
||||
q2 = -q2;
|
||||
}
|
||||
|
||||
_scale = lerp(srcPose._scale, _scale, alpha);
|
||||
_rot = safeLerp(srcPose._rot, _rot, alpha);
|
||||
_trans = lerp(srcPose._trans, _trans, alpha);
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ public:
|
|||
const glm::vec3& trans() const { return _trans; }
|
||||
glm::vec3& trans() { return _trans; }
|
||||
|
||||
void blend(const AnimPose& srcPose, float alpha);
|
||||
|
||||
private:
|
||||
friend QDebug operator<<(QDebug debug, const AnimPose& pose);
|
||||
glm::vec3 _scale { 1.0f };
|
||||
|
|
|
@ -282,3 +282,17 @@ void AnimSkeleton::dump(const AnimPoseVec& poses) const {
|
|||
qCDebug(animation) << "]";
|
||||
}
|
||||
|
||||
std::vector<int> AnimSkeleton::lookUpJointIndices(const std::vector<QString>& jointNames) const {
|
||||
std::vector<int> result;
|
||||
result.reserve(jointNames.size());
|
||||
for (auto& name : jointNames) {
|
||||
int index = nameToJointIndex(name);
|
||||
if (index == -1) {
|
||||
qWarning(animation) << "AnimSkeleton::lookUpJointIndices(): could not find bone with named " << name;
|
||||
}
|
||||
result.push_back(index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -61,6 +61,8 @@ public:
|
|||
void dump(bool verbose) const;
|
||||
void dump(const AnimPoseVec& poses) const;
|
||||
|
||||
std::vector<int> lookUpJointIndices(const std::vector<QString>& jointNames) const;
|
||||
|
||||
protected:
|
||||
void buildSkeletonFromJoints(const std::vector<FBXJoint>& joints);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ AnimStateMachine::~AnimStateMachine() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
|
||||
if (_currentState->getID() != desiredStateID) {
|
||||
|
@ -81,6 +81,9 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
|||
if (!_duringInterp) {
|
||||
_poses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
@ -107,7 +110,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon
|
|||
|
||||
// because dt is 0, we should not encounter any triggers
|
||||
const float dt = 0.0f;
|
||||
Triggers triggers;
|
||||
AnimVariantMap triggers;
|
||||
|
||||
if (_interpType == InterpType::SnapshotBoth) {
|
||||
// snapshot previous pose.
|
||||
|
|
|
@ -113,7 +113,7 @@ public:
|
|||
explicit AnimStateMachine(const QString& id);
|
||||
virtual ~AnimStateMachine() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; }
|
||||
|
||||
|
|
292
libraries/animation/src/AnimTwoBoneIK.cpp
Normal file
292
libraries/animation/src/AnimTwoBoneIK.cpp
Normal file
|
@ -0,0 +1,292 @@
|
|||
//
|
||||
// AnimTwoBoneIK.cpp
|
||||
//
|
||||
// Created by Anthony J. Thibault on 5/12/18.
|
||||
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimTwoBoneIK.h"
|
||||
|
||||
#include <DebugDraw.h>
|
||||
|
||||
#include "AnimationLogging.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
const float FRAMES_PER_SECOND = 30.0f;
|
||||
|
||||
AnimTwoBoneIK::AnimTwoBoneIK(const QString& id, float alpha, bool enabled, float interpDuration,
|
||||
const QString& baseJointName, const QString& midJointName,
|
||||
const QString& tipJointName, const glm::vec3& midHingeAxis,
|
||||
const QString& alphaVar, const QString& enabledVar,
|
||||
const QString& endEffectorRotationVarVar, const QString& endEffectorPositionVarVar) :
|
||||
AnimNode(AnimNode::Type::TwoBoneIK, id),
|
||||
_alpha(alpha),
|
||||
_enabled(enabled),
|
||||
_interpDuration(interpDuration),
|
||||
_baseJointName(baseJointName),
|
||||
_midJointName(midJointName),
|
||||
_tipJointName(tipJointName),
|
||||
_midHingeAxis(glm::normalize(midHingeAxis)),
|
||||
_alphaVar(alphaVar),
|
||||
_enabledVar(enabledVar),
|
||||
_endEffectorRotationVarVar(endEffectorRotationVarVar),
|
||||
_endEffectorPositionVarVar(endEffectorPositionVarVar),
|
||||
_prevEndEffectorRotationVar(),
|
||||
_prevEndEffectorPositionVar()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AnimTwoBoneIK::~AnimTwoBoneIK() {
|
||||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimTwoBoneIK::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
assert(_children.size() == 1);
|
||||
if (_children.size() != 1) {
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// evalute underPoses
|
||||
AnimPoseVec underPoses = _children[0]->evaluate(animVars, context, dt, triggersOut);
|
||||
|
||||
// if we don't have a skeleton, or jointName lookup failed.
|
||||
if (!_skeleton || _baseJointIndex == -1 || _midJointIndex == -1 || _tipJointIndex == -1 || underPoses.size() == 0) {
|
||||
// pass underPoses through unmodified.
|
||||
_poses = underPoses;
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// guard against size changes
|
||||
if (underPoses.size() != _poses.size()) {
|
||||
_poses = underPoses;
|
||||
}
|
||||
|
||||
const float MIN_ALPHA = 0.0f;
|
||||
const float MAX_ALPHA = 1.0f;
|
||||
float alpha = glm::clamp(animVars.lookup(_alphaVar, _alpha), MIN_ALPHA, MAX_ALPHA);
|
||||
|
||||
// don't perform IK if we have bad indices, or alpha is zero
|
||||
if (_tipJointIndex == -1 || _midJointIndex == -1 || _baseJointIndex == -1 || alpha == 0.0f) {
|
||||
_poses = underPoses;
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// determine if we should interpolate
|
||||
bool enabled = animVars.lookup(_enabledVar, _enabled);
|
||||
if (enabled != _enabled) {
|
||||
AnimChain poseChain;
|
||||
poseChain.buildFromRelativePoses(_skeleton, _poses, _tipJointIndex);
|
||||
if (enabled) {
|
||||
beginInterp(InterpType::SnapshotToSolve, poseChain);
|
||||
} else {
|
||||
beginInterp(InterpType::SnapshotToUnderPoses, poseChain);
|
||||
}
|
||||
}
|
||||
_enabled = enabled;
|
||||
|
||||
// don't build chains or do IK if we are disbled & not interping.
|
||||
if (_interpType == InterpType::None && !enabled) {
|
||||
_poses = underPoses;
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// compute chain
|
||||
AnimChain underChain;
|
||||
underChain.buildFromRelativePoses(_skeleton, underPoses, _tipJointIndex);
|
||||
AnimChain ikChain = underChain;
|
||||
|
||||
AnimPose baseParentPose = ikChain.getAbsolutePoseFromJointIndex(_baseParentJointIndex);
|
||||
AnimPose basePose = ikChain.getAbsolutePoseFromJointIndex(_baseJointIndex);
|
||||
AnimPose midPose = ikChain.getAbsolutePoseFromJointIndex(_midJointIndex);
|
||||
AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||
|
||||
QString endEffectorRotationVar = animVars.lookup(_endEffectorRotationVarVar, QString(""));
|
||||
QString endEffectorPositionVar = animVars.lookup(_endEffectorPositionVarVar, QString(""));
|
||||
|
||||
// if either of the endEffectorVars have changed
|
||||
if ((!_prevEndEffectorRotationVar.isEmpty() && (_prevEndEffectorRotationVar != endEffectorRotationVar)) ||
|
||||
(!_prevEndEffectorPositionVar.isEmpty() && (_prevEndEffectorPositionVar != endEffectorPositionVar))) {
|
||||
// begin interp to smooth out transition between prev and new end effector.
|
||||
AnimChain poseChain;
|
||||
poseChain.buildFromRelativePoses(_skeleton, _poses, _tipJointIndex);
|
||||
beginInterp(InterpType::SnapshotToSolve, poseChain);
|
||||
}
|
||||
|
||||
// Look up end effector from animVars, make sure to convert into geom space.
|
||||
// First look in the triggers then look in the animVars, so we can follow output joints underneath us in the anim graph
|
||||
AnimPose targetPose(tipPose);
|
||||
if (triggersOut.hasKey(endEffectorRotationVar)) {
|
||||
targetPose.rot() = triggersOut.lookupRigToGeometry(endEffectorRotationVar, tipPose.rot());
|
||||
} else if (animVars.hasKey(endEffectorRotationVar)) {
|
||||
targetPose.rot() = animVars.lookupRigToGeometry(endEffectorRotationVar, tipPose.rot());
|
||||
}
|
||||
|
||||
if (triggersOut.hasKey(endEffectorPositionVar)) {
|
||||
targetPose.trans() = triggersOut.lookupRigToGeometry(endEffectorPositionVar, tipPose.trans());
|
||||
} else if (animVars.hasKey(endEffectorRotationVar)) {
|
||||
targetPose.trans() = animVars.lookupRigToGeometry(endEffectorPositionVar, tipPose.trans());
|
||||
}
|
||||
|
||||
_prevEndEffectorRotationVar = endEffectorRotationVar;
|
||||
_prevEndEffectorPositionVar = endEffectorPositionVar;
|
||||
|
||||
glm::vec3 bicepVector = midPose.trans() - basePose.trans();
|
||||
float r0 = glm::length(bicepVector);
|
||||
bicepVector = bicepVector / r0;
|
||||
|
||||
glm::vec3 forearmVector = tipPose.trans() - midPose.trans();
|
||||
float r1 = glm::length(forearmVector);
|
||||
forearmVector = forearmVector / r1;
|
||||
|
||||
float d = glm::length(targetPose.trans() - basePose.trans());
|
||||
|
||||
float midAngle = 0.0f;
|
||||
if (d < r0 + r1) {
|
||||
float y = sqrtf((-d + r1 - r0) * (-d - r1 + r0) * (-d + r1 + r0) * (d + r1 + r0)) / (2.0f * d);
|
||||
midAngle = PI - (acosf(y / r0) + acosf(y / r1));
|
||||
}
|
||||
|
||||
// compute midJoint rotation
|
||||
glm::quat relMidRot = glm::angleAxis(midAngle, _midHingeAxis);
|
||||
|
||||
// insert new relative pose into the chain and rebuild it.
|
||||
ikChain.setRelativePoseAtJointIndex(_midJointIndex, AnimPose(relMidRot, underPoses[_midJointIndex].trans()));
|
||||
ikChain.buildDirtyAbsolutePoses();
|
||||
|
||||
// recompute tip pose after mid joint has been rotated
|
||||
AnimPose newTipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||
|
||||
glm::vec3 leverArm = newTipPose.trans() - basePose.trans();
|
||||
glm::vec3 targetLine = targetPose.trans() - basePose.trans();
|
||||
|
||||
// compute delta rotation that brings leverArm parallel to targetLine
|
||||
glm::vec3 axis = glm::cross(leverArm, targetLine);
|
||||
float axisLength = glm::length(axis);
|
||||
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
||||
if (axisLength > MIN_AXIS_LENGTH) {
|
||||
axis /= axisLength;
|
||||
float cosAngle = glm::clamp(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine)), -1.0f, 1.0f);
|
||||
float angle = acosf(cosAngle);
|
||||
glm::quat deltaRot = glm::angleAxis(angle, axis);
|
||||
|
||||
// combine deltaRot with basePose.
|
||||
glm::quat absRot = deltaRot * basePose.rot();
|
||||
|
||||
// transform result back into parent relative frame.
|
||||
glm::quat relBaseRot = glm::inverse(baseParentPose.rot()) * absRot;
|
||||
ikChain.setRelativePoseAtJointIndex(_baseJointIndex, AnimPose(relBaseRot, underPoses[_baseJointIndex].trans()));
|
||||
}
|
||||
|
||||
// recompute midJoint pose after base has been rotated.
|
||||
ikChain.buildDirtyAbsolutePoses();
|
||||
AnimPose midJointPose = ikChain.getAbsolutePoseFromJointIndex(_midJointIndex);
|
||||
|
||||
// transform target rotation in to parent relative frame.
|
||||
glm::quat relTipRot = glm::inverse(midJointPose.rot()) * targetPose.rot();
|
||||
ikChain.setRelativePoseAtJointIndex(_tipJointIndex, AnimPose(relTipRot, underPoses[_tipJointIndex].trans()));
|
||||
|
||||
// blend with the underChain
|
||||
ikChain.blend(underChain, alpha);
|
||||
|
||||
// start off by initializing output poses with the underPoses
|
||||
_poses = underPoses;
|
||||
|
||||
// apply smooth interpolation
|
||||
if (_interpType != InterpType::None) {
|
||||
_interpAlpha += _interpAlphaVel * dt;
|
||||
|
||||
if (_interpAlpha < 1.0f) {
|
||||
AnimChain interpChain;
|
||||
if (_interpType == InterpType::SnapshotToUnderPoses) {
|
||||
interpChain = underChain;
|
||||
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||
} else if (_interpType == InterpType::SnapshotToSolve) {
|
||||
interpChain = ikChain;
|
||||
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||
}
|
||||
// copy interpChain into _poses
|
||||
interpChain.outputRelativePoses(_poses);
|
||||
} else {
|
||||
// interpolation complete
|
||||
_interpType = InterpType::None;
|
||||
}
|
||||
}
|
||||
|
||||
if (_interpType == InterpType::None) {
|
||||
if (enabled) {
|
||||
// copy chain into _poses
|
||||
ikChain.outputRelativePoses(_poses);
|
||||
} else {
|
||||
// copy under chain into _poses
|
||||
underChain.outputRelativePoses(_poses);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.getEnableDebugDrawIKTargets()) {
|
||||
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
glm::mat4 rigToAvatarMat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3());
|
||||
|
||||
glm::mat4 geomTargetMat = createMatFromQuatAndPos(targetPose.rot(), targetPose.trans());
|
||||
glm::mat4 avatarTargetMat = rigToAvatarMat * context.getGeometryToRigMatrix() * geomTargetMat;
|
||||
|
||||
QString name = QString("%1_target").arg(_id);
|
||||
DebugDraw::getInstance().addMyAvatarMarker(name, glmExtractRotation(avatarTargetMat),
|
||||
extractTranslation(avatarTargetMat), _enabled ? GREEN : RED);
|
||||
} else if (_lastEnableDebugDrawIKTargets) {
|
||||
QString name = QString("%1_target").arg(_id);
|
||||
DebugDraw::getInstance().removeMyAvatarMarker(name);
|
||||
}
|
||||
_lastEnableDebugDrawIKTargets = context.getEnableDebugDrawIKTargets();
|
||||
|
||||
if (context.getEnableDebugDrawIKChains()) {
|
||||
if (_interpType == InterpType::None && enabled) {
|
||||
const vec4 CYAN(0.0f, 1.0f, 1.0f, 1.0f);
|
||||
ikChain.debugDraw(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix(), CYAN);
|
||||
}
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
const AnimPoseVec& AnimTwoBoneIK::getPosesInternal() const {
|
||||
return _poses;
|
||||
}
|
||||
|
||||
void AnimTwoBoneIK::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
|
||||
AnimNode::setSkeletonInternal(skeleton);
|
||||
lookUpIndices();
|
||||
}
|
||||
|
||||
void AnimTwoBoneIK::lookUpIndices() {
|
||||
assert(_skeleton);
|
||||
|
||||
// look up bone indices by name
|
||||
std::vector<int> indices = _skeleton->lookUpJointIndices({_baseJointName, _midJointName, _tipJointName});
|
||||
|
||||
// cache the results
|
||||
_baseJointIndex = indices[0];
|
||||
_midJointIndex = indices[1];
|
||||
_tipJointIndex = indices[2];
|
||||
|
||||
if (_baseJointIndex != -1) {
|
||||
_baseParentJointIndex = _skeleton->getParentIndex(_baseJointIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimTwoBoneIK::beginInterp(InterpType interpType, const AnimChain& chain) {
|
||||
// capture the current poses in a snapshot.
|
||||
_snapshotChain = chain;
|
||||
|
||||
_interpType = interpType;
|
||||
_interpAlphaVel = FRAMES_PER_SECOND / _interpDuration;
|
||||
_interpAlpha = 0.0f;
|
||||
}
|
83
libraries/animation/src/AnimTwoBoneIK.h
Normal file
83
libraries/animation/src/AnimTwoBoneIK.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// AnimTwoBoneIK.h
|
||||
//
|
||||
// Created by Anthony J. Thibault on 5/12/18.
|
||||
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// 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_AnimTwoBoneIK_h
|
||||
#define hifi_AnimTwoBoneIK_h
|
||||
|
||||
#include "AnimNode.h"
|
||||
#include "AnimChain.h"
|
||||
|
||||
// Simple two bone IK chain
|
||||
class AnimTwoBoneIK : public AnimNode {
|
||||
public:
|
||||
friend class AnimTests;
|
||||
|
||||
AnimTwoBoneIK(const QString& id, float alpha, bool enabled, float interpDuration,
|
||||
const QString& baseJointName, const QString& midJointName,
|
||||
const QString& tipJointName, const glm::vec3& midHingeAxis,
|
||||
const QString& alphaVar, const QString& enabledVar,
|
||||
const QString& endEffectorRotationVarVar, const QString& endEffectorPositionVarVar);
|
||||
virtual ~AnimTwoBoneIK() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
protected:
|
||||
|
||||
enum class InterpType {
|
||||
None = 0,
|
||||
SnapshotToUnderPoses,
|
||||
SnapshotToSolve,
|
||||
NumTypes
|
||||
};
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
|
||||
|
||||
void lookUpIndices();
|
||||
void beginInterp(InterpType interpType, const AnimChain& chain);
|
||||
|
||||
AnimPoseVec _poses;
|
||||
|
||||
float _alpha;
|
||||
bool _enabled;
|
||||
float _interpDuration; // in frames (1/30 sec)
|
||||
QString _baseJointName;
|
||||
QString _midJointName;
|
||||
QString _tipJointName;
|
||||
glm::vec3 _midHingeAxis; // in baseJoint relative frame, should be normalized
|
||||
|
||||
int _baseParentJointIndex { -1 };
|
||||
int _baseJointIndex { -1 };
|
||||
int _midJointIndex { -1 };
|
||||
int _tipJointIndex { -1 };
|
||||
|
||||
QString _alphaVar; // float - (0, 1) 0 means underPoses only, 1 means IK only.
|
||||
QString _enabledVar; // bool
|
||||
QString _endEffectorRotationVarVar; // string
|
||||
QString _endEffectorPositionVarVar; // string
|
||||
|
||||
QString _prevEndEffectorRotationVar;
|
||||
QString _prevEndEffectorPositionVar;
|
||||
|
||||
InterpType _interpType { InterpType::None };
|
||||
float _interpAlphaVel { 0.0f };
|
||||
float _interpAlpha { 0.0f };
|
||||
|
||||
AnimChain _snapshotChain;
|
||||
|
||||
bool _lastEnableDebugDrawIKTargets { false };
|
||||
|
||||
// no copies
|
||||
AnimTwoBoneIK(const AnimTwoBoneIK&) = delete;
|
||||
AnimTwoBoneIK& operator=(const AnimTwoBoneIK&) = delete;
|
||||
};
|
||||
|
||||
#endif // hifi_AnimTwoBoneIK_h
|
|
@ -53,7 +53,7 @@ glm::quat averageQuats(size_t numQuats, const glm::quat* quats) {
|
|||
}
|
||||
|
||||
float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag,
|
||||
const QString& id, AnimNode::Triggers& triggersOut) {
|
||||
const QString& id, AnimVariantMap& triggersOut) {
|
||||
|
||||
const float EPSILON = 0.0001f;
|
||||
float frame = currentFrame;
|
||||
|
@ -79,12 +79,12 @@ float accumulateTime(float startFrame, float endFrame, float timeScale, float cu
|
|||
if (framesRemaining >= framesTillEnd) {
|
||||
if (loopFlag) {
|
||||
// anim loop
|
||||
triggersOut.push_back(id + "OnLoop");
|
||||
triggersOut.setTrigger(id + "OnLoop");
|
||||
framesRemaining -= framesTillEnd;
|
||||
frame = clampedStartFrame;
|
||||
} else {
|
||||
// anim end
|
||||
triggersOut.push_back(id + "OnDone");
|
||||
triggersOut.setTrigger(id + "OnDone");
|
||||
frame = endFrame;
|
||||
framesRemaining = 0.0f;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A
|
|||
glm::quat averageQuats(size_t numQuats, const glm::quat* quats);
|
||||
|
||||
float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag,
|
||||
const QString& id, AnimNode::Triggers& triggersOut);
|
||||
const QString& id, AnimVariantMap& triggersOut);
|
||||
|
||||
inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) {
|
||||
// adjust signs if necessary
|
||||
|
|
|
@ -59,6 +59,21 @@ const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 0.9f, 0.0f);
|
|||
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 0.9f, 0.0f);
|
||||
const glm::vec3 DEFAULT_HEAD_POS(0.0f, 0.75f, 0.0f);
|
||||
|
||||
static const QString LEFT_FOOT_POSITION("leftFootPosition");
|
||||
static const QString LEFT_FOOT_ROTATION("leftFootRotation");
|
||||
static const QString LEFT_FOOT_IK_POSITION_VAR("leftFootIKPositionVar");
|
||||
static const QString LEFT_FOOT_IK_ROTATION_VAR("leftFootIKRotationVar");
|
||||
static const QString MAIN_STATE_MACHINE_LEFT_FOOT_ROTATION("mainStateMachineLeftFootRotation");
|
||||
static const QString MAIN_STATE_MACHINE_LEFT_FOOT_POSITION("mainStateMachineLeftFootPosition");
|
||||
|
||||
static const QString RIGHT_FOOT_POSITION("rightFootPosition");
|
||||
static const QString RIGHT_FOOT_ROTATION("rightFootRotation");
|
||||
static const QString RIGHT_FOOT_IK_POSITION_VAR("rightFootIKPositionVar");
|
||||
static const QString RIGHT_FOOT_IK_ROTATION_VAR("rightFootIKRotationVar");
|
||||
static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_ROTATION("mainStateMachineRightFootRotation");
|
||||
static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_POSITION("mainStateMachineRightFootPosition");
|
||||
|
||||
|
||||
Rig::Rig() {
|
||||
// Ensure thread-safe access to the rigRegistry.
|
||||
std::lock_guard<std::mutex> guard(rigRegistryMutex);
|
||||
|
@ -1049,7 +1064,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
|||
getGeometryToRigTransform(), rigToWorldTransform);
|
||||
|
||||
// evaluate the animation
|
||||
AnimNode::Triggers triggersOut;
|
||||
AnimVariantMap triggersOut;
|
||||
|
||||
_internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut);
|
||||
if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) {
|
||||
|
@ -1057,9 +1072,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
|||
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
|
||||
}
|
||||
_animVars.clearTriggers();
|
||||
for (auto& trigger : triggersOut) {
|
||||
_animVars.setTrigger(trigger);
|
||||
}
|
||||
_animVars = triggersOut;
|
||||
}
|
||||
applyOverridePoses();
|
||||
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
|
||||
|
@ -1241,7 +1254,7 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJoin
|
|||
}
|
||||
|
||||
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
|
||||
bool leftArmEnabled, bool rightArmEnabled, float dt,
|
||||
bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
|
||||
|
@ -1305,7 +1318,13 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
|
||||
_animVars.unset("leftHandPosition");
|
||||
_animVars.unset("leftHandRotation");
|
||||
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||
|
||||
if (headEnabled) {
|
||||
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||
} else {
|
||||
// disable hand IK for desktop mode
|
||||
_animVars.set("leftHandType", (int)IKTarget::Type::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
if (rightHandEnabled) {
|
||||
|
@ -1364,21 +1383,41 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
|
||||
_animVars.unset("rightHandPosition");
|
||||
_animVars.unset("rightHandRotation");
|
||||
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||
|
||||
if (headEnabled) {
|
||||
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||
} else {
|
||||
// disable hand IK for desktop mode
|
||||
_animVars.set("rightHandType", (int)IKTarget::Type::Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled,
|
||||
const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) {
|
||||
|
||||
const float KNEE_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
||||
|
||||
int hipsIndex = indexOfJoint("Hips");
|
||||
const float KNEE_POLE_VECTOR_BLEND_FACTOR = 0.85f;
|
||||
|
||||
if (headEnabled) {
|
||||
// always do IK if head is enabled
|
||||
_animVars.set("leftFootIKEnabled", true);
|
||||
_animVars.set("rightFootIKEnabled", true);
|
||||
} else {
|
||||
// only do IK if we have a valid foot.
|
||||
_animVars.set("leftFootIKEnabled", leftFootEnabled);
|
||||
_animVars.set("rightFootIKEnabled", rightFootEnabled);
|
||||
}
|
||||
|
||||
if (leftFootEnabled) {
|
||||
_animVars.set("leftFootPosition", leftFootPose.trans());
|
||||
_animVars.set("leftFootRotation", leftFootPose.rot());
|
||||
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||
|
||||
_animVars.set(LEFT_FOOT_POSITION, leftFootPose.trans());
|
||||
_animVars.set(LEFT_FOOT_ROTATION, leftFootPose.rot());
|
||||
|
||||
// We want to drive the IK directly from the trackers.
|
||||
_animVars.set(LEFT_FOOT_IK_POSITION_VAR, LEFT_FOOT_POSITION);
|
||||
_animVars.set(LEFT_FOOT_IK_ROTATION_VAR, LEFT_FOOT_ROTATION);
|
||||
|
||||
int footJointIndex = _animSkeleton->nameToJointIndex("LeftFoot");
|
||||
int kneeJointIndex = _animSkeleton->nameToJointIndex("LeftLeg");
|
||||
|
@ -1396,20 +1435,25 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose
|
|||
_prevLeftFootPoleVector = smoothDeltaRot * _prevLeftFootPoleVector;
|
||||
|
||||
_animVars.set("leftFootPoleVectorEnabled", true);
|
||||
_animVars.set("leftFootPoleReferenceVector", Vectors::UNIT_Z);
|
||||
_animVars.set("leftFootPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftFootPoleVector));
|
||||
} else {
|
||||
_animVars.unset("leftFootPosition");
|
||||
_animVars.unset("leftFootRotation");
|
||||
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||
// We want to drive the IK from the underlying animation.
|
||||
// This gives us the ability to squat while in the HMD, without the feet from dipping under the floor.
|
||||
_animVars.set(LEFT_FOOT_IK_POSITION_VAR, MAIN_STATE_MACHINE_LEFT_FOOT_POSITION);
|
||||
_animVars.set(LEFT_FOOT_IK_ROTATION_VAR, MAIN_STATE_MACHINE_LEFT_FOOT_ROTATION);
|
||||
|
||||
// We want to match the animated knee pose as close as possible, so don't use poleVectors
|
||||
_animVars.set("leftFootPoleVectorEnabled", false);
|
||||
_prevLeftFootPoleVectorValid = false;
|
||||
}
|
||||
|
||||
if (rightFootEnabled) {
|
||||
_animVars.set("rightFootPosition", rightFootPose.trans());
|
||||
_animVars.set("rightFootRotation", rightFootPose.rot());
|
||||
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||
_animVars.set(RIGHT_FOOT_POSITION, rightFootPose.trans());
|
||||
_animVars.set(RIGHT_FOOT_ROTATION, rightFootPose.rot());
|
||||
|
||||
// We want to drive the IK directly from the trackers.
|
||||
_animVars.set(RIGHT_FOOT_IK_POSITION_VAR, RIGHT_FOOT_POSITION);
|
||||
_animVars.set(RIGHT_FOOT_IK_ROTATION_VAR, RIGHT_FOOT_ROTATION);
|
||||
|
||||
int footJointIndex = _animSkeleton->nameToJointIndex("RightFoot");
|
||||
int kneeJointIndex = _animSkeleton->nameToJointIndex("RightLeg");
|
||||
|
@ -1427,13 +1471,16 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose
|
|||
_prevRightFootPoleVector = smoothDeltaRot * _prevRightFootPoleVector;
|
||||
|
||||
_animVars.set("rightFootPoleVectorEnabled", true);
|
||||
_animVars.set("rightFootPoleReferenceVector", Vectors::UNIT_Z);
|
||||
_animVars.set("rightFootPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightFootPoleVector));
|
||||
} else {
|
||||
_animVars.unset("rightFootPosition");
|
||||
_animVars.unset("rightFootRotation");
|
||||
// We want to drive the IK from the underlying animation.
|
||||
// This gives us the ability to squat while in the HMD, without the feet from dipping under the floor.
|
||||
_animVars.set(RIGHT_FOOT_IK_POSITION_VAR, MAIN_STATE_MACHINE_RIGHT_FOOT_POSITION);
|
||||
_animVars.set(RIGHT_FOOT_IK_ROTATION_VAR, MAIN_STATE_MACHINE_RIGHT_FOOT_ROTATION);
|
||||
|
||||
// We want to match the animated knee pose as close as possible, so don't use poleVectors
|
||||
_animVars.set("rightFootPoleVectorEnabled", false);
|
||||
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||
_prevRightFootPoleVectorValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1467,6 +1514,10 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
|
|||
deltaQuat = glm::angleAxis(glm::clamp(glm::angle(deltaQuat), -MAX_ANGLE, MAX_ANGLE), glm::axis(deltaQuat));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// directly set absolutePose rotation
|
||||
_internalPoseSet._absolutePoses[index].rot() = deltaQuat * headQuat;
|
||||
|
||||
|
@ -1561,31 +1612,18 @@ bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex,
|
|||
return true;
|
||||
}
|
||||
|
||||
// returns a poleVector for the knees that is a blend of the foot and the hips.
|
||||
// targetFootPose is in rig space
|
||||
// result poleVector is also in rig space.
|
||||
glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, int kneeIndex, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const {
|
||||
const float FOOT_THETA = 0.8969f; // 51.39 degrees
|
||||
const glm::vec3 localFootForward(0.0f, cosf(FOOT_THETA), sinf(FOOT_THETA));
|
||||
|
||||
glm::vec3 footForward = targetFootPose.rot() * localFootForward;
|
||||
AnimPose hipsPose = _externalPoseSet._absolutePoses[hipsIndex];
|
||||
AnimPose footPose = targetFootPose;
|
||||
AnimPose kneePose = _externalPoseSet._absolutePoses[kneeIndex];
|
||||
AnimPose upLegPose = _externalPoseSet._absolutePoses[upLegIndex];
|
||||
glm::vec3 hipsForward = hipsPose.rot() * Vectors::UNIT_Z;
|
||||
|
||||
// ray from foot to upLeg
|
||||
glm::vec3 d = glm::normalize(footPose.trans() - upLegPose.trans());
|
||||
|
||||
// form a plane normal to the hips x-axis
|
||||
glm::vec3 n = hipsPose.rot() * Vectors::UNIT_X;
|
||||
|
||||
// project d onto this plane
|
||||
glm::vec3 dProj = d - glm::dot(d, n) * n;
|
||||
|
||||
// rotate dProj by 90 degrees to get the poleVector.
|
||||
glm::vec3 poleVector = glm::angleAxis(-PI / 2.0f, n) * dProj;
|
||||
|
||||
// blend the foot oreintation into the pole vector
|
||||
glm::quat kneeToFootDelta = footPose.rot() * glm::inverse(kneePose.rot());
|
||||
const float WRIST_POLE_ADJUST_FACTOR = 0.5f;
|
||||
glm::quat poleAdjust = quatLerp(Quaternions::IDENTITY, kneeToFootDelta, WRIST_POLE_ADJUST_FACTOR);
|
||||
|
||||
return glm::normalize(poleAdjust * poleVector);
|
||||
return glm::normalize(lerp(hipsForward, footForward, 0.75f));
|
||||
}
|
||||
|
||||
void Rig::updateFromControllerParameters(const ControllerParameters& params, float dt) {
|
||||
|
@ -1610,12 +1648,12 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
|||
|
||||
updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]);
|
||||
|
||||
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, dt,
|
||||
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, headEnabled, dt,
|
||||
params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand],
|
||||
params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo,
|
||||
params.rigToSensorMatrix, sensorToRigMatrix);
|
||||
|
||||
updateFeet(leftFootEnabled, rightFootEnabled,
|
||||
updateFeet(leftFootEnabled, rightFootEnabled, headEnabled,
|
||||
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot],
|
||||
params.rigToSensorMatrix, sensorToRigMatrix);
|
||||
|
||||
|
|
|
@ -75,6 +75,10 @@ public:
|
|||
};
|
||||
|
||||
struct ControllerParameters {
|
||||
ControllerParameters() {
|
||||
memset(primaryControllerFlags, 0, NumPrimaryControllerTypes);
|
||||
memset(secondaryControllerFlags, 0, NumPrimaryControllerTypes);
|
||||
}
|
||||
glm::mat4 rigToSensorMatrix;
|
||||
AnimPose primaryControllerPoses[NumPrimaryControllerTypes]; // rig space
|
||||
uint8_t primaryControllerFlags[NumPrimaryControllerTypes];
|
||||
|
@ -229,12 +233,13 @@ protected:
|
|||
|
||||
void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix);
|
||||
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
|
||||
bool leftArmEnabled, bool rightArmEnabled, float dt,
|
||||
bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
|
||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
|
||||
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled,
|
||||
const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
|
||||
|
||||
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
||||
|
|
Loading…
Reference in a new issue