Skip to content

Commit

Permalink
feat: #45 Add multi animation blending
Browse files Browse the repository at this point in the history
  • Loading branch information
ducphamhong committed Jan 11, 2020
1 parent 04eb8b3 commit f29be43
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 64 deletions.
46 changes: 34 additions & 12 deletions Projects/Demo/Source/View/CViewInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "MeshManager/CMeshManager.h"
#include "Animation/CAnimationManager.h"
#include "Animation/CAnimationController.h"

#include "Context/CContext.h"

Expand Down Expand Up @@ -76,13 +77,13 @@ void CViewInit::initScene()
// grid
zone->createEmptyObject()->addComponent<CGridPlane>();

// test dae model & animation
/*
// load animation
CAnimationManager *animManager = CAnimationManager::getInstance();
CAnimation* anim = animManager->createAnimation("HeroAnimation");
anim->addClip(animManager->loadAnimation("Demo/Model3D/Hero@Idle.dae"));
CAnimationClip *animIdle = animManager->loadAnimation("Demo/Model3D/Hero@Idle.dae");
CAnimationClip *animWalkForward = animManager->loadAnimation("Demo/Model3D/Hero@WalkForward.dae");
CAnimationClip *animRunForward = animManager->loadAnimation("Demo/Model3D/Hero@RunForward.dae");
// test dae model
CMeshManager *meshManager = CMeshManager::getInstance();
CEntityPrefab *prefab = meshManager->loadModel("Demo/Model3D/Hero.dae", "Demo/Model3D/Textures", false);
if (prefab != NULL)
Expand All @@ -91,17 +92,38 @@ void CViewInit::initScene()
CGameObject *model = zone->createEmptyObject();
model->addComponent<CRenderMesh>()->initFromPrefab(prefab);
// setup animation
CAnimationController *animController = model->addComponent<CAnimationController>();
CSkeleton *skeletonIdle = animController->createSkeleton();
CSkeleton *skeletonWalkForward = animController->createSkeleton();
CSkeleton *skeletonRunForward = animController->createSkeleton();
CSkeleton *output = animController->createSkeleton();
output->setAnimationType(CSkeleton::Blending);
// set animation clip
skeletonIdle->setAnimation(animIdle, true);
skeletonIdle->getTimeline().Weight = 0.0f;
skeletonWalkForward->setAnimation(animWalkForward, true);
skeletonWalkForward->getTimeline().Weight = 0.0f;
skeletonRunForward->setAnimation(animRunForward, true);
skeletonRunForward->getTimeline().Weight = 1.0f;
// blending
skeletonIdle->setTarget(output);
skeletonWalkForward->setTarget(output);
skeletonRunForward->setTarget(output);
// output animation
animController->setOutput(output);
// setup transform
CTransformEuler *transform = model->getTransformEuler();
transform->setPosition(core::vector3df(0.0f, 0.0f, 2.0f));
transform->setYaw(45.0f);
// instance object 2
model = zone->createEmptyObject();
model->addComponent<CRenderMesh>()->initFromPrefab(prefab);
transform = model->getTransformEuler();
transform->setPosition(core::vector3df(0.0f, 0.0f, -3.0f));
transform->setYaw(-45.0f);
}
*/

Expand Down
12 changes: 12 additions & 0 deletions Projects/Skylicht/Engine/Source/Animation/CAnimationController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ namespace Skylicht

