mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-10 17:23:15 +02:00
Merge pull request #7021 from hyperlogic/tony/animation-mirror-support
AnimClip: Animation mirror support
This commit is contained in:
commit
eebf6f8883
8 changed files with 104 additions and 8 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue