363 lines · 15.3 KB · C++
RawDownload
////  AnimClip.cpp////  Created by Anthony J. Thibault on 9/2/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 "AnimClip.h" #include <assert.h> #include "GLMHelpers.h"#include "AnimationLogging.h"#include "AnimUtil.h" #ifdef USE_CUSTOM_ASSERT#undef ASSERT#define ASSERT(x)                     \    do {                              \        if (!(x)) {                   \            int* bad_ptr = 0;         \            *bad_ptr = 0x0badf00d;    \        }                             \    } while (0)#else#define ASSERT assert#endif static std::vector<int> buildJointIndexMap(const AnimSkeleton& dstSkeleton, const AnimSkeleton& srcSkeleton) {    std::vector<int> jointIndexMap;    int srcJointCount = srcSkeleton.getNumJoints();    jointIndexMap.reserve(srcJointCount);    for (int srcJointIndex = 0; srcJointIndex < srcJointCount; srcJointIndex++) {        QString srcJointName = srcSkeleton.getJointName(srcJointIndex);        int dstJointIndex = dstSkeleton.nameToJointIndex(srcJointName);        jointIndexMap.push_back(dstJointIndex);    }    return jointIndexMap;} static void bakeRelativeDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPoseVec& basePoses) {     // invert all the basePoses    AnimPoseVec invBasePoses = basePoses;    for (auto&& invBasePose : invBasePoses) {        invBasePose = invBasePose.inverse();    }     // for each frame of the animation    for (auto&& animPoses : anim) {        ASSERT(animPoses.size() == basePoses.size());         // for each joint in animPoses        for (size_t i = 0; i < animPoses.size(); ++i) {            // convert this relative AnimPose into a delta animation.            animPoses[i] = invBasePoses[i] * animPoses[i];        }    }} void bakeAbsoluteDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPoseVec& basePoses, AnimSkeleton::ConstPointer skeleton) {     // invert all the basePoses    AnimPoseVec invBasePoses = basePoses;    for (auto&& invBasePose : invBasePoses) {        invBasePose = invBasePose.inverse();    }     AnimPoseVec absBasePoses = basePoses;    skeleton->convertRelativePosesToAbsolute(absBasePoses);     // for each frame of the animation    for (auto&& animPoses : anim) {        ASSERT(animPoses.size() == basePoses.size());         // for each joint in animPoses        for (size_t i = 0; i < animPoses.size(); ++i) {             // scale and translation are relative frame            animPoses[i] = invBasePoses[i] * animPoses[i];             // convert from a rotation that happens in the relative space of the joint            // into a rotation that happens in the absolute space of the joint.            animPoses[i].rot() = absBasePoses[i].rot() * animPoses[i].rot() * glm::inverse(absBasePoses[i].rot());        }    }} static std::vector<AnimPoseVec> copyAndRetargetFromNetworkAnim(AnimationPointer networkAnim, AnimSkeleton::ConstPointer avatarSkeleton) {    ASSERT(networkAnim && networkAnim->isLoaded() && avatarSkeleton);    std::vector<AnimPoseVec> anim;     const HFMModel& animModel = networkAnim->getHFMModel();    AnimSkeleton animSkeleton(animModel);    const int animJointCount = animSkeleton.getNumJoints();    const int avatarJointCount = avatarSkeleton->getNumJoints();     // build a mapping from animation joint indices to avatar joint indices by matching joints with the same name.    std::vector<int> avatarToAnimJointIndexMap = buildJointIndexMap(animSkeleton, *avatarSkeleton);     const int animFrameCount = animModel.animationFrames.size();    anim.resize(animFrameCount);     // find the size scale factor for translation in the animation.    float boneLengthScale = 1.0f;    const int avatarHipsIndex = avatarSkeleton->nameToJointIndex("Hips");    const int animHipsIndex = animSkeleton.nameToJointIndex("Hips");    if (avatarHipsIndex != -1 && animHipsIndex != -1) {        const int avatarHipsParentIndex = avatarSkeleton->getParentIndex(avatarHipsIndex);        const int animHipsParentIndex = animSkeleton.getParentIndex(animHipsIndex);         const AnimPose& avatarHipsAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarHipsIndex);        const AnimPose& animHipsAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animHipsIndex);         // the get the units and the heights for the animation and the avatar        const float avatarUnitScale = extractScale(avatarSkeleton->getGeometryOffset()).y;        const float animationUnitScale = extractScale(animModel.offset).y;        const float avatarHeightInMeters = avatarUnitScale * avatarHipsAbsoluteDefaultPose.trans().y;        const float animHeightInMeters = animationUnitScale * animHipsAbsoluteDefaultPose.trans().y;         // get the parent scales for the avatar and the animation        float avatarHipsParentScale = 1.0f;        if (avatarHipsParentIndex != -1) {            const AnimPose& avatarHipsParentAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarHipsParentIndex);            avatarHipsParentScale = avatarHipsParentAbsoluteDefaultPose.scale().y;        }        float animHipsParentScale = 1.0f;        if (animHipsParentIndex != -1) {            const AnimPose& animationHipsParentAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animHipsParentIndex);            animHipsParentScale = animationHipsParentAbsoluteDefaultPose.scale().y;        }         const float EPSILON = 0.0001f;        // compute the ratios for the units, the heights in meters, and the parent scales        if ((fabsf(animHeightInMeters) > EPSILON) && (animationUnitScale > EPSILON) && (animHipsParentScale > EPSILON)) {            const float avatarToAnimationHeightRatio = avatarHeightInMeters / animHeightInMeters;            const float unitsRatio = 1.0f / (avatarUnitScale / animationUnitScale);            const float parentScaleRatio = 1.0f / (avatarHipsParentScale / animHipsParentScale);             boneLengthScale = avatarToAnimationHeightRatio * unitsRatio * parentScaleRatio;        }    }     for (int frame = 0; frame < animFrameCount; frame++) {        const HFMAnimationFrame& animFrame = animModel.animationFrames[frame];        ASSERT(frame >= 0 && frame < (int)animModel.animationFrames.size());         // extract the full rotations from the animFrame (including pre and post rotations from the animModel).        std::vector<glm::quat> animRotations;        animRotations.reserve(animJointCount);        for (int i = 0; i < animJointCount; i++) {            ASSERT(i >= 0 && i < (int)animModel.joints.size());            ASSERT(i >= 0 && i < (int)animFrame.rotations.size());            animRotations.push_back(animModel.joints[i].preRotation * animFrame.rotations[i] * animModel.joints[i].postRotation);        }         // convert rotations into absolute frame        animSkeleton.convertRelativeRotationsToAbsolute(animRotations);         // build absolute rotations for the avatar        std::vector<glm::quat> avatarRotations;        avatarRotations.reserve(avatarJointCount);        for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) {            ASSERT(avatarJointIndex >= 0 && avatarJointIndex < (int)avatarToAnimJointIndexMap.size());            int animJointIndex = avatarToAnimJointIndexMap[avatarJointIndex];            if (animJointIndex >= 0) {                // This joint is in both animation and avatar.                // Set the absolute rotation directly                ASSERT(animJointIndex >= 0 && animJointIndex < (int)animRotations.size());                avatarRotations.push_back(animRotations[animJointIndex]);            } else {                // This joint is NOT in the animation at all.                // Set it so that the default relative rotation remains unchanged.                glm::quat avatarRelativeDefaultRot = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex).rot();                glm::quat avatarParentAbsoluteRot;                int avatarParentJointIndex = avatarSkeleton->getParentIndex(avatarJointIndex);                if (avatarParentJointIndex >= 0) {                    ASSERT(avatarParentJointIndex >= 0 && avatarParentJointIndex < (int)avatarRotations.size());                    avatarParentAbsoluteRot = avatarRotations[avatarParentJointIndex];                }                avatarRotations.push_back(avatarParentAbsoluteRot * avatarRelativeDefaultRot);            }        }         // convert avatar rotations into relative frame        avatarSkeleton->convertAbsoluteRotationsToRelative(avatarRotations);         ASSERT(frame >= 0 && frame < (int)anim.size());        anim[frame].reserve(avatarJointCount);        for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) {            const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex);             // copy scale over from avatar default pose            glm::vec3 relativeScale = avatarDefaultPose.scale();             glm::vec3 relativeTranslation;            ASSERT(avatarJointIndex >= 0 && avatarJointIndex < (int)avatarToAnimJointIndexMap.size());            int animJointIndex = avatarToAnimJointIndexMap[avatarJointIndex];            if (animJointIndex >= 0) {                // This joint is in both animation and avatar.                ASSERT(animJointIndex >= 0 && animJointIndex < (int)animFrame.translations.size());                const glm::vec3& animTrans = animFrame.translations[animJointIndex];                 // retarget translation from animation to avatar                ASSERT(animJointIndex >= 0 && animJointIndex < (int)animModel.animationFrames[0].translations.size());                const glm::vec3& animZeroTrans = animModel.animationFrames[0].translations[animJointIndex];                relativeTranslation = avatarDefaultPose.trans() + boneLengthScale * (animTrans - animZeroTrans);            } else {                // This joint is NOT in the animation at all.                // preserve the default translation.                relativeTranslation = avatarDefaultPose.trans();            }             // build the final pose            ASSERT(avatarJointIndex >= 0 && avatarJointIndex < (int)avatarRotations.size());            anim[frame].push_back(AnimPose(relativeScale, avatarRotations[avatarJointIndex], relativeTranslation));        }    }     return anim;} AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag,                   AnimBlendType blendType, const QString& baseURL, float baseFrame) :    AnimNode(AnimNode::Type::Clip, id),    _startFrame(startFrame),    _endFrame(endFrame),    _timeScale(timeScale),    _loopFlag(loopFlag),    _mirrorFlag(mirrorFlag),    _frame(startFrame),    _blendType(blendType),    _baseFrame(baseFrame){    loadURL(url);     if (blendType != AnimBlendType_Normal) {        auto animCache = DependencyManager::get<AnimationCache>();        _baseNetworkAnim = animCache->getAnimation(baseURL);        _baseURL = baseURL;    }} AnimClip::~AnimClip() { } const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {     // lookup parameters from animVars, using current instance variables as defaults.    _startFrame = animVars.lookup(_startFrameVar, _startFrame);    _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);     // poll network anim to see if it's finished loading yet.    if (_blendType == AnimBlendType_Normal) {        if (_networkAnim && _networkAnim->isLoaded() && _skeleton) {            // loading is complete, copy & retarget animation.            _anim = copyAndRetargetFromNetworkAnim(_networkAnim, _skeleton);             // we no longer need the actual animation resource anymore.            _networkAnim.reset();             // mirrorAnim will be re-built on demand, if needed.            _mirrorAnim.clear();             _poses.resize(_skeleton->getNumJoints());        }    } else {        // an additive blend type        if (_networkAnim && _networkAnim->isLoaded() && _baseNetworkAnim && _baseNetworkAnim->isLoaded() && _skeleton) {            // loading is complete, copy & retarget animation.            _anim = copyAndRetargetFromNetworkAnim(_networkAnim, _skeleton);             // we no longer need the actual animation resource anymore.            _networkAnim.reset();             // mirrorAnim will be re-built on demand, if needed.            // TODO: handle mirrored relative animations.            _mirrorAnim.clear();             _poses.resize(_skeleton->getNumJoints());             // copy & retarget baseAnim!            auto baseAnim = copyAndRetargetFromNetworkAnim(_baseNetworkAnim, _skeleton);             if (_blendType == AnimBlendType_AddAbsolute) {                bakeAbsoluteDeltaAnim(_anim, baseAnim[(int)_baseFrame], _skeleton);            } else {                // AnimBlendType_AddRelative                bakeRelativeDeltaAnim(_anim, baseAnim[(int)_baseFrame]);            }        }    }     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) {            nextIndex = (int)glm::ceil(_startFrame);        } else {            nextIndex = (int)glm::ceil(_frame);        }         // It can be quite possible for the user to set _startFrame and _endFrame to        // values before or past valid ranges.  We clamp the frames here.        int frameCount = (int)_anim.size();        prevIndex = std::min(std::max(0, prevIndex), frameCount - 1);        nextIndex = std::min(std::max(0, nextIndex), frameCount - 1);         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]);    }     processOutputJoints(triggersOut);     return _poses;} void AnimClip::setCurrentFrameInternal(float frame) {    // because dt is 0, we should not encounter any triggers    const float dt = 0.0f;    AnimVariantMap triggers;    _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers);} 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;} void AnimClip::loadURL(const QString& url) {    auto animCache = DependencyManager::get<AnimationCache>();    _networkAnim = animCache->getAnimation(url);    _url = url;}