Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Beginning Direct3D

0.00/5 (No votes)
8 Mar 2004 1  
Start Direct3D programming and create a window

Introduction

In this tutorial I'm going to help you to begin learning Direct3D and create a Direct3D compatible window. I assume that you are familiar with Win32 Programming. I use Visual Studio.NET to do my works but you can use any C++ compiler, of course you need DirectX SDK 8.0 or higher for compiling the provided code that can download here (headers and libraries are sufficient).

The DirectX story begins in 1995 when Microsoft was introducing Windows 95 to the world, but Windows 95 had many problems such as bad support for different hardware especially graphics hardware. These problems caused most game programmers to use MS-DOS instead, but soon Microsoft provided Game SDK for Windows 95 developers to improve driver support and this project was later called DirectX. Between 1995 and 1998 Microsoft focused on DirectX and many new features were added to DirectX and about six different versions were released during that three year period, and at last they released Windows 98 which was well setup to support DirectX and made programming easier for game developers, so that almost all of them moved their projects to support DirectX and that made Microsoft very happy!

DirectX is a software interface the Hardware Abstraction Layer(HAL) layered on top of the standard hardware-specific drivers and hardware. DirectX creates a common hardware independent layer providing support of many different types of hardware. When using DirectX you write your program on a single hardware and DirectX will guarantee that your program will be run on all other DirectX compatible hardware components.

OK, enough about DirectX, Let's begin our work!

Getting started

If you use Microsoft Visual C++ create an empty Win32 project and in the Project Properties>Linker>Input>Additional Dependencies add d3d8.lib (main direct3d library), d3dx8.lib (extensions to the main library) and winmm.lib (windows multimedia library).

Then you can enter this code as a new source file:

#include <windows.h>

#include <d3d8.h>


HWND hWnd;
LPDIRECT3D8 pD3D;
LPDIRECT3DDEVICE8 pDevice; 

LRESULT CALLBACK WndProc( HWND hWnd , UINT message , 
    WPARAM wParam , LPARAM lParam){
    switch(message){ 
    case WM_CLOSE: 
        PostQuitMessage(0); 
        return 0; 
    } 
    return DefWindowProc(hWnd,message,wParam,lParam); 
}

bool CreateSimpWindow(int width, int height){

    // Create A Window Class Structure 

    WNDCLASSEX wc;
    wc.cbClsExtra = 0;
    wc.cbSize = sizeof(wc); 
    wc.cbWndExtra = 0; 
    wc.hbrBackground = NULL;
    wc.hCursor = NULL; 
    wc.hIcon = NULL; 
    wc.hIconSm = NULL; 
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = "X3D"; 
    wc.lpszMenuName = NULL;
    wc.style = CS_VREDRAW|CS_HREDRAW|CS_OWNDC;

    // Register Window Class

    RegisterClassEx(&wc); 

    // Create Window 

    hWnd = CreateWindowEx(0, 
       "X3D", "X3D Direct3D Tutorial 1", 
       WS_OVERLAPPEDWINDOW|WS_VISIBLE, 100,100,width,height,
       NULL,NULL,wc.hInstance,0);

    return true;
}

bool InitD3D(){
    if((pD3D = Direct3DCreate8(D3D_SDK_VERSION))==NULL){
        return false;
    }
    D3DDISPLAYMODE d3ddm;
    pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.BackBufferFormat = d3ddm.Format;
    d3dpp.Windowed = true;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    if(FAILED(pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
    hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDevice))){
        return false;
    }
    return true;
} 

void RenderD3DScene(){
    pDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1,0);
    pDevice->Present(NULL,NULL,NULL,NULL);
} 

void MessageLoop(){
    MSG msg; 
    while(true){
        RenderD3DScene();
        if(PeekMessage(&msg,hWnd,0,0,PM_REMOVE)){
             if(msg.message==WM_QUIT)return;
             TranslateMessage(&msg); 
             DispatchMessage(&msg); 
        }
    } 
}

INT WINAPI WinMain( HINSTANCE , HINSTANCE , LPSTR , INT ){

    if(!CreateSimpWindow(400, 400))return 1;

    if(!InitD3D())return 2; 

    MessageLoop(); 

    return 0;
} 

