Full Version: Introduction to DirectX
Dream.In.Code > Programming Tutorials > C++ Tutorials
KYA
We see thread after thread around here on what language to use to make a game or how to program the next halo. The truth is, any language can be used to program a video game (in which case it is simply a loop with animations, user input, sound, etc...) to create an awesome (and sometimes mediocre) interactive experience.

In order to have the computer draw awsome images for you, you need a framework. There are several APIs (Application programming interfaces) out there for game/graphics programming. Some include OpenGL, Allegro, Dark GDK, and of course DirectX. This is microsoft's API that they developed and that has pretty much dominated the commercial video game API scene (especially on PC's.) Since this is a tutorial on some beginning use of DirectX on a PC, I won't be going into MS's XNA or other game programming API's since:

a. they are closely guarded secrets
b. I don't know sqaut about them tongue.gif

Alright so let's delve into some fun!

(This tutorial assumes you know how to set up a basic windows app including WinMain, callback, etc...)

DirectX relates to your hardware and software in this manner:

Application --> Direct3D --> HAL (Hardware Abstraction Layer) --> Graphics Device (such as an Nvidia 8800 GTX, etc...)

Your application utilizes DirectX which takes care of the low level HAL and graphics device functions. Back in the earlier days each game/app coder had to write drivers for each type of device. Consider DirectX to be the C++ code instead of having everything in assembly (although it can be used to make algorithms more effcient, so on and so forht).

First, we must intialize DirectX in our program so we can use all of its goodies. This code utilizes a 'game loop' to illustrate where it would go in the code.

CODE

/* KYA DirectX tutorial
* 3-3-08
*/

//header files to include
#include <d3d9.h>
#include <time.h>

//application title
#define APPTITLE "Direct3D_Windowed"

//forward declarations
LRESULT WINAPI WinProc(HWND,UINT,WPARAM,LPARAM);
ATOM MyRegisterClass(HINSTANCE);
int Game_Init(HWND);
void Game_Run(HWND);
void Game_End(HWND);

//Direct3D objects
LPDIRECT3D9 d3d = NULL;
LPDIRECT3DDEVICE9 d3ddev = NULL;


//window event callback function
LRESULT WINAPI WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            Game_End(hWnd);
            PostQuitMessage(0);
            return 0;
    }

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


//helper function to set up the window properties
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    //create the window class structure
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);

    //fill the struct with info
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = (WNDPROC)WinProc;
    wc.cbClsExtra     = 0;
    wc.cbWndExtra     = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = APPTITLE;
    wc.hIconSm       = NULL;

    //set up the window with the class info
    return RegisterClassEx(&wc);
}


//entry point for a Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR     lpCmdLine,
                   int       nCmdShow)
{
    // declare variables
    MSG msg;

    // register the class
    MyRegisterClass(hInstance);

    // initialize application
    HWND hWnd;

    //create a new window
    hWnd = CreateWindow(
       APPTITLE,              //window class
       APPTITLE,              //title bar
       WS_OVERLAPPEDWINDOW,   //window style
       CW_USEDEFAULT,         //x position of window
       CW_USEDEFAULT,         //y position of window
       500,                   //width of the window
       400,                   //height of the window
       NULL,                  //parent window
       NULL,                  //menu
       hInstance,             //application instance
       NULL);                 //window parameters

    //was there an error creating the window?
    if (!hWnd)
      return FALSE;

    //display the window
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    
    //initialize the game
    if (!Game_Init(hWnd))
        return 0;


    // main message loop
    int done = 0;
    while (!done)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            //look for quit message
            if (msg.message == WM_QUIT)
            {
                MessageBox(hWnd, "Received WM_QUIT message", "WinMain", MB_OK);
                done = 1;
            }

            //decode and pass messages on to WndProc
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
            //process game loop (else prevents running after window is closed)
            Game_Run(hWnd);
    }

    return msg.wParam;
}


int Game_Init(HWND hwnd)
{
    //display init message
    MessageBox(hwnd, "Program is about to start", "Game_Init", MB_OK);

    //initialize Direct3D
    d3d = Direct3DCreate9(D3D_SDK_VERSION);
    if (d3d == NULL)
    {
        MessageBox(hwnd, "Error initializing Direct3D", "Error", MB_OK);
        return 0;
    }

    //set Direct3D presentation parameters
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

    //create Direct3D device
    d3d->CreateDevice(
        D3DADAPTER_DEFAULT,
        D3DDEVTYPE_HAL,
        hwnd,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING,
        &d3dpp,
        &d3ddev);

    if (d3ddev == NULL)
    {
        MessageBox(hwnd, "Error creating Direct3D device", "Error", MB_OK);
        return 0;
    }

    //set random number seed
    srand(time(NULL));

    //return okay
    return 1;
}

void Game_Run(HWND hwnd)
{
    //make sure the Direct3D device is valid
    if (d3ddev == NULL)
        return;

    //clear the backbuffer to black
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0), 1.0f, 0);
    
    //start rendering
    if (d3ddev->BeginScene())
    {
        
    
        //stop rendering
        d3ddev->EndScene();
    }

    //display the back buffer on the screen
    d3ddev->Present(NULL, NULL, NULL, NULL);

}

