mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 18:13:47 +02:00
AnimBlendDirectional node
This commit is contained in:
parent
6274ab4aa7
commit
92df5cccdc
7 changed files with 157 additions and 123 deletions
|
@ -13,11 +13,12 @@
|
|||
#include "AnimationLogging.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
AnimBlendDirectional::AnimBlendDirectional(const QString& id, float alpha, const QString& centerId,
|
||||
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),
|
||||
|
@ -35,52 +36,73 @@ AnimBlendDirectional::~AnimBlendDirectional() {
|
|||
|
||||
const AnimPoseVec& AnimBlendDirectional::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||
// lookupRaw don't transform the vector.
|
||||
_alpha = animVars.lookupRaw(_alphaVar, _alpha);
|
||||
float parentDebugAlpha = context.getDebugAlpha(_id);
|
||||
|
||||
/*
|
||||
if (_children.size() == 0) {
|
||||
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]);
|
||||
|
||||
} else {
|
||||
for (auto&& pose : _poses) {
|
||||
pose = AnimPose::identity;
|
||||
}
|
||||
} else if (_children.size() == 1) {
|
||||
_poses = _children[0]->evaluate(animVars, context, dt, triggersOut);
|
||||
context.setDebugAlpha(_children[0]->getID(), parentDebugAlpha, _children[0]->getType());
|
||||
} else if (_children.size() == 2 && _blendType != AnimBlendType_Normal) {
|
||||
// special case for additive blending
|
||||
float alpha = glm::clamp(_alpha, 0.0f, 1.0f);
|
||||
const size_t prevPoseIndex = 0;
|
||||
const size_t nextPoseIndex = 1;
|
||||
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt);
|
||||
|
||||
// for animation stack debugging
|
||||
float weight2 = alpha;
|
||||
float weight1 = 1.0f - weight2;
|
||||
context.setDebugAlpha(_children[prevPoseIndex]->getID(), weight1 * parentDebugAlpha, _children[prevPoseIndex]->getType());
|
||||
context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType());
|
||||
|
||||
} else {
|
||||
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
|
||||
size_t prevPoseIndex = glm::floor(clampedAlpha);
|
||||
size_t nextPoseIndex = glm::ceil(clampedAlpha);
|
||||
auto alpha = glm::fract(clampedAlpha);
|
||||
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt);
|
||||
|
||||
// weights are for animation stack debug purposes only.
|
||||
float weight1 = 0.0f;
|
||||
float weight2 = 0.0f;
|
||||
if (prevPoseIndex == nextPoseIndex) {
|
||||
weight2 = 1.0f;
|
||||
context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType());
|
||||
} else {
|
||||
weight2 = alpha;
|
||||
weight1 = 1.0f - weight2;
|
||||
context.setDebugAlpha(_children[prevPoseIndex]->getID(), weight1 * parentDebugAlpha, _children[prevPoseIndex]->getType());
|
||||
context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType());
|
||||
}
|
||||
}
|
||||
processOutputJoints(triggersOut);
|
||||
*/
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
@ -90,71 +112,29 @@ const AnimPoseVec& AnimBlendDirectional::getPosesInternal() const {
|
|||
return _poses;
|
||||
}
|
||||
|
||||
/*
|
||||
void AnimBlendDirectional::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||
size_t prevPoseIndex, size_t nextPoseIndex, float dt) {
|
||||
if (prevPoseIndex == nextPoseIndex) {
|
||||
// this can happen if alpha is on an integer boundary
|
||||
_poses = _children[prevPoseIndex]->evaluate(animVars, context, dt, triggersOut);
|
||||
} else {
|
||||
// need to eval and blend between two children.
|
||||
auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, context, dt, triggersOut);
|
||||
auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, context, dt, triggersOut);
|
||||
|
||||
if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) {
|
||||
_poses.resize(prevPoses.size());
|
||||
|
||||
if (_blendType == AnimBlendType_Normal) {
|
||||
::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]);
|
||||
} else if (_blendType == AnimBlendType_AddRelative) {
|
||||
::blendAdd(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]);
|
||||
} else if (_blendType == AnimBlendType_AddAbsolute) {
|
||||
// convert prev from relative to absolute
|
||||
AnimPoseVec absPrev = prevPoses;
|
||||
_skeleton->convertRelativePosesToAbsolute(absPrev);
|
||||
|
||||
// rotate the offset rotations from next into the parent relative frame of each joint.
|
||||
AnimPoseVec relOffsetPoses;
|
||||
relOffsetPoses.reserve(nextPoses.size());
|
||||
for (size_t i = 0; i < nextPoses.size(); ++i) {
|
||||
|
||||
// copy translation and scale from nextPoses
|
||||
AnimPose pose = nextPoses[i];
|
||||
|
||||
int parentIndex = _skeleton->getParentIndex((int)i);
|
||||
if (parentIndex >= 0) {
|
||||
// but transform nextPoses rot into absPrev parent frame.
|
||||
pose.rot() = glm::inverse(absPrev[parentIndex].rot()) * pose.rot() * absPrev[parentIndex].rot();
|
||||
}
|
||||
|
||||
relOffsetPoses.push_back(pose);
|
||||
}
|
||||
|
||||
// then blend
|
||||
::blendAdd(_poses.size(), &prevPoses[0], &relOffsetPoses[0], alpha, &_poses[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
bool AnimBlendDirectional::lookupChildIds() {
|
||||
_center = findChildIndexByName(_centerId);
|
||||
_up = findChildIndexByName(_upId);
|
||||
_down = findChildIndexByName(_downId);
|
||||
_left = findChildIndexByName(_leftId);
|
||||
_right = findChildIndexByName(_rightId);
|
||||
_upLeft = findChildIndexByName(_upLeftId);
|
||||
_upRight = findChildIndexByName(_upRightId);
|
||||
_downLeft = findChildIndexByName(_downLeftId);
|
||||
_downRight = findChildIndexByName(_downRightId);
|
||||
_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.
|
||||
if (_center == -1 || _up == -1 || _down == -1 || _left == -1 || _right== -1 ||
|
||||
_upLeft == -1 || _upRight == -1 || _downLeft == -1 || _downRight == -1) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class AnimBlendDirectional : public AnimNode {
|
|||
public:
|
||||
friend class AnimTests;
|
||||
|
||||
AnimBlendDirectional(const QString& id, float alpha, const QString& centerId,
|
||||
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;
|
||||
|
@ -36,7 +36,7 @@ protected:
|
|||
|
||||
AnimPoseVec _poses;
|
||||
|
||||
float _alpha;
|
||||
glm::vec3 _alpha;
|
||||
QString _centerId;
|
||||
QString _upId;
|
||||
QString _downId;
|
||||
|
@ -49,15 +49,7 @@ protected:
|
|||
|
||||
QString _alphaVar;
|
||||
|
||||
int _center;
|
||||
int _up;
|
||||
int _down;
|
||||
int _left;
|
||||
int _right;
|
||||
int _upLeft;
|
||||
int _upRight;
|
||||
int _downLeft;
|
||||
int _downRight;
|
||||
int _childIndices[3][3];
|
||||
|
||||
// no copies
|
||||
AnimBlendDirectional(const AnimBlendDirectional&) = delete;
|
||||
|
|
|
@ -61,7 +61,7 @@ static void bakeRelativeDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPose
|
|||
}
|
||||
}
|
||||
|
||||
void bakeAbsoluteDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPoseVec& basePoses, AnimSkeleton::ConstPointer skeleton) {
|
||||
void bakeAbsoluteDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPoseVec& basePoses, AnimSkeleton::ConstPointer skeleton, const QString& url, int baseFrame) {
|
||||
|
||||
// invert all the basePoses
|
||||
AnimPoseVec invBasePoses = basePoses;
|
||||
|
@ -73,6 +73,7 @@ void bakeAbsoluteDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPoseVec& ba
|
|||
skeleton->convertRelativePosesToAbsolute(absBasePoses);
|
||||
|
||||
// for each frame of the animation
|
||||
int frame = 0;
|
||||
for (auto&& animPoses : anim) {
|
||||
ASSERT(animPoses.size() == basePoses.size());
|
||||
|
||||
|
@ -88,6 +89,7 @@ void bakeAbsoluteDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPoseVec& ba
|
|||
animPoses[i].rot() = absBasePoses[parentIndex].rot() * animPoses[i].rot() * glm::inverse(absBasePoses[parentIndex].rot());
|
||||
}
|
||||
}
|
||||
frame++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,7 +297,7 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const Anim
|
|||
auto baseAnim = copyAndRetargetFromNetworkAnim(_baseNetworkAnim, _skeleton);
|
||||
|
||||
if (_blendType == AnimBlendType_AddAbsolute) {
|
||||
bakeAbsoluteDeltaAnim(_anim, baseAnim[(int)_baseFrame], _skeleton);
|
||||
bakeAbsoluteDeltaAnim(_anim, baseAnim[(int)_baseFrame], _skeleton, _url, _baseFrame);
|
||||
} else {
|
||||
// AnimBlendType_AddRelative
|
||||
bakeRelativeDeltaAnim(_anim, baseAnim[(int)_baseFrame]);
|
||||
|
|
|
@ -780,7 +780,7 @@ static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj
|
|||
|
||||
static AnimNode::Pointer loadBlendDirectionalNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||
|
||||
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_VEC3(alpha, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_OPTIONAL_STRING(alphaVar, jsonObj);
|
||||
|
||||
READ_OPTIONAL_STRING(centerId, jsonObj);
|
||||
|
|
|
@ -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
|
||||
void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) {
|
||||
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
// this is where the magic happens
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
case AnimVariant::Type::Vec3: {
|
||||
// To prevent filling up debug stats, don't show vec3 values
|
||||
/*
|
||||
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: {
|
||||
// To prevent filling up the anim stats, don't show quat values
|
||||
/*
|
||||
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:
|
||||
// To prevent filling up anim stats, don't show string values
|
||||
/*
|
||||
result[pair.first] = pair.second.getString();
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
// invalid AnimVariant::Type
|
||||
|
|
Loading…
Reference in a new issue