Path: blob/master/thirdparty/jolt_physics/Jolt/Skeleton/SkeletalAnimation.cpp
9912 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Skeleton/SkeletalAnimation.h>7#include <Jolt/Skeleton/SkeletonPose.h>8#include <Jolt/ObjectStream/TypeDeclarations.h>9#include <Jolt/Core/StreamIn.h>10#include <Jolt/Core/StreamOut.h>1112JPH_NAMESPACE_BEGIN1314JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::JointState)15{16JPH_ADD_ATTRIBUTE(JointState, mRotation)17JPH_ADD_ATTRIBUTE(JointState, mTranslation)18}1920JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::Keyframe)21{22JPH_ADD_BASE_CLASS(Keyframe, JointState)2324JPH_ADD_ATTRIBUTE(Keyframe, mTime)25}2627JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::AnimatedJoint)28{29JPH_ADD_ATTRIBUTE(AnimatedJoint, mJointName)30JPH_ADD_ATTRIBUTE(AnimatedJoint, mKeyframes)31}3233JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation)34{35JPH_ADD_ATTRIBUTE(SkeletalAnimation, mAnimatedJoints)36JPH_ADD_ATTRIBUTE(SkeletalAnimation, mIsLooping)37}383940void SkeletalAnimation::JointState::FromMatrix(Mat44Arg inMatrix)41{42mRotation = inMatrix.GetQuaternion();43mTranslation = inMatrix.GetTranslation();44}4546float SkeletalAnimation::GetDuration() const47{48if (!mAnimatedJoints.empty() && !mAnimatedJoints[0].mKeyframes.empty())49return mAnimatedJoints[0].mKeyframes.back().mTime;50else51return 0.0f;52}5354void SkeletalAnimation::ScaleJoints(float inScale)55{56for (SkeletalAnimation::AnimatedJoint &j : mAnimatedJoints)57for (SkeletalAnimation::Keyframe &k : j.mKeyframes)58k.mTranslation *= inScale;59}6061void SkeletalAnimation::Sample(float inTime, SkeletonPose &ioPose) const62{63// Correct time when animation is looping64JPH_ASSERT(inTime >= 0.0f);65float duration = GetDuration();66float time = duration > 0.0f && mIsLooping? fmod(inTime, duration) : inTime;6768for (const AnimatedJoint &aj : mAnimatedJoints)69{70// Do binary search for keyframe71int high = (int)aj.mKeyframes.size(), low = -1;72while (high - low > 1)73{74int probe = (high + low) / 2;75if (aj.mKeyframes[probe].mTime < time)76low = probe;77else78high = probe;79}8081JointState &state = ioPose.GetJoint(ioPose.GetSkeleton()->GetJointIndex(aj.mJointName));8283if (low == -1)84{85// Before first key, return first key86state = static_cast<const JointState &>(aj.mKeyframes.front());87}88else if (high == (int)aj.mKeyframes.size())89{90// Beyond last key, return last key91state = static_cast<const JointState &>(aj.mKeyframes.back());92}93else94{95// Interpolate96const Keyframe &s1 = aj.mKeyframes[low];97const Keyframe &s2 = aj.mKeyframes[low + 1];9899float fraction = (time - s1.mTime) / (s2.mTime - s1.mTime);100JPH_ASSERT(fraction >= 0.0f && fraction <= 1.0f);101102state.mTranslation = (1.0f - fraction) * s1.mTranslation + fraction * s2.mTranslation;103JPH_ASSERT(s1.mRotation.IsNormalized());104JPH_ASSERT(s2.mRotation.IsNormalized());105state.mRotation = s1.mRotation.SLERP(s2.mRotation, fraction);106JPH_ASSERT(state.mRotation.IsNormalized());107}108}109}110111void SkeletalAnimation::SaveBinaryState(StreamOut &inStream) const112{113inStream.Write((uint32)mAnimatedJoints.size());114for (const AnimatedJoint &j : mAnimatedJoints)115{116// Write Joint name and number of keyframes117inStream.Write(j.mJointName);118inStream.Write((uint32)j.mKeyframes.size());119for (const Keyframe &k : j.mKeyframes)120{121inStream.Write(k.mTime);122inStream.Write(k.mRotation);123inStream.Write(k.mTranslation);124}125}126127// Save additional parameters128inStream.Write(mIsLooping);129}130131SkeletalAnimation::AnimationResult SkeletalAnimation::sRestoreFromBinaryState(StreamIn &inStream)132{133AnimationResult result;134135Ref<SkeletalAnimation> animation = new SkeletalAnimation;136137// Restore animated joints138uint32 len = 0;139inStream.Read(len);140animation->mAnimatedJoints.resize(len);141for (AnimatedJoint &j : animation->mAnimatedJoints)142{143// Read joint name144inStream.Read(j.mJointName);145146// Read keyframes147len = 0;148inStream.Read(len);149j.mKeyframes.resize(len);150for (Keyframe &k : j.mKeyframes)151{152inStream.Read(k.mTime);153inStream.Read(k.mRotation);154inStream.Read(k.mTranslation);155}156}157158// Read additional parameters159inStream.Read(animation->mIsLooping);160result.Set(animation);161return result;162}163164JPH_NAMESPACE_END165166167