mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 14:09:01 +02:00
Merge pull request #10822 from hyperlogic/feature/no-toe-wiggle
Eliminated toe wiggle while pucks are enabled.
This commit is contained in:
commit
1e23fa7baa
10 changed files with 157 additions and 20 deletions
|
@ -150,28 +150,19 @@
|
||||||
"children": []
|
"children": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "hipsManipulatorOverlay",
|
"id": "defaultPoseOverlay",
|
||||||
"type": "overlay",
|
"type": "overlay",
|
||||||
"data": {
|
"data": {
|
||||||
"alpha": 0.0,
|
"alpha": 0.0,
|
||||||
"boneSet": "hipsOnly"
|
"alphaVar": "defaultPoseOverlayAlpha",
|
||||||
|
"boneSet": "fullBody",
|
||||||
|
"boneSetVar": "defaultPoseOverlayBoneSet"
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "hipsManipulator",
|
"id": "defaultPose",
|
||||||
"type": "manipulator",
|
"type": "defaultPose",
|
||||||
"data": {
|
"data": {
|
||||||
"alpha": 0.0,
|
|
||||||
"alphaVar": "hipsManipulatorAlpha",
|
|
||||||
"joints": [
|
|
||||||
{
|
|
||||||
"jointName": "Hips",
|
|
||||||
"rotationType": "absolute",
|
|
||||||
"translationType": "absolute",
|
|
||||||
"rotationVar": "hipsManipulatorRotation",
|
|
||||||
"translationVar": "hipsManipulatorPosition"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"children": []
|
"children": []
|
||||||
},
|
},
|
||||||
|
|
34
libraries/animation/src/AnimDefaultPose.cpp
Normal file
34
libraries/animation/src/AnimDefaultPose.cpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
//
|
||||||
|
// AnimDefaultPose.cpp
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 6/26/17.
|
||||||
|
// Copyright (c) 2017 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 "AnimDefaultPose.h"
|
||||||
|
|
||||||
|
AnimDefaultPose::AnimDefaultPose(const QString& id) :
|
||||||
|
AnimNode(AnimNode::Type::DefaultPose, id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimDefaultPose::~AnimDefaultPose() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimPoseVec& AnimDefaultPose::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||||
|
if (_skeleton) {
|
||||||
|
_poses = _skeleton->getRelativeDefaultPoses();
|
||||||
|
} else {
|
||||||
|
_poses.clear();
|
||||||
|
}
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimPoseVec& AnimDefaultPose::getPosesInternal() const {
|
||||||
|
return _poses;
|
||||||
|
}
|
36
libraries/animation/src/AnimDefaultPose.h
Normal file
36
libraries/animation/src/AnimDefaultPose.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
//
|
||||||
|
// AnimDefaultPose.h
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 6/26/17.
|
||||||
|
// Copyright (c) 2017 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_AnimDefaultPose_h
|
||||||
|
#define hifi_AnimDefaultPose_h
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "AnimNode.h"
|
||||||
|
|
||||||
|
// Always returns the default pose of the current skeleton.
|
||||||
|
|
||||||
|
class AnimDefaultPose : public AnimNode {
|
||||||
|
public:
|
||||||
|
AnimDefaultPose(const QString& id);
|
||||||
|
virtual ~AnimDefaultPose() override;
|
||||||
|
|
||||||
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||||
|
protected:
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||||
|
|
||||||
|
AnimPoseVec _poses;
|
||||||
|
|
||||||
|
// no copies
|
||||||
|
AnimDefaultPose(const AnimDefaultPose&) = delete;
|
||||||
|
AnimDefaultPose& operator=(const AnimDefaultPose&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AnimDefaultPose_h
|
|
@ -870,9 +870,9 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
||||||
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
|
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
|
||||||
glm::vec3 hipsOffset = scaleFactor * _hipsOffset;
|
glm::vec3 hipsOffset = scaleFactor * _hipsOffset;
|
||||||
if (_hipsParentIndex == -1) {
|
if (_hipsParentIndex == -1) {
|
||||||
_relativePoses[_hipsIndex].trans() = underPoses[_hipsIndex].trans() + hipsOffset;
|
_relativePoses[_hipsIndex].trans() = _relativePoses[_hipsIndex].trans() + hipsOffset;
|
||||||
} else {
|
} else {
|
||||||
auto absHipsPose = _skeleton->getAbsolutePose(_hipsIndex, underPoses);
|
auto absHipsPose = _skeleton->getAbsolutePose(_hipsIndex, _relativePoses);
|
||||||
absHipsPose.trans() += hipsOffset;
|
absHipsPose.trans() += hipsOffset;
|
||||||
_relativePoses[_hipsIndex] = _skeleton->getAbsolutePose(_hipsParentIndex, _relativePoses).inverse() * absHipsPose;
|
_relativePoses[_hipsIndex] = _skeleton->getAbsolutePose(_hipsParentIndex, _relativePoses).inverse() * absHipsPose;
|
||||||
}
|
}
|
||||||
|
@ -1732,6 +1732,10 @@ void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource s
|
||||||
break;
|
break;
|
||||||
case SolutionSource::RelaxToLimitCenterPoses:
|
case SolutionSource::RelaxToLimitCenterPoses:
|
||||||
blendToPoses(_limitCenterPoses, underPoses, RELAX_BLEND_FACTOR);
|
blendToPoses(_limitCenterPoses, underPoses, RELAX_BLEND_FACTOR);
|
||||||
|
// special case for hips: copy over hips pose whether or not IK is enabled.
|
||||||
|
if (_hipsIndex >= 0 && _hipsIndex < (int)_relativePoses.size()) {
|
||||||
|
_relativePoses[_hipsIndex] = _limitCenterPoses[_hipsIndex];
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SolutionSource::PreviousSolution:
|
case SolutionSource::PreviousSolution:
|
||||||
// do nothing... _relativePoses is already the previous solution
|
// do nothing... _relativePoses is already the previous solution
|
||||||
|
|
|
@ -44,6 +44,7 @@ public:
|
||||||
StateMachine,
|
StateMachine,
|
||||||
Manipulator,
|
Manipulator,
|
||||||
InverseKinematics,
|
InverseKinematics,
|
||||||
|
DefaultPose,
|
||||||
NumTypes
|
NumTypes
|
||||||
};
|
};
|
||||||
using Pointer = std::shared_ptr<AnimNode>;
|
using Pointer = std::shared_ptr<AnimNode>;
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "AnimStateMachine.h"
|
#include "AnimStateMachine.h"
|
||||||
#include "AnimManipulator.h"
|
#include "AnimManipulator.h"
|
||||||
#include "AnimInverseKinematics.h"
|
#include "AnimInverseKinematics.h"
|
||||||
|
#include "AnimDefaultPose.h"
|
||||||
|
|
||||||
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
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);
|
using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
@ -35,6 +36,7 @@ static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QStri
|
||||||
static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
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 loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
|
||||||
// called after children have been loaded
|
// called after children have been loaded
|
||||||
// returns node on success, nullptr on failure.
|
// returns node on success, nullptr on failure.
|
||||||
|
@ -50,6 +52,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
|
||||||
case AnimNode::Type::StateMachine: return "stateMachine";
|
case AnimNode::Type::StateMachine: return "stateMachine";
|
||||||
case AnimNode::Type::Manipulator: return "manipulator";
|
case AnimNode::Type::Manipulator: return "manipulator";
|
||||||
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
||||||
|
case AnimNode::Type::DefaultPose: return "defaultPose";
|
||||||
case AnimNode::Type::NumTypes: return nullptr;
|
case AnimNode::Type::NumTypes: return nullptr;
|
||||||
};
|
};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -109,6 +112,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
||||||
case AnimNode::Type::StateMachine: return loadStateMachineNode;
|
case AnimNode::Type::StateMachine: return loadStateMachineNode;
|
||||||
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
||||||
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
||||||
|
case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;
|
||||||
case AnimNode::Type::NumTypes: return nullptr;
|
case AnimNode::Type::NumTypes: return nullptr;
|
||||||
};
|
};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -123,6 +127,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
||||||
case AnimNode::Type::StateMachine: return processStateMachineNode;
|
case AnimNode::Type::StateMachine: return processStateMachineNode;
|
||||||
case AnimNode::Type::Manipulator: return processDoNothing;
|
case AnimNode::Type::Manipulator: return processDoNothing;
|
||||||
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
||||||
|
case AnimNode::Type::DefaultPose: return processDoNothing;
|
||||||
case AnimNode::Type::NumTypes: return nullptr;
|
case AnimNode::Type::NumTypes: return nullptr;
|
||||||
};
|
};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -347,7 +352,8 @@ static const char* boneSetStrings[AnimOverlay::NumBoneSets] = {
|
||||||
"empty",
|
"empty",
|
||||||
"leftHand",
|
"leftHand",
|
||||||
"rightHand",
|
"rightHand",
|
||||||
"hipsOnly"
|
"hipsOnly",
|
||||||
|
"bothFeet"
|
||||||
};
|
};
|
||||||
|
|
||||||
static AnimOverlay::BoneSet stringToBoneSetEnum(const QString& str) {
|
static AnimOverlay::BoneSet stringToBoneSetEnum(const QString& str) {
|
||||||
|
@ -517,6 +523,11 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||||
|
auto node = std::make_shared<AnimDefaultPose>(id);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
void buildChildMap(std::map<QString, int>& map, AnimNode::Pointer node) {
|
void buildChildMap(std::map<QString, int>& map, AnimNode::Pointer node) {
|
||||||
for (int i = 0; i < (int)node->getChildCount(); ++i) {
|
for (int i = 0; i < (int)node->getChildCount(); ++i) {
|
||||||
map.insert(std::pair<QString, int>(node->getChild(i)->getID(), i));
|
map.insert(std::pair<QString, int>(node->getChild(i)->getID(), i));
|
||||||
|
|
|
@ -35,6 +35,7 @@ void AnimOverlay::buildBoneSet(BoneSet boneSet) {
|
||||||
case LeftHandBoneSet: buildLeftHandBoneSet(); break;
|
case LeftHandBoneSet: buildLeftHandBoneSet(); break;
|
||||||
case RightHandBoneSet: buildRightHandBoneSet(); break;
|
case RightHandBoneSet: buildRightHandBoneSet(); break;
|
||||||
case HipsOnlyBoneSet: buildHipsOnlyBoneSet(); break;
|
case HipsOnlyBoneSet: buildHipsOnlyBoneSet(); break;
|
||||||
|
case BothFeetBoneSet: buildBothFeetBoneSet(); break;
|
||||||
default:
|
default:
|
||||||
case EmptyBoneSet: buildEmptyBoneSet(); break;
|
case EmptyBoneSet: buildEmptyBoneSet(); break;
|
||||||
}
|
}
|
||||||
|
@ -196,6 +197,20 @@ void AnimOverlay::buildHipsOnlyBoneSet() {
|
||||||
_boneSetVec[hipsJoint] = 1.0f;
|
_boneSetVec[hipsJoint] = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimOverlay::buildBothFeetBoneSet() {
|
||||||
|
assert(_skeleton);
|
||||||
|
buildEmptyBoneSet();
|
||||||
|
int rightFoot = _skeleton->nameToJointIndex("RightFoot");
|
||||||
|
for_each_child_joint(_skeleton, rightFoot, [&](int i) {
|
||||||
|
_boneSetVec[i] = 1.0f;
|
||||||
|
});
|
||||||
|
int leftFoot = _skeleton->nameToJointIndex("LeftFoot");
|
||||||
|
for_each_child_joint(_skeleton, leftFoot, [&](int i) {
|
||||||
|
_boneSetVec[i] = 1.0f;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// for AnimDebugDraw rendering
|
// for AnimDebugDraw rendering
|
||||||
const AnimPoseVec& AnimOverlay::getPosesInternal() const {
|
const AnimPoseVec& AnimOverlay::getPosesInternal() const {
|
||||||
return _poses;
|
return _poses;
|
||||||
|
|
|
@ -38,6 +38,7 @@ public:
|
||||||
LeftHandBoneSet,
|
LeftHandBoneSet,
|
||||||
RightHandBoneSet,
|
RightHandBoneSet,
|
||||||
HipsOnlyBoneSet,
|
HipsOnlyBoneSet,
|
||||||
|
BothFeetBoneSet,
|
||||||
NumBoneSets
|
NumBoneSets
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,6 +78,7 @@ public:
|
||||||
void buildLeftHandBoneSet();
|
void buildLeftHandBoneSet();
|
||||||
void buildRightHandBoneSet();
|
void buildRightHandBoneSet();
|
||||||
void buildHipsOnlyBoneSet();
|
void buildHipsOnlyBoneSet();
|
||||||
|
void buildBothFeetBoneSet();
|
||||||
|
|
||||||
// no copies
|
// no copies
|
||||||
AnimOverlay(const AnimOverlay&) = delete;
|
AnimOverlay(const AnimOverlay&) = delete;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
#include "AnimClip.h"
|
#include "AnimClip.h"
|
||||||
#include "AnimInverseKinematics.h"
|
#include "AnimInverseKinematics.h"
|
||||||
|
#include "AnimOverlay.h"
|
||||||
#include "AnimSkeleton.h"
|
#include "AnimSkeleton.h"
|
||||||
#include "AnimUtil.h"
|
#include "AnimUtil.h"
|
||||||
#include "IKTarget.h"
|
#include "IKTarget.h"
|
||||||
|
@ -1459,13 +1460,28 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
||||||
updateFeet(leftFootEnabled, rightFootEnabled,
|
updateFeet(leftFootEnabled, rightFootEnabled,
|
||||||
params.controllerPoses[ControllerType_LeftFoot], params.controllerPoses[ControllerType_RightFoot]);
|
params.controllerPoses[ControllerType_LeftFoot], params.controllerPoses[ControllerType_RightFoot]);
|
||||||
|
|
||||||
if (hipsEnabled) {
|
// if the hips or the feet are being controlled.
|
||||||
|
if (hipsEnabled || rightFootEnabled || leftFootEnabled) {
|
||||||
|
// for more predictable IK solve from the center of the joint limits, not from the underpose
|
||||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToLimitCenterPoses);
|
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToLimitCenterPoses);
|
||||||
|
|
||||||
|
// replace the feet animation with the default pose, this is to prevent unexpected toe wiggling.
|
||||||
|
_animVars.set("defaultPoseOverlayAlpha", 1.0f);
|
||||||
|
_animVars.set("defaultPoseOverlayBoneSet", (int)AnimOverlay::BothFeetBoneSet);
|
||||||
|
} else {
|
||||||
|
// augment the IK with the underPose.
|
||||||
|
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToUnderPoses);
|
||||||
|
|
||||||
|
// feet should follow source animation
|
||||||
|
_animVars.unset("defaultPoseOverlayAlpha");
|
||||||
|
_animVars.unset("defaultPoseOverlayBoneSet");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hipsEnabled) {
|
||||||
_animVars.set("hipsType", (int)IKTarget::Type::RotationAndPosition);
|
_animVars.set("hipsType", (int)IKTarget::Type::RotationAndPosition);
|
||||||
_animVars.set("hipsPosition", params.controllerPoses[ControllerType_Hips].trans());
|
_animVars.set("hipsPosition", params.controllerPoses[ControllerType_Hips].trans());
|
||||||
_animVars.set("hipsRotation", params.controllerPoses[ControllerType_Hips].rot());
|
_animVars.set("hipsRotation", params.controllerPoses[ControllerType_Hips].rot());
|
||||||
} else {
|
} else {
|
||||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToUnderPoses);
|
|
||||||
_animVars.set("hipsType", (int)IKTarget::Type::Unknown);
|
_animVars.set("hipsType", (int)IKTarget::Type::Unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
27
tools/avatar-json-to-dot.js
Normal file
27
tools/avatar-json-to-dot.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// usage:
|
||||||
|
// node avatar-json-to-dot.js /path/to/avatar-animaton.json > out.dot
|
||||||
|
//
|
||||||
|
// Then if you have graphviz installed you can run the following command to generate a png.
|
||||||
|
// dot -Tpng out.dot > out.png
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var filename = process.argv[2];
|
||||||
|
|
||||||
|
function dumpNodes(node) {
|
||||||
|
node.children.forEach(function (child) {
|
||||||
|
console.log(' ' + node.id + ' -> ' + child.id + ';');
|
||||||
|
dumpNodes(child);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.readFile(filename, 'utf8', function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.log('error opening ' + filename + ', err = ' + err);
|
||||||
|
} else {
|
||||||
|
var graph = JSON.parse(data);
|
||||||
|
console.log('digraph graphname {');
|
||||||
|
console.log(' rankdir = "LR";');
|
||||||
|
dumpNodes(graph.root);
|
||||||
|
console.log('}');
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in a new issue