블로그 이미지
대갈장군

calendar

1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Notice

2008. 2. 19. 06:31 프로그래밍/DirectX
후후. 드디어 조금 쓸만한게 나오는 구만...

1. About Subsets
음. 바로 전 레슨에서 아무 설명없이 넘어갔던 거 설명하려고 하는 구나.

meshTeapot->DrawSubset(0);    // draw the teapot

이 명령을 기억하나? 메쉬를 그려줄때 줬던 명령인데 뭣하는 놈인지는 제대로 설명을 안해줬다.

서브셋이라함은 메쉬의 한 부분, 한 조각이다. 위의 명령에서 0이 의미하는 것은 어떤 서브셋을 그리라고 D3D에 알려주는 역활을 한다.

음, 물론 여기서 의문점이 생기는데 그럼 서브셋이 여러개일수도 있느냐 이다. 물론이다. 티팟의 경우 뚜겅과 본체가 서로 다른 서브셋에 들어가 있을 수도 있고 그런 경우에는 다음과 같은 명령이 들어가야 할 것이다.

meshTeapot->DrawSubset(0);    // draw the teapot
meshTeapot->DrawSubset(1);    // draw the teapot lid

각각의 서브셋은 사실상 분리된 모델이며 각각이 서로 다른 속성을 가질수 있다. (material, texture, animations)등등

자 실제로 이것이 어떻게 적용되는지 함 보자

Image 2.1 - Space Ship 2 'spaceship 2.x'

Image 2.1 - Space-Ship 2 "spaceship 2.x"

이것은 DX에서 제공하는 기본 모델인데 3가지 다른 색을 가지고 있다. 보아하니 텍스쳐를 입힌것도 아닌데 부분별로 다른 색상이 들어가 있다. 그렇다면 어떻게 한걸까?

바로 해답은 다음 그림이다.

Image 2.2 - Space Ship 2: Disassebled

Image 2.2 - Space-Ship 2: Disassebled

사실 이 모델은 3개의 서브셋으로 이루어져 있다. 각각이 다른 색상을 가지고 다른 재질을 표현한다.

자, 결론적으로 서브셋은 메쉬의 일부이지만 서로다른 속성과 특성을 소유 할수 있다는 것이다.

2. Loading an X File to a Mesh
X file을 로딩하는 것은 생각보다 쉽지만은 않다는 구나..허허허

1. X file로 부터 mesh를 로딩한다.
2. 각각의 서브셋에 대해서 material 정보를 추출하고 그정보를 material buffers에 저장한다.
3. materials를 지정하고 subset을 그린다.


2-1. Load the mesh from the .x file

HRESULT D3DXLoadMeshFromX(LPCTSTR pFilename,
                          DWORD Options,
                          LPDIRECT3DDEVICE9 pD3DDevice,
                          LPD3DXBUFFER *ppAdjacency,
                          LPD3DXBUFFER *ppMaterials,
                          LPD3DXBUFFER *ppEffectInstances,
                          DWORD *pNumMaterials,
                          LPD3DXMESH *ppMesh);

후아~ 길다 :) 새로운 인자가 많이도 등장했구나~

LPCTSTR pFilename : 파일 경로를 저장하고 있는 포인터를 받는 인자. 예를 들자면 L"spaceship 2.x" 와 같겠다.

DWORD Options : 이 인자에서는 어떻게 메쉬를 관리할것인가에 대한 전반적이고 다양한 설정을 할수 있다. 물론 플래그로써... 대부분은 메모리 관리에 대한 옵션 설정인데 지금 다 설명하기는 너무 많아서 일단 잘 사용되고 잘 작동되는 적당한 플래그를 사용할것이고 차후에 설명하도록 한다.

여기서 사용하는 플래그는 D3DXMESH_SYSTEMMEM 인데 이 플래그는 D3D에게 System memory를 사용하라고 (메쉬 저장을 위해서) 알려 주는 것이다.

