본문 바로가기
공부/Animation

[Skeleton] Skeleton.h & Skeleton.cpp 정리

by MY블로그 2023. 6. 28.

캐릭터의 애니메이션 구현을 위하여 필요한 Skeleton에 대하여 정리 합니다.

Skeleton 클래스의 .h & .cpp 코드 정리 및 각 코드에 대한 주석입니다.

 

해당 클래스가 사용되는 ASSIMP 프로젝트는 모든작업이 완료된후 정리할 예정 입니다.

Skeleton.h

#pragma once
#define MAX_BONE 256 // 상수정의 최대값 256
class Skeleton
{
    static ID3D11Buffer* bonesBuffer;
    // 정적 멤버 변수는 클래스의 모든 인스턴스에서 공유되는 변수입니다.
    // ID3D11Buffer 형식의 포인터입니다.
    // bonesBuffer라는 정적 멤버 변수를 선언합니다. 

public:
    Skeleton() {}; // 기본생성자만 정의. 구현되어있지 않음.

    //                   버퍼에 갱신할 값
    Matrix				 bones[MAX_BONE];

    //                   파일로 쓰여진값
    Matrix				 bonesOffset[MAX_BONE];// * W
    // 매트릭스가 2개인 이유는 처음 T포즈의 본즈가 생성될때
    // 각노드에 본즈를 위치시키게된다면 정확한 위치가 잡히지 않는다.
    // 때문에 원점인0에 모아줄 기능을 할 필요가 있기에 생성하는 것이다.

    // 경로 지정에 사용될 변수
    string               file;

    void BonesUpdate(GameObject* node); // 본즈의 배열을 업데이트할 함수
    static void CreateStaticMember(); // 정적 멤버 함수를 선언 (본즈버퍼생성)
    static void DeleteStaticMember(); // 본즈 버퍼 해제
    virtual void Set(); // 다형성을 지원하기위한 가상 멤버 함수 선언
    void LoadFile(string file); // 파일로드 함수
    void SaveFile(string file); // 파일저장 함수
};

Skeleton.cpp

#include "framework.h"

ID3D11Buffer* Skeleton::bonesBuffer = nullptr;
// 이 변수는 상수 버퍼(ID3D11Buffer)를 가리키는 포인터입니다.
// Skeleton 클래스의 bonesBuffer 멤버 변수를 정의하고, 
// 초기값을 nullptr로 설정합니다. 

void Skeleton::BonesUpdate(GameObject* node) // Skeleton 클래스의 BonesUpdate 정의
{
    // 현재노드의 루트노드가 스켈레톤을 가지고 있을경우
    if (node->root->skeleton) 
    {
        bones[node->boneIndex] = bonesOffset[node->boneIndex]*node->W;
        // bonesOffset 배열과 node->W 행렬을 곱한 결과를 
        // bones 배열의 해당 인덱스에 할당합니다. 
        // 이 코드는 현재 노드의 boneIndex를 사용하여 
        // bones 배열의 해당 요소를 업데이트합니다.

        bones[node->boneIndex] = bones[node->boneIndex].Transpose();
        // bones 배열의 해당 인덱스에 저장된 행렬을 전치(transpose)합니다.
        // 이 코드는 행렬의 업데이트를 완료한 후에 해당 행렬을 전치시킵니다.
        // 열우선을 행우선으로 변경하도록 합니다.

        // 컨테이너 반복문 실행. 현재노드의 자식노드들을 모두 순회합니다.
        for (auto it = node->children.begin(); it != node->children.end(); it++)
        {
            BonesUpdate(it->second);
            // 자식 노드(it->second)에 대해 재귀적으로 BonesUpdate 함수를 호출
            // 이를 통해 자식 노드들에 대해서도 bones 배열을 업데이트합니다.
        }
    }
}

