mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 19:43:49 +02:00
Added AnimBlendLinearMove node
AnimBlendLinearMove is now in use by forward, backward and strafe movements. Tuned rig moving average speeds to be more sensitive.
This commit is contained in:
parent
a9ed033b20
commit
a66f31bb20
7 changed files with 290 additions and 55 deletions
|
@ -527,13 +527,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "walkFwd",
|
"id": "walkFwd",
|
||||||
"type": "blendLinear",
|
"type": "blendLinearMove",
|
||||||
"data": {
|
"data": {
|
||||||
"alpha": 0.0,
|
"alpha": 0.0,
|
||||||
"sync": true,
|
"desiredSpeed": 1.4,
|
||||||
"timeScale": 1.0,
|
"characteristicSpeeds": [0.5, 1.4, 4.5],
|
||||||
"timeScaleVar": "moveForwardTimeScale",
|
"alphaVar": "moveForwardAlpha",
|
||||||
"alphaVar": "moveForwardAlpha"
|
"desiredSpeedVar": "moveForwardSpeed"
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
|
@ -576,13 +576,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "walkBwd",
|
"id": "walkBwd",
|
||||||
"type": "blendLinear",
|
"type": "blendLinearMove",
|
||||||
"data": {
|
"data": {
|
||||||
"alpha": 1.0,
|
"alpha": 0.0,
|
||||||
"sync": true,
|
"desiredSpeed": 1.4,
|
||||||
"timeScale": 1.0,
|
"characteristicSpeeds": [0.6, 1.45],
|
||||||
"timeScaleVar": "moveBackwardTimeScale",
|
"alphaVar": "moveBackwardAlpha",
|
||||||
"alphaVar": "moveBackwardAlpha"
|
"desiredSpeedVar": "moveBackwardSpeed"
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
|
@ -637,13 +637,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "strafeLeft",
|
"id": "strafeLeft",
|
||||||
"type": "blendLinear",
|
"type": "blendLinearMove",
|
||||||
"data": {
|
"data": {
|
||||||
"alpha": 0.0,
|
"alpha": 0.0,
|
||||||
"sync": true,
|
"desiredSpeed": 1.4,
|
||||||
"timeScale": 1.0,
|
"characteristicSpeeds": [0.2, 0.65],
|
||||||
"timeScaleVar": "moveLateralTimeScale",
|
"alphaVar": "moveLateralAlpha",
|
||||||
"alphaVar": "moveLateralAlpha"
|
"desiredSpeedVar": "moveLateralSpeed"
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
|
@ -674,13 +674,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "strafeRight",
|
"id": "strafeRight",
|
||||||
"type": "blendLinear",
|
"type": "blendLinearMove",
|
||||||
"data": {
|
"data": {
|
||||||
"alpha": 0.0,
|
"alpha": 0.0,
|
||||||
"sync": true,
|
"desiredSpeed": 1.4,
|
||||||
"timeScale": 1.0,
|
"characteristicSpeeds": [0.2, 0.65],
|
||||||
"timeScaleVar": "moveLateralTimeScale",
|
"alphaVar": "moveLateralAlpha",
|
||||||
"alphaVar": "moveLateralAlpha"
|
"desiredSpeedVar": "moveLateralSpeed"
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
|
|
126
libraries/animation/src/AnimBlendLinearMove.cpp
Normal file
126
libraries/animation/src/AnimBlendLinearMove.cpp
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
//
|
||||||
|
// AnimBlendLinearMove.cpp
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 10/22/15.
|
||||||
|
// Copyright (c) 2015 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 "AnimBlendLinearMove.h"
|
||||||
|
#include "GLMHelpers.h"
|
||||||
|
#include "AnimationLogging.h"
|
||||||
|
#include "AnimUtil.h"
|
||||||
|
#include "AnimClip.h"
|
||||||
|
|
||||||
|
AnimBlendLinearMove::AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector<float>& characteristicSpeeds) :
|
||||||
|
AnimNode(AnimNode::Type::BlendLinearMove, id),
|
||||||
|
_alpha(alpha),
|
||||||
|
_desiredSpeed(desiredSpeed),
|
||||||
|
_characteristicSpeeds(characteristicSpeeds) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimBlendLinearMove::~AnimBlendLinearMove() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
|
||||||
|
|
||||||
|
assert(_children.size() == _characteristicSpeeds.size());
|
||||||
|
|
||||||
|
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||||
|
_desiredSpeed = animVars.lookup(_desiredSpeedVar, _desiredSpeed);
|
||||||
|
|
||||||
|
if (_children.size() == 0) {
|
||||||
|
for (auto&& pose : _poses) {
|
||||||
|
pose = AnimPose::identity;
|
||||||
|
}
|
||||||
|
} else if (_children.size() == 1) {
|
||||||
|
const float alpha = 0.0f;
|
||||||
|
const int prevPoseIndex = 0;
|
||||||
|
const int nextPoseIndex = 0;
|
||||||
|
float prevDeltaTime, nextDeltaTime;
|
||||||
|
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
||||||
|
evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
|
||||||
|
size_t prevPoseIndex = glm::floor(clampedAlpha);
|
||||||
|
size_t nextPoseIndex = glm::ceil(clampedAlpha);
|
||||||
|
float alpha = glm::fract(clampedAlpha);
|
||||||
|
float prevDeltaTime, nextDeltaTime;
|
||||||
|
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
||||||
|
evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
||||||
|
}
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
const AnimPoseVec& AnimBlendLinearMove::getPosesInternal() const {
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha,
|
||||||
|
size_t prevPoseIndex, size_t nextPoseIndex,
|
||||||
|
float prevDeltaTime, float nextDeltaTime) {
|
||||||
|
if (prevPoseIndex == nextPoseIndex) {
|
||||||
|
// this can happen if alpha is on an integer boundary
|
||||||
|
_poses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut);
|
||||||
|
} else {
|
||||||
|
// need to eval and blend between two children.
|
||||||
|
auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut);
|
||||||
|
auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextDeltaTime, triggersOut);
|
||||||
|
|
||||||
|
if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) {
|
||||||
|
_poses.resize(prevPoses.size());
|
||||||
|
|
||||||
|
::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
|
||||||
|
float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut) {
|
||||||
|
|
||||||
|
const float FRAMES_PER_SECOND = 30.0f;
|
||||||
|
auto prevClipNode = std::dynamic_pointer_cast<AnimClip>(_children[prevPoseIndex]);
|
||||||
|
assert(prevClipNode);
|
||||||
|
auto nextClipNode = std::dynamic_pointer_cast<AnimClip>(_children[nextPoseIndex]);
|
||||||
|
assert(nextClipNode);
|
||||||
|
|
||||||
|
float v0 = _characteristicSpeeds[prevPoseIndex];
|
||||||
|
float n0 = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f;
|
||||||
|
float v1 = _characteristicSpeeds[nextPoseIndex];
|
||||||
|
float n1 = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) + 1.0f;
|
||||||
|
|
||||||
|
// rate of change in phase space, necessary to achive desired speed.
|
||||||
|
float omega = (_desiredSpeed * FRAMES_PER_SECOND) / ((1.0f - alpha) * v0 * n0 + alpha * v1 * n1);
|
||||||
|
|
||||||
|
float f0 = prevClipNode->getStartFrame() + _phase * n0;
|
||||||
|
prevClipNode->setCurrentFrame(f0);
|
||||||
|
|
||||||
|
float f1 = nextClipNode->getStartFrame() + _phase * n1;
|
||||||
|
nextClipNode->setCurrentFrame(f1);
|
||||||
|
|
||||||
|
// integrate phase forward in time.
|
||||||
|
_phase += omega * dt;
|
||||||
|
|
||||||
|
// detect loop trigger events
|
||||||
|
if (_phase >= 1.0f) {
|
||||||
|
triggersOut.push_back(_id + "Loop");
|
||||||
|
_phase = glm::fract(_phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
*prevDeltaTimeOut = omega * dt * (n0 / FRAMES_PER_SECOND);
|
||||||
|
*nextDeltaTimeOut = omega * dt * (n1 / FRAMES_PER_SECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimBlendLinearMove::setCurrentFrameInternal(float frame) {
|
||||||
|
assert(_children.size() > 0);
|
||||||
|
auto clipNode = std::dynamic_pointer_cast<AnimClip>(_children.front());
|
||||||
|
assert(clipNode);
|
||||||
|
const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f;
|
||||||
|
_phase = fmodf(frame, NUM_FRAMES);
|
||||||
|
}
|
77
libraries/animation/src/AnimBlendLinearMove.h
Normal file
77
libraries/animation/src/AnimBlendLinearMove.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// AnimBlendLinearMove.h
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 10/22/15.
|
||||||
|
// Copyright (c) 2015 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_AnimBlendLinearMove_h
|
||||||
|
#define hifi_AnimBlendLinearMove_h
|
||||||
|
|
||||||
|
#include "AnimNode.h"
|
||||||
|
|
||||||
|
// Synced linear blend between two AnimNodes, where the playback speed of
|
||||||
|
// the animation is timeScaled to match movement speed.
|
||||||
|
//
|
||||||
|
// Each child animation is associated with a chracteristic speed.
|
||||||
|
// This defines the speed of that animation when played at the normal playback rate, 30 frames per second.
|
||||||
|
//
|
||||||
|
// The user also specifies a desired speed. This desired speed is used to timescale
|
||||||
|
// the animation to achive the desired movement velocity.
|
||||||
|
//
|
||||||
|
// Blending is determined by the alpha parameter.
|
||||||
|
// If the number of children is 2, then the alpha parameters should be between
|
||||||
|
// 0 and 1. The first animation will have a (1 - alpha) factor, and the second
|
||||||
|
// will have factor of alpha.
|
||||||
|
//
|
||||||
|
// This node supports more then 2 children. In this case the alpha should be
|
||||||
|
// between 0 and n - 1. This alpha can be used to linearly interpolate between
|
||||||
|
// the closest two children poses. This can be used to sweep through a series
|
||||||
|
// of animation poses.
|
||||||
|
|
||||||
|
class AnimBlendLinearMove : public AnimNode {
|
||||||
|
public:
|
||||||
|
friend class AnimTests;
|
||||||
|
|
||||||
|
AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector<float>& characteristicSpeeds);
|
||||||
|
virtual ~AnimBlendLinearMove() override;
|
||||||
|
|
||||||
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
|
||||||
|
|
||||||
|
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||||
|
void setDesiredSpeedVar(const QString& desiredSpeedVar) { _desiredSpeedVar = desiredSpeedVar; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||||
|
|
||||||
|
void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& 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);
|
||||||
|
|
||||||
|
virtual void setCurrentFrameInternal(float frame) override;
|
||||||
|
|
||||||
|
AnimPoseVec _poses;
|
||||||
|
|
||||||
|
float _alpha;
|
||||||
|
float _desiredSpeed;
|
||||||
|
|
||||||
|
float _phase = 0.0f;
|
||||||
|
|
||||||
|
QString _alphaVar;
|
||||||
|
QString _desiredSpeedVar;
|
||||||
|
|
||||||
|
std::vector<float> _characteristicSpeeds;
|
||||||
|
|
||||||
|
// no copies
|
||||||
|
AnimBlendLinearMove(const AnimBlendLinearMove&) = delete;
|
||||||
|
AnimBlendLinearMove& operator=(const AnimBlendLinearMove&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AnimBlendLinearMove_h
|
|
@ -38,6 +38,7 @@ public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Clip = 0,
|
Clip = 0,
|
||||||
BlendLinear,
|
BlendLinear,
|
||||||
|
BlendLinearMove,
|
||||||
Overlay,
|
Overlay,
|
||||||
StateMachine,
|
StateMachine,
|
||||||
Manipulator,
|
Manipulator,
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "AnimNode.h"
|
#include "AnimNode.h"
|
||||||
#include "AnimClip.h"
|
#include "AnimClip.h"
|
||||||
#include "AnimBlendLinear.h"
|
#include "AnimBlendLinear.h"
|
||||||
|
#include "AnimBlendLinearMove.h"
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
#include "AnimOverlay.h"
|
#include "AnimOverlay.h"
|
||||||
#include "AnimNodeLoader.h"
|
#include "AnimNodeLoader.h"
|
||||||
|
@ -29,6 +30,7 @@ using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& json
|
||||||
// factory functions
|
// factory functions
|
||||||
static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadOverlayNode(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 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);
|
||||||
|
@ -36,17 +38,14 @@ static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, c
|
||||||
|
|
||||||
// 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.
|
||||||
static bool processClipNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
||||||
static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
|
||||||
static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
|
||||||
bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static bool processManipulatorNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
|
||||||
static bool processInverseKinematicsNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
|
||||||
|
|
||||||
static const char* animNodeTypeToString(AnimNode::Type type) {
|
static const char* animNodeTypeToString(AnimNode::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AnimNode::Type::Clip: return "clip";
|
case AnimNode::Type::Clip: return "clip";
|
||||||
case AnimNode::Type::BlendLinear: return "blendLinear";
|
case AnimNode::Type::BlendLinear: return "blendLinear";
|
||||||
|
case AnimNode::Type::BlendLinearMove: return "blendLinearMove";
|
||||||
case AnimNode::Type::Overlay: return "overlay";
|
case AnimNode::Type::Overlay: return "overlay";
|
||||||
case AnimNode::Type::StateMachine: return "stateMachine";
|
case AnimNode::Type::StateMachine: return "stateMachine";
|
||||||
case AnimNode::Type::Manipulator: return "manipulator";
|
case AnimNode::Type::Manipulator: return "manipulator";
|
||||||
|
@ -60,6 +59,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AnimNode::Type::Clip: return loadClipNode;
|
case AnimNode::Type::Clip: return loadClipNode;
|
||||||
case AnimNode::Type::BlendLinear: return loadBlendLinearNode;
|
case AnimNode::Type::BlendLinear: return loadBlendLinearNode;
|
||||||
|
case AnimNode::Type::BlendLinearMove: return loadBlendLinearMoveNode;
|
||||||
case AnimNode::Type::Overlay: return loadOverlayNode;
|
case AnimNode::Type::Overlay: return loadOverlayNode;
|
||||||
case AnimNode::Type::StateMachine: return loadStateMachineNode;
|
case AnimNode::Type::StateMachine: return loadStateMachineNode;
|
||||||
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
||||||
|
@ -71,12 +71,13 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
||||||
|
|
||||||
static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AnimNode::Type::Clip: return processClipNode;
|
case AnimNode::Type::Clip: return processDoNothing;
|
||||||
case AnimNode::Type::BlendLinear: return processBlendLinearNode;
|
case AnimNode::Type::BlendLinear: return processDoNothing;
|
||||||
case AnimNode::Type::Overlay: return processOverlayNode;
|
case AnimNode::Type::BlendLinearMove: return processDoNothing;
|
||||||
|
case AnimNode::Type::Overlay: return processDoNothing;
|
||||||
case AnimNode::Type::StateMachine: return processStateMachineNode;
|
case AnimNode::Type::StateMachine: return processStateMachineNode;
|
||||||
case AnimNode::Type::Manipulator: return processManipulatorNode;
|
case AnimNode::Type::Manipulator: return processDoNothing;
|
||||||
case AnimNode::Type::InverseKinematics: return processInverseKinematicsNode;
|
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
||||||
case AnimNode::Type::NumTypes: return nullptr;
|
case AnimNode::Type::NumTypes: return nullptr;
|
||||||
};
|
};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -243,6 +244,45 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||||
|
|
||||||
|
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_FLOAT(desiredSpeed, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
|
||||||
|
std::vector<float> characteristicSpeeds;
|
||||||
|
auto speedsValue = jsonObj.value("characteristicSpeeds");
|
||||||
|
if (!speedsValue.isArray()) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad array \"characteristicSpeeds\" in blendLinearMove node, id =" << id << ", url =" << jsonUrl.toDisplayString();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto speedsArray = speedsValue.toArray();
|
||||||
|
for (const auto& speedValue : speedsArray) {
|
||||||
|
if (!speedValue.isDouble()) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad number in \"characteristicSpeeds\", id =" << id << ", url =" << jsonUrl.toDisplayString();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
float speedVal = (float)speedValue.toDouble();
|
||||||
|
characteristicSpeeds.push_back(speedVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
READ_OPTIONAL_STRING(alphaVar, jsonObj);
|
||||||
|
READ_OPTIONAL_STRING(desiredSpeedVar, jsonObj);
|
||||||
|
|
||||||
|
auto node = std::make_shared<AnimBlendLinearMove>(id, alpha, desiredSpeed, characteristicSpeeds);
|
||||||
|
|
||||||
|
if (!alphaVar.isEmpty()) {
|
||||||
|
node->setAlphaVar(alphaVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!desiredSpeedVar.isEmpty()) {
|
||||||
|
node->setDesiredSpeedVar(desiredSpeedVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char* boneSetStrings[AnimOverlay::NumBoneSets] = {
|
static const char* boneSetStrings[AnimOverlay::NumBoneSets] = {
|
||||||
"fullBody",
|
"fullBody",
|
||||||
"upperBody",
|
"upperBody",
|
||||||
|
|
|
@ -408,11 +408,11 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const {
|
||||||
return _jointStates[jointIndex].getTransform();
|
return _jointStates[jointIndex].getTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::calcAnimAlphaAndTimeScale(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut, float* timeScaleOut) const {
|
void Rig::calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const {
|
||||||
|
|
||||||
assert(referenceSpeeds.size() > 0);
|
assert(referenceSpeeds.size() > 0);
|
||||||
|
|
||||||
// first calculate alpha by lerping between speeds.
|
// calculate alpha from linear combination of referenceSpeeds.
|
||||||
float alpha = 0.0f;
|
float alpha = 0.0f;
|
||||||
if (speed <= referenceSpeeds.front()) {
|
if (speed <= referenceSpeeds.front()) {
|
||||||
alpha = 0.0f;
|
alpha = 0.0f;
|
||||||
|
@ -427,20 +427,12 @@ void Rig::calcAnimAlphaAndTimeScale(float speed, const std::vector<float>& refer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now keeping the alpha fixed compute the timeScale.
|
|
||||||
// NOTE: This makes the assumption that the velocity of a linear blend between two animations is also linear.
|
|
||||||
int prevIndex = glm::floor(alpha);
|
|
||||||
int nextIndex = glm::ceil(alpha);
|
|
||||||
float animSpeed = lerp(referenceSpeeds[prevIndex], referenceSpeeds[nextIndex], (float)glm::fract(alpha));
|
|
||||||
float timeScale = glm::clamp(0.5f, 2.0f, speed / animSpeed);
|
|
||||||
|
|
||||||
*alphaOut = alpha;
|
*alphaOut = alpha;
|
||||||
*timeScaleOut = timeScale;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// animation reference speeds.
|
// animation reference speeds.
|
||||||
static const std::vector<float> FORWARD_SPEEDS = { 0.4f, 1.4f, 2.5f }; // m/s
|
static const std::vector<float> FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s
|
||||||
static const std::vector<float> BACKWARD_SPEEDS = { 0.45f, 1.4f }; // m/s
|
static const std::vector<float> BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s
|
||||||
static const std::vector<float> LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s
|
static const std::vector<float> LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s
|
||||||
|
|
||||||
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
|
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
|
||||||
|
@ -477,22 +469,21 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
_animVars.set("sine", 2.0f * static_cast<float>(0.5 * sin(t) + 0.5));
|
_animVars.set("sine", 2.0f * static_cast<float>(0.5 * sin(t) + 0.5));
|
||||||
|
|
||||||
float moveForwardAlpha = 0.0f;
|
float moveForwardAlpha = 0.0f;
|
||||||
float moveForwardTimeScale = 1.0f;
|
|
||||||
float moveBackwardAlpha = 0.0f;
|
float moveBackwardAlpha = 0.0f;
|
||||||
float moveBackwardTimeScale = 1.0f;
|
|
||||||
float moveLateralAlpha = 0.0f;
|
float moveLateralAlpha = 0.0f;
|
||||||
float moveLateralTimeScale = 1.0f;
|
|
||||||
|
|
||||||
// calcuate the animation alpha and timeScale values based on current speeds and animation reference speeds.
|
// calcuate the animation alpha and timeScale values based on current speeds and animation reference speeds.
|
||||||
calcAnimAlphaAndTimeScale(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha, &moveForwardTimeScale);
|
calcAnimAlpha(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha);
|
||||||
calcAnimAlphaAndTimeScale(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha, &moveBackwardTimeScale);
|
calcAnimAlpha(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha);
|
||||||
calcAnimAlphaAndTimeScale(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha, &moveLateralTimeScale);
|
calcAnimAlpha(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha);
|
||||||
|
|
||||||
_animVars.set("moveFowardTimeScale", moveForwardTimeScale);
|
_animVars.set("moveForwardSpeed", _averageForwardSpeed.getAverage());
|
||||||
_animVars.set("moveForwardAlpha", moveForwardAlpha);
|
_animVars.set("moveForwardAlpha", moveForwardAlpha);
|
||||||
_animVars.set("moveBackwardTimeScale", moveBackwardTimeScale);
|
|
||||||
|
_animVars.set("moveBackwardSpeed", -_averageForwardSpeed.getAverage());
|
||||||
_animVars.set("moveBackwardAlpha", moveBackwardAlpha);
|
_animVars.set("moveBackwardAlpha", moveBackwardAlpha);
|
||||||
_animVars.set("moveLateralTimeScale", moveLateralTimeScale);
|
|
||||||
|
_animVars.set("moveLateralSpeed", fabsf(_averageLateralSpeed.getAverage()));
|
||||||
_animVars.set("moveLateralAlpha", moveLateralAlpha);
|
_animVars.set("moveLateralAlpha", moveLateralAlpha);
|
||||||
|
|
||||||
const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec
|
const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec
|
||||||
|
|
|
@ -208,7 +208,7 @@ public:
|
||||||
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
|
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
|
||||||
void updateNeckJoint(int index, const HeadParameters& params);
|
void updateNeckJoint(int index, const HeadParameters& params);
|
||||||
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
||||||
void calcAnimAlphaAndTimeScale(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut, float* timeScaleOut) const;
|
void calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const;
|
||||||
|
|
||||||
QVector<JointState> _jointStates;
|
QVector<JointState> _jointStates;
|
||||||
int _rootJointIndex = -1;
|
int _rootJointIndex = -1;
|
||||||
|
@ -244,8 +244,8 @@ public:
|
||||||
float _leftHandOverlayAlpha = 0.0f;
|
float _leftHandOverlayAlpha = 0.0f;
|
||||||
float _rightHandOverlayAlpha = 0.0f;
|
float _rightHandOverlayAlpha = 0.0f;
|
||||||
|
|
||||||
SimpleMovingAverage _averageForwardSpeed{ 25 };
|
SimpleMovingAverage _averageForwardSpeed{ 10 };
|
||||||
SimpleMovingAverage _averageLateralSpeed{ 25 };
|
SimpleMovingAverage _averageLateralSpeed{ 10 };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__Rig__) */
|
#endif /* defined(__hifi__Rig__) */
|
||||||
|
|
Loading…
Reference in a new issue