mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-05-29 17:00:19 +02:00
222 lines
9.8 KiB
C++
222 lines
9.8 KiB
C++
//
|
|
// 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 "GLMHelpers.h"
|
|
#include "AnimationLogging.h"
|
|
#include "AnimUtil.h"
|
|
|
|
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);
|
|
}
|
|
|
|
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 (_networkAnim && _networkAnim->isLoaded() && _skeleton) {
|
|
// loading is complete, copy animation frames from network animation, then throw it away.
|
|
copyFromNetworkAnim();
|
|
_networkAnim.reset();
|
|
}
|
|
|
|
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::loadURL(const QString& url) {
|
|
auto animCache = DependencyManager::get<AnimationCache>();
|
|
_networkAnim = animCache->getAnimation(url);
|
|
_url = url;
|
|
}
|
|
|
|
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::copyFromNetworkAnim() {
|
|
assert(_networkAnim && _networkAnim->isLoaded() && _skeleton);
|
|
_anim.clear();
|
|
|
|
auto avatarSkeleton = getSkeleton();
|
|
|
|
// build a mapping from animation joint indices to avatar joint indices.
|
|
// by matching joints with the same name.
|
|
const HFMModel& animModel = _networkAnim->getHFMModel();
|
|
AnimSkeleton animSkeleton(animModel);
|
|
const int animJointCount = animSkeleton.getNumJoints();
|
|
const int avatarJointCount = avatarSkeleton->getNumJoints();
|
|
std::vector<int> animToAvatarJointIndexMap;
|
|
animToAvatarJointIndexMap.reserve(animJointCount);
|
|
for (int animJointIndex = 0; animJointIndex < animJointCount; animJointIndex++) {
|
|
QString animJointName = animSkeleton.getJointName(animJointIndex);
|
|
int avatarJointIndex = avatarSkeleton->nameToJointIndex(animJointName);
|
|
animToAvatarJointIndexMap.push_back(avatarJointIndex);
|
|
}
|
|
|
|
const int animFrameCount = animModel.animationFrames.size();
|
|
_anim.resize(animFrameCount);
|
|
|
|
for (int frame = 0; frame < animFrameCount; frame++) {
|
|
|
|
const HFMAnimationFrame& animFrame = animModel.animationFrames[frame];
|
|
|
|
// init all joints in animation to default pose
|
|
// this will give us a resonable result for bones in the avatar skeleton but not in the animation.
|
|
_anim[frame].reserve(avatarJointCount);
|
|
for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) {
|
|
_anim[frame].push_back(avatarSkeleton->getRelativeDefaultPose(avatarJointIndex));
|
|
}
|
|
|
|
for (int animJointIndex = 0; animJointIndex < animJointCount; animJointIndex++) {
|
|
int avatarJointIndex = animToAvatarJointIndexMap[animJointIndex];
|
|
|
|
// skip joints that are in the animation but not in the avatar.
|
|
if (avatarJointIndex >= 0 && avatarJointIndex < avatarJointCount) {
|
|
|
|
const glm::vec3& animTrans = animFrame.translations[animJointIndex];
|
|
const glm::quat& animRot = animFrame.rotations[animJointIndex];
|
|
|
|
const AnimPose& animPreRotPose = animSkeleton.getPreRotationPose(animJointIndex);
|
|
AnimPose animPostRotPose = animSkeleton.getPostRotationPose(animJointIndex);
|
|
AnimPose animRotPose(glm::vec3(1.0f), animRot, glm::vec3());
|
|
|
|
// adjust anim scale to equal the scale from the avatar joint.
|
|
// we do not support animated scale.
|
|
const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex);
|
|
animPostRotPose.scale() = avatarDefaultPose.scale();
|
|
|
|
// retarget translation from animation to avatar
|
|
const glm::vec3& animZeroTrans = animModel.animationFrames[0].translations[animJointIndex];
|
|
float boneLengthScale = 1.0f;
|
|
const float EPSILON = 0.0001f;
|
|
|
|
const int avatarHipsParentIndex = avatarSkeleton->getParentIndex(avatarSkeleton->nameToJointIndex("Hips"));
|
|
const int animHipsParentIndex = animSkeleton.getParentIndex(animSkeleton.nameToJointIndex("Hips"));
|
|
if (avatarJointIndex == avatarSkeleton->nameToJointIndex("Hips") && (avatarHipsParentIndex >= 0) || (animHipsParentIndex >= 0)) {
|
|
|
|
const AnimPose& animHipsAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animSkeleton.nameToJointIndex("Hips"));
|
|
const AnimPose& avatarHipsAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarSkeleton->nameToJointIndex("Hips"));
|
|
|
|
// the get the units and the heights for the animation and the avatar
|
|
const float animationUnitScale = extractScale(animModel.offset).y;
|
|
const float avatarUnitScale = extractScale(avatarSkeleton->getGeometryOffset()).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;
|
|
float animHipsParentScale = 1.0f;
|
|
if (avatarHipsParentIndex >= 0) {
|
|
const AnimPose& avatarHipsParentAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarHipsParentIndex);
|
|
avatarHipsParentScale = avatarHipsParentAbsoluteDefaultPose.scale().y;
|
|
}
|
|
if (animHipsParentIndex >= 0) {
|
|
const AnimPose& animationHipsParentAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animHipsParentIndex);
|
|
animHipsParentScale = animationHipsParentAbsoluteDefaultPose.scale().y;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
} else {
|
|
|
|
if (fabsf(glm::length(animZeroTrans)) > EPSILON) {
|
|
boneLengthScale = glm::length(avatarDefaultPose.trans()) / glm::length(animZeroTrans);
|
|
}
|
|
}
|
|
|
|
AnimPose animTransPose = AnimPose(glm::vec3(1.0f), glm::quat(), avatarDefaultPose.trans() + boneLengthScale * (animTrans - animZeroTrans));
|
|
|
|
_anim[frame][avatarJointIndex] = animTransPose * animPreRotPose * animRotPose * animPostRotPose;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// mirrorAnim will be re-built on demand, if needed.
|
|
_mirrorAnim.clear();
|
|
|
|
_poses.resize(avatarJointCount);
|
|
}
|
|
|
|
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;
|
|
}
|