void Skeleton::CreateStaticMember() // 상수 버퍼 생성
{
    D3D11_BUFFER_DESC desc = { 0 }; // 구조체생성 및 초기화 버퍼속성 설정
    desc.ByteWidth = sizeof(Matrix) * MAX_BONE; // 버퍼에 저장될 상수데이터의 전체크기 확보
    desc.Usage = D3D11_USAGE_DYNAMIC; // 버퍼의 사용법(동적) CPU에서 데이터변경 GPU에 전달
    desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; // 상수버퍼
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // CPU가 버퍼에 쓰기작업 수행가능
    desc.MiscFlags = 0; // 추가플래그 지정 X
    desc.StructureByteStride = 0; // 구조체 요소의 바이트 크기설정. 구조체사용안해서 0
    
    HRESULT hr = D3D->GetDevice()->CreateBuffer(&desc, NULL, &bonesBuffer);
    // 상수 버퍼를 생성합니다. D3D 객체의 GetDevice() 함수를 통해 디바이스(Device)를 가져온 후, 
    // CreateBuffer 함수를 호출하여 버퍼를 생성합니다. 
    // desc 구조체를 사용하여 버퍼의 속성을 설정하고, 
    // 생성된 버퍼는 bonesBuffer 변수에 저장됩니다.

    D3D->GetDC()->VSSetConstantBuffers(2, 1, &bonesBuffer);
    // 버텍스쉐이더에 상수버퍼를 설정합니다.
    // D3D 객체의 GetDC() 함수를 통해 디바이스 컨텍스트(Device Context)를 가져온 후,
    // VSSetConstantBuffers 함수를 호출하여 상수 버퍼를 설정합니다. 
    // 이 코드는 2번 슬롯에 1개의 상수 버퍼를 설정하는 것을 의미합니다.

    assert(SUCCEEDED(hr)); // 상수 버퍼 생성이 실패했는지를 검사합니다.
}

void Skeleton::DeleteStaticMember() // 상수 버퍼 해제
{
    SafeRelease(bonesBuffer);
}

void Skeleton::Set() // 버퍼를 매핑하고 데이터를 복사한수 언매핑하는 함수
{
    // 상수버퍼를 매핑하여 CPU에서 데이터를 쓰고,
    // 버퍼를 언매핑하여 GPU에서 읽을 수 있도록하는 과정.
    // 이과정을 통해 상수 버퍼에 새로운 데이터를 업데이트 합니다.
    
    // 같은이름을 사용하기위한 익명 스코프 사용
    {
        D3D11_MAPPED_SUBRESOURCE mappedResource; // 매핑된 서브리소스에대한 정보를 담을 구조체

        D3D->GetDC()->Map(bonesBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
        // D3D 객체의 GetDC() 함수를 통해 디바이스 컨텍스트를 가져온 후, 
        // Map 함수를 호출하여 상수 버퍼를 매핑합니다. 
        // 매핑된 서브리소스에 대한 정보는 mappedResource에 저장됩니다. 
        // 이 때, bonesBuffer는 매핑할 상수 버퍼를 의미하며, 
        // D3D11_MAP_WRITE_DISCARD 플래그는 버퍼의 기존 내용을 무시하고 쓰기 작업을 수행함을 나타냅니다.

        memcpy_s(mappedResource.pData, sizeof(Matrix) * MAX_BONE, &bones, sizeof(Matrix) * MAX_BONE);
        // memcpy_s 함수를 사용하여 매핑된 서브리소스에 데이터를 복사합니다.
        // mappedResource.pData는 매핑된 서브리소스의 데이터 포인터를 의미하며,
        // sizeof(Matrix) * MAX_BONE은 복사할 데이터의 크기입니다. 
        // &bones는 복사할 데이터의 소스 주소를 의미합니다. 
        // 이 코드는 bones라는 Matrix 배열을 상수 버퍼에 복사합니다.

        D3D->GetDC()->Unmap(bonesBuffer, 0);
        // 상수 버퍼의 매핑을 해제합니다. 
        // Unmap 함수를 호출하여 매핑된 서브리소스를 언매핑합니다. 
        // 이로써 CPU와 GPU 간의 동기화가 이루어집니다.
    }
}

void Skeleton::LoadFile(string file) // 파일 불러오기
{
    this->file = file;
    BinaryReader in;
    wstring path = L"../Contents/Skeleton/" + Util::ToWString(file);
    in.Open(path);
    for (int i = 0; i < MAX_BONE; i++)
    {
        bones[i] = in.matrix();
    }
    for (int i = 0; i < MAX_BONE; i++)
    {
        bonesOffset[i] = in.matrix();
    }
    in.Close();
}

void Skeleton::SaveFile(string file) // 파일 저장하기
{
    this->file = file;
    BinaryWriter out;
    wstring path = L"../Contents/Skeleton/" + Util::ToWString(file);
    out.Open(path);
    for (int i = 0; i < MAX_BONE; i++)
    {
        out.matrix(bones[i]);
    }
    for (int i = 0; i < MAX_BONE; i++)
    {
        out.matrix(bonesOffset[i]);
    }
    out.Close();
}

댓글