D3DXLoadMeshHierarchyFromX
• 원래 상속받아서 쓰는 인터페이스임
• 즉, 원형만 만들어 놓은 추상 클래스임
• 프로그래머가 상속받아서 이용
• 스키닝 방법이나 기타 모든 것에 자유를 주면서, 일정한 형식을 갖추도록 한 것임
• 그 목적은 ID3DXAnimationController라는 인터페이스와 데이터가 묶이도록 하기 위해서임
• 즉, CAllocateHierarchy 클래스를 제대로 만들면 애니메이션을 다 구현했다는 것을 의미
• *.X 파일을 읽어오는 D3DXLoadMeshHierarchyFromX()가 이 클래스로 만든 객체의 포인터를 받아서 실제로 프레임 정보와 메시 컨테이너를 만들때 CAllocateHierarchy에 있는 함수를 사용하기 때문에 사용
• 지울 때도 CAllocateHierarchy에 있는 DestroyFrame() 함수와 DestroyMeshContainer()함수를 사용함
AllocateName()
class CAllocateHierarchy: public ID3DXAllocateHierarchy
//----------------------------------------------------------------------
// Name: AllocateName() - Global
// Desc: Allocates memory for a string to hold the name of a frame or mesh
//----------------------------------------------------------------------
HRESULT AllocateName( LPCTSTR Name, LPTSTR *pNewName )
{
UINT cbLength;
if (Name != NULL)
{
cbLength = lstrlen(Name) + 1;
*pNewName = new TCHAR[cbLength];
if (*pNewName == NULL)
return E_OUTOFMEMORY;
memcpy(*pNewName, Name, cbLength*sizeof(TCHAR));
}
else
{
*pNewName = NULL;
}
return S_OK;
}
• 뼈(프레임)에 이름을 넣어주는 함수임
• 3DsMax 같은데서 디자인 할 때 뼈에 이름을 심어주는데, 그 이름을 인자로 받아와서 새로 들어간
본 구조체 안의 이름에 동적 메모리를 할당해서 이름을 넣어주는 역할
CAllocateHierarchy
//-------------------------------------------------------------------
// Name: class CAllocateHierarchy
// Desc: Custom version of ID3DXAllocateHierarchy with custom methods to create
// frames and meshcontainers.
//-------------------------------------------------------------------
class CAllocateHierarchy: public ID3DXAllocateHierarchy
{
public:
STDMETHOD(CreateFrame)(THIS_ LPCTSTR Name, LPD3DXFRAME *ppNewFrame);
STDMETHOD(CreateMeshContainer)(THIS_ LPCTSTR Name, LPD3DXMESHDATA pMeshData,
LPD3DXMATERIAL pMaterials, LPD3DXEFFECTINSTANCE pEffectInstances, DWORD
NumMaterials,
DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo,
LPD3DXMESHCONTAINER *ppNewMeshContainer);
STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree);
STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER pMeshContainerBase);
CAllocateHierarchy( CPROD3DANIMESH* m_pApp) :m_pObject(pObject) {}
public:
CPROD3DANIMESH* m_pApp;
};
• 이 클래스가 생성될 때 애니메이션 객체의 포인터를 넘겨줘서... 그 포인터를 사용해서 애니메이션 클래스에 접근 을 함
//----------------------------------------------------------------------
// Name: CAllocateHierarchy::CreateFrame()
// Desc: making new frame by got name and pointing new frame pointer
//----------------------------------------------------------------------
HRESULT CAllocateHierarchy::CreateFrame( LPCTSTR Name, LPD3DXFRAME *ppNewFrame )
{
HRESULT hr = S_OK;
D3DXFRAME_DERIVED *pFrame;
*ppNewFrame = NULL;
pFrame = new D3DXFRAME_DERIVED;
if( pFrame == NULL )
{
hr = E_OUTOFMEMORY;
delete pFrame;
return hr;
}
hr = AllocateName( Name, &pFrame->Name ); // 위에 나왔던 AllocateName() 전역함수.
if( FAILED(hr) )
{
delete pFrame;
return hr;
}
// Initialize other data member of the frame
D3DXMatrixIdentity( &pFrame->CombinedTransformationMatrix );
D3DXMatrixIdentity( &pFrame->TransformationMatrix );
pFrame->pMeshContainer = NULL;
pFrame->pFrameFirstChild = NULL;
pFrame->pFrameSibling = NULL;
*ppNewFrame = pFrame;
pFrame = NULL;
return S_OK;
}
새로운 프레임 - 뼈를 만들어 주는 함수 : 인자로 이름을 받고, 프레임 구조체 형의 더블 포인터를 받아서 새로
메모리를 할당해서프레임을 생성해주고 포인터를 연결해 줌. 그 프레임 안에 있는 각종 값들을 초기화 시킴.
프레임 전체를 트리구조로 꾸미기 위한 준비 작업 (뼈 전체가 트리 구조)
CombinedTransformationMatrix
//-----------------------------------------------------------
// Name: struct D3DXFRAME_DERIVED
// Desc: Structure derived from D3DXFRAME so we can add some app-specific
// info that will be stored with each frame
//-----------------------------------------------------------
struct D3DXFRAME_DERIVED: public D3DXFRAME
{
D3DXMATRIXA16 CombinedTransformationMatrix;
};
CombinedTransformationMatrix : 누적된 행렬 정보
예: 이 프레임이 손 끝의 프레임이라고 한다면, 이 행렬 정보에는 어깨의 회전값,
팔의 회전값, 손목의 회전 값이 누적되어 들어가게 됨 (손 끝의 회전은 손목을 기준으로 돌아가기 때문)
• 이 구조체의 부모 클래스인 D3DXFRAME
D3DXFRAME
typedef struct _D3DXFRAME {
LPTSTR Name; // 프레임 이름
D3DXMATRIX TransformationMatrix; // 변환 행렬 정보
LPD3DXMESHCONTAINER pMeshContainer; // 메시 컨테이너 포인터
struct _D3DXFRAME *pFrameSibling; // 형제 프레임의 포인터
struct _D3DXFRAME *pFrameFirstChild; // 자식 프레임의 포인터
} D3DXFRAME, *LPD3DXFRAME;
트리 구조를 위한 형제, 자식의 포인터와 프레임 이름과.. 자신의 변환 행렬 정보..메시 컨테이너의 포인터가 있음
LPD3DXMESHCONTAINER : 메시를 담고 있는 컨테이너 구조체
D3DXMESHCONTAINER
typedef struct _D3DXMESHCONTAINER {
LPTSTR Name; // 메시의 이름
D3DXMESHDATA MeshData; // 메시 데이터 구조체
LPD3DXMATERIAL pMaterials; // 메시의 메터리얼 정보 구조체의 포인터
LPD3DXEFFECTINSTANCE pEffects; // EffectInstance 구조체의 포인터
DWORD NumMaterials; // 메터리얼 갯수
DWORD *pAdjacency; // 인접정보를 갖고 있는 DWORD형의 삼각형 하
// 나당 3개의 배열의 포인터.
LPD3DXSKININFO pSkinInfo; // 스킨 정보를 가지고 있는
// ID3DXSKININFO 인터페이스의 포인터
struct _D3DXMESHCONTAINER *pNextMeshContainer; // 링크드 리스트..
} D3DXMESHCONTAINER, *LPD3DXMESHCONTAINER;
typedef struct D3DXMESHDATA {
D3DXMESHDATATYPE Type; // 메시의 타입.
union
{
LPD3DXMESH pMesh; // 일반적인 메시의 포인터
LPD3DXMESH pPMesh; // 프로그레시브 메시의 포인터
LPD3DXPATCHMESH pPatchMesh; // 패치 메시(..?)의 포인터
}
} D3DXMESHDATA, *LPD3DXMESHDATA;
typedef enum D3DXMESHDATATYPE {
D3DXMESHTYPE_MESH = 0x001,
D3DXMESHTYPE_PMESH = 0x002,
D3DXMESHTYPE_PATCHMESH = 0x003,
D3DXEDT_FORCE_DWORD = 0x7fffffff
} D3DXMESHDATATYPE;
D3DXMESHCONTAINER_DERIVED
//-------------------------------------------------------------------
//Name: struct D3DXMESHCONTAINER_DERIVED
// Desc: Structure derived from D3DXMESHCONTAINER so we can add some app-specific
// info that will be stored with each mesh
//-------------------------------------------------------------------
struct D3DXMESHCONTAINER_DERIVED: public D3DXMESHCONTAINER
{
// array of textures, entries are NULL if no texture specified
- LPDIRECT3DTEXTURE9* ppTextures;
// SkinMesh info
LPD3DXMESH pOrigMesh; // 원래 메시의 임시 저장을 위한..
LPD3DXATTRIBUTERANGE pAttributeTable; // 메시의 속성 테이블 구조체.
DWORD NumAttributeGroups; // 속성 갯수.
DWORD NumInfl; // 이 디바이스가 최대한 블랜딩 할 수 있는 매트릭스 갯수
LPD3DXBUFFER pBoneCombinationBuf;// 본 콤비네이션 버퍼
D3DXMATRIX** ppBoneMatrixPtrs; // 프레임에 저장되어있는 변환매트릭스들의 배열의 포인터.
D3DXMATRIX* pBoneOffsetMatrices;// 본의 위치를 담아둘 매트릭스 배열의 포인터.
DWORD NumPaletteEntries; // 행렬 팔렛트 엔트리.. NonIndexed에선 사용되지 않음.
bool UseSoftwareVP; // 소프트웨어 버텍스 프로세싱 사용?
DWORD iAttributeSW; // used to denote the split between SW and HW if necessary
for non-indexed skinning
// 즉, Mixed 에서 SW와 HW 버텍스 프로세싱을 사용할 때
// 나누기 위한 정보를 저장하기 위한 변수.
};
* 스킨드 애니메이션을 위해서 좀 더 필요한 정보를 저장하기 위해서 메시컨테이너를
상속 받아 확장 시킨 구조체
* 속성 : 메시가 나눠지는 것
* 텍스쳐를 기준으로 나눠짐 : 매핑 소스가 1개면 메시가 한 덩어리
* tiny.x는 매핑 소스가 1개 이므로 속성 테이블의 갯수도 1개
CreateMeshContainer
HRESULT CAllocateHierarchy::CreateMeshContainer( LPCTSTR Name,
LPD3DXMESHDATA pMeshData,
LPD3DXMATERIAL pMaterials,
LPD3DXEFFECTINSTANCE pEffectInstances, DWORD NumMaterials,
DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo,
LPD3DXMESHCONTAINER *ppNewMeshContainer )
• 메시 컨테이너를 만들어 주는 함수
• 받는 인자들은 모두 D3DXMESHCONTAINER 구조체 속에 모두 있는 것들임
HRESULT hr;
D3DXMESHCONTAINER_DERIVED *pMeshContainer = NULL;
// 임시로 사용할 메시 컨테이너의 포인터
UINT NumFaces; // 삼각형 면의 갯수
UINT iMaterial; // 메터리얼 갯수
UINT iBone, cBones; // 본의 갯수, 본 카운트 임시 변수
LPDIRECT3DDEVICE9 pd3dDevice = NULL; // 임시로 받아둘 디바이스의 포인터
LPD3DXMESH pMesh = NULL; // 임시로 사용할 메시
*ppNewMeshContainer = NULL;
// this sample does not handle patch mesh, so fail when one is found
if( pMeshData->Type != D3DXMESHTYPE_MESH )
{
// 종료 루틴
SAFE_RELEASE(pd3dDevice);
// call Destroy function to properly clean up the memory allocated
if (pMeshContainer != NULL)
{
DestroyMeshContainer(pMeshContainer);
}
return E_FAIL;
}
• 더블 포인터 형으로 받은 메시 컨테이너의 포인터를 초기화 시키는 작업
• 메시 타입이 D3DXMESHTYPE_MESH (일반적인 메시)가 아니라면 함수를 끝내버리는 일을 함
// get the pMesh interface pointer out of the mesh data structure
pMesh = pMeshData->pMesh;
// this sample does not FVF compatible meshes, so fail when one is found
if( pMesh->GetFVF() == 0 )
{
// 종료 루틴 생략
return E_FAIL;
}
// allocate the overloaded structure to return as a D3DXMESHCONTAINER
pMeshContainer = new D3DXMESHCONTAINER_DERIVED;
if( pMeshContainer == NULL )
{
// 종료 루틴 생략
return E_OUTOFMEMORY;
}
memset( pMeshContainer, 0, sizeof(D3DXMESHCONTAINER_DERIVED) );
// make sure and copy the name. All memory as input belongs to caller, interfaces can be addref'd
though
hr = AllocateName( Name, &pMeshContainer->Name );
if( FAILED(hr) )
{
// 종료 루틴 생략
return E_FAIL;
}
그리고 나서, 임시로 만들었던 pMesh 메시 포인터에 인자로 넘어온 메시 데이터 안에 있는 메시의 포인터를 그대로 물려줌
FVF가 제대로 있는지 검사를하고 메시 컨테이너를 하나 동적으로 생성
• 생성 못하면 그냥 종료
만들어진 구조체를 초기화 시킨 후 AllocateName() 전역함수로 생성된 메시 컨테이너 안에 인자로 받았던 이름을 달아줌
pMesh->GetDevice( &pd3dDevice );
NumFaces = pMesh->GetNumFaces();
// if no normals are in the mesh, add them
if( !(pMesh->GetFVF() & D3DFVF_NORMAL) )
{
pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
// clone the mesh to make room for the normals;
hr = pMesh->CloneMeshFVF( pMesh->GetOptions(), pMesh->GetFVF() | D3DFVF_NORMAL,
pd3dDevice, &pMeshContainer->MeshData.pMesh );
if( FAILED(hr) )
{
// 종료 루틴 생략
return E_FAIL;
}
// get the new pMesh pointer back out of the mesh container use
// NOTE: we do not release pMesh because we don't have a reference to it yet
pMesh = pMeshContainer->MeshData.pMesh;
// now generate the normal for the pmesh
D3DXComputeNormals( pMesh, NULL );
}
else // if no normals, just add a reference to the mesh for mesh container
{
pMeshContainer->MeshData.pMesh = pMesh;
pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
// Increases the interface's reference count by 1.
pMesh->AddRef();
}
// allocate memory to contain the meterial information. This sample uses
// the D3D9 materials and texture names instead of the EffectInstance style materials
pMeshContainer->NumMaterials = max( 1, NumMaterials );
pMeshContainer->pMaterials = new D3DXMATERIAL[ pMeshContainer->NumMaterials ];
pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9[ pMeshContainer->NumMaterials ];
pMeshContainer->pAdjacency = new DWORD[ NumFaces*3 ];
if( (pMeshContainer->pAdjacency == NULL) || (pMeshContainer->pMaterials == NULL) )
{
// 종료 루틴 생략.
return E_OUTOFMEMORY;
}
• pMesh로 인터페이스에 접근을 해서 디바이스를 하나 얻어옴
• 폴리곤 수를 따로 저장
• FVF를 체크하는데, 만약에 NORMAL값이 없는 메시라면 CloneMeshFVF 함수를 사용해서
빛이 들어간 FVF로 설정된 메시를 새로 복사해서 만들어 냄
• 새로 생성되었던 지금 만들고 있는 메시컨테이너 안의 즉, pMeshContainer->MeshData.pMesh 안에 새로 클론된 메시를 집어 넣음
pMesh->AddRef()
• IUnknown 이라는 클래스는
– AddRef()
– QueryInterface()
– Release() 이 세가지를 멤버 함수로 가지고 있음
– 따라서 다이렉트 X의 모든 인터페이스는 저 3가지 함수를 공통으로 가지고 있습니다.
– Release()는 자주 쓰는 것
– Release()와 반대되는 개념이 AddRef()
– 내부적으로 레퍼런스 카운트라고 있는데
– 레퍼런스 카운트가 0이 되어야만 비로소 진짜 메모리에서 릴리즈가 됨
– Release()는 레퍼런스 카운트를 하나 줄여줌
// allocate memory to contain the meterial information. This sample uses
// the D3D9 materials and texture names instead of the EffectInstance style materials
pMeshContainer->NumMaterials = max( 1, NumMaterials );
pMeshContainer->pMaterials = new D3DXMATERIAL[ pMeshContainer->NumMaterials ];
pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9[ pMeshContainer->NumMaterials ];
pMeshContainer->pAdjacency = new DWORD[ NumFaces*3 ];
if( (pMeshContainer->pAdjacency == NULL) || (pMeshContainer->pMaterials == NULL) )
{
// 종료 루틴 생략.
return E_OUTOFMEMORY;
}
memcpy( pMeshContainer->pAdjacency, pAdjacency, sizeof(DWORD)*NumFaces*3 );
memset( pMeshContainer->ppTextures, 0, sizeof(LPDIRECT3DTEXTURE9)*pMeshContainer->NumMaterials );
// if materials provided, copy them
if( NumMaterials > 0 )
{
memcpy( pMeshContainer->pMaterials, pMaterials, sizeof(D3DXMATERIAL)*NumMaterials );
for( iMaterial = 0; iMaterial < NumMaterials; iMaterial++ )
{
// texture loading..
if( pMeshContainer->pMaterials[ iMaterial ].pTextureFilename != NULL )
{
TCHAR strTexturePath[ MAX_PATH ] = "";
DXUtil_FindMediaFileCb( strTexturePath, sizeof(strTexturePath),
pMeshContainer->pMaterials[ iMaterial ].pTextureFilename );
if( FAILED( D3DXCreateTextureFromFile( pd3dDevice, strTexturePath,
&pMeshContainer->ppTextures[ iMaterial ] ) ) )
pMeshContainer->ppTextures[ iMaterial ] = NULL;
// don't remember a pointer into the dynamic memory, just forget the name after loading
pMeshContainer->pMaterials[ iMaterial ].pTextureFilename = NULL;
}
}
}
else // if no materials provided, use a default one
{
pMeshContainer->pMaterials[ 0 ].pTextureFilename = NULL;
memset( &pMeshContainer->pMaterials[ 0 ].MatD3D, 0, sizeof(D3DMATERIAL9) );
pMeshContainer->pMaterials[ 0 ].MatD3D.Diffuse.r = 0.5f;
pMeshContainer->pMaterials[ 0 ].MatD3D.Diffuse.g = 0.5f;
pMeshContainer->pMaterials[ 0 ].MatD3D.Diffuse.b = 0.5f;
pMeshContainer->pMaterials[ 0 ].MatD3D.Specular = pMeshContainer->pMaterials[ 0 ].MatD3D.Diffuse;
• 메시 컨테이너 안에서 메터리얼의 갯수와 텍스쳐, 기타 메시에 필요한 정보들의 공간만 new 로 확보해 놓음
• adjacency 정보는 인자로 들어온 값을 그대로 카피
• 텍스쳐 포인터 배열은 0으로 초기화, 매터리얼 갯수가 0이 넘으면 즉, 매터리얼이 존재한다면 인자로 넘어온 메터리얼 구조체를 또 그대로 카피
• 텍스쳐 이름을 체크해서 텍스쳐를 생성해주고 이름이 있던 자리엔 NULL을 넣어줌
• 만약 매터리얼이 없다면 메터리얼에 디폴트 값들을 넣어줌
// 스킨 정보가 있을 때 실행
// if there is skinning information, save off the required data and then setup for HW skinning
if( pSkinInfo != NULL )
{
// first save off the skinInfo and original mesh data
pMeshContainer->pSkinInfo = pSkinInfo;
pSkinInfo->AddRef();
pMeshContainer->pOrigMesh = pMesh;
UINT temp = pMesh->AddRef();
// Will need an array of offset matrices to move vertices from the figure space to the bone's space
cBones = pSkinInfo->GetNumBones();
pMeshContainer->pBoneOffsetMatrices = new D3DXMATRIX[ cBones ];
if( pMeshContainer->pBoneOffsetMatrices == NULL )
{
// 종료 루틴 생략.
return E_FAIL;
}
// get each of the bone offset matrices so that we don't need to get them later
for( iBone = 0; iBone < cBones; ++iBone )
{
pMeshContainer->pBoneOffsetMatrices[ iBone ] = *(pMeshContainer->pSkinInfo-
>GetBoneOffsetMatrix(iBone) );
}
// GenerateSkinniedMesh Will take general skinning information and transform it to a HW friendly version
hr = m_pApp->GenerateSkinnedMesh( pMeshContainer );
if( FAILED(hr) )
{
// 종료 루틴 생략.
return E_FAIL;
}
}
- 스킨 정보의 포인터를 복사해 주면서 또 AddRef()로 레퍼런스 카운트를 늘려줌
– pMeshContainer->pSkinInfo 를 나중에 따로 릴리즈 하기 때문
– 메시컨테이너의 오리지날 메시에 pMesh 포인터를 또 복사해주면서 레퍼런스 카운트를 늘려줌
– pMeshContainer->pOrigMesh 를 따로 릴리즈 해주기 때문
– 메시 컨테이너 안에 본 오프셋 매트릭스(프레임의 원래 위치)를 셋팅해주기 위해서 스킨 정보 인터페이스 안에 저장되어있는 뼈의 갯수를 얻어옴
– 뼈의 갯수만큼 for문을 돌면서 뼈의 번호에 따라서 메시 컨테이너 안에 있는 본 오프셋 매트릭스를 저장
– CPROD3DANIMESH* m_pApp포인터를 이용해서 GenerateSkinnedMesh() 함수를 호출
– 메시 컨테이너 안에 있는 메시 데이터 안의 메시를 스킨 정보가 들어간 메시로 바꿔주는 일을 함
*ppNewMeshContainer = pMeshContainer;
pMeshContainer = NULL;
SAFE_RELEASE(pd3dDevice);
// call Destroy function to properly clean up the memory allocated
if (pMeshContainer != NULL)
{
DestroyMeshContainer(pMeshContainer);
}
return S_OK;
완성된 메시 컨테이너의 포인터를 인자로 받은 더블 포인터를 사용해 연결시켜 준 다음에 pd3dDevice를 릴리즈
//----------------------------------------------------------------------------
// Name: CAllocateHierarchy::DestroyFrame()
// Desc: Destroy Frame
//----------------------------------------------------------------------------
HRESULT CAllocateHierarchy::DestroyFrame( LPD3DXFRAME pFrameToFree )
{
// first, delete string of name. it will be leak the memory, if you do not this work..
SAFE_DELETE_ARRAY( pFrameToFree->Name );
SAFE_DELETE( pFrameToFree );
return S_OK;
}
//----------------------------------------------------------------------------
// Name: CAllocateHierarchy::DestroyMeshContainer()
// Desc: Destroy mesh container
//----------------------------------------------------------------------------
HRESULT CAllocateHierarchy::DestroyMeshContainer( LPD3DXMESHCONTAINER pMeshContainerBase )
{
UINT iMaterial;
D3DXMESHCONTAINER_DERIVED *pMeshContainer = (D3DXMESHCONTAINER_DERIVED *)pMeshContainerBase;
SAFE_DELETE_ARRAY( pMeshContainer->Name );
SAFE_DELETE_ARRAY( pMeshContainer->pAdjacency );
SAFE_DELETE_ARRAY( pMeshContainer->pMaterials );
SAFE_DELETE_ARRAY( pMeshContainer->pBoneOffsetMatrices );
// release all the allocated textures
if( pMeshContainer->ppTextures != NULL )
{
for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; ++iMaterial )
{
SAFE_RELEASE( pMeshContainer->ppTextures[ iMaterial ] );
}
}
SAFE_DELETE_ARRAY( pMeshContainer->ppTextures );
SAFE_DELETE_ARRAY( pMeshContainer->ppBoneMatrixPtrs );
SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
SAFE_RELEASE( pMeshContainer->pSkinInfo );
SAFE_RELEASE( pMeshContainer->pOrigMesh );
SAFE_DELETE( pMeshContainer );
return S_OK;
}
HRESULT CPROD3DANIMESH::LoadSKNMesh(char * szFileName)
{
HRESULT hr;
CAllocateHierarchy Alloc(this);
if(m_ppDevice == NULL)
{
MessageBox(NULL, "NULL Device?", "ERROR", MB_OK);
return FALSE;
}
hr = D3DXLoadMeshHierarchyFromX(szFileName, D3DXMESH_MANAGED,
m_ppDevice, &Alloc, NULL, &m_pFrameRoot, &m_pAnimController);
if(hr == D3DERR_INVALIDCALL)
{
MessageBox(NULL, "The method call is invalid.", "ERROR", MB_OK);
return FALSE;
}
if(hr == E_OUTOFMEMORY)
{
MessageBox(NULL, "not sufficient memory to complete the call.", "ERROR", MB_OK);
return FALSE;
}
hr = SetupBoneMatrixPointers(m_pFrameRoot);
return TRUE;
}
D3DXLoadMeshHierarchyFromX() 함수를 호출
D3DXMESH_MANAGED 라는 것은 메시의 버텍스 버퍼와 인덱스 버퍼의 메모리 풀을 D3DPOOL_MANAGED로 사용
D3DPOOL_MANAGED는 데이타가 시스템 메모리와 비디오 카드 메모리를 왔다갔다하며 알아서 D3D가 관리
D3DPOOL_DEFAULT는 오로지 비디오 카드 메모리에만 데이타가 있겠다고 하는 것임
D3DPOOL_SYSTEMMEM는 오로지 시스템 메모리에만 데이터를 두겠다는 것임
MANAGED는 디바이스를 잃더라도 알아서 관리를 해주기 때문에 편한 반면,
시스템 메모리와 비디오 메모리 간에 왔다갔다 하기 때문에 약간의 속도가 저하
D3DPOOL_DEFAULT는 비디오 메모리에만 있어서 속도는 빠르지만 디바이스를 잃어버리면
프로그래머가 수동적으로 데이터를 Restore 시켜줘야 하는 단점이 있음
SetUpBoneMatrixPointers( m_pFrameRoot ); // 본 매트릭스들을 생성해서 트리로 꾸며진 뼈에 매트릭스들을 달아주는 일을 함
GenerateSkinnedMesh()
HRESULT hr = S_OK;
// 스킨 정보가 없으면 리턴
if( pMeshContainer->pSkinInfo == NULL )
return hr;
SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
hr = pMeshContainer->pSkinInfo->ConvertToBlendedMesh( pMeshContainer->pOrigMesh,
D3DXMESH_MANAGED |
D3DXMESHOPT_VERTEXCACHE,
pMeshContainer->pAdjacency,
NULL, NULL, NULL,
&pMeshContainer->NumInfl,
&pMeshContainer->NumAttributeGroups,
&pMeshContainer->pBoneCombinationBuf,
&pMeshContainer->MeshData.pMesh );
// 실패하면 바로 리턴.
if( FAILED(hr) )
return hr;
CAllocateMeshContainer::CreateMeshContainer() 함수 안에서 마지막에 호출했던 함수
스킨 정보가 있는지 검사. 없으면 바로 리턴
메시 컨테이너의 메시 데이터 안에 있는 메시를 릴리즈 시키고, 본 콤비네이션 버퍼를 없앰
ConvertToBlendMesh() 오리지날 메시를 받아서 약간의 옵션을 정해준 뒤 메시 컨테이너의 여러 정보들을 채워줌
메시도 새로 생성 : 원래 메시가 있던 자리를 밀어내고 새로 생성되니까.. 메모리 누수가 생기기 전에 미리 릴리즈
본 콤비네이션 : 각각의 Attribute에 맞는 BoneID를 가지고 있는 것(나중에 그릴 때 사용)
// 디바이스 캡에 맞지 않은 본 콤비네이션 셋을 찾는다.
for( pMeshContainer->iAttributeSW = 0; pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups;
++pMeshContainer->iAttributeSW )
{
DWORD cInfl = 0;
for( DWORD iInfl = 0; iInfl < pMeshContainer->NumInfl; iInfl++ )
{
if( rgBoneCombinations[ pMeshContainer->iAttributeSW ].BoneId[ iInfl ] != UINT_MAX )
{
++cInfl;
}
}
if( cInfl > m_d3dCaps.MaxVertexBlendMatrices )
break;
}
소프트웨어 버텍스 프로세싱을 해줄 갯수를 정해 주는 것임
tiny.x 에서는 31개의 본 콤비네이션이 있음
DrawMeshContainer()
// first, check for skinning
if( pMeshContainer->pSkinInfo != NULL )
{
AttribIdPrev = UNUSED32;
pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>(pMeshContainer-
>pBoneCombinationBuf->GetBufferPointer());
// Draw using default vertex processing of the device (typically HW)
for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; ++iAttrib )
{
NumBlend = 0;
for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
// 현재 뼈에서 블랜딩할 매트릭스 갯수를 정확히 지정해주는 것
{
if( pBoneComb[ iAttrib ].BoneId[ i ] != UINT_MAX )
{
NumBlend = i;
}
}
// If necessary, draw parts that HW could not handle using SW
if( pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups )
{
AttribIdPrev = UNUSED32;
m_pd3dDevice->SetSoftwareVertexProcessing( TRUE );
for( iAttrib = pMeshContainer->iAttributeSW; iAttrib < pMeshContainer->NumAttributeGroups; ++iAttrib )
{
NumBlend = 0;
for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
{
if( pBoneComb[ iAttrib ].BoneId[ i ] != UINT_MAX )
{
NumBlend = i;
}
}
if( m_d3dCaps.MaxVertexBlendMatrices < NumBlend+1 )
{
// first calculate the world matrices for the current set of blend weights and get the accurate
// count of the number of blends
for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
{
if( iMatrixIndex != UINT_MAX )
{
D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[ iMatrixIndex ],
pMeshContainer->ppBoneMatrixPtrs[ iMatrixIndex ] );
m_pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp );
}
}
m_pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend );
// Lookup the material used this subset of faces
if( (AttribIdPrev != pBoneComb[ iAttrib ].AttribId) || (AttribIdPrev == UNUSED32) )
{
m_pd3dDevice->SetMaterial( &pMeshContainer-
>pMaterials[ pBoneComb[ iAttrib ].AttribId ].MatD3D );
m_pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[ pBoneComb[ iAttrib ].AttribId ] );
AttribIdPrev = pBoneComb[ iAttrib ].AttribId;
}
// draw the subset now that the correct material and matrices are loaded
pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib );
}
}
m_pd3dDevice->SetSoftwareVertexProcessing( FALSE );
}
//----------------------------------------------------------------------------
// Name: SetupBoneMatrixPointers()
// Desc: Called to setup the pointers for a given bone to its transformation matrix
//----------------------------------------------------------------------------
HRESULT CPROD3DANIMESH::SetupBoneMatrixPointersOnMesh(LPD3DXMESHCONTAINER
pMeshContainerBase)
{
UINT iBone, cBones;
D3DXFRAME_DERIVED *pFrame;
D3DXMESHCONTAINER_DERIVED *pMeshContainer =
(D3DXMESHCONTAINER_DERIVED*)pMeshContainerBase;
// if there is skinned mesh, then setup the bone matrices
if( pMeshContainer->pSkinInfo != NULL )
{
cBones = pMeshContainer->pSkinInfo->GetNumBones();
pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[ cBones ];
if( pMeshContainer->ppBoneMatrixPtrs == NULL )
return E_OUTOFMEMORY;
for( iBone = 0; iBone < cBones; iBone++ )
{
pFrame = (D3DXFRAME_DERIVED*)D3DXFrameFind( m_pFrameRoot, pMeshContainer-
>pSkinInfo->GetBoneName(iBone) );
if( pFrame == NULL )
return E_FAIL;
pMeshContainer->ppBoneMatrixPtrs[ iBone ] = &pFrame->CombinedTransformationMatrix;
}
}
return S_OK;
}
메시컨테이너에 매트릭스를 달아주는 함수
DrawMeshContainer()에서 그려줄 때 BoneID로 행렬을 찾아줌 : 그 배열을 생성
스킨 정보로 전체 뼈의 갯수를 얻어옴
그 개수대로 매트릭스 배열을 동적 메모리 할당을 해서 만들어 냄
뼈의 갯수대로 루프를 돌며 D3DXFrameFind() 함수를 사용하여 뼈를 이름으로 찾아내고 그 프레임의 최종 변환
행렬의 포인터를 배열에 물려줌
41
//----------------------------------------------------------------------------
// Name: SetupBoneMatrixPointers()
// Desc: Called to setup the pointers for a given bone to its transformation matrix
//----------------------------------------------------------------------------
HRESULT CPROD3DANIMESH::SetupBoneMatrixPointers(LPD3DXFRAME
pFrame)
{
HRESULT hr;
if( pFrame->pMeshContainer != NULL )
{
hr = SetUpBoneMatrixPointersOnMesh( pFrame->pMeshContainer );
if( FAILED(hr) )
return hr;
}
if( pFrame->pFrameSibling != NULL )
{
hr = SetUpBoneMatrixPointers( pFrame->pFrameSibling );
if( FAILED(hr) )
return hr;
}
if( pFrame->pFrameFirstChild != NULL )
{
hr = SetUpBoneMatrixPointers( pFrame->pFrameFirstChild );
if( FAILED(hr) )
return hr;
}
}
트리 구조의 뼈를 돌면서 메시컨테이너에 최종 변환 매트릭스를 할당하고 물려줌
42
void CPROD3DANIMESH::UpdateFrameMatrices(LPD3DXFRAME
pFrameBase, LPD3DXMATRIX pParentMatrix)
{
D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED*)pFrameBase;
if( pParentMatrix != NULL )
D3DXMatrixMultiply( &pFrame->CombinedTransformationMatrix, &pFrame->TransformationMatrix, pParentMatrix );
else
pFrame->CombinedTransformationMatrix = pFrame->TransformationMatrix;
if( pFrame->pFrameSibling != NULL )
{
UpdateFrameMatrices( pFrame->pFrameSibling, pParentMatrix );
}
if( pFrame->pFrameFirstChild != NULL )
{
UpdateFrameMatrices( pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix );
}
return S_OK;
}
사람모델인 경우 머리부터 발 끝까지 순서대로 순회를 하며 적절한 애니메이션 타임에 따른 매트
릭스대로 업데이트
m_pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, 0 );
//----------------------------------------------------------------------------
// Name: DrawFrame()
// Desc: Called to render a frame in the hierarchy
//----------------------------------------------------------------------------
HRESULT DrawFrame(LPD3DXFRAME pFrame)
{
LPD3DXMESHCONTAINER pMeshContainer;
pMeshContainer = pFrame->pMeshContainer;
while( pMeshContainer != NULL )
{
DrawMeshContainer( pMeshContainer, pFrame );
pMeshContainer = pMeshContainer->pNextMeshContainer;
}
if( pFrame->pFrameSibling != NULL )
{
DrawFrame( pFrame->pFrameSibling );
}
if( pFrame->pFrameFirstChild != NULL )
{
DrawFrame( pFrame->pFrameFirstChild );
}
return S_OK;
}
현재 있는 메시 컨테이너를 모두 그려준 후에, 그 밑에 if 문으로 가서 형제 프레임이있는지 검사
만약 존재한다면 자기 자신을 또 호출. 그런식으로 계속 깊게 깊게 들어가다보면 결국 자신과
동등한 트리 깊이를 가지고 있는 메시 컨테이너들은 다 그리게 됨
//----------------------------------------------------------------------
// Name: SetupBoneMatrixPointers()
// Desc: Called to setup the pointers for a given bone to its transformation matrix
//----------------------------------------------------------------------
HRESULT CPROD3DANIMESH::SetupBoneMatrixPointersOnMesh(LPD3DXMESHCONTAINER
pMeshContainerBase)
{
UINT iBone, cBones;
D3DXFRAME_DERIVED *pFrame;
D3DXMESHCONTAINER_DERIVED *pMeshContainer =
(D3DXMESHCONTAINER_DERIVED*)pMeshContainerBase;
// if there is skinned mesh, then setup the bone matrices
if( pMeshContainer->pSkinInfo != NULL )
{
cBones = pMeshContainer->pSkinInfo->GetNumBones();
pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[ cBones ];
if( pMeshContainer->ppBoneMatrixPtrs == NULL )
return E_OUTOFMEMORY;
for( iBone = 0; iBone < cBones; iBone++ )
{
pFrame = (D3DXFRAME_DERIVED*)D3DXFrameFind( m_pFrameRoot, pMeshContainer-
>pSkinInfo->GetBoneName(iBone) );
if( pFrame == NULL )
return E_FAIL;
pMeshContainer->ppBoneMatrixPtrs[ iBone ] = &pFrame->CombinedTransformationMatrix;
}
}
return S_OK;
}
메시컨테이너에 매트릭스를 달아주는 함수
DrawMeshContainer()에서 그려줄 때 BoneID로 행렬을 찾아줌 : 그 배열을 생성
스킨 정보로 전체 뼈의 갯수를 얻어옴
그 개수대로 매트릭스 배열을 동적 메모리 할당을 해서 만들어 냄
뼈의 갯수대로 루프를 돌며 D3DXFrameFind() 함수를 사용하여 뼈를 이름으로 찾아내고 그 프레임의 최종 변환 행렬의 포인터를 배열에 물려줌
//----------------------------------------------------------------------------
// Name: SetupBoneMatrixPointers()
// Desc: Called to setup the pointers for a given bone to its transformation matrix
//----------------------------------------------------------------------------
HRESULT CPROD3DANIMESH::SetupBoneMatrixPointers(LPD3DXFRAME
pFrame)
{
HRESULT hr;
if( pFrame->pMeshContainer != NULL )
{
hr = SetUpBoneMatrixPointersOnMesh( pFrame->pMeshContainer );
if( FAILED(hr) )
return hr;
}
if( pFrame->pFrameSibling != NULL )
{
hr = SetUpBoneMatrixPointers( pFrame->pFrameSibling );
if( FAILED(hr) )
return hr;
}
if( pFrame->pFrameFirstChild != NULL )
{
hr = SetUpBoneMatrixPointers( pFrame->pFrameFirstChild );
if( FAILED(hr) )
return hr;
}
}
트리 구조의 뼈를 돌면서 메시컨테이너에 최종 변환 매트릭스를 할당하고 물려줌
void CPROD3DANIMESH::UpdateFrameMatrices(LPD3DXFRAME
pFrameBase, LPD3DXMATRIX pParentMatrix)
{
D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED*)pFrameBase;
if( pParentMatrix != NULL )
D3DXMatrixMultiply( &pFrame->CombinedTransformationMatrix, &pFrame->TransformationMatrix, pParentMatrix );
else
pFrame->CombinedTransformationMatrix = pFrame->TransformationMatrix;
if( pFrame->pFrameSibling != NULL )
{
UpdateFrameMatrices( pFrame->pFrameSibling, pParentMatrix );
}
if( pFrame->pFrameFirstChild != NULL )
{
UpdateFrameMatrices( pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix );
}
return S_OK;
}
사람모델인 경우 머리부터 발 끝까지 순서대로 순회를 하며 적절한 애니메이션 타임에 따른 매트릭스대로 업데이트
출처 - http://sdi1982.springnote.com/
sdi1982 님의 노트에서 발췌
----- sdi1982님의 노트에는 로그인을 하지 않으면
댓글을 쓸 수 없어서 동의를 얻지 못하고 퍼왔습니다.
문제가 된다면 삭제 하겠습니다.-----
'프로그래밍 > DirectX' 카테고리의 다른 글
[DirectX] Direct3D X File Load (DirectX 9.0) (1) | 2009.11.28 |
---|---|
[DirectX] 스킨드 메쉬 직접 구현(엑스파일 파싱) (2) | 2009.11.28 |