Merge pull request #7021 from hyperlogic/tony/animation-mirror-support

AnimClip: Animation mirror support
This commit is contained in:
Brad Hefta-Gaub 2016-02-05 19:25:01 -08:00
commit eebf6f8883
8 changed files with 104 additions and 8 deletions

View file

@ -15,12 +15,13 @@
bool AnimClip::usePreAndPostPoseFromAnim = false;
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) :
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) :
AnimNode(AnimNode::Type::Clip, id),
_startFrame(startFrame),
_endFrame(endFrame),
_timeScale(timeScale),
_loopFlag(loopFlag),
_mirrorFlag(mirrorFlag),
_frame(startFrame)
{
loadURL(url);
@ -37,6 +38,7 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt,
_endFrame = animVars.lookup(_endFrameVar, _endFrame);
_timeScale = animVars.lookup(_timeScaleVar, _timeScale);
_loopFlag = animVars.lookup(_loopFlagVar, _loopFlag);
_mirrorFlag = animVars.lookup(_mirrorFlagVar, _mirrorFlag);
float frame = animVars.lookup(_frameVar, _frame);
_frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut);
@ -49,6 +51,12 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt,
}
if (_anim.size()) {
// lazy creation of mirrored animation frames.
if (_mirrorFlag && _anim.size() != _mirrorAnim.size()) {
buildMirrorAnim();
}
int prevIndex = (int)glm::floor(_frame);
int nextIndex;
if (_loopFlag && _frame >= _endFrame) {
@ -63,8 +71,8 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt,
prevIndex = std::min(std::max(0, prevIndex), frameCount - 1);
nextIndex = std::min(std::max(0, nextIndex), frameCount - 1);
const AnimPoseVec& prevFrame = _anim[prevIndex];
const AnimPoseVec& nextFrame = _anim[nextIndex];
const AnimPoseVec& prevFrame = _mirrorFlag ? _mirrorAnim[prevIndex] : _anim[prevIndex];
const AnimPoseVec& nextFrame = _mirrorFlag ? _mirrorAnim[nextIndex] : _anim[nextIndex];
float alpha = glm::fract(_frame);
::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]);
@ -162,9 +170,22 @@ void AnimClip::copyFromNetworkAnim() {
}
}
// mirrorAnim will be re-built on demand, if needed.
_mirrorAnim.clear();
_poses.resize(skeletonJointCount);
}
void AnimClip::buildMirrorAnim() {
assert(_skeleton);
_mirrorAnim.clear();
_mirrorAnim.reserve(_anim.size());
for (auto& relPoses : _anim) {
_mirrorAnim.push_back(relPoses);
_skeleton->mirrorRelativePoses(_mirrorAnim.back());
}
}
const AnimPoseVec& AnimClip::getPosesInternal() const {
return _poses;

View file

@ -27,7 +27,7 @@ public:
static bool usePreAndPostPoseFromAnim;
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag);
virtual ~AnimClip() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
@ -36,6 +36,7 @@ public:
void setEndFrameVar(const QString& endFrameVar) { _endFrameVar = endFrameVar; }
void setTimeScaleVar(const QString& timeScaleVar) { _timeScaleVar = timeScaleVar; }
void setLoopFlagVar(const QString& loopFlagVar) { _loopFlagVar = loopFlagVar; }
void setMirrorFlagVar(const QString& mirrorFlagVar) { _mirrorFlagVar = mirrorFlagVar; }
void setFrameVar(const QString& frameVar) { _frameVar = frameVar; }
float getStartFrame() const { return _startFrame; }
@ -49,12 +50,16 @@ public:
bool getLoopFlag() const { return _loopFlag; }
void setLoopFlag(bool loopFlag) { _loopFlag = loopFlag; }
bool getMirrorFlag() const { return _mirrorFlag; }
void setMirrorFlag(bool mirrorFlag) { _mirrorFlag = mirrorFlag; }
void loadURL(const QString& url);
protected:
virtual void setCurrentFrameInternal(float frame) override;
void copyFromNetworkAnim();
void buildMirrorAnim();
// for AnimDebugDraw rendering
virtual const AnimPoseVec& getPosesInternal() const override;
@ -64,18 +69,21 @@ protected:
// _anim[frame][joint]
std::vector<AnimPoseVec> _anim;
std::vector<AnimPoseVec> _mirrorAnim;
QString _url;
float _startFrame;
float _endFrame;
float _timeScale;
bool _loopFlag;
bool _mirrorFlag;
float _frame;
QString _startFrameVar;
QString _endFrameVar;
QString _timeScaleVar;
QString _loopFlagVar;
QString _mirrorFlagVar;
QString _frameVar;
// no copies

View file

@ -155,6 +155,14 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
} \
bool NAME = NAME##_VAL.toBool()
#define READ_OPTIONAL_BOOL(NAME, JSON_OBJ, DEFAULT) \
auto NAME##_VAL = JSON_OBJ.value(#NAME); \
bool NAME = DEFAULT; \
if (NAME##_VAL.isBool()) { \
NAME = NAME##_VAL.toBool(); \
} \
do {} while (0)
#define READ_FLOAT(NAME, JSON_OBJ, ID, URL, ERROR_RETURN) \
auto NAME##_VAL = JSON_OBJ.value(#NAME); \
if (!NAME##_VAL.isDouble()) { \
@ -232,13 +240,15 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString&
READ_FLOAT(endFrame, jsonObj, id, jsonUrl, nullptr);
READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr);
READ_BOOL(loopFlag, jsonObj, id, jsonUrl, nullptr);
READ_OPTIONAL_BOOL(mirrorFlag, jsonObj, false);
READ_OPTIONAL_STRING(startFrameVar, jsonObj);
READ_OPTIONAL_STRING(endFrameVar, jsonObj);
READ_OPTIONAL_STRING(timeScaleVar, jsonObj);
READ_OPTIONAL_STRING(loopFlagVar, jsonObj);
READ_OPTIONAL_STRING(mirrorFlagVar, jsonObj);
auto node = std::make_shared<AnimClip>(id, url, startFrame, endFrame, timeScale, loopFlag);
auto node = std::make_shared<AnimClip>(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag);
if (!startFrameVar.isEmpty()) {
node->setStartFrameVar(startFrameVar);
@ -252,6 +262,9 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString&
if (!loopFlagVar.isEmpty()) {
node->setLoopFlagVar(loopFlagVar);
}
if (!mirrorFlagVar.isEmpty()) {
node->setMirrorFlagVar(mirrorFlagVar);
}
return node;
}