LPDIRECT3DDEVICE9 pD3DDevice : This, as you should know by now, is the pointer to the Direct3D device.  Put 'd3ddev' here.

LPD3DXBUFFER *ppAdjacency : LPD3DXBUFFER 는 저장 버퍼를 가리키는 포인터형인데 Direct3D에 의해 사용된다. 메쉬에 관련해서는 수많은 옵션과 설정이 가능(필요)한데 그것은 보다 나은 성능을 위해서 필요하다. 그리고 이 버퍼가 그에관한 모든것을 저장한단다.

Adjacency 는 어떤 정보로 구성되냐면, 어떤 삼각형이 어떤것 옆에 위치할 것인가에 대한 정보를 가진단다. 이 옵션은 나중에 고급 기술에서 많이 사용되지만 지금은 사용할 곳이 없으니 NULL로 그냥 가져다 놓는다.

LPD3DXBUFFER *ppMaterials : 이건 또 다른 버퍼인데 각각의 서브셋에 대한 material information을 저장하는 버퍼래요. 만약 우리가 materials를 사용하고 있다면 이 정보는 꼭 필요하다. bufShipMaterials라는 버퍼를 하나 만들어서 이 인자에 그 버퍼 주소를 넘겨 줄 것이다.

LPD3DXBUFFER *ppEffectInstances : 또 다른 버퍼다.. 헐헐헐... 이것은 특별한 렌더링 효과를 위해서 사용된다. 이것도 나중에 다룰 내용이라서 지금은 일단 NULL로 해놓는다.

DWORD *pNumMaterials : This is a pointer to a DWORD. 이 인자 값은 몇개의 materials를 사용하는지 설정한다. 이 인자는 몇개의 서브셋이 한개의 메쉬에 들어가 있는지를 말해준다.

이 인자를 위해서 전역 변수를 하나 선언해서 (numMaterials) 이 주소값을 이 인자에 넣는다.

LPD3DXMESH *ppMesh : 메쉬에 대한 포인터다.

// global variables
LPD3DXMESH meshSpaceship;
DWORD numMaterials;

// local variable (in the init_graphics() function)
LPD3DXBUFFER bufShipMaterial;

D3DXLoadMeshFromX(L"spaceship 2.x",    // load this file
                  D3DXMESH_SYSTEMMEM,    // load the mesh into system memory
                  d3ddev,    // the Direct3D Device
                  NULL,    // we aren't using adjacency
                  &bufShipMaterial,    // put the materials here
                  NULL,    // we aren't using effect instances
                  &numMaterials,    // the number of materials in this model
                  &meshSpaceship);    // put the mesh here

전역 변수로 두개를 선언했는데 OpenGL과 마찬가지로 객체 지향성은 잃어버릴수 밖에 없나보다. 저 인자들은 여러함수에서 두루 사용되므로 부득이 하게 전역변수로 선언한다.

이 명령을 실행함으로써 sapceship 2.x라는 파일은 mesh로 저장되게 된다. 모든 material 정보는 bufShipMaterial에 저장될 것이다. 그리고 질감의 수는 3개.

하지만 아직 그려지는 단계는 아닌데 만약 여기서 그냥 그리면 회색 질감의 물체만 나타나게 된다. 왜? material 사용을 셋팅 안해줬으니까...

2-2. Extract the material information for each subset and place them into material buffers
D3DMATERIAL9* materal;    // a pointer to a material buffer

// retrieve the pointer to the buffer containing the material information
D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)bufShipMaterial->GetBufferPointer();

// create a new material buffer for each material in the mesh
material = new D3DMATERIAL9[numMaterials];

for(DWORD i = 0; i < numMaterials; i++)    // for each material...
{
    material[i] = tempMaterials[i].MatD3D;    // get the material info...
    material[i].Ambient = material[i].Diffuse;    // and make ambient the same as diffuse
}

허걱, 코드를 보라. 뭔 말인가 알겠나? 어려워 보이지만 뜯어보자.

