Animation Tween
게임에서 보면 각 애니메이션 클립을 바꿀때 애니메이션이 바로바로 바뀌는게 아니고 부드럽게 바뀐다.
ModelAnimator.h
struct TweenDesc
{
float TakeTime = 1.0f;
float TweenTime = 0.0f;
float ChangeTime = 0.0f;
float Padding;
KeyframeDesc Curr;
KeyframeDesc Next;
TweenDesc()
{
Curr.Clip = 0;
Next.Clip = -1;
}
}tweenDesc;
이 전에 만들었던 keyframeDesc를 갖고있는 TweenDesc를 만든다.
ModelAnimator.cpp
void ModelAnimator::Update()
{
TweenDesc& desc = tweenDesc;
// Current Animation
{
ModelClip* clip = model->ClipByIndex(desc.Curr.Clip);
desc.Curr.RunningTime += Time::Delta();
float time = 1.0f / clip->FrameRate() / desc.Curr.Speed;
if (desc.Curr.Time >= 1.0f)
{
desc.Curr.RunningTime = 0;
desc.Curr.CurrFrame = (desc.Curr.CurrFrame + 1) % clip->FrameCount();
desc.Curr.NextFrame = (desc.Curr.CurrFrame + 1) % clip->FrameCount();
}
desc.Curr.Time = desc.Curr.RunningTime / time;
}
if (desc.Next.Clip > -1)
{
desc.ChangeTime += Time::Delta();
desc.TweenTime = desc.ChangeTime / desc.TakeTime;
if (desc.TweenTime >= 1.0f)
{
desc.Curr = desc.Next;
desc.Next.Clip = -1;
desc.Next.CurrFrame = 0;
desc.Next.NextFrame = 0;
desc.Next.Time = 0;
desc.Next.RunningTime = 0.0f;
desc.ChangeTime = 0.0f;
desc.TweenTime = 0.0f;
}
else
{
ModelClip* clip = model->ClipByIndex(desc.Next.Clip);
desc.Next.RunningTime += Time::Delta();
float time = 1.0f / clip->FrameRate() / desc.Next.Speed;
if (desc.Next.Time >= 1.0f)
{
desc.Next.RunningTime = 0;
desc.Next.CurrFrame = (desc.Next.CurrFrame + 1) % clip->FrameCount();
desc.Next.NextFrame = (desc.Next.CurrFrame + 1) % clip->FrameCount();
}
desc.Next.Time = desc.Next.RunningTime / time;
}
}
if (texture == NULL)
{
for (ModelMesh* mesh : model->Meshes())
{
mesh->SetShader(shader);
}
CreateTexture();
}
for (ModelMesh* mesh : model->Meshes())
{
mesh->Update();
}
}
다음 클립이 없는 경우는 클립이 0 이상이므로 그때만 검사한다.
다음 클립의 프레임들을 넘겨주고 시간을 다시 설정해준다.
void ModelAnimator::PlayTweenMode(UINT clip, float speed, float takeTime)
{
tweenDesc.TakeTime = takeTime;
tweenDesc.Next.Clip = clip;
tweenDesc.Next.Speed = speed;
}
그리고 클립을 이 함수로 바꾼다.
Shader
struct TweenFrame
{
float TakeTime;
float TweenTime;
float RunningTime;
float Padding;
AnimationFrame Curr;
AnimationFrame Next;
};
void SetAniamtionWorld(inout matrix world, VertexModel input)
{
float indices[4] = { input.BlendIndices.x, input.BlendIndices.y, input.BlendIndices.z, input.BlendIndices.w };
float weights[4] = { input.BlendWeights.x, input.BlendWeights.y, input.BlendWeights.z, input.BlendWeights.w };
int clip[2];
int currFrame[2];
int nextFrame[2];
float time[2];
clip[0] = Tweenframes.Curr.Clip;
currFrame[0] = Tweenframes.Curr.CurrFrame;
nextFrame[0] = Tweenframes.Curr.NextFrame;
time[0] = Tweenframes.Curr.Time;
clip[1] = Tweenframes.Next.Clip;
currFrame[1] = Tweenframes.Next.CurrFrame;
nextFrame[1] = Tweenframes.Next.NextFrame;
time[1] = Tweenframes.Next.Time;
float4 c0, c1, c2, c3;
float4 n0, n1, n2, n3;
matrix curr = 0, next = 0;
matrix currAnim = 0, nextAnim = 0;
matrix transform = 0;
[unroll(4)]
for (int i = 0; i < 4; i++)
{
c0 = TransformsMap.Load(int4(indices[i] * 4 + 0, currFrame[0], clip[0], 0));
c1 = TransformsMap.Load(int4(indices[i] * 4 + 1, currFrame[0], clip[0], 0));
c2 = TransformsMap.Load(int4(indices[i] * 4 + 2, currFrame[0], clip[0], 0));
c3 = TransformsMap.Load(int4(indices[i] * 4 + 3, currFrame[0], clip[0], 0));
curr = matrix(c0, c1, c2, c3);
n0 = TransformsMap.Load(int4(indices[i] * 4 + 0, nextFrame[0], clip[0], 0));
n1 = TransformsMap.Load(int4(indices[i] * 4 + 1, nextFrame[0], clip[0], 0));
n2 = TransformsMap.Load(int4(indices[i] * 4 + 2, nextFrame[0], clip[0], 0));
n3 = TransformsMap.Load(int4(indices[i] * 4 + 3, nextFrame[0], clip[0], 0));
next = matrix(n0, n1, n2, n3);
currAnim = lerp(curr, next, time[0]);
[flatten]
if (clip[1] > -1)
{
c0 = TransformsMap.Load(int4(indices[i] * 4 + 0, currFrame[1], clip[1], 0));
c1 = TransformsMap.Load(int4(indices[i] * 4 + 1, currFrame[1], clip[1], 0));
c2 = TransformsMap.Load(int4(indices[i] * 4 + 2, currFrame[1], clip[1], 0));
c3 = TransformsMap.Load(int4(indices[i] * 4 + 3, currFrame[1], clip[1], 0));
curr = matrix(c0, c1, c2, c3);
n0 = TransformsMap.Load(int4(indices[i] * 4 + 0, nextFrame[1], clip[1], 0));
n1 = TransformsMap.Load(int4(indices[i] * 4 + 1, nextFrame[1], clip[1], 0));
n2 = TransformsMap.Load(int4(indices[i] * 4 + 2, nextFrame[1], clip[1], 0));
n3 = TransformsMap.Load(int4(indices[i] * 4 + 3, nextFrame[1], clip[1], 0));
next = matrix(n0, n1, n2, n3);
nextAnim = lerp(curr, next, time[1]);
currAnim = lerp(currAnim, nextAnim, Tweenframes.TweenTime);
}
transform += mul(weights[i], currAnim);
}
world = mul(transform, world);
}
clip[0],clip[1]로 나눠서 현재와 다음 클립으로 나눠준다. 그런 뒤, 각 클립을 보간 해주고 다음 클립이 있을 경우 보간 한 값을 서로 보간해서 넘겨준다.
이렇게 하면
서로 tween 속도를 변경한 거이다 좀 찌그러지는 느낌은 트윈 속도를 조절하면 잘 안보이는 것으로 해결 가능하다.