View file

@ -51,6 +51,11 @@ AnimPose AnimPose::inverse() const {
return AnimPose(glm::inverse(static_cast<glm::mat4>(*this)));
}
// mirror about x-axis without applying negative scale.
AnimPose AnimPose::mirror() const {
return AnimPose(scale, glm::quat(rot.w, rot.x, -rot.y, -rot.z), glm::vec3(-trans.x, trans.y, trans.z));
}
AnimPose::operator glm::mat4() const {
glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f);
glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f);

View file

@ -30,6 +30,7 @@ struct AnimPose {
AnimPose operator*(const AnimPose& rhs) const;
AnimPose inverse() const;
AnimPose mirror() const;
operator glm::mat4() const;
glm::vec3 scale;

View file

@ -87,7 +87,8 @@ AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& poses)
void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const {
// poses start off relative and leave in absolute frame
for (int i = 0; i < (int)poses.size() && i < (int)_joints.size(); ++i) {
int lastIndex = std::min((int)poses.size(), (int)_joints.size());
for (int i = 0; i < lastIndex; ++i) {
int parentIndex = _joints[i].parentIndex;
if (parentIndex != -1) {
poses[i] = poses[parentIndex] * poses[i];
@ -95,6 +96,30 @@ void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const {
}
}
void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const {
// poses start off absolute and leave in relative frame
int lastIndex = std::min((int)poses.size(), (int)_joints.size());
for (int i = lastIndex - 1; i >= 0; --i) {
int parentIndex = _joints[i].parentIndex;
if (parentIndex != -1) {
poses[i] = poses[parentIndex].inverse() * poses[i];
}
}
}
void AnimSkeleton::mirrorRelativePoses(AnimPoseVec& poses) const {
convertRelativePosesToAbsolute(poses);
mirrorAbsolutePoses(poses);
convertAbsolutePosesToRelative(poses);
}
void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const {
AnimPoseVec temp = poses;
for (int i = 0; i < (int)poses.size(); i++) {
poses[_mirrorMap[i]] = temp[i].mirror();
}
}
void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints) {
_joints = joints;
@ -150,6 +175,24 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
}
}
}
// build mirror map.
_mirrorMap.reserve(_joints.size());
for (int i = 0; i < (int)joints.size(); i++) {
int mirrorJointIndex = -1;
if (_joints[i].name.startsWith("Left")) {
QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right");
mirrorJointIndex = nameToJointIndex(mirrorJointName);
} else if (_joints[i].name.startsWith("Right")) {
QString mirrorJointName = QString(_joints[i].name).replace(0, 5, "Left");
mirrorJointIndex = nameToJointIndex(mirrorJointName);
}
if (mirrorJointIndex >= 0) {
_mirrorMap.push_back(mirrorJointIndex);
} else {
_mirrorMap.push_back(i);
}
}
}
#ifndef NDEBUG

View file

@ -53,6 +53,10 @@ public:
AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& poses) const;
void convertRelativePosesToAbsolute(AnimPoseVec& poses) const;
void convertAbsolutePosesToRelative(AnimPoseVec& poses) const;
void mirrorRelativePoses(AnimPoseVec& poses) const;
void mirrorAbsolutePoses(AnimPoseVec& poses) const;
#ifndef NDEBUG
void dump() const;
@ -69,6 +73,7 @@ protected:
AnimPoseVec _absoluteDefaultPoses;
AnimPoseVec _relativePreRotationPoses;
AnimPoseVec _relativePostRotationPoses;
std::vector<int> _mirrorMap;
// no copies
AnimSkeleton(const AnimSkeleton&) = delete;

View file

@ -120,7 +120,7 @@ void Rig::overrideRoleAnimation(const QString& role, const QString& url, float f
_origRoleAnimations[role] = node;
const float REFERENCE_FRAMES_PER_SECOND = 30.0f;
float timeScale = fps / REFERENCE_FRAMES_PER_SECOND;
auto clipNode = std::make_shared<AnimClip>(role, url, firstFrame, lastFrame, timeScale, loop);
auto clipNode = std::make_shared<AnimClip>(role, url, firstFrame, lastFrame, timeScale, loop, false);
AnimNode::Pointer parent = node->getParent();
parent->replaceChild(node, clipNode);
} else {
@ -152,7 +152,7 @@ void Rig::prefetchAnimation(const QString& url) {
// This will begin loading the NetworkGeometry for the given URL.
// which should speed us up if we request it later via overrideAnimation.
auto clipNode = std::make_shared<AnimClip>("prefetch", url, 0, 0, 1.0, false);
auto clipNode = std::make_shared<AnimClip>("prefetch", url, 0, 0, 1.0, false, false);
_prefetchedAnimations.push_back(clipNode);
}