mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 13:28:09 +02:00
First pass at improving anim stats.
This commit is contained in:
parent
03f51352aa
commit
3a3ffcc98c
14 changed files with 264 additions and 85 deletions
|
@ -193,18 +193,63 @@ Item {
|
||||||
text: "Yaw: " + root.yaw.toFixed(1)
|
text: "Yaw: " + root.yaw.toFixed(1)
|
||||||
}
|
}
|
||||||
StatText {
|
StatText {
|
||||||
visible: root.animStackNames.length > 0;
|
visible: root.animAlphaValues.length > 0;
|
||||||
text: "Anim Stack Names:"
|
text: "Anim Alpha Values:"
|
||||||
}
|
}
|
||||||
ListView {
|
ListView {
|
||||||
width: geoCol.width
|
width: geoCol.width
|
||||||
height: root.animStackNames.length * 15
|
height: root.animAlphaValues.length * 15
|
||||||
visible: root.animStackNames.length > 0;
|
visible: root.animAlphaValues.length > 0;
|
||||||
model: root.animStackNames
|
model: root.animAlphaValues
|
||||||
delegate: StatText {
|
delegate: StatText {
|
||||||
text: modelData.length > 30
|
text: {
|
||||||
? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22)
|
var actualText = modelData.split("|")[1];
|
||||||
: modelData
|
if (actualText) {
|
||||||
|
if (actualText.length > 30) {
|
||||||
|
return actualText.substring(0, 5) + "..." + actualText.substring(actualText.length - 22);
|
||||||
|
} else {
|
||||||
|
return actualText;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color: {
|
||||||
|
var grayScale = parseFloat(modelData.split("|")[0]);
|
||||||
|
return Qt.rgba(0.3 * grayScale + 0.7,
|
||||||
|
0.3 * grayScale + 0.7,
|
||||||
|
0.3 * grayScale + 0.7, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StatText {
|
||||||
|
visible: root.animVars.length > 0;
|
||||||
|
text: "AnimVars:"
|
||||||
|
}
|
||||||
|
ListView {
|
||||||
|
width: geoCol.width
|
||||||
|
height: root.animVars.length * 15
|
||||||
|
visible: root.animVars.length > 0;
|
||||||
|
model: root.animVars
|
||||||
|
delegate: StatText {
|
||||||
|
text: {
|
||||||
|
var actualText = modelData.split("|")[1];
|
||||||
|
if (actualText) {
|
||||||
|
if (actualText.length > 30) {
|
||||||
|
return actualText.substring(0, 5) + "..." + actualText.substring(actualText.length - 22);
|
||||||
|
} else {
|
||||||
|
return actualText;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color: {
|
||||||
|
var grayScale = parseFloat(modelData.split("|")[0]);
|
||||||
|
return Qt.rgba(0.3 * grayScale + 0.7,
|
||||||
|
0.3 * grayScale + 0.7,
|
||||||
|
0.3 * grayScale + 0.7, 1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatText {
|
StatText {
|
||||||
|
|
|
@ -207,13 +207,94 @@ void Stats::updateStats(bool force) {
|
||||||
|
|
||||||
// Third column, avatar stats
|
// Third column, avatar stats
|
||||||
auto myAvatar = avatarManager->getMyAvatar();
|
auto myAvatar = avatarManager->getMyAvatar();
|
||||||
auto animStack = myAvatar->getSkeletonModel()->getRig().getAnimStack();
|
auto debugAlphaMap = myAvatar->getSkeletonModel()->getRig().getDebugAlphaMap();
|
||||||
|
|
||||||
_animStackNames.clear();
|
// update animation debug alpha values
|
||||||
for (auto animStackIterator = animStack.begin(); animStackIterator != animStack.end(); ++animStackIterator) {
|
QStringList newAnimAlphaValues;
|
||||||
_animStackNames << animStackIterator->first + ": " + QString::number(animStackIterator->second,'f',3);
|
qint64 now = usecTimestampNow();
|
||||||
|
for (auto& iter : debugAlphaMap) {
|
||||||
|
QString key = iter.first;
|
||||||
|
float alpha = std::get<0>(iter.second);
|
||||||
|
|
||||||
|
auto prevIter = _prevDebugAlphaMap.find(key);
|
||||||
|
if (prevIter != _prevDebugAlphaMap.end()) {
|
||||||
|
float prevAlpha = std::get<0>(iter.second);
|
||||||
|
if (prevAlpha != alpha) {
|
||||||
|
// change detected: reset timer
|
||||||
|
_animAlphaValueChangedTimers[key] = now;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// new value: start timer
|
||||||
|
_animAlphaValueChangedTimers[key] = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimNodeType type = std::get<1>(iter.second);
|
||||||
|
if (type == AnimNodeType::Clip) {
|
||||||
|
|
||||||
|
// figure out the grayScale color of this line.
|
||||||
|
const float LIT_TIME = 2.0f;
|
||||||
|
const float FADE_OUT_TIME = 1.0f;
|
||||||
|
float grayScale = 0.0f;
|
||||||
|
float secondsElapsed = (float)(now - _animAlphaValueChangedTimers[key]) / (float)USECS_PER_SECOND;
|
||||||
|
if (secondsElapsed < LIT_TIME) {
|
||||||
|
grayScale = 1.0f;
|
||||||
|
} else if (secondsElapsed < LIT_TIME + FADE_OUT_TIME) {
|
||||||
|
grayScale = (LIT_TIME - (secondsElapsed - FADE_OUT_TIME)) / LIT_TIME;
|
||||||
|
} else {
|
||||||
|
grayScale = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grayScale > 0.0f) {
|
||||||
|
// append grayScaleColor to start of debug string
|
||||||
|
newAnimAlphaValues << QString::number(grayScale, 'f', 2) + "|" + key + ": " + QString::number(alpha, 'f', 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
emit animStackNamesChanged();
|
|
||||||
|
_animAlphaValues = newAnimAlphaValues;
|
||||||
|
_prevDebugAlphaMap = debugAlphaMap;
|
||||||
|
|
||||||
|
emit animAlphaValuesChanged();
|
||||||
|
|
||||||
|
// update animation anim vars
|
||||||
|
_animVarsList.clear();
|
||||||
|
auto animVars = myAvatar->getSkeletonModel()->getRig().getAnimVars().toDebugMap();
|
||||||
|
for (auto& iter : animVars) {
|
||||||
|
QString key = iter.first;
|
||||||
|
QString value = iter.second;
|
||||||
|
|
||||||
|
auto prevIter = _prevAnimVars.find(key);
|
||||||
|
if (prevIter != _prevAnimVars.end()) {
|
||||||
|
QString prevValue = prevIter->second;
|
||||||
|
if (value != prevValue) {
|
||||||
|
// change detected: reset timer
|
||||||
|
_animVarChangedTimers[key] = now;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// new value: start timer
|
||||||
|
_animVarChangedTimers[key] = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out the grayScale color of this line.
|
||||||
|
const float LIT_TIME = 2.0f;
|
||||||
|
const float FADE_OUT_TIME = 0.5f;
|
||||||
|
float grayScale = 0.0f;
|
||||||
|
float secondsElapsed = (float)(now - _animVarChangedTimers[key]) / (float)USECS_PER_SECOND;
|
||||||
|
if (secondsElapsed < LIT_TIME) {
|
||||||
|
grayScale = 1.0f;
|
||||||
|
} else if (secondsElapsed < LIT_TIME + FADE_OUT_TIME) {
|
||||||
|
grayScale = (LIT_TIME - (secondsElapsed - FADE_OUT_TIME)) / LIT_TIME;
|
||||||
|
} else {
|
||||||
|
grayScale = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grayScale > 0.0f) {
|
||||||
|
// append grayScaleColor to start of debug string
|
||||||
|
_animVarsList << QString::number(grayScale, 'f', 2) + "|" + key + ": " + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_prevAnimVars = animVars;
|
||||||
|
emit animVarsChanged();
|
||||||
|
|
||||||
glm::vec3 avatarPos = myAvatar->getWorldPosition();
|
glm::vec3 avatarPos = myAvatar->getWorldPosition();
|
||||||
STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z));
|
STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z));
|
||||||
|
@ -347,6 +428,7 @@ void Stats::updateStats(bool force) {
|
||||||
}
|
}
|
||||||
sendingModeStream << "] " << serverCount << " servers";
|
sendingModeStream << "] " << serverCount << " servers";
|
||||||
if (movingServerCount > 0) {
|
if (movingServerCount > 0) {
|
||||||
|
|
||||||
sendingModeStream << " <SCENE NOT STABLE>";
|
sendingModeStream << " <SCENE NOT STABLE>";
|
||||||
} else {
|
} else {
|
||||||
sendingModeStream << " <SCENE STABLE>";
|
sendingModeStream << " <SCENE STABLE>";
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <OffscreenQmlElement.h>
|
#include <OffscreenQmlElement.h>
|
||||||
#include <AudioIOStats.h>
|
#include <AudioIOStats.h>
|
||||||
#include <render/Args.h>
|
#include <render/Args.h>
|
||||||
|
#include <AnimContext.h>
|
||||||
|
|
||||||
#define STATS_PROPERTY(type, name, initialValue) \
|
#define STATS_PROPERTY(type, name, initialValue) \
|
||||||
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
|
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
|
||||||
|
@ -134,7 +135,6 @@ private: \
|
||||||
* @property {number} batchFrameTime - <em>Read-only.</em>
|
* @property {number} batchFrameTime - <em>Read-only.</em>
|
||||||
* @property {number} engineFrameTime - <em>Read-only.</em>
|
* @property {number} engineFrameTime - <em>Read-only.</em>
|
||||||
* @property {number} avatarSimulationTime - <em>Read-only.</em>
|
* @property {number} avatarSimulationTime - <em>Read-only.</em>
|
||||||
* @property {string[]} animStackNames - <em>Read-only.</em>
|
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @property {number} x
|
* @property {number} x
|
||||||
|
@ -292,7 +292,8 @@ class Stats : public QQuickItem {
|
||||||
STATS_PROPERTY(float, batchFrameTime, 0)
|
STATS_PROPERTY(float, batchFrameTime, 0)
|
||||||
STATS_PROPERTY(float, engineFrameTime, 0)
|
STATS_PROPERTY(float, engineFrameTime, 0)
|
||||||
STATS_PROPERTY(float, avatarSimulationTime, 0)
|
STATS_PROPERTY(float, avatarSimulationTime, 0)
|
||||||
Q_PROPERTY(QStringList animStackNames READ animStackNames NOTIFY animStackNamesChanged)
|
Q_PROPERTY(QStringList animAlphaValues READ animAlphaValues NOTIFY animAlphaValuesChanged)
|
||||||
|
Q_PROPERTY(QStringList animVars READ animVars NOTIFY animVarsChanged)
|
||||||
|
|
||||||
STATS_PROPERTY(int, stylusPicksCount, 0)
|
STATS_PROPERTY(int, stylusPicksCount, 0)
|
||||||
STATS_PROPERTY(int, rayPicksCount, 0)
|
STATS_PROPERTY(int, rayPicksCount, 0)
|
||||||
|
@ -326,7 +327,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList downloadUrls () { return _downloadUrls; }
|
QStringList downloadUrls () { return _downloadUrls; }
|
||||||
QStringList animStackNames() { return _animStackNames; }
|
QStringList animAlphaValues() { return _animAlphaValues; }
|
||||||
|
QStringList animVars() { return _animVarsList; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void forceUpdateStats() { updateStats(true); }
|
void forceUpdateStats() { updateStats(true); }
|
||||||
|
@ -1028,12 +1030,8 @@ signals:
|
||||||
*/
|
*/
|
||||||
void avatarSimulationTimeChanged();
|
void avatarSimulationTimeChanged();
|
||||||
|
|
||||||
/**jsdoc
|
void animAlphaValuesChanged();
|
||||||
* Triggered when the value of the <code>animStackNames</code> property changes.
|
void animVarsChanged();
|
||||||
* @function Stats.animStackNamesChanged
|
|
||||||
* @returns {Signal}
|
|
||||||
*/
|
|
||||||
void animStackNamesChanged();
|
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Triggered when the value of the <code>rectifiedTextureCount</code> property changes.
|
* Triggered when the value of the <code>rectifiedTextureCount</code> property changes.
|
||||||
|
@ -1336,7 +1334,14 @@ private:
|
||||||
QString _monospaceFont;
|
QString _monospaceFont;
|
||||||
const AudioIOStats* _audioStats;
|
const AudioIOStats* _audioStats;
|
||||||
QStringList _downloadUrls = QStringList();
|
QStringList _downloadUrls = QStringList();
|
||||||
QStringList _animStackNames = QStringList();
|
|
||||||
|
QStringList _animAlphaValues = QStringList();
|
||||||
|
AnimContext::DebugAlphaMap _prevDebugAlphaMap; // alpha values from previous frame
|
||||||
|
std::map<QString, qint64> _animAlphaValueChangedTimers; // last time alpha value has changed
|
||||||
|
|
||||||
|
QStringList _animVarsList = QStringList();
|
||||||
|
std::map<QString, QString> _prevAnimVars; // anim vars from previous frame
|
||||||
|
std::map<QString, qint64> _animVarChangedTimers; // last time animVar value has changed.
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Stats_h
|
#endif // hifi_Stats_h
|
||||||
|
|
|
@ -27,7 +27,7 @@ AnimBlendLinear::~AnimBlendLinear() {
|
||||||
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||||
float parentAlpha = _animStack[_id];
|
float parentDebugAlpha = context.getDebugAlpha(_id);
|
||||||
|
|
||||||
if (_children.size() == 0) {
|
if (_children.size() == 0) {
|
||||||
for (auto&& pose : _poses) {
|
for (auto&& pose : _poses) {
|
||||||
|
@ -35,7 +35,7 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con
|
||||||
}
|
}
|
||||||
} else if (_children.size() == 1) {
|
} else if (_children.size() == 1) {
|
||||||
_poses = _children[0]->evaluate(animVars, context, dt, triggersOut);
|
_poses = _children[0]->evaluate(animVars, context, dt, triggersOut);
|
||||||
_animStack[_children[0]->getID()] = parentAlpha;
|
context.setDebugAlpha(_children[0]->getID(), parentDebugAlpha, _children[0]->getType());
|
||||||
} else {
|
} else {
|
||||||
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
|
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
|
||||||
size_t prevPoseIndex = glm::floor(clampedAlpha);
|
size_t prevPoseIndex = glm::floor(clampedAlpha);
|
||||||
|
@ -48,12 +48,12 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con
|
||||||
float weight2 = 0.0f;
|
float weight2 = 0.0f;
|
||||||
if (prevPoseIndex == nextPoseIndex) {
|
if (prevPoseIndex == nextPoseIndex) {
|
||||||
weight2 = 1.0f;
|
weight2 = 1.0f;
|
||||||
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha;
|
context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType());
|
||||||
} else {
|
} else {
|
||||||
weight2 = alpha;
|
weight2 = alpha;
|
||||||
weight1 = 1.0f - weight2;
|
weight1 = 1.0f - weight2;
|
||||||
_animStack[_children[prevPoseIndex]->getID()] = weight1 * parentAlpha;
|
context.setDebugAlpha(_children[prevPoseIndex]->getID(), weight1 * parentDebugAlpha, _children[prevPoseIndex]->getType());
|
||||||
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha;
|
context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processOutputJoints(triggersOut);
|
processOutputJoints(triggersOut);
|
||||||
|
|
|
@ -62,9 +62,7 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
|
||||||
speed = animVars.lookup("moveForwardSpeed", speed);
|
speed = animVars.lookup("moveForwardSpeed", speed);
|
||||||
}
|
}
|
||||||
_alpha = calculateAlpha(speed, _characteristicSpeeds);
|
_alpha = calculateAlpha(speed, _characteristicSpeeds);
|
||||||
float parentAlpha = _animStack[_id];
|
float parentDebugAlpha = context.getDebugAlpha(_id);
|
||||||
|
|
||||||
_animStack["speed"] = speed;
|
|
||||||
|
|
||||||
if (_children.size() == 0) {
|
if (_children.size() == 0) {
|
||||||
for (auto&& pose : _poses) {
|
for (auto&& pose : _poses) {
|
||||||
|
@ -77,7 +75,7 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
|
||||||
float prevDeltaTime, nextDeltaTime;
|
float prevDeltaTime, nextDeltaTime;
|
||||||
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
||||||
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
||||||
_animStack[_children[0]->getID()] = parentAlpha;
|
context.setDebugAlpha(_children[0]->getID(), parentDebugAlpha, _children[0]->getType());
|
||||||
} else {
|
} else {
|
||||||
auto clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
|
auto clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
|
||||||
auto prevPoseIndex = glm::floor(clampedAlpha);
|
auto prevPoseIndex = glm::floor(clampedAlpha);
|
||||||
|
@ -87,17 +85,11 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
|
||||||
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
||||||
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
||||||
|
|
||||||
// weights are for animation stack debug purposes only.
|
|
||||||
float weight1 = 0.0f;
|
|
||||||
float weight2 = 0.0f;
|
|
||||||
if (prevPoseIndex == nextPoseIndex) {
|
if (prevPoseIndex == nextPoseIndex) {
|
||||||
weight2 = 1.0f;
|
context.setDebugAlpha(_children[nextPoseIndex]->getID(), parentDebugAlpha, _children[nextPoseIndex]->getType());
|
||||||
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha;
|
|
||||||
} else {
|
} else {
|
||||||
weight2 = alpha;
|
context.setDebugAlpha(_children[prevPoseIndex]->getID(), (1.0f - alpha) * parentDebugAlpha, _children[prevPoseIndex]->getType());
|
||||||
weight1 = 1.0f - weight2;
|
context.setDebugAlpha(_children[nextPoseIndex]->getID(), alpha * parentDebugAlpha, _children[nextPoseIndex]->getType());
|
||||||
_animStack[_children[prevPoseIndex]->getID()] = weight1 * parentAlpha;
|
|
||||||
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,27 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <QString.h>
|
||||||
|
#include <QStringList.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
enum class AnimNodeType {
|
||||||
|
Clip = 0,
|
||||||
|
BlendLinear,
|
||||||
|
BlendLinearMove,
|
||||||
|
Overlay,
|
||||||
|
StateMachine,
|
||||||
|
Manipulator,
|
||||||
|
InverseKinematics,
|
||||||
|
DefaultPose,
|
||||||
|
TwoBoneIK,
|
||||||
|
PoleVectorConstraint,
|
||||||
|
NumTypes
|
||||||
|
};
|
||||||
|
|
||||||
class AnimContext {
|
class AnimContext {
|
||||||
public:
|
public:
|
||||||
|
AnimContext() {}
|
||||||
AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
|
AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
|
||||||
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix);
|
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix);
|
||||||
|
|
||||||
|
@ -25,6 +44,26 @@ public:
|
||||||
const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; }
|
const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; }
|
||||||
const glm::mat4& getRigToWorldMatrix() const { return _rigToWorldMatrix; }
|
const glm::mat4& getRigToWorldMatrix() const { return _rigToWorldMatrix; }
|
||||||
|
|
||||||
|
float getDebugAlpha(const QString& key) const {
|
||||||
|
auto it = _debugAlphaMap.find(key);
|
||||||
|
if (it != _debugAlphaMap.end()) {
|
||||||
|
return std::get<0>(it->second);
|
||||||
|
} else {
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using DebugAlphaMapValue = std::tuple<float, AnimNodeType>;
|
||||||
|
using DebugAlphaMap = std::map<QString, DebugAlphaMapValue>;
|
||||||
|
|
||||||
|
void setDebugAlpha(const QString& key, float alpha, AnimNodeType type) const {
|
||||||
|
_debugAlphaMap[key] = DebugAlphaMapValue(alpha, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DebugAlphaMap& getDebugAlphaMap() const {
|
||||||
|
return _debugAlphaMap;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
bool _enableDebugDrawIKTargets { false };
|
bool _enableDebugDrawIKTargets { false };
|
||||||
|
@ -32,6 +71,9 @@ protected:
|
||||||
bool _enableDebugDrawIKChains { false };
|
bool _enableDebugDrawIKChains { false };
|
||||||
glm::mat4 _geometryToRigMatrix;
|
glm::mat4 _geometryToRigMatrix;
|
||||||
glm::mat4 _rigToWorldMatrix;
|
glm::mat4 _rigToWorldMatrix;
|
||||||
|
|
||||||
|
// used for debugging internal state of animation system.
|
||||||
|
mutable DebugAlphaMap _debugAlphaMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AnimContext_h
|
#endif // hifi_AnimContext_h
|
||||||
|
|
|
@ -12,10 +12,6 @@
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
|
||||||
std::map<QString, float> AnimNode::_animStack = {
|
|
||||||
{"none", 0.0f}
|
|
||||||
};
|
|
||||||
|
|
||||||
AnimNode::Pointer AnimNode::getParent() {
|
AnimNode::Pointer AnimNode::getParent() {
|
||||||
return _parent.lock();
|
return _parent.lock();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,19 +36,7 @@ class QJsonObject;
|
||||||
|
|
||||||
class AnimNode : public std::enable_shared_from_this<AnimNode> {
|
class AnimNode : public std::enable_shared_from_this<AnimNode> {
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
using Type = AnimNodeType;
|
||||||
Clip = 0,
|
|
||||||
BlendLinear,
|
|
||||||
BlendLinearMove,
|
|
||||||
Overlay,
|
|
||||||
StateMachine,
|
|
||||||
Manipulator,
|
|
||||||
InverseKinematics,
|
|
||||||
DefaultPose,
|
|
||||||
TwoBoneIK,
|
|
||||||
PoleVectorConstraint,
|
|
||||||
NumTypes
|
|
||||||
};
|
|
||||||
using Pointer = std::shared_ptr<AnimNode>;
|
using Pointer = std::shared_ptr<AnimNode>;
|
||||||
using ConstPointer = std::shared_ptr<const AnimNode>;
|
using ConstPointer = std::shared_ptr<const AnimNode>;
|
||||||
|
|
||||||
|
@ -84,7 +72,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCurrentFrame(float frame);
|
void setCurrentFrame(float frame);
|
||||||
const std::map<QString, float> getAnimStack() { return _animStack; }
|
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool traverse(F func) {
|
bool traverse(F func) {
|
||||||
|
@ -127,9 +114,6 @@ protected:
|
||||||
std::weak_ptr<AnimNode> _parent;
|
std::weak_ptr<AnimNode> _parent;
|
||||||
std::vector<QString> _outputJointNames;
|
std::vector<QString> _outputJointNames;
|
||||||
|
|
||||||
// global available to Stats.h
|
|
||||||
static std::map<QString, float> _animStack;
|
|
||||||
|
|
||||||
// no copies
|
// no copies
|
||||||
AnimNode(const AnimNode&) = delete;
|
AnimNode(const AnimNode&) = delete;
|
||||||
AnimNode& operator=(const AnimNode&) = delete;
|
AnimNode& operator=(const AnimNode&) = delete;
|
||||||
|
|
|
@ -23,9 +23,7 @@ AnimStateMachine::~AnimStateMachine() {
|
||||||
|
|
||||||
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
if (_id.contains("userAnimStateMachine")) {
|
float parentDebugAlpha = context.getDebugAlpha(_id);
|
||||||
_animStack.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
|
QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
|
||||||
if (_currentState->getID() != desiredStateID) {
|
if (_currentState->getID() != desiredStateID) {
|
||||||
|
@ -33,8 +31,6 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
||||||
bool foundState = false;
|
bool foundState = false;
|
||||||
for (auto& state : _states) {
|
for (auto& state : _states) {
|
||||||
if (state->getID() == desiredStateID) {
|
if (state->getID() == desiredStateID) {
|
||||||
// parenthesis means previous state, which is a snapshot.
|
|
||||||
_previousStateID = "(" + _currentState->getID() + ")";
|
|
||||||
switchState(animVars, context, state);
|
switchState(animVars, context, state);
|
||||||
foundState = true;
|
foundState = true;
|
||||||
break;
|
break;
|
||||||
|
@ -48,8 +44,6 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
||||||
// evaluate currentState transitions
|
// evaluate currentState transitions
|
||||||
auto desiredState = evaluateTransitions(animVars);
|
auto desiredState = evaluateTransitions(animVars);
|
||||||
if (desiredState != _currentState) {
|
if (desiredState != _currentState) {
|
||||||
// parenthesis means previous state, which is a snapshot.
|
|
||||||
_previousStateID = "(" + _currentState->getID() + ")";
|
|
||||||
switchState(animVars, context, desiredState);
|
switchState(animVars, context, desiredState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,17 +51,8 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
||||||
auto currentStateNode = _children[_currentState->getChildIndex()];
|
auto currentStateNode = _children[_currentState->getChildIndex()];
|
||||||
assert(currentStateNode);
|
assert(currentStateNode);
|
||||||
|
|
||||||
if (!_previousStateID.contains("none")) {
|
|
||||||
_animStack[_previousStateID] = 1.0f - _alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_duringInterp) {
|
if (_duringInterp) {
|
||||||
_alpha += _alphaVel * dt;
|
_alpha += _alphaVel * dt;
|
||||||
if (_alpha > 1.0f) {
|
|
||||||
_animStack[_currentState->getID()] = 1.0f;
|
|
||||||
} else {
|
|
||||||
_animStack[_currentState->getID()] = _alpha;
|
|
||||||
}
|
|
||||||
if (_alpha < 1.0f) {
|
if (_alpha < 1.0f) {
|
||||||
AnimPoseVec* nextPoses = nullptr;
|
AnimPoseVec* nextPoses = nullptr;
|
||||||
AnimPoseVec* prevPoses = nullptr;
|
AnimPoseVec* prevPoses = nullptr;
|
||||||
|
@ -88,18 +73,16 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
||||||
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
||||||
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
|
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
|
||||||
}
|
}
|
||||||
|
context.setDebugAlpha(_currentState->getID(), _alpha * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||||
} else {
|
} else {
|
||||||
_duringInterp = false;
|
_duringInterp = false;
|
||||||
if (_animStack.count(_previousStateID) > 0) {
|
|
||||||
_animStack.erase(_previousStateID);
|
|
||||||
}
|
|
||||||
_previousStateID = "none";
|
|
||||||
_prevPoses.clear();
|
_prevPoses.clear();
|
||||||
_nextPoses.clear();
|
_nextPoses.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_duringInterp) {
|
if (!_duringInterp) {
|
||||||
_animStack[_currentState->getID()] = 1.0f;
|
context.setDebugAlpha(_currentState->getID(), parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||||
_poses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
_poses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||||
}
|
}
|
||||||
processOutputJoints(triggersOut);
|
processOutputJoints(triggersOut);
|
||||||
|
|
|
@ -138,7 +138,6 @@ protected:
|
||||||
float _alpha = 0.0f;
|
float _alpha = 0.0f;
|
||||||
AnimPoseVec _prevPoses;
|
AnimPoseVec _prevPoses;
|
||||||
AnimPoseVec _nextPoses;
|
AnimPoseVec _nextPoses;
|
||||||
QString _previousStateID { "none" };
|
|
||||||
|
|
||||||
State::Pointer _currentState;
|
State::Pointer _currentState;
|
||||||
std::vector<State::Pointer> _states;
|
std::vector<State::Pointer> _states;
|
||||||
|
|
|
@ -67,6 +67,7 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine,
|
||||||
}
|
}
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) {
|
void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) {
|
||||||
for (auto& pair : other._map) {
|
for (auto& pair : other._map) {
|
||||||
_map[pair.first] = pair.second;
|
_map[pair.first] = pair.second;
|
||||||
|
@ -124,3 +125,43 @@ void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<QString, QString> AnimVariantMap::toDebugMap() const {
|
||||||
|
std::map<QString, QString> result;
|
||||||
|
for (auto& pair : _map) {
|
||||||
|
switch (pair.second.getType()) {
|
||||||
|
case AnimVariant::Type::Bool:
|
||||||
|
result[pair.first] = QString("%1").arg(pair.second.getBool());
|
||||||
|
break;
|
||||||
|
case AnimVariant::Type::Int:
|
||||||
|
result[pair.first] = QString("%1").arg(pair.second.getInt());
|
||||||
|
break;
|
||||||
|
case AnimVariant::Type::Float:
|
||||||
|
result[pair.first] = QString::number(pair.second.getFloat(), 'f', 3);
|
||||||
|
break;
|
||||||
|
case AnimVariant::Type::Vec3: {
|
||||||
|
glm::vec3 value = pair.second.getVec3();
|
||||||
|
result[pair.first] = QString("(%1, %2, %3)").
|
||||||
|
arg(QString::number(value.x, 'f', 3)).
|
||||||
|
arg(QString::number(value.y, 'f', 3)).
|
||||||
|
arg(QString::number(value.z, 'f', 3));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AnimVariant::Type::Quat: {
|
||||||
|
glm::quat value = pair.second.getQuat();
|
||||||
|
result[pair.first] = QString("(%1, %2, %3, %4)").
|
||||||
|
arg(QString::number(value.x, 'f', 3)).
|
||||||
|
arg(QString::number(value.y, 'f', 3)).
|
||||||
|
arg(QString::number(value.z, 'f', 3)).
|
||||||
|
arg(QString::number(value.w, 'f', 3));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AnimVariant::Type::String:
|
||||||
|
result[pair.first] = pair.second.getString();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(("invalid AnimVariant::Type", false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -235,6 +235,9 @@ public:
|
||||||
void animVariantMapFromScriptValue(const QScriptValue& object);
|
void animVariantMapFromScriptValue(const QScriptValue& object);
|
||||||
void copyVariantsFrom(const AnimVariantMap& other);
|
void copyVariantsFrom(const AnimVariantMap& other);
|
||||||
|
|
||||||
|
// For stat debugging.
|
||||||
|
std::map<QString, QString> toDebugMap() const;
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
void dump() const {
|
void dump() const {
|
||||||
qCDebug(animation) << "AnimVariantMap =";
|
qCDebug(animation) << "AnimVariantMap =";
|
||||||
|
|
|
@ -1061,8 +1061,10 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
||||||
// animations haven't fully loaded yet.
|
// animations haven't fully loaded yet.
|
||||||
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
|
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
|
||||||
}
|
}
|
||||||
|
_lastAnimVars = _animVars;
|
||||||
_animVars.clearTriggers();
|
_animVars.clearTriggers();
|
||||||
_animVars = triggersOut;
|
_animVars = triggersOut;
|
||||||
|
_lastContext = context;
|
||||||
}
|
}
|
||||||
applyOverridePoses();
|
applyOverridePoses();
|
||||||
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
|
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
|
||||||
|
|
|
@ -222,7 +222,9 @@ public:
|
||||||
// input assumed to be in rig space
|
// input assumed to be in rig space
|
||||||
void computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut) const;
|
void computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut) const;
|
||||||
|
|
||||||
const std::map<QString, float> getAnimStack() { return _animNode->getAnimStack(); }
|
// used to debug animation playback
|
||||||
|
const AnimContext::DebugAlphaMap& getDebugAlphaMap() const { return _lastContext.getDebugAlphaMap(); }
|
||||||
|
const AnimVariantMap& getAnimVars() const { return _lastAnimVars; }
|
||||||
|
|
||||||
void toggleSmoothPoleVectors() { _smoothPoleVectors = !_smoothPoleVectors; };
|
void toggleSmoothPoleVectors() { _smoothPoleVectors = !_smoothPoleVectors; };
|
||||||
signals:
|
signals:
|
||||||
|
@ -388,6 +390,9 @@ protected:
|
||||||
|
|
||||||
int _rigId;
|
int _rigId;
|
||||||
bool _headEnabled { false };
|
bool _headEnabled { false };
|
||||||
|
|
||||||
|
AnimContext _lastContext;
|
||||||
|
AnimVariantMap _lastAnimVars;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__Rig__) */
|
#endif /* defined(__hifi__Rig__) */
|
||||||
|
|
Loading…
Reference in a new issue