블로그 이미지
대갈장군

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

Notice

2008. 2. 16. 06:34 프로그래밍/DirectX
사실 꼭 필요한 부분은 이제부터 나온다고 생각한다. 과연 Mesh란 무엇인가? 알다시피 지금까지는 정말 무식하게 점을 사용하여 물체를 표현하고 점들을 이어서 면을 표현했다. 하지만 만약 3D MAX같은 프로그램을 사용해봐라... 얼마나 멋진 물체를 만들수 있나...

하지만 그것을 점으로 다 표현한다면? 불가능하다. 고래서 필요한 간단한 방법이 있으니 바로 C++ 객체를 이용하여 이것들을 하나의 패키지로 묶어서 표현하는 것이고 그것이 바로 mesh다.

메쉬에 대한 간단한 정의를 했으니 이지 이것이 뭣하는 놈인지 슬슬 살펴보자.

1. What is the Mesh?
간단하게 말하자면 Mesh는 3D Model이다. 아,,,,,, 하하.. 탄식이 나오는 구나. 다음줄에서 바로 그것이 무엇인지 알려준다.

보다 정확하게 말하자면 3차원 모델에 대한 모든 정보를 담고 있는 파일이다. 그것은 vertices와 texture 그리고 framed animation을 가지고 있다. 매우 유용한 사용 툴을 갖추고 있어서 예전처럼 무식하게 점으로 찍어서 표현할 일은 더이상 없다고 봐야 한다.

한개의 모델은 주로 3D modeling program에서 만들어지고 .x 파일로 출력되어 나온다... 즉, .x 파일은 그저 3차원 모델을 저장한 파일일 뿐이라는 말... (졸라 대단한 건줄 알았네.. 헐) 일단 이 파일이 불러들여져서 메모리상에 올라가면 한개의 함수호출로 즉각 호출이 가능하다...

Image 1.1 - Meshes and Their Contents

Image 1.1 - Meshes and Their Contents

2. Making Preconstructed Meshes
슬슬 재미있어지기 시작하는 구나. 완전히 멋지게 그려진 3차원 물체를 불러와서 멋지게 그려내기 전에 간단한 것부터 배워보자. OpenGL에서도 확장형 함수로 간단한 물체를 그리는 함수가 있었는데 바로 DX에도 그런게 있다. 이것을 Preconstructed mesh라고 하는 구나.

일단 그런 것들을 그려주기 위해서는 다음과 같은 일련의 과정을 공통적으로 거쳐야 하나본데,

1. Creating the Mesh
2. Drawing the Mesh
3. Closing the Mesh

헐헐... 매우 간단하게 설명하고 있구나...

2-1. Creating the Mesh
일단 만들어야 쓸거 아니니.... 물론 여러가지 형태를 만들수 있는 다양한 preconstructed mesh 함수가 있으나 여기서는 주전자 만드는 함수를 살펴보자.

Image 1.2 - The Mythical Teapot Pipe-Joint

Image 1.2 - The Mythical Teapot Pipe-Joint

3D MAX에서 많이 본거다. 어라.. 근데 윈도우 스크린 세이버에서 가끔 저게 등장한다고라? 정말이냐? 허허...

하지만 이건 윈도우 95 98에서만 잠깐 나타난 현상이라는 군... 헐헐헐

HRESULT D3DXCreateTeapot(LPDIRECT3DDEVICE9 pDevice,
                         LPD3DXMESH *ppMesh,
                         LPD3DXBUFFER *ppAdjacency);

바로 위의 함수가 주전자 만드는 함수다. 각각의 인수를 한번 살펴 볼까나.

LPDIRECT3DDEVICE9 pDevice : This is simply the pointer to the Direct3D device.  We'll just put d3ddev here.

LPD3DXMESH *ppMesh : This is a pointer to a pointer to a mesh.  Whoa!

이놈이 메쉬에 대한 포인터라는데.. 왜 후아!? 지금은 이놈에 대해서 무시하자고 하네. 이것은 정의된 포인터의 위치에 메쉬를 가져다 저장하는 것인데... 일단은 남겨두고 다음 으로 넘어가자.

LPD3DXBUFFER *ppAdjacency : This one is advanced, so we'll leave it out for now.  Just set it to NULL.

LPD3DXMESH meshTeapot;    // define the mesh pointer

D3DXCreateTeapot(d3ddev, &meshTeapot, NULL);    // create the teapot


Image 1.3 - The Rendered Utah Teapot
Image 1.3 - The Rendered Utah Teapot

이렇게 해주면 그려진다는 구만. 이외에도 몇가지 더 있는데... 뭐뭐가 있나하면...

2-1-1. The Rectangular Solid
사각형이네.

HRESULT D3DXCreateBox(LPDIRECT3DDEVICE9 pDevice,
                      FLOAT Width,
                      FLOAT Height,
                      FLOAT Depth,
                      LPD3DXMESH *ppMesh,
                      LPD3DXBUFFER *ppAdjacency);