If every thing goes right, this code will create a window with a black background. It may make you think that I can create a black window using much easier method, and of course you can however the goal here is to properly initialize Direct3D with a minimum amount of code and as simply as possible. In later tutorials you will do a lot more with Direct3D. To make life easier I decomposed the code into several functions: CreateSimpWindow, MessageLoop, WndProc, Init3D (Initialize Direct3D), and Render3DScene (Render Direct3D Scene). In this tutorial only the Direct3D functions are explained.

We begin with line two where we included the Direct3D 8 header file: d3d8.h, then we defined two new variables:

LPDIRECT3D8 pD3D;
LPDIRECT3DDEVICE8 pDevice; 

pD3D is a pointer to IDirect3D8 interface that is really a COM object and you can create other Direct3D interfaces such as IDirect3DDevice using it.

Now, let's take a look at the InitD3D function:

if((pD3D = Direct3DCreate8(D3D_SDK_VERSION))==NULL){
   return false;
}

Direct3DCreate8 Macro easily creates an IDirect3D8 interface for us, the only parameter required is D3D_SDK_VERSION which is a macro specifying the Direct3D version of the header to ensures that Direct3D header and library files are compatible. Now we have an IDirect3D8 object, but to begin drawing we need a IDirect3DDevice8 object. IDirect3D8:CreateDevice creates an IDirect3DDevice8 object for us, but first we should provide it's arguments:

D3DDISPLAYMODE d3ddm;
pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

Here we created an object of D3DDISPLAYMODE structure and then fill it with default graphics adapter display mode. After running these commands, d3ddm will contains some information about desktop display settings such as resolution, color depth, refresh frequency (you can customize these values and this will be explained further in later tutorials) and then we create an object of D3DPRESENT_PARAMETERS structure:

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

First we make it empty by calling the ZeroMemory macro (this macro simply fill all of allocated memory with zero values), then we set buffer depth color to the same as the desktop depth color, and we specify that program will run in a window, and finally we specify D3D_SWAPEFFECT_DISCARD which makes Direct3D to fill buffer with random values after beginning to draw (This option usually used for debug purposes to ensure were are properly clearing the screen). We did all of this to prepare d3dpp for passing to the IDirect3D8:CreateDevice function as the fifth argument:

if(FAILED(pD3D->CreateDevice( D3DADAPTER_DEFAULT,
   D3DDEVTYPE_HAL, hWnd,
   D3DCREATE_SOFTWARE_VERTEXPROCESSING,
   &d3dpp, &pDevice))){
   return false;
}

First parameter specifies that our IDirect3DDevice8 object uses default adapter. Second parameter is device type, that we set to D3DDEVTYPE_HAL, it makes device to be created in hardware abstraction layer as opposed to D3DDEVTYPE_REF that causes to create buffers in a software layer that is slower than hardware but that supports the full feature set this is used for debugging purposes. Next parameter is simply a handle to our window and fourth parameter specifies that Direct3D should process all vertex operations in software, this could also be D3DCREATE_HARDWARE_VERTEXPROCESSING for hardware vertex processing that is usually faster however supported on fewer cards. The fifth parameter specifies the device display mode that we use via d3dpp as well as defining other properties and at last pDevice is the pointer used to hold the created Direct3D device. OK, Direct3D should now be initialized successfully, now time for Drawing with the RenderD3DScene function

pDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1,0);
pDevice->Present(NULL,NULL,NULL,NULL);

First we fill color buffer with black using the IDirect3DDevice8:Clear() function, first parameter is number of regions that we want to clear, setting that to 0 specifies that we want entire buffer to be cleared. Second parameter is the pointer to the array of rectangles that we want to clear, specifying 0 as first parameter make this parameter be ignored so we use NULL. Next parameter specifies the buffer type, in later tutorials you will learn about other types of buffers, and the fourth parameter specifies the color, you can use D3DCOLOR_XRGB(r,g,b) macro to calculate your desired color in this case 0 is black, the last two parameters used are depth and stencil buffers that you will learn later for now just use the default values. Then we present the buffer to the screen using the Present function for now just use NULL for the parameters they will be explained more in later tutorials.

Conclusion

OK, compile the code and say hello to the Direct3D programming world! If any thing seems complicated don't worry about it at the moment, you will be familiar with Direct3D soon, just continue to next tutorials and you could also try changing some of the values, if you read more about the explained functions in the DirectX documentations it will really help you to understand how the program works. And finally feel free to contact me if you have any questions. Good Luck!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here