이코드가 기본적으로 하는 일은 material information이 저장되어 있는 곳의 포인터를 얻어와서 각각의 material 을 분리된 material object로 옮기며, 렌더링을 준비한다.

D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)bufShipMaterial->GetBufferPointer();

자 위에 코드는 뭣하는 놈인가... GetBufferPointer() 함수는 단순한게 버퍼의 주소를 리턴한다. 조금 이상한게 있다면 왜 굳이 함수를 이용해서 주소값을 대입하느냐 이다. 사실 캐스팅 연산자를 이용했다면 이미 좌변과 같은 타입이니 바로 대입해도 될텐데? 흠.... 아마도 로딩할때 인자값으로 들어가는 bufShipMaterial의 구조나 처리 방식이 조금 다른가 보다.

material = new D3DMATERIAL9[numMaterials];

여기서는 서브셋의 갯수만큼 material을 저장할 버퍼를 만든다.  

material[i] = tempMaterials[i].MatD3D;

여기서 material information을 임시 재질 버퍼로 부터 복사하여 새로운 material 변수에 (버퍼에) 가져다 복사한다.

material[i].Ambient = material[i].Diffuse;

아, 기본적으로 로딩된 메쉬는 ambient material 정보를 가지고 있지 않은데 만약 이걸 셋팅 안해주면 어두운 주변광이 설정되므로 어둡게 보이게 된다. 고로 diffuse 색상과 같에 만들어줘서 잘보이게 만든다.

2-3. Set the materials and draw the subsets

이제 그릴차례? 

for(DWORD i = 0; i < numMaterials; i++)    // loop through each subset
{
    d3ddev->SetMaterial(&material[i]);    // set the appropriate material for the subset
    meshSpaceship->DrawSubset(i);    // draw the subset
}


서브셋의 갯수만큼 루프를 돌면서 각 루프마다 각 재질 선택후 서브셋을 그려준다. 끝~

3. The Finished Program

// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>

// define the screen resolution and keyboard macros
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// include the Direct3D Library files
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")

// global declarations
LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev;    // the pointer to the device class
LPDIRECT3DVERTEXBUFFER9 t_buffer = NULL;    // the pointer to the vertex buffer
LPDIRECT3DSURFACE9 z_buffer = NULL;    // the pointer to the z-buffer

// mesh declarations
LPD3DXMESH meshSpaceship;    // define the mesh pointer
D3DMATERIAL9* material;    // define the material object
DWORD numMaterials;    // stores the number of materials in the mesh


// function prototypes
void initD3D(HWND hWnd);    // sets up and initializes Direct3D
void render_frame(void);    // renders a single frame
void cleanD3D(void);    // closes Direct3D and releases memory
void init_graphics(void);    // 3D declarations
void init_light(void);    // sets up the light and the material

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = L"WindowClass1";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL,
                          L"WindowClass1",
                          L"Our Direct3D Program",
                          WS_EX_TOPMOST | WS_POPUP,
                          0, 0,
                          SCREEN_WIDTH, SCREEN_HEIGHT,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

    ShowWindow(hWnd, nCmdShow);

    // set up and initialize Direct3D
    initD3D(hWnd);

    // enter the main loop:

    MSG msg;

    while(TRUE)
    {
        DWORD starting_point = GetTickCount();

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        render_frame();

        // check the 'escape' key
        if(KEY_DOWN(VK_ESCAPE))
            PostMessage(hWnd, WM_DESTROY, 0, 0);

        while ((GetTickCount() - starting_point) < 25);
    }

    // clean up DirectX and COM
    cleanD3D();

    return msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;
    }

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


// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = FALSE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;
    d3dpp.EnableAutoDepthStencil = TRUE;    // automatically run the z-buffer for us
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;    // 16-bit pixel format for the z-buffer

    // create a device class using this information and the info from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    init_graphics();    // call the function to initialize the triangle
    init_light();    // call the function to initialize the light and material

    d3ddev->SetRenderState(D3DRS_LIGHTING, TRUE);    // turn on the 3D lighting
    d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);    // turn on the z-buffer
    d3ddev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(50, 50, 50));    // ambient light

    return;
}


