블로그 이미지
대갈장군

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. 20. 01:32 프로그래밍/DirectX
어느새 mesh 마지막 강의군... 일반적으로 메쉬는 .x 파일안에 텍스쳐를 포함하고 있단다. (그랬어?)
하지만 사용하기 위해서는 프로그램에서 약간의 코드를 가미해 줘야 하는데...

1. Loading and Displaying Textures
자, 아래 그림을 보자. 텍스쳐가 입혀지지 않은 비행기다. 이 .x 파일을 바로 전 수업에 배운대로 해보면 아래처럼 나타나게 된다.

Image 3.1 - The Airplane 2, Untextured

Image 3.1 - The Airplane 2, Untextured

근데 이것을 DirectX가 제공하는 메쉬 뷰어로 보면 아래그림처럼 텍스쳐가 입혀져서 나오게 된다.

Image 3.2 - The Airplane 2, Textured

Image 3.2 - The Airplane 2, Textured

그말인 즉슨, 텍스쳐가 존재하나 코딩이 안되었다는 말.. 그렇다면 어떻게 코딩하는지 살펴보자...는 말.

두가지를 해줘야 하는데,
1. Load the textures while loading the mesh
2. Set the subest's texture before each subset is drawn

1-1. Load the textures while loading the mesh
우선 메쉬를 로딩할때 텍스쳐도 같이 로딩해줘야 한다는 말인데. 당연한 이야기 처럼 들린다. 메쉬를 로딩할때 텍스쳐도 같이 로딩할수 있는데 (만약 텍스쳐가 존재 한다면), 우리의 업무는 extract it, read it, and load the texture다.

D3DMATERIAL9* materal;    // a pointer to a material buffer
LPDIRECT3DTEXTURE9* texture;    // a pointer to a texture

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

// create a new material buffer and texture for each material in the mesh
material = new D3DMATERIAL9[numMaterials];
texture = new LPDIRECT3DTEXTURE9[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
    USES_CONVERSION;    // allows certain string conversions
    // if there is a texture to load, load it
    if(FAILED(D3DXCreateTextureFromFile(d3ddev,
                                        CA2W(tempMaterials[i].pTextureFilename),
                                        &texture[i])))
        texture[i] = NULL;    // if there is no texture, set the texture to NULL

}

자 위가 새롭게 바뀐 코드이고 굵은 글자가 바뀐 부분이다. 하나하나 보자꾸나.

LPDIRECT3DTEXTURE9* texture : 이것은 텍스쳐에 대한 포인터이다. 전역 변수로 선언되어야 함.

texture = new LPDIRECT3DTEXTURE9[numMaterials] : 각각의 재질은 각각의 텍스쳐 버퍼를 가지는데 위에 비행기 그림을 보면 텍스쳐가 없는 서브셋도 존재한다. 그런 경우에도 텍스쳐 버퍼는 일단 만들어 놓는다. (비록 쓰지 않더라도)

USES_CONVERSION : 이건 매크로인데 우리가 특정한 string conversion을 할수있게 해주는 매크로랜다.. 바로 다음 줄에서 LPSTR을 LPCWSTR로 바꿔야 하는데 이것을 이 매크로가 가능케 해준다.

이 매크로는 atlbase.h에 있으므로 꼭 그 헤더 파일을 포함시켜야 한다.

if(FAILED(... : 이거 많이 본건데... RET_FAILED라고 본것 같기도 하고. 이것은 텍스쳐 이름이 해당 서브셋에 유효한가를 체크한다. 아까 본것처럼 텍스쳐가 없는 서브셋의 경우 이것이 실패로 처리될 것이고... 그게 실패한경우 바로 다음줄 명령인 texture[i] = NULL;이 실행되어 해당 서브셋의 텍스쳐는 없는 걸로 저장된다.

D3DXCreateTextureFromFile() : 이건 이미 본적이 있는 거란다. 단순하게 파일로 부터 텍스쳐를 로딩하는 것인데 첫번째 인자는 Direct3D Device인 d3ddev이고 두번째 인자는 파일이름이다. 각각의 서브셋에 해당하는 텍스쳐 파일 이름은 tempMaterials[i].pTextureFilename에 있다. 하지만 이 파일 이름은 LPSTR 형인데 이 형은 Visual C++ 2005에서는 사용 할 수가 없단다. 그래서 LPCWSTR로 바꿔야 하는데 이때 필요한 함수가 바로 CA2W라는 함수랜다..... (젠장할 유니코드) 고로 다음과 같이 써주면 OK

CA2W(tempMaterials[i].pTextureFilename),

세번째 인자는 어디다 저장 할 건지 알려주는 것.

1-2. Set the subset's texture before each subset is drawn
이제 해야 할 일은 각각의 서브셋이 그려지기 직전에 해당 텍스쳐로 착착 바꿔주는 거다. 어떻게 하느냐면,

// draw the spaceship
for(DWORD i = 0; i < numMaterials; i++)    // loop through each subset
{
    d3ddev->SetMaterial(&material[i]);    // set the material for the subset
    if(texture[i] != NULL)    // if the subset has a texture (if texture is not NULL)
        d3ddev->SetTexture(0, texture[i]);    // ...then set the texture


    meshSpaceship->DrawSubset(i);    // draw the subset
}

해당 서브셋의 텍스쳐가 NULL이 아니라면 텍스쳐를 설정, 할당한다.

2. 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>
#include <atlbase.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
LPDIRECT3DTEXTURE9* texture;    // a pointer to a texture
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);

    // create the z-buffer
    d3ddev->CreateDepthStencilSurface(SCREEN_WIDTH,
                                      SCREEN_HEIGHT,
                                      D3DFMT_D16,
                                      D3DMULTISAMPLE_NONE,
                                      0,
                                      TRUE,
                                      &z_buffer,
                                      NULL);

    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, 8.0f, 16.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
        if(texture[i] != NULL)    // if the subset has a texture (if texture is not NULL)
            d3ddev->SetTexture(0, texture[i]);    // ...then set the texture


        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"airplane 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 and texture for each material in the mesh
    material = new D3DMATERIAL9[numMaterials];
    texture = new LPDIRECT3DTEXTURE9[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
        USES_CONVERSION;    // allows certain string conversions
        // if there is a texture to load, load it
        if(FAILED(D3DXCreateTextureFromFile(d3ddev,
                                            CA2W(tempMaterials[i].pTextureFilename),
                                            &texture[i])))
        texture[i] = NULL;    // if there is no texture, set the texture to NULL

      }

    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 Biplane In Action

posted by 대갈장군