#include "stdafx.h" #include "ModelAnimator.h" #include "AnimationBlender.h" ModelAnimator::ModelAnimator(MeshFilter* pMeshFilter) : m_pMeshFilter{ pMeshFilter } { SetAnimation(0); } ModelAnimator::~ModelAnimator() { delete m_pAnimationBlender; } void ModelAnimator::Update(const SceneContext& sceneContext) { //TODO_W6_(); float elapsedSec{ sceneContext.pGameTime->GetElapsed() }; //We only update the transforms if the animation is running and the clip is set if (m_IsPlaying && m_ClipSet) { if (m_UseBlending && m_pAnimationBlender->IsBlending()) { m_pAnimationBlender->Update(elapsedSec); } else { //1. //Calculate the passedTicks (see the lab document) auto passedTicks = elapsedSec * m_CurrentClip.ticksPerSecond * m_AnimationSpeed; //Make sure that the passedTicks stay between the m_CurrentClip.Duration bounds (fmod) passedTicks = fmod(passedTicks, m_CurrentClip.duration); //2. //IF m_Reversed is true // Subtract passedTicks from m_TickCount // If m_TickCount is smaller than zero, add m_CurrentClip.Duration to m_TickCount //ELSE // Add passedTicks to m_TickCount // if m_TickCount is bigger than the clip duration, subtract the duration from m_TickCount if (m_Reversed) { m_TickCount -= passedTicks; if (m_TickCount < 0) { m_TickCount += m_CurrentClip.duration; } } else { m_TickCount += passedTicks; if (m_TickCount > m_CurrentClip.duration) { m_TickCount -= m_CurrentClip.duration; } } //3. //Find the enclosing keys AnimationKey keyA, keyB; //Iterate all the keys of the clip and find the following keys: //keyA > Closest Key with Tick before/smaller than m_TickCount //keyB > Closest Key with Tick after/bigger than m_TickCount for (const auto& key : m_CurrentClip.keys) { if (key.tick < m_TickCount) { keyA = key; } else if (key.tick > m_TickCount) { keyB = key; break; } } //4. //Interpolate between keys //Figure out the BlendFactor float blendFactor{}; blendFactor = (m_TickCount - keyA.tick) / (keyB.tick - keyA.tick); //Clear the m_Transforms vector m_Transforms.clear(); //FOR every boneTransform in a key (So for every bone) for (size_t i = 0; i < m_pMeshFilter->m_BoneCount; i++) { // Retrieve the transform from keyA (transformA) auto transformA = keyA.boneTransforms[i]; // Retrieve the transform from keyB (transformB) auto transformB = keyB.boneTransforms[i]; // Decompose both transforms XMVECTOR scaleA, rotQuatA, transA; XMVECTOR scaleB, rotQuatB, transB; XMMatrixDecompose(&scaleA, &rotQuatA, &transA, XMLoadFloat4x4(&transformA)); XMMatrixDecompose(&scaleB, &rotQuatB, &transB, XMLoadFloat4x4(&transformB)); // Lerp between all the transformations (Position, Scale, Rotation) auto lerpScale = XMVectorLerp(scaleA, scaleB, blendFactor); auto lerpRotQuat = XMQuaternionSlerp(rotQuatA, rotQuatB, blendFactor); auto lerpTrans = XMVectorLerp(transA, transB, blendFactor); // Compose a transformation matrix with the lerp-results XMMATRIX newTransform = XMMatrixAffineTransformation(lerpScale, XMVectorZero(), lerpRotQuat, lerpTrans); // Add the resulting matrix to the m_Transforms vector XMFLOAT4X4 result; XMStoreFloat4x4(&result, newTransform); m_Transforms.push_back(result); } } } } void ModelAnimator::SetAnimation(const std::wstring& clipName) { //TODO_W6_() //Set m_ClipSet to false m_ClipSet = false; //Iterate the m_AnimationClips vector and search for an AnimationClip with the given name (clipName) //If found, // Call SetAnimation(Animation Clip) with the found clip //Else // Call Reset // Log a warning with an appropriate message bool found{ false }; for (auto& clip : m_pMeshFilter->m_AnimationClips) { if (clip.name == clipName) { SetAnimation(clip); found = true; break; } } if (!found) { Reset(); Logger::LogWarning(L"Failed to set AnimationClip by name!"); } } void ModelAnimator::SetAnimation(UINT clipNumber) { //TODO_W6_() //Set m_ClipSet to false m_ClipSet = false; //Check if clipNumber is smaller than the actual m_AnimationClips vector size //If not, // Call Reset // Log a warning with an appropriate message // return //else // Retrieve the AnimationClip from the m_AnimationClips vector based on the given clipNumber // Call SetAnimation(AnimationClip clip) if (clipNumber < m_pMeshFilter->m_AnimationClips.size()) { const AnimationClip& clip = m_pMeshFilter->m_AnimationClips[clipNumber]; SetAnimation(clip); } else { Reset(); Logger::LogWarning(L"Failed to set AnimationClip by clip number!"); } } void ModelAnimator::SetAnimation(const AnimationClip& clip) { if (m_UseBlending) { m_pAnimationBlender->StartBlend(clip); } //Set m_ClipSet to true m_ClipSet = true; //Set m_CurrentClip m_CurrentClip = clip; //Call Reset(false) Reset(false); } void ModelAnimator::Reset(bool pause) { //TODO_W6_() //If pause is true, set m_IsPlaying to false if (pause) m_IsPlaying = false; //Set m_TickCount to zero m_TickCount = 0; //Set m_AnimationSpeed to 1.0f m_AnimationSpeed = 1.f; //If m_ClipSet is true // Retrieve the BoneTransform from the first Key from the current clip (m_CurrentClip) // Refill the m_Transforms vector with the new BoneTransforms (have a look at vector::assign) //Else // Create an IdentityMatrix // Refill the m_Transforms vector with this IdentityMatrix (Amount = BoneCount) (have a look at vector::assign) if (m_ClipSet) { const std::vector& boneTransforms = m_CurrentClip.keys[0].boneTransforms; m_Transforms.assign(boneTransforms.begin(), boneTransforms.end()); } else { XMFLOAT4X4 identityMat; XMStoreFloat4x4(&identityMat, XMMatrixIdentity()); m_Transforms.assign(m_pMeshFilter->m_BoneCount, identityMat); } } void ModelAnimator::SetUseBlending(bool useBlending) { if (useBlending && m_pAnimationBlender == nullptr) m_pAnimationBlender = new AnimationBlender(this); m_UseBlending = useBlending; }