Graphics/DirectX

Direct 3D - *메시

MOLOKINI 2014. 6. 19. 15:16
3D파일 포맷
X파일은 파일의 확장자가 .x로 끝나는 파일로, DirectX에서 지원하는 기본적인 3차원 메시파일 포맷이다
지금까지 좌표를 이용해서 삼각형 원통을 그렸지만, 실제 필드에서는 3D 그래픽 디자이너들이 만들어낸 메시를 가지고 출력을 해야한다
디자이너들은 3DMAX, MAYA같은 툴로 만들어낸다, 내가 만들어볼수도있는거겠지요?
3DMAX나 MAYA의 파일을 이용하려면 각각의 SDK를 추가적으로 설치해야한다




#include <Windows.h>
#include <mmsystem.h>
#include <d3dx9.h>

LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
LPD3DXMESH g_pMesh = NULL; /// 메시 객체
D3DMATERIAL9* g_pMeshMaterials = NULL; /// 메시에서 사용할 재질
LPDIRECT3DTEXTURE9* g_pMeshTextures = NULL; /// 메시에서 사용할 텍스쳐
DWORD g_dwNumMaterials = 0L; /// 메시에서 사용중인 재질의 개수


HRESULT InitD3D( HWND hWnd )
{
    /// 디바이스를 생성하기위한 D3D객체 생성
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;

    D3DPRESENT_PARAMETERS d3dpp;                /// 디바이스 생성을 위한 구조체
    ZeroMemory( &d3dpp, sizeof(d3dpp) );        /// 반드시 ZeroMemory()함수로 미리 구조체를 깨끗이 지워야 한다.
    d3dpp.Windowed = TRUE;                      /// 창모드로 생성
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;   /// 가장 효율적인 SWAP효과
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;    /// 현재 바탕화면 모드에 맞춰서 후면버퍼를 생성
d3dpp.AutoDepthStencilFormat = D3DFMT_D16; /// 복잡한 오브젝트를 그릴것이라 Z버퍼가 필요하다
d3dpp.EnableAutoDepthStencil = TRUE;

    /// 디바이스를 다음과 같은 설정으로 생성한다.
    /// 1. 디폴트 비디오카드를 사용(대부분은 비디오카드가 1개 이다.)
    /// 2. HAL디바이스를 생성한다.(HW가속장치를 사용하겠다는 의미)
    /// 3. 정점처리는 모든 카드에서 지원하는 SW처리로 생성한다.(HW로 생성할경우 더욱 높은 성능을 낸다.)
    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      &d3dpp, &g_pd3dDevice ) ) )
    {
        return E_FAIL;
    }

/// Z버퍼를 켠다
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0xffffffff);

    return S_OK;
}

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, "tiger.x가 없어요^^", "^^", 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, "텍스쳐가 없어요^^", "MESH.EXE", MB_OK);
}
}
}
}

/// 임시 생성 재질버퍼 객체 소거
pD3DXMtrlBuffer->Release();

return S_OK;
}

VOID Cleanup()
{
if( g_pMeshMaterials != NULL)
delete[] g_pMeshMaterials;

if( g_pMeshTextures)
{
for(DWORD i = 0; i < g_dwNumMaterials; i++)
{
if(g_pMeshTextures[i])
g_pMeshTextures[i]->Release();
}
delete[] g_pMeshTextures;
}

    if( g_pd3dDevice != NULL) 
        g_pd3dDevice->Release();

    if( g_pD3D != NULL)
        g_pD3D->Release();

if( g_pMesh != NULL)
g_pMesh->Release();
}

VOID SetupMatrices()
{
/// 월드 행렬
D3DXMATRIXA16 matWorld;
D3DXMatrixIdentity(&matWorld);
D3DXMatrixRotationY(&matWorld, timeGetTime()/1000.0f); /// Y축 중심 회전행렬 생성
X축으로 하면 상하로 회전, Y축으로 하면 좌우로 회전 당연한거지요?^^
g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld); /// 생성한 회전행렬을 월드행렬로 디바이스에 설정

/// 뷰행렬을 정의하기 위해서는 세가지 값이 필요하다
D3DXVECTOR3 vEyePt(0.0f, 3.0f, -5.0f); /// 1. 눈의 위치 (0, 3.0, -5)
D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f); /// 2. 눈이 바라보는 위치 (0,0,0)
D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);   /// 3. 천정 방향의 상방벡터 (0,1,0)

D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
/// 1,2,3의 값으로 뷰 행렬 생성

g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
/// 생성한 뷰 행렬을 디바이스에 설정

/// 프로젝션 행렬을 정의하기 위해서는 시야각(FOV=Field Of View)과 
/// 종횡비(aspect ratio), 클리핑 평면의 값이 필요하다.
D3DXMATRIXA16 matProj;
/// matProj : 값이 설정 될 행렬
/// D3DX_PI/4 : FOV(D3DX_PI/4 = 45도)
/// 1.0f : 종횡비
/// 1,0f : 근접 클리핑 평면(near clipping plane)
/// 100.0f : 원거리 클리핑 평면(far clipping plane)
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj); /// 생성한 프로젝션 행렬을 디바이스에 설정

}

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 );
}

Render함수에서 주의할 점
모든 메시파일은 여러개의 폴리곤으로 구성되어있고, 폴리곤은 각각의 재질을 가질 수 있다는 것이다. 따라서 대부분은 재질이 여러개일 경우 메시를 재질별로 부분 메시로 분할한다. 이렇게 분할된 부분 메시는 부분 메시별로 따로 그려야 하는데, D3D에서는 DrawSubset()이라는 함수가 그 역할을 한다.

메시를 그리기 직전에 그 메시에 해당하는 텍스쳐와 재질을 적용한다는 것을 잘 기억하자.
이렇게 여러개의 재질은 여러번의 SetTexture() + SetMaterial() + DrawSubset()으로 구성된다. 만약 텍스쳐 없이 재질만 있다면 SetTexture() 과정을 생략할 수 있다.

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            Cleanup();
            PostQuitMessage( 0 );
            return 0;

        case WM_PAINT:
            Render();
            ValidateRect( hWnd, NULL );
            return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
    /// 윈도우 클래스 등록
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, 
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      "D3D Tutorial", NULL };
    RegisterClassEx( &wc );

    /// 윈도우 생성
    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial", 
                              WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
                              GetDesktopWindow(), NULL, wc.hInstance, NULL );

    /// Direct3D 초기화
    if( SUCCEEDED( InitD3D( hWnd ) ) )
    { 
/// 정점버퍼 초기화
if(SUCCEEDED(InitGeometry())){

/// 윈도우 출력
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );

/// 메시지 루프
MSG msg; 
ZeroMemory(&msg, sizeof(msg));

while(msg.message!=WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)){
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
Render();
}
}
    }

    /// 등록된 클래스 소거
    UnregisterClass( "D3D Tutorial", wc.hInstance );
    return 0;
}


이 어흥이 한마리 띄우자고 이렇게 많은수의 줄이 필요한거다.

 

'Graphics > DirectX' 카테고리의 다른 글

Direct 3D - 프레임워크  (0) 2014.06.19
Direct 3D - *인덱스 버퍼  (0) 2014.06.19
Direct 3D - *텍스쳐  (0) 2014.06.19
Direct 3D - *광원  (0) 2014.06.19
Direct X - *행렬  (0) 2014.06.19