• Home
  • About
    • 게임 개발자 유정룡 photo

      게임 개발자 유정룡

      포트폴리오

    • Learn More
    • Email
    • Github
    • Bitbucket
  • Projects
    • All Projects
    • All Tags

Direct3D11 공부 24일차(ModelAnimator - Tween)

30 Jun 2021

Reading time ~3 minutes

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 속도를 변경한 거이다 좀 찌그러지는 느낌은 트윈 속도를 조절하면 잘 안보이는 것으로 해결 가능하다.



DirectX Share Tweet +1