XML Exporter: 하드웨어 스킨 애니메이션

하드웨어 팔레트를 이용하여 애니메이션을 구현해 본다.
그래픽 카드에서 하드웨어 팔레트를 제대로 지원하는 카드가 없기 때문에 다이렉트 X 디바이스를 생성할 때, 버텍스 프로세스 처리를 D3DCREATE_HARDWARE_VERTEXPROCESSING 대신 D3DCREATE_MIXED_VERTEXPROCESSING 혼합 모드를 사용한다.

vertexProcessing = D3DCREATE_MIXED_VERTEXPROCESSING

하드웨어 팔레트로 구현한 실행 화면이다.

하드웨어 팔레트를 이용 하려면 다이렉트X API에 전달하기 위해서 정점의 구조체를 정의한다.

< 정점 선언 >

HardwareBlendVertex 변수 순서와 FVF를 잘못 지정하면 애니메이션이 안되던가, 메시가 깨진채로 나올 것이다.

struct HardwareBlendVertex

{

    D3DXVECTOR3    p;

 

    FLOAT        weight[3];        // BLEND WEIGHT

    BYTE        boneIndex[4];    // MATRIX Index

 

    HardwareBlendVertex() : p(0,0,0)

    {

        boneIndex[0] = boneIndex[1] = boneIndex[2] = boneIndex[3] = 0;

        weight[0] = weight[1] = weight[2] = 0;

    }

 

    enum {    FVF = (D3DFVF_XYZB4 | D3DFVF_LASTBETA_UBYTE4),    };

};

3DFVF_XYZB4 : 매트릭스 팔레트에 대응되는 가중치 필드(베타 필드)가 4개로 본의 가중치를  최대 4개 사용

D3DFVF_LASTBETA_ UBYTE4 : 마지막 필드를 매트릭스 팔레트의 인덱스 UBYTE형이 4개가 가진다.

팔레트 인덱스는 4개이지만 가중치는 3개 뿐이다. 가중치의 합은 1이 되기 때문에 1 - (3개의 가중치 합)을 빼면 나머지 가중치를 구할수 있다.

가중치의 갯수를 별도로 SetRenderState()에서도 지정해 준다.

DIRECT3DDEVICE9::SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_3WEIGHTS )

< 정점 만들기 >

EHardwareBoneMesh::CreateVertexBuffer()를 보면 모든 정점의 초기화후 정점을 읽어온다.
그 다음에, 영향을 주는 본의 갯수 만큼 본 인덱스를 읽고, 본  인덱스는 최대 4개 가중치는 최대 3개까지 읽어 온다.

int EHardwareBoneMesh::CreateVertexBuffer( XmlNode* pXmlNode )

{

    ................

        m_pMeshVerts = new HardwareBlendVertex[ m_nVertex ];

        memset( m_pMeshVerts, 0, sizeof(HardwareBlendVertex) * m_nVertex );

        for( int i = 0; i < n; ++i )

        {

            m_pMeshVerts[i].p.x = xmlVx[i].p.x;

            m_pMeshVerts[i].p.y = xmlVx[i].p.y;

            m_pMeshVerts[i].p.z = xmlVx[i].p.z;

 

            int count = (int)xmlVx[i].boneIndex.size();

 

            for( int j = 0; j < count; ++j )

            {

                if( j >= 4 )

                    break;

                m_pMeshVerts[i].boneIndex[j] =  xmlVx[i].boneIndex[j];   

                if( j < 3 )

                    m_pMeshVerts[i].weight[j] = xmlVx[i].boneWeight[j];

            }

        }

    ..............

}

< 팔레트 전달하기 위한 준비 행렬에 채우기 >

렌더링시 하드웨어 팔레트를 전달하기 위한 매트릭스 배열은 ENode에 정적 멤버 변수로 선언 한다.

    static D3DXMATRIX        m_matBlend[MAX_BLEND];

애니메이션이 있으면 매트릭스 배열 m_matBlend에 넣는다.

void EHardwareBoneMesh::OnFrameMove()

{

    //소프트웨어적으로 스키닝 애니메이션 구현

    if( this->m_nAniSize > 0 )

    {

        int nFrame = GameTime::GetFrame();   

        m_matWorld = m_aniArray[nFrame];   

    }

    else

        D3DXMatrixIdentity(&m_matWorld);

 

    if( m_id >= 0 )

        m_matBlend[m_id] = m_matWorld;

}

< 렌더링 >

하드웨어적으로 팔레트를 재대로 지원 하지 못하기 때문에 D3DCREATE_MIXED_VERTEXPROCESSING 혼합 모드로 디바이스를 생성 했을 것이다.
렌더링시 소프트웨어 정점 처리와 D3DRS_INDEXEDVERTEXBLENDENABLE를 TRUE로설정한다. D3DRS_VERTEXBLEND값을 D3DVBF_3WEIGHTS로 설정한다.
D3DTS_WORLDMATRIX(K)행렬의 값을 m_matBlend 배열로 채운다.
DrawIndexedPrimitiveUP로 렌더링후 렌더 상태와 D3DTS_WORLDMATRIX(K)행렬을   초기화 시킨다.

int EHardwareBoneMesh::OnRender()

{

    .....................

    if( m_pMeshVerts == 0 )

    {

        if( m_id >= 0 )

            m_pDevice->SetTransform(D3DTS_WORLD, &m_matBlend[m_id]);

        return 1;

    }

 

    m_pDevice->SetSoftwareVertexProcessing( TRUE );

    m_pDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, TRUE );

    m_pDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_3WEIGHTS );

 

    for(int k = 0; k < MAX_BLEND; ++k)

        m_pDevice->SetTransform( D3DTS_WORLDMATRIX(k), &( m_matBlend[k] * scaleMat ) );

 

    m_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME);

    m_pDevice->SetFVF( HardwareBlendVertex::FVF );

    m_pDevice->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST,

        0, m_nVertex, m_nTriangle, m_pIndex, D3DFMT_INDEX16, m_pMeshVerts, sizeof(HardwareBlendVertex) );

 

    //렌더 상태와 D3DTS_WORLDMATRIX 초기화 시킴

    .....................

    return 1;

}

프로젝트: viewer_hardwareSkin.zip