// this is the function used to render a single frame
void render_frame(void)
{
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();

    // SET UP THE TRANSFORMS

    D3DXMATRIX matView;    // the view transform matrix
    D3DXMatrixLookAtLH(&matView,
    &D3DXVECTOR3 (0.0f, 4.0f, 8.0f),    // the camera position
    &D3DXVECTOR3 (0.0f, 0.0f, 0.0f),    // the look-at position
    &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));    // the up direction
    d3ddev->SetTransform(D3DTS_VIEW, &matView);    // set the view transform to matView

    D3DXMATRIX matProjection;    // the projection transform matrix
    D3DXMatrixPerspectiveFovLH(&matProjection,
                               D3DXToRadian(45),    // the horizontal field of view
                               SCREEN_WIDTH / SCREEN_HEIGHT,    // the aspect ratio
                               1.0f,    // the near view-plane
                               100.0f);    // the far view-plane
    d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);    // set the projection

    static float index = 0.0f; index+=0.03f;    // an ever-increasing float value
    D3DXMATRIX matRotateY;    // a matrix to store the rotation for each triangle
    D3DXMatrixRotationY(&matRotateY, index);    // the rotation matrix
    d3ddev->SetTransform(D3DTS_WORLD, &(matRotateY));    // set the world transform


    // draw the spaceship
    for(DWORD i = 0; i < numMaterials; i++)    // loop through each subset
    {
        d3ddev->SetMaterial(&material[i]);    // set the material for the subset
        meshSpaceship->DrawSubset(i);    // draw the subset
    }

    d3ddev->EndScene();

    d3ddev->Present(NULL, NULL, NULL, NULL);

    return;
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
    meshSpaceship->Release();    // close and release the spaceship mesh
    d3ddev->Release();    // close and release the 3D device
    d3d->Release();    // close and release Direct3D

    return;
}


// this is the function that puts the 3D models into video RAM
void init_graphics(void)
{
    LPD3DXBUFFER bufShipMaterial;

    D3DXLoadMeshFromX(L"spaceship 2.x",    // load this file
                      D3DXMESH_SYSTEMMEM,    // load the mesh into system memory
                      d3ddev,    // the Direct3D Device
                      NULL,    // we aren't using adjacency
                      &bufShipMaterial,    // put the materials here
                      NULL,    // we aren't using effect instances
                      &numMaterials,    // the number of materials in this model
                      &meshSpaceship);    // put the mesh here

    // retrieve the pointer to the buffer containing the material information
    D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)bufShipMaterial->GetBufferPointer();

    // create a new material buffer for each material in the mesh
    material = new D3DMATERIAL9[numMaterials];

    for(DWORD i = 0; i < numMaterials; i++)    // for each material...
    {
        material[i] = tempMaterials[i].MatD3D;    // get the material info
        material[i].Ambient = material[i].Diffuse;    // make ambient the same as diffuse
    }

    return;
}


// this is the function that sets up the lights and materials
void init_light(void)
{
    D3DLIGHT9 light;    // create the light struct

    ZeroMemory(&light, sizeof(light));    // clear out the struct for use
    light.Type = D3DLIGHT_DIRECTIONAL;    // make the light type 'directional light'
    light.Diffuse.r = 0.5f;    // .5 red
    light.Diffuse.g = 0.5f;    // .5 green
    light.Diffuse.b = 0.5f;    // .5 blue
    light.Diffuse.a = 1.0f;    // full alpha (we'll get to that soon)

    D3DVECTOR vecDirection = {-1.0f, -0.3f, -1.0f};    // the direction of the light
    light.Direction = vecDirection;    // set the direction

    d3ddev->SetLight(0, &light);    // send the light struct properties to light #0
    d3ddev->LightEnable(0, TRUE);    // turn on light #0

    return;
}



Image 2.3 - The Spaceship In Action

Image 2.3 - The Spaceship In Action
posted by 대갈장군