void Game_End(HWND hwnd)
{
    //display close message
    MessageBox(hwnd, "Program is about to end", "Game_End", MB_OK);

    //release the Direct3D device
    if (d3ddev != NULL)
        d3ddev->Release();

    //release the Direct3D object
    if (d3d != NULL)
        d3d->Release();
}


Wow, that was alot of code! Let's break it down:

First, you need to link d3d9.lib to your project. Feel free to do it under your project settings or pragma it in whatever you want. Secondly, I compiled all the code in this tutorial using MSVS 2005. Provided you have the DirectX SDK and know your way around your compiler, this should compile for darn near everything.

OK Let's look at the windows portions first:

CODE

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR     lpCmdLine,
                   int       nCmdShow)
{
    // declare variables
    MSG msg;

    // register the class
    MyRegisterClass(hInstance);

    // initialize application
    HWND hWnd;

    //create a new window
    hWnd = CreateWindow(
       APPTITLE,              //window class
       APPTITLE,              //title bar
       WS_OVERLAPPEDWINDOW,   //window style
       CW_USEDEFAULT,         //x position of window
       CW_USEDEFAULT,         //y position of window
       500,                   //width of the window
       400,                   //height of the window
       NULL,                  //parent window
       NULL,                  //menu
       hInstance,             //application instance
       NULL);                 //window parameters

    //was there an error creating the window?
    if (!hWnd)
      return FALSE;

    //display the window
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    
    //initialize the game
    if (!Game_Init(hWnd))
        return 0;


    // main message loop
    int done = 0;
    while (!done)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            //look for quit message
            if (msg.message == WM_QUIT)
            {
                MessageBox(hWnd, "Received WM_QUIT message", "WinMain", MB_OK);
                done = 1;
            }

            //decode and pass messages on to WndProc
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
            //process game loop (else prevents running after window is closed)
            Game_Run(hWnd);
    }

    return msg.wParam;
}


Here we set up our window, various properties, so that we can color, paint, etc... on our window later.

Assuming that everyone understands how windows programs are set up let's discuss the game functions called using some directX features:

Game_Init sets up our 'game' using directX. Notice the DX9 intialization. Usually this creates a pointer to a dirextX9 object that will be referenced throughout the life of a program:

CODE

//initialize Direct3D
    d3d = Direct3DCreate9(D3D_SDK_VERSION);
    if (d3d == NULL)
    {
        MessageBox(hwnd, "Error initializing Direct3D", "Error", MB_OK);
        return 0;
    }


In case there is some sort of error we have the message box tell us so we don't pull our hair out thinking it is a software issue. The most important part of all this code is this:

CODE

//create Direct3D device
    d3d->CreateDevice(
        D3DADAPTER_DEFAULT,
        D3DDEVTYPE_HAL,
        hwnd,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING,
        &d3dpp,
        &d3ddev);


At this point, we won't delve into all of these various parameters, but know that there are many different routes we can take with DX9 objects, and can custom tailor it to our needs (such as bump mapping, lighting, texturing, etc...)

Next we have the Game_Run function:

CODE

void Game_Run(HWND hwnd)
{
    //make sure the Direct3D device is valid
    if (d3ddev == NULL)
        return;

    //clear the backbuffer to black
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0), 1.0f, 0);
    
    //start rendering
    if (d3ddev->BeginScene())
    {
        
    
        //stop rendering
        d3ddev->EndScene();
    }

    //display the back buffer on the screen
    d3ddev->Present(NULL, NULL, NULL, NULL);

}


This is the function that is ran every 'loop' of the program. It is barebones here -- i.e. it does much of nothing, but illustrates how to use directx in a possible game program. the D3D device renders the scene, then ends the scene each time the method is ran. Consecutive images appear as animations to the human eye and thus we are entertained. See this segment:

CODE

//clear the backbuffer to black
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0), 1.0f, 0);


Just like the WinAPI functions such as BitBLT, DirectX draws to an offscreen buffer before the image is sent to your screen (which could be a hardware tutorial all on its own).

After creating objects/devices you must clean up after them! Forgetting to do so can lead to a nasty memory leak later on down the road:

CODE

void Game_End(HWND hwnd)
{
    //display close message
    MessageBox(hwnd, "Program is about to end", "Game_End", MB_OK);

    //release the Direct3D device
    if (d3ddev != NULL)
        d3ddev->Release();

    //release the Direct3D object
    if (d3d != NULL)
        d3d->Release();
}


When running the program, a small window should pop up with a message box that states the 'game is about to start. The window should turn a shade of green and then when you press the 'X' in the upper right corner a message box will state the the 'game' is ending. Hopefully this helped you on your quest to learn more about directX. This barely scratched the surface on what directX can do or what you can do with its features. If this is somewhat well received I'll sit down and write a few more on vertex/matrix processing, some shaders, etc... We can write our D3D functions to create a reusable framework in applications over and over. Never reinvent the wheel!

--KYA
born2c0de
Nice Work. icon_up.gif
bhandari
that's quite a different topic touched.
thanks for that!!!
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2008 Invision Power Services, Inc.