첫번째 인자는 계속 봤던 거고, 두번째 세번째 네번째는 사각형의 크기 지정하는 것이고 ppMesh가 또 나왔는데 메쉬에 대한 포인터라는 것만 언급할뿐 다른 이야기는 없다.

마지막것도 역시나 advanced이므로 언급하지 않았다. 다만 NULL을 넣을뿐.

사용방법을 보면,

LPD3DXMESH meshBox;    // define the mesh pointer

D3DXCreateBox(d3ddev, 7.0f, 3.0f, 3.0f, &meshBox, NULL);    // create a box

메쉬 포인터를 만든다음 d#dXCreateBox 함수를 사용하면 끝이네. 근데 직접 그려주는건 어디서 하는걸까? 위치를 잡아야 할 것 아닌감?

Image 1.4 - The Rectangular Solid In Action
Image 1.4 - The Rectangular Solid In Action

2-1-2. The Cylinder
요번것은 실린더. 둥근 원통인데.

 HRESULT D3DXCreateCylinder(LPDIRECT3DDEVICE9 pDevice,
                           FLOAT Radius1,
                           FLOAT Radius2,
                           FLOAT Length,
                           UINT Slices,
                           UINT Stacks,
                           LPD3DXMESH *ppMesh,
                           LPD3DXBUFFER *ppAdjacency);

내나 위에서 본 사각형 만드는 것과 비슷하고 OpenGL에서도 사용했던 확장 함수와 유사하게 slices와 stack을 설정하는 옵션이 존재한다.

첫번째 인자는 두말하면 잔소리고 원주 1과 원주 2가 있는데 위 아래 뚜껑의 원주를 말한다. 길이는 당연히 원통의 길이를 말하는 것이고 slices는 원통의 둘레를 따라 둥그스름하게 표현하는 정도를 설정하는 것이고 stack은 원통의 높이에 따라서 몇개로 쪼갤것이냐고 묻는 것이다.

ppMesh는 역시나 포인터이고, 마지막은 advanced이라서 여기선 언급 안한다.

LPD3DXMESH meshCylinder;    // define the mesh pointer

D3DXCreateCylinder(d3ddev, 1.5f, 1.5f, 7.0f, 20, 10, &meshCylinder, NULL);



Image 1.5 - The Cylinder In Action
Image 1.5 - The Cylinder In Action

2-1-3. The Sphere
이제는 구
HRESULT D3DXCreateSphere(LPDIRECT3DDEVICE9 pDevice,
                         FLOAT Radius,
                         UINT Slices,
                         UINT Stacks,
                         LPD3DXMESH *ppMesh,
                         LPD3DXBUFFER *ppAdjacency);다.

여기서 나오는 것은 설명할 필요도 없네. 다 앞에서 설명한 것이다.


LPD3DXMESH meshSphere;    // define the mesh pointer

D3DXCreateSphere(d3ddev, 2.0f, 20, 10, &meshSphere, NULL);    // create a sphere

Image 1.6 - The Sphere In Action

Image 1.6 - The Sphere In Action

2-2. Drawing the Mesh
이제 메쉬를 만들었으니 그려야 할 차례... 그리는것은 상당히 쉽다고 하는데.... 심지어 vertex buffer를 그리는 것보다 쉬우니..

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

이 다음장에서 왜 저기에 0이 들어갔는지 설명해 준대...

2-3. Closing the Mesh
꼭 해줘야 할게 있으니 바로 메쉬를 닫는 것이다. 이것은 다른 모든 자원을 리턴하는 함수인 cleanD3D에서 해주면 끝~

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

    return;
}

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

// mesh declarations
LPD3DXMESH meshTeapot;    // define the mesh pointer

// 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"WindowClass";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL, L"WindowClass", 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;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

    // 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 the view transform
    D3DXMATRIX matView;    // the view transform matrix
    D3DXMatrixLookAtLH(&matView,
    &D3DXVECTOR3 (0.0f, 3.0f, 6.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

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

      // set the world transform
    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 teapot
    meshTeapot->DrawSubset(0);


    d3ddev->EndScene();

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

    return;
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
    meshTeapot->Release();    // close and release the teapot 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)
{
    D3DXCreateTeapot(d3ddev, &meshTeapot, NULL);    // create the teapot

    return;
}


// this is the function that sets up the lights and materials
void init_light(void)
{
    D3DLIGHT9 light;    // create the light struct
    D3DMATERIAL9 material;    // create the material 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

    ZeroMemory(&material, sizeof(D3DMATERIAL9));    // clear out the struct for use
    material.Diffuse.r = material.Ambient.r = 1.0f;    // set the material to full red
    material.Diffuse.g = material.Ambient.g = 1.0f;    // set the material to full green
    material.Diffuse.b = material.Ambient.b = 1.0f;    // set the material to full blue
    material.Diffuse.a = material.Ambient.a = 1.0f;    // set the material to full alpha

    d3ddev->SetMaterial(&material);    // set the globably-used material to &material

    return;
}



Image 1.7 - The Teapot In Action

Image 1.7 - The Teapot In Action




posted by 대갈장군