void CAnimationController::updateComponent()
{
for (CSkeleton *&skeleton : m_skeletons)
{
if (skeleton->isEnable() == true && skeleton->getAnimationType() == CSkeleton::KeyFrame)
skeleton->getTimeline().update();
}

for (CSkeleton *&skeleton : m_skeletons)
{
if (skeleton->isEnable() == true && skeleton->getAnimationType() == CSkeleton::Blending)
skeleton->syncAnimationByTimeScale();
}

for (CSkeleton *&skeleton : m_skeletons)
{
if (skeleton->isEnable() == true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,10 @@ namespace Skylicht
{

CAnimationTimeline::CAnimationTimeline() :
AnimationFrame(0.0f),
AnimationDuration(0.0f),
AnimationSpeed(1.0f),
AnimationSpeedMultiply(1.0f),
AnimationDurationSyncRatio(1.0f),
AnimationWeight(1.0f),
AnimationSleep(0.0f),
EndTrackSleep(0.0f),
Frame(0.0f),
Duration(0.0f),
Speed(1.0f),
Weight(1.0f),
SyncSeekRatio(0.0f),
Pause(false)
{
Expand All @@ -50,29 +46,19 @@ namespace Skylicht
float milisecondToSecond = 1.0f / 1000.0f;
if (Pause == false)
{
float secFrameStep = getTimeStep()*AnimationSpeed*AnimationSpeedMultiply*milisecondToSecond;
float secFrameStep = getTimeStep()*Speed*milisecondToSecond;

// seek animation frame
AnimationFrame = AnimationFrame + secFrameStep;
Frame = Frame + secFrameStep;

// if end of animation
if (AnimationFrame > AnimationDuration)
if (Frame > Duration)
{
AnimationFrame = AnimationDuration;
Frame = Duration;

// if animation is loop
if (AnimationLoop == true)
{
if (AnimationSleep > 0)
{
AnimationSleep = AnimationSleep - secFrameStep;
}
else
{
AnimationFrame = 0.0f;
AnimationSleep = EndTrackSleep;
}
}
if (Loop == true)
Frame = 0.0f;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,12 @@ namespace Skylicht
class CAnimationTimeline
{
public:
float AnimationDuration; // second
float AnimationDurationSyncRatio;
float AnimationFrame; // second
float AnimationSpeed; // todo use for sync on animation blending
float AnimationSpeedMultiply;
float AnimationWeight;
float AnimationSleep;
float Duration; // second
float Frame; // second
float Speed;
float Weight;
float SyncSeekRatio;
float EndTrackSleep;
bool AnimationLoop;
bool Loop;
bool Pause;

public:
Expand Down
141 changes: 123 additions & 18 deletions Projects/Skylicht/Engine/Source/Animation/Skeleton/CSkeleton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ namespace Skylicht
CSkeleton::CSkeleton(int id) :
m_id(id),
m_enable(true),
m_animationType(TrackKeyFrame),
m_clip(NULL)
m_animationType(KeyFrame),
m_clip(NULL),
m_target(NULL)
{

}
Expand Down Expand Up @@ -91,9 +92,9 @@ namespace Skylicht
animationData->DefaultRotation = core::quaternion(worldTransform->Relative);

// default anim pos, scale, rot
animationData->AnimPosition = animationData->DefaultPosition;
animationData->AnimScale = animationData->DefaultScale;
animationData->AnimRotation = animationData->DefaultRotation;
COPY_VECTOR3DF(animationData->AnimPosition, animationData->DefaultPosition);
COPY_VECTOR3DF(animationData->AnimScale, animationData->DefaultScale);
COPY_QUATERNION(animationData->AnimRotation, animationData->DefaultRotation);
}
}

Expand All @@ -107,9 +108,9 @@ namespace Skylicht
{
m_clip = clip;

m_timeline.AnimationDuration = 0.0f;
m_timeline.AnimationFrame = 0.0f;
m_timeline.AnimationLoop = loop;
m_timeline.Duration = 0.0f;
m_timeline.Frame = 0.0f;
m_timeline.Loop = loop;
m_timeline.Pause = pause;

for (CAnimationTransformData *&entity : m_entitiesData)
Expand All @@ -133,8 +134,8 @@ namespace Skylicht
else if (anim->Data.ScaleKeys.size() > 0)
totalFrame = anim->Data.ScaleKeys.getLast().frame;

if (m_timeline.AnimationDuration < totalFrame)
m_timeline.AnimationDuration = totalFrame;
if (m_timeline.Duration < totalFrame)
m_timeline.Duration = totalFrame;

// notify this track have animation
track.HaveAnimation = true;
Expand All @@ -144,7 +145,7 @@ namespace Skylicht

void CSkeleton::update()
{
if (m_animationType == TrackKeyFrame)
if (m_animationType == KeyFrame)
updateTrackKeyFrame();
else
updateBlending();
Expand All @@ -156,6 +157,7 @@ namespace Skylicht
{
// todo calc relative matrix & position
core::matrix4 &relativeMatrix = entity->WorldTransform->Relative;
relativeMatrix.makeIdentity();

// rotation
entity->AnimRotation.getMatrix(relativeMatrix);
Expand Down Expand Up @@ -190,35 +192,33 @@ namespace Skylicht

void CSkeleton::updateTrackKeyFrame()
{
m_timeline.update();

for (CAnimationTransformData *&entity : m_entitiesData)
{
CAnimationTrack& track = entity->AnimationTrack;

if (track.HaveAnimation == true)
{
float frame = m_timeline.AnimationFrame;
float frame = m_timeline.Frame;
frame = offsetFrame(frame);
track.getFrameData(frame, entity->AnimPosition, entity->AnimScale, entity->AnimRotation);
}
else
{
COPY_VECTOR3DF(entity->AnimPosition, entity->DefaultPosition);
COPY_VECTOR3DF(entity->AnimRotation, entity->DefaultRotation);
COPY_VECTOR3DF(entity->AnimScale, entity->DefaultScale);
COPY_QUATERNION(entity->AnimRotation, entity->DefaultRotation);
}
}
}

float CSkeleton::offsetFrame(float frame)
{
// need offset the timeline
float offset = m_timeline.AnimationDuration * m_timeline.SyncSeekRatio;
float offset = m_timeline.Duration * m_timeline.SyncSeekRatio;
frame = frame + offset;

if (frame > m_timeline.AnimationDuration)
frame = frame - m_timeline.AnimationDuration;
if (frame > m_timeline.Duration)
frame = frame - m_timeline.Duration;

return frame;
}
Expand Down Expand Up @@ -247,6 +247,111 @@ namespace Skylicht

void CSkeleton::updateBlending()
{
int id = 0;

for (CAnimationTransformData *&entity : m_entitiesData)
{
bool first = true;

for (CSkeleton *&skeleton : m_blending)
{
core::quaternion *rotation = &skeleton->m_entitiesData[id]->AnimRotation;
core::vector3df *position = &skeleton->m_entitiesData[id]->AnimPosition;
core::vector3df *scale = &skeleton->m_entitiesData[id]->AnimScale;

float weight = skeleton->getTimeline().Weight;
if (weight == 0.0f)
continue;

if (first == true)
{
first = false;

entity->AnimRotation.X = rotation->X * weight;
entity->AnimRotation.Y = rotation->Y * weight;
entity->AnimRotation.Z = rotation->Z * weight;
entity->AnimRotation.W = rotation->W * weight;

entity->AnimPosition.X = position->X * weight;
entity->AnimPosition.Y = position->Y * weight;
entity->AnimPosition.Z = position->Z * weight;

entity->AnimScale.X = scale->X * weight;
entity->AnimScale.Y = scale->Y * weight;
entity->AnimScale.Z = scale->Z * weight;
}
else
{
entity->AnimPosition.X += position->X*weight;
entity->AnimPosition.Y += position->Y*weight;
entity->AnimPosition.Z += position->Z*weight;

entity->AnimScale.X += scale->X*weight;
entity->AnimScale.Y += scale->Y*weight;
entity->AnimScale.Z += scale->Z*weight;

float Rx = rotation->X;
float Ry = rotation->Y;
float Rz = rotation->Z;
float Rw = rotation->W;

float dot = entity->AnimRotation.X * Rx + entity->AnimRotation.Y * Ry + entity->AnimRotation.Z * Rz + entity->AnimRotation.W * Rw;

if (dot < 0.0f)
{
Rx *= -1.0f;
Ry *= -1.0f;
Rz *= -1.0f;
Rw *= -1.0f;
}

entity->AnimRotation.X += Rx * weight;
entity->AnimRotation.Y += Ry * weight;
entity->AnimRotation.Z += Rz * weight;
entity->AnimRotation.W += Rw * weight;
}
}

id++;
}
}

void CSkeleton::syncAnimationByTimeScale()
{
float frameRatio = 0.0f;
float maxWeight = -1;

CSkeleton *baseSkeleton = NULL;

for (CSkeleton *&skeleton : m_blending)
{
CAnimationTimeline &trackInfo = skeleton->getTimeline();
if (trackInfo.Duration > 0 && trackInfo.Weight > 0.0f)
{
float targetBlend = 1.0f;

float animWeight = trackInfo.Weight * targetBlend;
float animDuration = trackInfo.Duration;

// find frame ratio
if (maxWeight < animWeight)
{
maxWeight = animWeight;
frameRatio = trackInfo.Frame / animDuration;
baseSkeleton = skeleton;
}
}
}

if (baseSkeleton == NULL)
return;

// sync speed
for (CSkeleton *&skeleton : m_blending)
{
CAnimationTimeline &trackInfo = skeleton->getTimeline();
frameRatio = core::clamp<float>(frameRatio, 0.0f, 1.0f);
trackInfo.Frame = frameRatio * trackInfo.Duration;
}
}
}
Loading

0 comments on commit f29be43

Please sign in to comment.