FBX 파일은 Assimp를 통해 불러올 수 있지만, 매번 파싱하는 건 속도와 효율성에서 큰 손해입니다.
따라서 Tank.anim이라는 Binary 애니메이션 파일을 미리 저장하고, 런타임에는 빠르게 읽어오는 구조를 설계합니다.
Model* model = new Model();
model->ReadMaterial(L"Tank/Tank.material");
model->ReadModel(L"Tank/Tank.mesh");
model->ReadClip(L"Tank/Tank.anim");ReadMaterial() → 머티리얼 데이터를 로딩합니다 (텍스처, 샘플러 등 포함).ReadModel() → 메시와 본(Bone) 계층 구조를 포함한 모델 구조를 읽어옵니다.ReadClip() → 애니메이션 키프레임 데이터를 읽어옵니다.애니메이션 클립이 성공적으로 로딩되었다면, 이제 모델에 적용할 수 있습니다.
ModelRender 클래스는 정적 모델의 렌더링 전담 클래스입니다.ModelAnimator입니다.ModelAnimator* tank = new ModelAnimator(model);
tank->Position(0, 0, 0);
tank->Scale(0.01f, 0.01f, 0.01f);
tank->PlayClip(0); // 0번째 클립 재생여기서 중요한 점은 ModelAnimator는 단순히 Bone 정보를 사용하는 것이 아니라, 현재 시간에 따라 Bone Transform을 계산하고, 쉐이더에 넘겨줘야 한다는 점입니다.
애니메이션은 시간 기반으로 Bone의 위치/회전/스케일이 바뀌는 구조입니다.
이를 위해 ModelAnimator::Update()에서 매 프레임마다 애니메이션 시간을 갱신하고, 해당 시간에 맞는 Bone Transform을 구합니다.
void ModelAnimator::Update()
{
    if (clip == nullptr)
        return;
    // 1. 시간 갱신
    playTime += Time::Delta() * speed;
    // 2. 루프 처리
    if (playTime > clip->Duration())
    {
        if (bLoop == true)
            playTime = fmod(playTime, clip->Duration());
        else
            playTime = clip->Duration();
    }
    // 3. 현재 시간의 Bone Transform 계산
    transforms.clear();
    clip->GetKeyframe(playTime, transforms);
}Time::Delta() : 프레임 간 시간 차이clip->Duration() : 전체 애니메이션 길이GetKeyframe(time, out) : 현재 시간에 대응되는 본 행렬 계산이렇게 계산된 transforms는 GPU에 전송되어 스키닝을 처리하는 데 사용됩니다.
스키닝은 모델 정점(Vertex)이 여러 본(Bone)의 영향을 받아 움직이도록 하는 기법입니다.
예를 들어, 캐릭터의 팔 정점은 어깨 본과 팔꿈치 본에 걸쳐 영향을 받습니다. 이때 각 본의 영향력을 숫자로 나타낸 것이 BlendWeight입니다.
정점 위치 =
= bone1의 변환  가중치1 + bone2의 변환  가중치2 + ...
쉐이더에서 본 행렬을 적용하기 위해 먼저 C++ 측에서 본 데이터를 넘겨야 합니다.
cbuffer BoneBuffer : register(b1)
{
    float4x4 boneTransforms[MAX_MODEL_TRANSFORMS];
};그리고 정점 쉐이더에서는 다음과 같이 처리합니다:
float4x4 skinTransform = 0;
[unroll]
for (int i = 0; i < 4; i++)
{
    int index = input.BlendIndices[i];
    float weight = input.BlendWeights[i];
    skinTransform += weight * boneTransforms[index];
}
output.Position = mul(input.Position, skinTransform);애니메이션 A에서 B로 즉시 바뀌면 어색한 전환이 발생합니다.
이를 막기 위해 두 클립을 시간을 두고 보간하는 기능이 트윈 애니메이션입니다.
animator->PlayTweenMode(0, 1, 2.0f); // 0번 클립에서 1번 클립으로 2초간 전환내부적으로는 두 애니메이션의 Bone 데이터를 Lerp/Slerp 보간하여 처리합니다.
Matrix S = XMMatrixScalingFromVector(Lerp(scale1, scale2, tweenTime));
Matrix R = XMMatrixRotationQuaternion(Slerp(rot1, rot2, tweenTime));
Matrix T = XMMatrixTranslationFromVector(Lerp(trans1, trans2, tweenTime));
Matrix result = S * R * T;| 컴포넌트 | 설명 | 
|---|---|
| Model | FBX 기반 모델/애니메이션/머티리얼 로딩 | 
| ModelRender | 정적인 메시 렌더링 담당 | 
| ModelAnimator | 애니메이션 시간 처리, 본 행렬 계산, 쉐이더 전송 | 
| ModelSkinning.fx | 스키닝 계산 전용 쉐이더 (boneTransforms 적용) | 
| ModelAnimation.fx | 트윈 애니메이션 포함 애니메이션 렌더링 쉐이더 |