Bomberman-OverlordEngine-x64 / OverlordEngine / Misc / ModelAnimator.cpp
ModelAnimator.cpp
Raw
#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<XMFLOAT4X4>& 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;
}