mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 21:56:26 +02:00
Merge pull request #16136 from hyperlogic/feature/3x3-blend
Directional Blending Support
This commit is contained in:
commit
2857086861
12 changed files with 333 additions and 22 deletions
|
@ -118,7 +118,7 @@ void AnimStats::updateStats(bool force) {
|
||||||
|
|
||||||
auto prevIter = _prevDebugAlphaMap.find(key);
|
auto prevIter = _prevDebugAlphaMap.find(key);
|
||||||
if (prevIter != _prevDebugAlphaMap.end()) {
|
if (prevIter != _prevDebugAlphaMap.end()) {
|
||||||
float prevAlpha = std::get<0>(iter.second);
|
float prevAlpha = std::get<0>(prevIter->second);
|
||||||
if (prevAlpha != alpha) {
|
if (prevAlpha != alpha) {
|
||||||
// change detected: reset timer
|
// change detected: reset timer
|
||||||
_animAlphaValueChangedTimers[key] = now;
|
_animAlphaValueChangedTimers[key] = now;
|
||||||
|
|
148
libraries/animation/src/AnimBlendDirectional.cpp
Normal file
148
libraries/animation/src/AnimBlendDirectional.cpp
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
//
|
||||||
|
// AnimBlendDirectional.cpp
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on Augest 30 2019.
|
||||||
|
// Copyright (c) 2019 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 "AnimBlendDirectional.h"
|
||||||
|
#include "GLMHelpers.h"
|
||||||
|
#include "AnimationLogging.h"
|
||||||
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
|
AnimBlendDirectional::AnimBlendDirectional(const QString& id, glm::vec3 alpha, const QString& centerId,
|
||||||
|
const QString& upId, const QString& downId, const QString& leftId, const QString& rightId,
|
||||||
|
const QString& upLeftId, const QString& upRightId, const QString& downLeftId, const QString& downRightId) :
|
||||||
|
AnimNode(AnimNode::Type::BlendDirectional, id),
|
||||||
|
_alpha(alpha),
|
||||||
|
_centerId(centerId),
|
||||||
|
_upId(upId),
|
||||||
|
_downId(downId),
|
||||||
|
_leftId(leftId),
|
||||||
|
_rightId(rightId),
|
||||||
|
_upLeftId(upLeftId),
|
||||||
|
_upRightId(upRightId),
|
||||||
|
_downLeftId(downLeftId),
|
||||||
|
_downRightId(downRightId) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimBlendDirectional::~AnimBlendDirectional() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimPoseVec& AnimBlendDirectional::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
|
// lookupRaw don't transform the vector.
|
||||||
|
_alpha = animVars.lookupRaw(_alphaVar, _alpha);
|
||||||
|
float parentDebugAlpha = context.getDebugAlpha(_id);
|
||||||
|
|
||||||
|
if (_children.size() == 9) {
|
||||||
|
|
||||||
|
// try to keep the order the same as quadrants, for _childIndices.
|
||||||
|
// +---+---+
|
||||||
|
// | 1 | 0 |
|
||||||
|
// +---+---+
|
||||||
|
// | 2 | 3 |
|
||||||
|
// +---+---+
|
||||||
|
|
||||||
|
std::array<int, 4> indices;
|
||||||
|
glm::vec2 alpha = _alpha;
|
||||||
|
if (_alpha.x > 0.0f) {
|
||||||
|
if (_alpha.y > 0.0f) {
|
||||||
|
// quadrant 0
|
||||||
|
indices = {{_childIndices[0][2], _childIndices[0][1], _childIndices[1][1], _childIndices[1][2]}};
|
||||||
|
} else {
|
||||||
|
// quadrant 3
|
||||||
|
indices = {{_childIndices[1][2], _childIndices[1][1], _childIndices[2][1], _childIndices[2][2]}};
|
||||||
|
// shift alpha up so both alpha.x and alpha.y are in the (0, 1) range.
|
||||||
|
alpha.y += 1.0f;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_alpha.y > 0.0f) {
|
||||||
|
// quadrant 1
|
||||||
|
indices = {{_childIndices[0][1], _childIndices[0][0], _childIndices[1][0], _childIndices[1][1]}};
|
||||||
|
// shift alpha right so both alpha.x and alpha.y are in the (0, 1) range.
|
||||||
|
alpha.x += 1.0f;
|
||||||
|
} else {
|
||||||
|
// quadrant 2
|
||||||
|
indices = {{_childIndices[1][1], _childIndices[1][0], _childIndices[2][0], _childIndices[2][1]}};
|
||||||
|
// shift alpha up and right so both alpha.x and alpha.y are in the (0, 1) range.
|
||||||
|
alpha.x += 1.0f;
|
||||||
|
alpha.y += 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::array<float, 4> alphas = {{
|
||||||
|
alpha.x * alpha.y,
|
||||||
|
(1.0f - alpha.x) * alpha.y,
|
||||||
|
(1.0f - alpha.x) * (1.0f - alpha.y),
|
||||||
|
alpha.x * (1.0f - alpha.y)
|
||||||
|
}};
|
||||||
|
|
||||||
|
// evaluate children
|
||||||
|
std::array<AnimPoseVec, 4> poseVecs;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
poseVecs[i] = _children[indices[i]]->evaluate(animVars, context, dt, triggersOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// blend children
|
||||||
|
size_t minSize = INT_MAX;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (poseVecs[i].size() < minSize) {
|
||||||
|
minSize = poseVecs[i].size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_poses.resize(minSize);
|
||||||
|
blend4(minSize, &poseVecs[0][0], &poseVecs[1][0], &poseVecs[2][0], &poseVecs[3][0], &alphas[0], &_poses[0]);
|
||||||
|
|
||||||
|
// animation stack debug stats
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
context.setDebugAlpha(_children[i]->getID(), 0.0f, _children[i]->getType());
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
context.setDebugAlpha(_children[indices[i]]->getID(), alphas[i] * parentDebugAlpha, _children[indices[i]]->getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (auto&& pose : _poses) {
|
||||||
|
pose = AnimPose::identity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
const AnimPoseVec& AnimBlendDirectional::getPosesInternal() const {
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimBlendDirectional::lookupChildIds() {
|
||||||
|
_childIndices[0][0] = findChildIndexByName(_upLeftId);
|
||||||
|
_childIndices[0][1] = findChildIndexByName(_upId);
|
||||||
|
_childIndices[0][2] = findChildIndexByName(_upRightId);
|
||||||
|
|
||||||
|
_childIndices[1][0] = findChildIndexByName(_leftId);
|
||||||
|
_childIndices[1][1] = findChildIndexByName(_centerId);
|
||||||
|
_childIndices[1][2] = findChildIndexByName(_rightId);
|
||||||
|
|
||||||
|
_childIndices[2][0] = findChildIndexByName(_downLeftId);
|
||||||
|
_childIndices[2][1] = findChildIndexByName(_downId);
|
||||||
|
_childIndices[2][2] = findChildIndexByName(_downRightId);
|
||||||
|
|
||||||
|
// manditory children
|
||||||
|
// TODO: currently all are manditory.
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
if (_childIndices[i][j] == -1) {
|
||||||
|
qDebug(animation) << "Error in AnimBlendDirectional::lookupChildIds() could not find child[" << i << "," << j << "]";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
59
libraries/animation/src/AnimBlendDirectional.h
Normal file
59
libraries/animation/src/AnimBlendDirectional.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
//
|
||||||
|
// AnimBlendDirectonal.h
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on Augest 30 2019.
|
||||||
|
// Copyright (c) 2019 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_AnimBlendDirectional_h
|
||||||
|
#define hifi_AnimBlendDirectional_h
|
||||||
|
|
||||||
|
#include "AnimNode.h"
|
||||||
|
|
||||||
|
// blend between up to nine AnimNodes.
|
||||||
|
|
||||||
|
class AnimBlendDirectional : public AnimNode {
|
||||||
|
public:
|
||||||
|
friend class AnimTests;
|
||||||
|
|
||||||
|
AnimBlendDirectional(const QString& id, glm::vec3 alpha, const QString& centerId,
|
||||||
|
const QString& upId, const QString& downId, const QString& leftId, const QString& rightId,
|
||||||
|
const QString& upLeftId, const QString& upRightId, const QString& downLeftId, const QString& downRightId);
|
||||||
|
virtual ~AnimBlendDirectional() override;
|
||||||
|
|
||||||
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
|
|
||||||
|
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||||
|
|
||||||
|
bool lookupChildIds();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||||
|
|
||||||
|
AnimPoseVec _poses;
|
||||||
|
|
||||||
|
glm::vec3 _alpha;
|
||||||
|
QString _centerId;
|
||||||
|
QString _upId;
|
||||||
|
QString _downId;
|
||||||
|
QString _leftId;
|
||||||
|
QString _rightId;
|
||||||
|
QString _upLeftId;
|
||||||
|
QString _upRightId;
|
||||||
|
QString _downLeftId;
|
||||||
|
QString _downRightId;
|
||||||
|
|
||||||
|
QString _alphaVar;
|
||||||
|
|
||||||
|
int _childIndices[3][3];
|
||||||
|
|
||||||
|
// no copies
|
||||||
|
AnimBlendDirectional(const AnimBlendDirectional&) = delete;
|
||||||
|
AnimBlendDirectional& operator=(const AnimBlendDirectional&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AnimBlendDirectional_h
|
|
@ -110,11 +110,9 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, c
|
||||||
// copy translation and scale from nextPoses
|
// copy translation and scale from nextPoses
|
||||||
AnimPose pose = nextPoses[i];
|
AnimPose pose = nextPoses[i];
|
||||||
|
|
||||||
int parentIndex = _skeleton->getParentIndex((int)i);
|
// convert from a rotation that happens in the absolute space of the joint
|
||||||
if (parentIndex >= 0) {
|
// into a rotation that happens in the relative space of the joint.
|
||||||
// but transform nextPoses rot into absPrev parent frame.
|
pose.rot() = glm::inverse(absPrev[i].rot()) * pose.rot() * absPrev[i].rot();
|
||||||
pose.rot() = glm::inverse(absPrev[parentIndex].rot()) * pose.rot() * absPrev[parentIndex].rot();
|
|
||||||
}
|
|
||||||
|
|
||||||
relOffsetPoses.push_back(pose);
|
relOffsetPoses.push_back(pose);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ static void bakeRelativeDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPose
|
||||||
// for each joint in animPoses
|
// for each joint in animPoses
|
||||||
for (size_t i = 0; i < animPoses.size(); ++i) {
|
for (size_t i = 0; i < animPoses.size(); ++i) {
|
||||||
// convert this relative AnimPose into a delta animation.
|
// convert this relative AnimPose into a delta animation.
|
||||||
animPoses[i] = animPoses[i] * invBasePoses[i];
|
animPoses[i] = invBasePoses[i] * animPoses[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,13 +80,11 @@ void bakeAbsoluteDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPoseVec& ba
|
||||||
for (size_t i = 0; i < animPoses.size(); ++i) {
|
for (size_t i = 0; i < animPoses.size(); ++i) {
|
||||||
|
|
||||||
// scale and translation are relative frame
|
// scale and translation are relative frame
|
||||||
animPoses[i] = animPoses[i] * invBasePoses[i];
|
animPoses[i] = invBasePoses[i] * animPoses[i];
|
||||||
|
|
||||||
// but transform the rotation delta into the absolute frame.
|
// convert from a rotation that happens in the relative space of the joint
|
||||||
int parentIndex = skeleton->getParentIndex((int)i);
|
// into a rotation that happens in the absolute space of the joint.
|
||||||
if (parentIndex >= 0) {
|
animPoses[i].rot() = absBasePoses[i].rot() * animPoses[i].rot() * glm::inverse(absBasePoses[i].rot());
|
||||||
animPoses[i].rot() = absBasePoses[parentIndex].rot() * animPoses[i].rot() * glm::inverse(absBasePoses[parentIndex].rot());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ enum class AnimNodeType {
|
||||||
TwoBoneIK,
|
TwoBoneIK,
|
||||||
SplineIK,
|
SplineIK,
|
||||||
PoleVectorConstraint,
|
PoleVectorConstraint,
|
||||||
|
BlendDirectional,
|
||||||
NumTypes
|
NumTypes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,15 @@ public:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int findChildIndexByName(const QString& id) {
|
||||||
|
for (size_t i = 0; i < _children.size(); ++i) {
|
||||||
|
if (_children[i]->getID() == id) {
|
||||||
|
return (int)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
const AnimPoseVec& getPoses() const { return getPosesInternal(); }
|
const AnimPoseVec& getPoses() const { return getPosesInternal(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "AnimTwoBoneIK.h"
|
#include "AnimTwoBoneIK.h"
|
||||||
#include "AnimSplineIK.h"
|
#include "AnimSplineIK.h"
|
||||||
#include "AnimPoleVectorConstraint.h"
|
#include "AnimPoleVectorConstraint.h"
|
||||||
|
#include "AnimBlendDirectional.h"
|
||||||
#include "AnimUtil.h"
|
#include "AnimUtil.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);
|
||||||
|
@ -47,6 +48,7 @@ static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const Q
|
||||||
static AnimNode::Pointer loadTwoBoneIKNode(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 loadSplineIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadSplineIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
static AnimNode::Pointer loadBlendDirectionalNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
|
||||||
static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
|
static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
|
||||||
|
|
||||||
|
@ -55,6 +57,7 @@ static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
|
||||||
static bool processDoNothing(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; }
|
||||||
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);
|
||||||
bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
bool processBlendDirectionalNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
|
||||||
static const char* animNodeTypeToString(AnimNode::Type type) {
|
static const char* animNodeTypeToString(AnimNode::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -70,6 +73,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
|
||||||
case AnimNode::Type::TwoBoneIK: return "twoBoneIK";
|
case AnimNode::Type::TwoBoneIK: return "twoBoneIK";
|
||||||
case AnimNode::Type::SplineIK: return "splineIK";
|
case AnimNode::Type::SplineIK: return "splineIK";
|
||||||
case AnimNode::Type::PoleVectorConstraint: return "poleVectorConstraint";
|
case AnimNode::Type::PoleVectorConstraint: return "poleVectorConstraint";
|
||||||
|
case AnimNode::Type::BlendDirectional: return "blendDirectional";
|
||||||
case AnimNode::Type::NumTypes: return nullptr;
|
case AnimNode::Type::NumTypes: return nullptr;
|
||||||
};
|
};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -211,6 +215,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
||||||
case AnimNode::Type::TwoBoneIK: return loadTwoBoneIKNode;
|
case AnimNode::Type::TwoBoneIK: return loadTwoBoneIKNode;
|
||||||
case AnimNode::Type::SplineIK: return loadSplineIKNode;
|
case AnimNode::Type::SplineIK: return loadSplineIKNode;
|
||||||
case AnimNode::Type::PoleVectorConstraint: return loadPoleVectorConstraintNode;
|
case AnimNode::Type::PoleVectorConstraint: return loadPoleVectorConstraintNode;
|
||||||
|
case AnimNode::Type::BlendDirectional: return loadBlendDirectionalNode;
|
||||||
case AnimNode::Type::NumTypes: return nullptr;
|
case AnimNode::Type::NumTypes: return nullptr;
|
||||||
};
|
};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -230,6 +235,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
||||||
case AnimNode::Type::TwoBoneIK: return processDoNothing;
|
case AnimNode::Type::TwoBoneIK: return processDoNothing;
|
||||||
case AnimNode::Type::SplineIK: return processDoNothing;
|
case AnimNode::Type::SplineIK: return processDoNothing;
|
||||||
case AnimNode::Type::PoleVectorConstraint: return processDoNothing;
|
case AnimNode::Type::PoleVectorConstraint: return processDoNothing;
|
||||||
|
case AnimNode::Type::BlendDirectional: return processBlendDirectionalNode;
|
||||||
case AnimNode::Type::NumTypes: return nullptr;
|
case AnimNode::Type::NumTypes: return nullptr;
|
||||||
};
|
};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -402,7 +408,6 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString&
|
||||||
auto tempUrl = QUrl(url);
|
auto tempUrl = QUrl(url);
|
||||||
tempUrl = jsonUrl.resolved(tempUrl);
|
tempUrl = jsonUrl.resolved(tempUrl);
|
||||||
|
|
||||||
// AJT:
|
|
||||||
AnimBlendType blendTypeEnum = AnimBlendType_Normal; // default value
|
AnimBlendType blendTypeEnum = AnimBlendType_Normal; // default value
|
||||||
if (!blendType.isEmpty()) {
|
if (!blendType.isEmpty()) {
|
||||||
blendTypeEnum = stringToAnimBlendType(blendType);
|
blendTypeEnum = stringToAnimBlendType(blendType);
|
||||||
|
@ -772,6 +777,32 @@ static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AnimNode::Pointer loadBlendDirectionalNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||||
|
|
||||||
|
READ_VEC3(alpha, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_OPTIONAL_STRING(alphaVar, jsonObj);
|
||||||
|
|
||||||
|
READ_OPTIONAL_STRING(centerId, jsonObj);
|
||||||
|
READ_OPTIONAL_STRING(upId, jsonObj);
|
||||||
|
READ_OPTIONAL_STRING(downId, jsonObj);
|
||||||
|
READ_OPTIONAL_STRING(leftId, jsonObj);
|
||||||
|
READ_OPTIONAL_STRING(rightId, jsonObj);
|
||||||
|
READ_OPTIONAL_STRING(upLeftId, jsonObj);
|
||||||
|
READ_OPTIONAL_STRING(upRightId, jsonObj);
|
||||||
|
READ_OPTIONAL_STRING(downLeftId, jsonObj);
|
||||||
|
READ_OPTIONAL_STRING(downRightId, jsonObj);
|
||||||
|
|
||||||
|
auto node = std::make_shared<AnimBlendDirectional>(id, alpha, centerId,
|
||||||
|
upId, downId, leftId, rightId,
|
||||||
|
upLeftId, upRightId, downLeftId, downRightId);
|
||||||
|
|
||||||
|
if (!alphaVar.isEmpty()) {
|
||||||
|
node->setAlphaVar(alphaVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
@ -1042,7 +1073,12 @@ bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObje
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool processBlendDirectionalNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) {
|
||||||
|
auto blendNode = std::static_pointer_cast<AnimBlendDirectional>(node);
|
||||||
|
assert(blendNode);
|
||||||
|
|
||||||
|
return blendNode->lookupChildIds();
|
||||||
|
}
|
||||||
|
|
||||||
AnimNodeLoader::AnimNodeLoader(const QUrl& url) :
|
AnimNodeLoader::AnimNodeLoader(const QUrl& url) :
|
||||||
_url(url)
|
_url(url)
|
||||||
|
|
|
@ -26,6 +26,31 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void blend3(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, float* alphas, AnimPose* result) {
|
||||||
|
for (size_t i = 0; i < numPoses; i++) {
|
||||||
|
const AnimPose& aPose = a[i];
|
||||||
|
const AnimPose& bPose = b[i];
|
||||||
|
const AnimPose& cPose = c[i];
|
||||||
|
|
||||||
|
result[i].scale() = alphas[0] * aPose.scale() + alphas[1] * bPose.scale() + alphas[2] * cPose.scale();
|
||||||
|
result[i].rot() = safeLinearCombine3(aPose.rot(), bPose.rot(), cPose.rot(), alphas);
|
||||||
|
result[i].trans() = alphas[0] * aPose.trans() + alphas[1] * bPose.trans() + alphas[2] * cPose.trans();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blend4(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, const AnimPose* d, float* alphas, AnimPose* result) {
|
||||||
|
for (size_t i = 0; i < numPoses; i++) {
|
||||||
|
const AnimPose& aPose = a[i];
|
||||||
|
const AnimPose& bPose = b[i];
|
||||||
|
const AnimPose& cPose = c[i];
|
||||||
|
const AnimPose& dPose = d[i];
|
||||||
|
|
||||||
|
result[i].scale() = alphas[0] * aPose.scale() + alphas[1] * bPose.scale() + alphas[2] * cPose.scale() + alphas[3] * dPose.scale();
|
||||||
|
result[i].rot() = safeLinearCombine4(aPose.rot(), bPose.rot(), cPose.rot(), dPose.rot(), alphas);
|
||||||
|
result[i].trans() = alphas[0] * aPose.trans() + alphas[1] * bPose.trans() + alphas[2] * cPose.trans() + alphas[3] * dPose.trans();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// additive blend
|
// additive blend
|
||||||
void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) {
|
void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) {
|
||||||
|
|
||||||
|
@ -43,8 +68,7 @@ void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha
|
||||||
delta = -delta;
|
delta = -delta;
|
||||||
}
|
}
|
||||||
delta = glm::lerp(identity, delta, alpha);
|
delta = glm::lerp(identity, delta, alpha);
|
||||||
result[i].rot() = glm::normalize(delta * aPose.rot());
|
result[i].rot() = glm::normalize(aPose.rot() * delta);
|
||||||
|
|
||||||
result[i].trans() = aPose.trans() + (alpha * bPose.trans());
|
result[i].trans() = aPose.trans() + (alpha * bPose.trans());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,12 @@
|
||||||
// this is where the magic happens
|
// this is where the magic happens
|
||||||
void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result);
|
void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result);
|
||||||
|
|
||||||
|
// blend between three sets of poses
|
||||||
|
void blend3(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, float* alphas, AnimPose* result);
|
||||||
|
|
||||||
|
// blend between four sets of poses
|
||||||
|
void blend4(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, const AnimPose* d, float* alphas, AnimPose* result);
|
||||||
|
|
||||||
// additive blending
|
// additive blending
|
||||||
void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result);
|
void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result);
|
||||||
|
|
||||||
|
@ -34,6 +40,41 @@ inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) {
|
||||||
return glm::normalize(glm::lerp(a, bTemp, alpha));
|
return glm::normalize(glm::lerp(a, bTemp, alpha));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline glm::quat safeLinearCombine3(const glm::quat& a, const glm::quat& b, const glm::quat& c, float* alphas) {
|
||||||
|
// adjust signs for b & c if necessary
|
||||||
|
glm::quat bTemp = b;
|
||||||
|
float dot = glm::dot(a, bTemp);
|
||||||
|
if (dot < 0.0f) {
|
||||||
|
bTemp = -bTemp;
|
||||||
|
}
|
||||||
|
glm::quat cTemp = c;
|
||||||
|
dot = glm::dot(a, cTemp);
|
||||||
|
if (dot < 0.0f) {
|
||||||
|
cTemp = -cTemp;
|
||||||
|
}
|
||||||
|
return glm::normalize(alphas[0] * a + alphas[1] * bTemp + alphas[2] * cTemp);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline glm::quat safeLinearCombine4(const glm::quat& a, const glm::quat& b, const glm::quat& c, const glm::quat& d, float* alphas) {
|
||||||
|
// adjust signs for b, c & d if necessary
|
||||||
|
glm::quat bTemp = b;
|
||||||
|
float dot = glm::dot(a, bTemp);
|
||||||
|
if (dot < 0.0f) {
|
||||||
|
bTemp = -bTemp;
|
||||||
|
}
|
||||||
|
glm::quat cTemp = c;
|
||||||
|
dot = glm::dot(a, cTemp);
|
||||||
|
if (dot < 0.0f) {
|
||||||
|
cTemp = -cTemp;
|
||||||
|
}
|
||||||
|
glm::quat dTemp = d;
|
||||||
|
dot = glm::dot(a, dTemp);
|
||||||
|
if (dot < 0.0f) {
|
||||||
|
dTemp = -dTemp;
|
||||||
|
}
|
||||||
|
return glm::normalize(alphas[0] * a + alphas[1] * bTemp + alphas[2] * cTemp + alphas[3] * dTemp);
|
||||||
|
}
|
||||||
|
|
||||||
AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone);
|
AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone);
|
||||||
|
|
||||||
// This will attempt to determine the proper body facing of a characters body
|
// This will attempt to determine the proper body facing of a characters body
|
||||||
|
|
|
@ -141,32 +141,26 @@ std::map<QString, QString> AnimVariantMap::toDebugMap() const {
|
||||||
break;
|
break;
|
||||||
case AnimVariant::Type::Vec3: {
|
case AnimVariant::Type::Vec3: {
|
||||||
// To prevent filling up debug stats, don't show vec3 values
|
// To prevent filling up debug stats, don't show vec3 values
|
||||||
/*
|
|
||||||
glm::vec3 value = pair.second.getVec3();
|
glm::vec3 value = pair.second.getVec3();
|
||||||
result[pair.first] = QString("(%1, %2, %3)").
|
result[pair.first] = QString("(%1, %2, %3)").
|
||||||
arg(QString::number(value.x, 'f', 3)).
|
arg(QString::number(value.x, 'f', 3)).
|
||||||
arg(QString::number(value.y, 'f', 3)).
|
arg(QString::number(value.y, 'f', 3)).
|
||||||
arg(QString::number(value.z, 'f', 3));
|
arg(QString::number(value.z, 'f', 3));
|
||||||
*/
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AnimVariant::Type::Quat: {
|
case AnimVariant::Type::Quat: {
|
||||||
// To prevent filling up the anim stats, don't show quat values
|
// To prevent filling up the anim stats, don't show quat values
|
||||||
/*
|
|
||||||
glm::quat value = pair.second.getQuat();
|
glm::quat value = pair.second.getQuat();
|
||||||
result[pair.first] = QString("(%1, %2, %3, %4)").
|
result[pair.first] = QString("(%1, %2, %3, %4)").
|
||||||
arg(QString::number(value.x, 'f', 3)).
|
arg(QString::number(value.x, 'f', 3)).
|
||||||
arg(QString::number(value.y, 'f', 3)).
|
arg(QString::number(value.y, 'f', 3)).
|
||||||
arg(QString::number(value.z, 'f', 3)).
|
arg(QString::number(value.z, 'f', 3)).
|
||||||
arg(QString::number(value.w, 'f', 3));
|
arg(QString::number(value.w, 'f', 3));
|
||||||
*/
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AnimVariant::Type::String:
|
case AnimVariant::Type::String:
|
||||||
// To prevent filling up anim stats, don't show string values
|
// To prevent filling up anim stats, don't show string values
|
||||||
/*
|
|
||||||
result[pair.first] = pair.second.getString();
|
result[pair.first] = pair.second.getString();
|
||||||
*/
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// invalid AnimVariant::Type
|
// invalid AnimVariant::Type
|
||||||
|
|
|
@ -171,6 +171,8 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> m
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AJT: DON'T CHECK THIS IN, disable model URL overrides.
|
||||||
|
/*
|
||||||
// only accept an override if this is for a trait type we override
|
// only accept an override if this is for a trait type we override
|
||||||
// and the version matches what we last sent for skeleton
|
// and the version matches what we last sent for skeleton
|
||||||
if (traitType == AvatarTraits::SkeletonModelURL
|
if (traitType == AvatarTraits::SkeletonModelURL
|
||||||
|
@ -192,6 +194,7 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> m
|
||||||
} else {
|
} else {
|
||||||
message->seek(message->getPosition() + traitBinarySize);
|
message->seek(message->getPosition() + traitBinarySize);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue