Tutorial6- 메쉬
- 프로그래밍/3D그래픽스 & 쉐이더
- 2011. 3. 30. 00:09
3D 파일 포멧 기초
.X File Format은 DirectX에서 지원하는 기본적인 3차원 메시파일 포맷이다.
지금까지는 모든 메시데이터는 3차원 좌표를 직접 지정하여 생성했지만 실제 게임을 제작한다면 전문 3D 디자이너들에 의해서 만들어진 메시 파일을 사용하여야만 한다.
맥스나 마야에서 X 파일 포맷으로 Export하려면 DX9 SDK가 아닌 추가 SDK(Extras SDK)를 다운 받아서 추가해야 한다. 추가 SDK를 설치하면 맥스와 마야에서 사용되는 X 파일 Exporter의 빌드 되지 않은 상태의 소스코드가 있다. 이 소스들 중에 사용하려는 버전에 맞는 플러그인을 빌드해서 사용하면 된다. 하지만 이 코드를 빌드하려면 DX9 외에 MAX나 MAYA에서 제공하는 SDK도 있어야 한다. 이 SDK들은 각 소프트웨어를 설치하면서 옵션으로 설치가 가능하다.
이렇게 추출된 X 파일은 IDirect3DXMesh를 통해서 완벽하게 제어가 가능하다.
<사진출저: 한국콘텐츠 진흥원>
//메시와 X파일을 이용하는 방법에 대해서 알아보자
// 소스분석
LPDIRECT3D9 g_pD3D = NULL; //D3D 디바이스를 생성할 D3D객체변수
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; //렌더링에 사용될 D3D디바이스
LPD3DXMESH g_pMesh = NULL; //메시 객체
D3DMATERIAL9* g_pMeshMaterials = NULL; //메시에서 사용할 재질
LPDIRECT3DTEXTURE9* g_pMeshTextures = NULL; //메시에서 사용할 텍스쳐
DWORD g_dwNumMaterials = 0L; //메시에서 사용중인 재질의 개수
//위에서 처럼 메시를 사용하기 위해서는 많은 정보에 대한 선언을 해 두어야 한다.
HRESULT InitGeometry()
{
// 재질을 임시로 보관할 버퍼선언
LPD3DXBUFFER pD3DXMtrlBuffer;
// Tiger.x파일을 메시로 읽어들인다. 이때 재질정보도 함께 읽는다.
if( FAILED( D3DXLoadMeshFromX( "Tiger.x", D3DXMESH_SYSTEMMEM,
g_pd3dDevice, NULL,
&pD3DXMtrlBuffer, NULL, &g_dwNumMaterials,
&g_pMesh ) ) )
{
// 현재 폴더에 파일이 없으면 상위폴더 검색
if( FAILED( D3DXLoadMeshFromX( "..\\Tiger.x",
D3DXMESH_SYSTEMMEM,
g_pd3dDevice, NULL,
&pD3DXMtrlBuffer, NULL, &g_dwNumMaterials,
&g_pMesh ) ) )
{
MessageBox(NULL, "Could not find tiger.x", "Meshes.exe", MB_OK);
return E_FAIL;
}
}
// 재질정보와 텍스쳐 정보를 따로 뽑아낸다.
D3DXMATERIAL* d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();
// 재질개수만큼 재질구조체 배열 생성
g_pMeshMaterials = new D3DMATERIAL9[g_dwNumMaterials];
// 재질개수만큼 텍스쳐 배열 생성
g_pMeshTextures = new LPDIRECT3DTEXTURE9[g_dwNumMaterials];
for( DWORD i=0; i<g_dwNumMaterials; i++ )
{
// 재질정보를 복사
g_pMeshMaterials[i] = d3dxMaterials[i].MatD3D;
// 주변광원정보를 Diffuse정보로
g_pMeshMaterials[i].Ambient = g_pMeshMaterials[i].Diffuse;
g_pMeshTextures[i] = NULL;
if( d3dxMaterials[i].pTextureFilename != NULL &&
lstrlen(d3dxMaterials[i].pTextureFilename) > 0 )
{
// 텍스쳐를 파일에서 로드한다
if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice,
d3dxMaterials[i].pTextureFilename,
&g_pMeshTextures[i] ) ) )
{
// 텍스쳐가 현재 폴더에 없으면 상위폴더 검색
const TCHAR* strPrefix = TEXT("..\\");
const int lenPrefix = lstrlen( strPrefix );
TCHAR strTexture[MAX_PATH];
lstrcpyn( strTexture, strPrefix, MAX_PATH );
lstrcpyn( strTexture + lenPrefix,
d3dxMaterials[i].pTextureFilename, MAX_PATH - lenPrefix );
if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice,
strTexture,
&g_pMeshTextures[i] ) ) )
{
MessageBox(NULL, "Could not find texture map",
"Meshes.exe", MB_OK);
}
}
}
}
/// 임시로 생성한 재질버퍼 소거
pD3DXMtrlBuffer->Release();
return S_OK;
}
중요:
여기서 내가 잠시 몰랐던것이 있는데 X 파일은 텍스처파일 및 기타 정보들을 가지고 있다.
그래서 구조체 포인터만으로 텍스쳐파일을 불러올수있는것이다. 또한 X 파일을 읽어들일 때 주의해야 할 것은 모든 메시 파일은 여러 개의 폴리곤으로 구성되어 있고, 폴리곤은 각각의 재질을 가질 수 있다는 것이다. 따라서 대부분은 재질이 여러 개일경우 메시를 재질별로 부분 메시로 분할한다. 이렇게 분할된 부분 메시는 부분 메시별로 따로 그려야 하는데, D3D에서는 DrawSubset()함수가 그 역할을 한다.
VOID Render()
{
// 후면버퍼와 Z버퍼를 지운다.
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
// 렌더링 시작
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
// 월드,뷰,프로젝션 행렬을 설정한다.
SetupMatrices();
// 메시는 재질이 다른 메시별로 부분집합을 이루고 있다.
// 이들을 루프를 수행해서 모두 그려준다.
for( DWORD i=0; i<g_dwNumMaterials; i++ )
{
// 부분집합 메시의 재질과 텍스쳐 설정
g_pd3dDevice->SetMaterial( &g_pMeshMaterials[i] );
g_pd3dDevice->SetTexture( 0, g_pMeshTextures[i] );
// 부분집합 메시 출력
g_pMesh->DrawSubset( i );
}
// 렌더링 종료
g_pd3dDevice->EndScene();
}
// 후면버퍼를 보이는 화면으로!
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
메시를 그리기 직전에 그 메시에 해당하는 텍스쳐와 그 재질을 적용한다는 것을 잘 기억해 두자.
이렇게 여러 개의 재질은 여러 번의 SetTexture()+SetMaterial()+DrawSubset()으로 구성된다.
만약 텍스쳐 없이 재질만 있다면 SetTexture()과정을 생략할 수 있다.
[출처] Mesh - X File과
\Mesh 기법|작성자 누리블루
이 글을 공유하기