다이렉트X 11에서, "트윈"(tween)은 시간이 지남에 따라 두 값 사이를 보간하는 방법이다. 애니메이션 및 기타 그래픽 효과에서 서로 다른 상태 간의 원활한 전환을 위해 일반적으로 사용된다. DirectX 11의 tween 방법은 게임이나 응용 프로그램에 생기를 불어넣을 수 있는 부드럽고 실제와 같은 애니메이션을 만드는 강력한 도구이다.

목차

     

     


     

     

    Play Animation

     

     

    Shader
      25_Mesh.fx
    38_Model.fx
    45_Animator.fx
    48_AnimationTweening.fx
    Framework
      Model
      Model.h .cpp
    ModelAnimator.h .cpp
    ModelClip.h .cpp
    ModelMesh.h .cpp
    ModelRender.h .cpp
      Renders
      Material.h. cpp
    ModelEditor
      Demo
      AnimationDemo.h .cpp
    ModelDemo.h .cpp
      Writer
      Converter.h .cpp
      ExportFile.h .cpp
    Type.h

     


     

    Tween

     

    다이렉트X 11에서, "트윈"(tween)은 시간이 지남에 따라 두 값 사이를 보간하는 방법이다. 애니메이션 및 기타 그래픽 효과에서 서로 다른 상태 간의 원활한 전환을 위해 일반적으로 사용된다.

    트위닝 뒤에 있는 기본적인 아이디어는 화면에 있는 물체의 위치와 같은 두 개의 값으로 시작한 다음 지정된 시간 동안 물체를 시작 위치에서 끝 위치로 이동시키는 일련의 중간 값을 계산하는 것이다. 결과 애니메이션은 개체가 실제로 개별 단계로 이동하고 있음에도 불구하고 부드럽고 연속적으로 나타낸다.

    다이렉트X 11은 개발자들이 이러한 애니메이션을 만드는 데 사용할 수 있는 몇 가지 내장된 트위닝 기능을 제공한다. 예를 들어, ID3DX11 애니메이션 인터페이스는 애니메이션의 두 키 프레임 사이를 원활하게 보간하는 데 사용할 수 있는 인터폴레이트와 같은 메서드를 포함합니다.

    내장된 트위닝 기능 외에도 다이렉트X11은 개발자가 구현할 수 있는 커스텀 트위닝 알고리즘도 지원한다. 이러한 알고리즘은 더 복잡한 애니메이션을 만들거나 내장된 기능으로는 불가능한 특정 시각적 효과를 얻기 위해 사용될 수 있다.

    전반적으로, DirectX 11의 twen 방법은 게임이나 응용 프로그램에 생기를 불어넣을 수 있는 부드럽고 실제와 같은 애니메이션을 만드는 강력한 도구이다.

     


     

    Lerp

     

    DirectX 11에서 Lerp("선형 보간"의 줄임말)와 함께 twen 메소드를 사용하려면 다음과 같은 일반적인 단계를 수행한다.

    1. 시작 및 종료 값을 정의: 이 경우 개체의 시작 위치와 끝 위치와 같이 보간할 두 값이 필요하다.
    2. 기간 지정: 이 둘 사이의 지속 시간을 밀리초 또는 초 단위로 결정한다.
    3. 경과 시간을 계산: 이 작업은 시스템 타이머 또는 기타 시간 기록 메커니즘을 사용하여 수행할 수 있다.
    4. 보간 값을 계산: 시작 값과 끝 값 사이를 보간하는 데 사용할 값입니다. 이 경우 Lerp를 사용하여 이 값을 계산할 수 있다. Lerp는 시작 값, 끝 값 및 보간 값의 세 가지 인수를 사용합니다. 이 값은 0과 1 사이의 값이어야 하며 이 값은 두 값 사이의 진행률을 나타낸다. 
    5. 보간된 값을 적용: 보간된 값을 사용하여 개체의 위치(또는 애니메이션 중인 다른 속성)를 업데이트한다.

     

    float startValue = 0.0f;
    float endValue = 10.0f;
    float duration = 1000.0f; // 1 second
    
    // Get the elapsed time since the start of the tween
    float elapsedTime = GetElapsedTime();
    
    // Calculate the interpolation value using Lerp
    float t = elapsedTime / duration; // t should be between 0 and 1
    float interpolatedValue = Lerp(startValue, endValue, t);
    
    // Apply the interpolated value to your object's position (or any other property)
    object.position = interpolatedValue;

     

    이 예제에서 Lerp는 시작 값, 끝 값 및 보간 값을 인수로 사용하고 보간 값을 반환하는 사용자 정의 함수이다. 이 기능을 직접 정의하거나 DirectX 11에서 제공하는 기본 제공 Lerp 기능 중 하나 또는 원하는 프로그래밍 언어를 사용할 수 있다.

     


     

    fx

     

    Animator.fx

    더보기
    #include "00_Global.fx"
    
    float3 Direction = float3(-1, -1, +1);
    
    struct VertexModel
    {
        float4 Position : Position;
        float2 Uv : Uv;
        float3 Normal : Normal;
        float3 Tangent : Tangent;
        float4 BlendIndices : BlendIndices;
        float4 BlendWeights : BlendWeights;
    };
    
    #define MAX_MODEL_TRANSFORMS 250
    #define MAX_MODEL_KEYFRAMES 500
    
    cbuffer CB_Bone
    {
        matrix BoneTransforms[MAX_MODEL_TRANSFORMS];
        
        uint BoneIndex;
    };
    
    //애니메이션 프레임 구조체
    struct AnimationFrame
    {
        int Clip;
    
        uint CurrFrame;
        uint NextFrame;
    
        float Time;
        float Running;
    
        float3 Padding;
    };
    
    cbuffer CB_TweenFrame
    {
        AnimationFrame Keyframes;
    };
    
    Texture2DArray TransformsMap;
    
    struct VertexOutput
    {
        float4 Position : SV_Position;
        float3 Normal : Normal;
        float2 Uv : Uv;
    };
    
    void SetAnimationWorld(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;
        int currFrame;
        int nextFrame;
        float time;
    
        clip = Keyframes.Clip;
        currFrame = Keyframes.CurrFrame;
        nextFrame = Keyframes.NextFrame;
        time = Keyframes.Time;
        
        float4 c0, c1, c2, c3;
        float4 n0, n1, n2, n3;
        
        matrix curr = 0;
        matrix transform = 0; //최종행렬 초기값
        
        [unroll(4)]
        for (int i = 0; i < 4; i++)
        {   //x=Bone번호, y=Keyframe, z=Bone이었던것, z=MipMap
            c0 = TransformsMap.Load(int4(indices[i] * 4 + 0, currFrame, clip, 0));
            c1 = TransformsMap.Load(int4(indices[i] * 4 + 1, currFrame, clip, 0));
            c2 = TransformsMap.Load(int4(indices[i] * 4 + 2, currFrame, clip, 0));
            c3 = TransformsMap.Load(int4(indices[i] * 4 + 3, currFrame, clip, 0));
            curr = matrix(c0, c1, c2, c3);
            
            //최종행렬에 'curr에 weight(가중치)만큼 곱해줌' 누적시켜서 더해준다.
            transform += mul(weights[i], curr); 
        }
        
        //최종행렬 World로 변환
        //transform은 애니메이션이 이동할 행렬. world는 모델이 출력될 world
        world = mul(transform, world);
    }
    
    VertexOutput VS(VertexModel input)
    {
        VertexOutput output;
        
        //World = mul(BoneTransforms[BoneIndex], World);
        SetAnimationWorld(World, input);
        
        output.Position = WorldPosition(input.Position);
        output.Position = ViewProjection(output.Position);
        
        output.Normal = WorldNormal(input.Normal);
        output.Uv = input.Uv;
        
        return output;
    }
    
    float4 PS(VertexOutput input) : SV_Target
    {
        float NdotL = dot(normalize(input.Normal), -Direction);
        
        return DiffuseMap.Sample(LinearSampler, input.Uv) * NdotL;
    }
    
    technique11 T0
    {
        P_VP(P0, VS, PS)
        P_RS_VP(P1, FillMode_WireFrame, VS, PS)
    }

     

    AnimatorTweening.fx

    더보기
    #include "00_Global.fx"
    
    float3 Direction = float3(-1, -1, +1);
    
    struct VertexModel
    {
        float4 Position : Position;
        float2 Uv : Uv;
        float3 Normal : Normal;
        float3 Tangent : Tangent;
        float4 BlendIndices : BlendIndices;
        float4 BlendWeights : BlendWeights;
    };
    
    #define MAX_MODEL_TRANSFORMS 250
    #define MAX_MODEL_KEYFRAMES 500
    
    cbuffer CB_Bone
    {
        matrix BoneTransforms[MAX_MODEL_TRANSFORMS];
        
        uint BoneIndex;
    };
    
    struct AnimationFrame
    {
        int Clip;
    
        uint CurrFrame;
        uint NextFrame;
    
        float Time;
        float Running;
    
        float3 Padding;
    };
    
    struct TweenFrame
    {
        float TakeTime;
        float TweenTime;
        float RunningTime;
        float Padding;
    
        AnimationFrame Curr;
        AnimationFrame Next;
    };
    
    cbuffer CB_TweenFrame
    {
        TweenFrame TweenFrames;
    };
    
    Texture2DArray TransformsMap;
    
    struct VertexOutput
    {
        float4 Position : SV_Position;
        float3 Normal : Normal;
        float2 Uv : Uv;
    };
    
    void SetAnimationWorld(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;
        matrix 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);
    }
    
    VertexOutput VS(VertexModel input)
    {
        VertexOutput output;
        
        //World = mul(BoneTransforms[BoneIndex], World);
        SetAnimationWorld(World, input);
        
        output.Position = WorldPosition(input.Position);
        output.Position = ViewProjection(output.Position);
        
        output.Normal = WorldNormal(input.Normal);
        output.Uv = input.Uv;
        
        return output;
    }
    
    float4 PS(VertexOutput input) : SV_Target
    {
        float NdotL = dot(normalize(input.Normal), -Direction);
        
        return DiffuseMap.Sample(LinearSampler, input.Uv) * NdotL;
    }
    
    technique11 T0
    {
        P_VP(P0, VS, PS)
        P_RS_VP(P1, FillMode_WireFrame, VS, PS)
    }

     

     


     

     

    Model Animator

     

    ModelAnimator.h

    더보기
    #pragma once
    
    class ModelAnimator
    {
    public:
    	ModelAnimator(Shader* shader);
    	~ModelAnimator();
    
    	void Update();
    	void Render();
    
    public:
    	void ReadMesh(wstring file);
    	void ReadMaterial(wstring file);
    	void ReadClip(wstring file);
    
    	Transform* GetTransform() { return transform; }
    	Model* GetModel() { return model; }
    
    	void Pass(UINT pass);
    
    	void PlayTweenMode(UINT clip, float speed = 1.0f, float takeTime = 1.0f);
    
    private:
    	void CreateTexture();
    	void CreateClipTransform(UINT index);
    
    private:
    	struct ClipTransform
    	{
    		Matrix** Transform;
    
    		ClipTransform()
    		{
    			Transform = new Matrix*[MAX_MODEL_KEYFRAMES];
    
    			for (UINT i = 0; i < MAX_MODEL_KEYFRAMES; i++)
    				Transform[i] = new Matrix[MAX_MODEL_TRANSFORMS];
    		}
    
    		~ClipTransform()
    		{
    			for (UINT i = 0; i < MAX_MODEL_KEYFRAMES; i++)
    				SafeDeleteArray(Transform[i]);
    
    			SafeDeleteArray(Transform);
    		}
    	};
    	ClipTransform* clipTransforms = NULL;
    
    	ID3D11Texture2D* texture = NULL;
    	ID3D11ShaderResourceView* srv = NULL;
    
    private:
    	struct KeyframeDesc
    	{
    		int Clip = 0;
    
    		UINT CurrFrame = 0;
    		UINT NextFrame = 0;
    
    		float Time = 0.0f;
    		float RunningTime = 0.0f;
    
    		float Speed = 1.0f;
    
    		Vector2 Padding;
    	}; // keyframeDesc;
    
    	ConstantBuffer* frameBuffer;
    	ID3DX11EffectConstantBuffer* sFrameBuffer;
    
    	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;
    
    private:
    	Shader* shader;
    	Model* model;
    	Transform* transform;
    };

     

     

    ModelAnimator.cpp

    더보기
    #include "Framework.h"
    #include "ModelAnimator.h"
    
    ModelAnimator::ModelAnimator(Shader * shader)
    	: shader(shader)
    {
    	model = new Model();
    	transform = new Transform(shader);
    
    	frameBuffer = new ConstantBuffer(&tweenDesc, sizeof(TweenDesc));
    	sFrameBuffer = shader->AsConstantBuffer("CB_TweenFrame");
    }
    
    ModelAnimator::~ModelAnimator()
    {
    	SafeDelete(model);
    	SafeDelete(transform);
    
    	SafeDeleteArray(clipTransforms);
    	SafeRelease(texture);
    	SafeRelease(srv);
    
    	SafeDelete(frameBuffer);
    }
    
    void ModelAnimator::Update()
    {
    	TweenDesc& desc = tweenDesc;
    
    	//현재 애니메이션
    	{
    		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) //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();
    }
    
    void ModelAnimator::Render()
    {
    	frameBuffer->Render();
    	sFrameBuffer->SetConstantBuffer(frameBuffer->Buffer());
    
    	for (ModelMesh* mesh : model->Meshes())
    	{
    		mesh->SetTransform(transform);
    		mesh->Render();
    	}
    }
    
    void ModelAnimator::ReadMesh(wstring file)
    {
    	model->ReadMesh(file);
    }
    
    void ModelAnimator::ReadMaterial(wstring file)
    {
    	model->ReadMaterial(file);
    }
    
    void ModelAnimator::ReadClip(wstring file)
    {
    	model->ReadClip(file);
    }
    
    void ModelAnimator::Pass(UINT pass)
    {
    	for (ModelMesh* mesh : model->Meshes())
    		mesh->Pass(pass);
    }
    
    void ModelAnimator::PlayTweenMode(UINT clip, float speed, float takeTime)
    {
    	tweenDesc.TakeTime = takeTime;
    
    	tweenDesc.Next.Clip = clip;
    	tweenDesc.Next.Speed = speed;
    }
    
    void ModelAnimator::CreateTexture()
    {
    	//Matrix matrix[MAX_MODEL_KEYFRAMES][MAX_MODEL_TRANSFORMS];
    
    	clipTransforms = new ClipTransform[model->ClipCount()];
    	for (UINT i = 0; i < model->ClipCount(); i++)	
    		CreateClipTransform(i);
    	
    
    	//Create Texture
    	{
    		D3D11_TEXTURE2D_DESC desc;
    		ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
    		desc.Width = MAX_MODEL_TRANSFORMS * 4;
    		desc.Height = MAX_MODEL_KEYFRAMES;
    		desc.ArraySize = model->ClipCount();
    		desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; //16Byte * 4 = 64Byte
    		desc.Usage = D3D11_USAGE_IMMUTABLE;
    		desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    		desc.MipLevels = 1;
    		desc.SampleDesc.Count = 1;
    
    		UINT pageSize = MAX_MODEL_TRANSFORMS * 4 * 16 * MAX_MODEL_KEYFRAMES;
    		//void* p = malloc(pageSize * model->ClipCount());
    		void* p = VirtualAlloc(NULL, pageSize * model->ClipCount(), MEM_RESERVE, PAGE_READWRITE);
    
    		//MEMORY_BASIC_INFORMATION, VirtualQuery
    
    		for (UINT c = 0; c < model->ClipCount(); c++)
    		{
    			UINT start = c * pageSize;
    
    			for (UINT k = 0; k < MAX_MODEL_KEYFRAMES; k++)
    			{
    				void* temp = (BYTE *)p + MAX_MODEL_TRANSFORMS * k * sizeof(Matrix) + start;
    
    				VirtualAlloc(temp, MAX_MODEL_TRANSFORMS * sizeof(Matrix), MEM_COMMIT, PAGE_READWRITE);
    				memcpy(temp, clipTransforms[c].Transform[k], MAX_MODEL_TRANSFORMS * sizeof(Matrix));
    			}
    		}//for(c)
    
    
    		D3D11_SUBRESOURCE_DATA* subResources = new D3D11_SUBRESOURCE_DATA[model->ClipCount()];
    		for (UINT c = 0; c < model->ClipCount(); c++)
    		{
    			void* temp = (BYTE *)p + c * pageSize;
    
    			subResources[c].pSysMem = temp;
    			subResources[c].SysMemPitch = MAX_MODEL_TRANSFORMS * sizeof(Matrix);
    			subResources[c].SysMemSlicePitch = pageSize;
    		}
    		Check(D3D::GetDevice()->CreateTexture2D(&desc, subResources, &texture));
    
    		
    		SafeDeleteArray(subResources);
    		VirtualFree(p, 0, MEM_RELEASE);
    	}
    
    
    	//Create SRV
    	{
    		D3D11_SHADER_RESOURCE_VIEW_DESC desc;
    		ZeroMemory(&desc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
    		desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    		desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
    		desc.Texture2DArray.MipLevels = 1;
    		desc.Texture2DArray.ArraySize = model->ClipCount();
    
    		Check(D3D::GetDevice()->CreateShaderResourceView(texture, &desc, &srv));
    	}
    
    	for (ModelMesh* mesh : model->Meshes())
    		mesh->TransformsSRV(srv);
    }
    
    void ModelAnimator::CreateClipTransform(UINT index)
    {
    	Matrix* bones = new Matrix[MAX_MODEL_TRANSFORMS];
    
    	ModelClip* clip = model->ClipByIndex(index);
    	for (UINT f = 0; f < clip->FrameCount(); f++)
    	{
    		for (UINT b = 0; b < model->BoneCount(); b++)
    		{
    			ModelBone* bone = model->BoneByIndex(b);
    
    
    			Matrix parent;
    			Matrix invGlobal = bone->Transform();
    			D3DXMatrixInverse(&invGlobal, NULL, &invGlobal);
    
    			int parentIndex = bone->ParentIndex();
    			if (parentIndex < 0)
    				D3DXMatrixIdentity(&parent);
    			else
    				parent = bones[parentIndex];
    
    
    			Matrix animation;
    			ModelKeyframe* frame = clip->Keyframe(bone->Name());
    
    			if (frame != NULL)
    			{
    				ModelKeyframeData& data = frame->Transforms[f];
    
    				Matrix S, R, T;
    				D3DXMatrixScaling(&S, data.Scale.x, data.Scale.y, data.Scale.z);
    				D3DXMatrixRotationQuaternion(&R, &data.Rotation);
    				D3DXMatrixTranslation(&T, data.Translation.x, data.Translation.y, data.Translation.z);
    
    				animation = S * R * T;
    			}
    			else
    			{
    				D3DXMatrixIdentity(&animation);
    			}
    
    			bones[b] = animation * parent;
    			clipTransforms[index].Transform[f][b] = invGlobal * bones[b];
    		}//for(b)
    	}//for(f)
    }

     

    다음동작의 clip = -1이면 없는걸로 간주. clip의 값이 -1~1 사이의 어느값으로 변화하면 다음동작 수행. 

     

     

     


    Animation Demo

     

    AnimationDemo.cpp

    더보기
    #include "stdafx.h"
    #include "AnimationDemo.h"
    #include "Converter.h"
    
    void AnimationDemo::Initialize()
    {
    	Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
    	Context::Get()->GetCamera()->Position(1, 36, -85);
    
    
    	shader = new Shader(L"45_Animation.fx");
    
    	Kachujin();
    
    }
    
    void AnimationDemo::Update()
    {
    	if (kachujin != NULL) kachujin->Update();
    }
    
    void AnimationDemo::Render()
    {
    	static int speed = 1;
    
    	ImGui::SliderFloat("Speed", &speed, 0.1f, 5.0f);
    
    	static int clip = 0;
    	if (Keyboard::Get()->Down(VK_SPACE))
    	{
    		clip++;
    		clip %= 4;
    
    		kachujin->PlayTweenMode(clip, speed);
    	}
    
    	ImGui::SliderFloat3("Direction2", direction, -1, +1);
    	shader->AsVector("Direction")->SetFloatVector(direction);
    
    	static int pass = 0;
    	ImGui::InputInt("Pass2", &pass);
    	pass %= 2;
    
    
    
    	if (kachujin != NULL)
    	{
    		kachujin->Pass(pass);
    		kachujin->Render();
    	}
    }
    
    void AnimationDemo::Kachujin()
    {
    	kachujin = new ModelAnimator(shader);
    	kachujin->ReadMesh(L"Kachujin/Mesh");
    	kachujin->ReadMaterial(L"Kachujin/Mesh");
    	kachujin->ReadClip(L"Kachujin/Sword And Shield Idle");
    	kachujin->ReadClip(L"Kachujin/Sword And Shield Run");
    	kachujin->ReadClip(L"Kachujin/Sword And Shield Slash");
    	kachujin->ReadClip(L"Kachujin/Salsa Dancing");
    	kachujin->GetTransform()->Position(0, 0, -30);
    	kachujin->GetTransform()->Scale(0.025f, 0.025f, 0.025f);
    }

     

     


     

    실행화면