home

Tutorial Index

DirectX tutorial 1

            To begin with, create a new Win32 application project. What we will do in this tutorial is initialize DirectX, use a continuous game loop, and set DirectX up to render a blue screen, which is essentially the background of an empty scene.

            Before we begin, we need to add the libraries of DirectX. To do this in Visual Studio 2010, right click on the project (beneath the Solution) and select properties. Go to Linker->Input and select the Additional Dependencies field. Click it again, and select edit. Add d3d9.lib and click ok and Apply. If you cannot get it to work, open the sample from the link at the end of the tutorial and paste your code over it’s code.

            Include d3d9.h. Now you are ready to write DirectX.

            First off, we need a pointer to the DirectX object. The DirectX object contains all the functions and constants needed, and is essentially a .dll file. .dll files, or dynamic link libraries, contain a table with the address of all the functions within them, and always contain all the functions of older versions of the file, thus they are backwards compatible. The pointer is defined as
LPDIRECT3D9 pD3D;
pD3D is the name we gave to it. As you probably know from the Win32 tutorials, LP stands for Long Pointer. The 9 at the end shows which version we are using.

            Second, we need the device we will be using. Here, device is a bit more abstract than the physical device, the graphics card you use. The device refers more to the Hardware Abstraction Layer, or HAL, which allows you to emulate in software what functions are not available by the hardware. No need to worry about that, you won’t be writing any code on it any time soon.
The device is declared as such:
PLDIRECT2DDEVICE9 pd3dDevice;
You will again notice the 9 at the end. This is a general, untold rule.

            Now we have to define three functions: render(), cleanup() and initDirect3D(). You can name them as you wish, but these names are probably more standard. The render() function handles the rendering, this is where we draw our awesome blue background. The cleanup() function does the cleaning up needed before we close the program. To be honest, it is not that necessary, but it’s always better to have your back covered. initDirect3D() is the function that performs the initialization. This is the longest function, and as such, I’ll describe it last.

            Let’s start with render():

//Note: As always with code copy-pasting, use your IDE's automatic inlining
{
if(NULL == pd3dDevice) //if we don’t have a device
{
return; //we can’t draw
}
pd3dDevice->Clear(
0,
NULL,
D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0,0,255),
1.0f,
0);

pd3dDevice->Present(NULL, NULL, NULL, NULL);
}

            So, first we check if the device exists and has been properly initialized. If not, we abort. If it exists, we clear the buffers and the screen. The first two parameters, zero and null, are there so we clear the entire buffer and window. The third parameter indicates that it is to be completely cleared. The fourth parameter is the color to which it is cleared, which we set to Blue. Note that the Alpha channel is not passed, the X in XRGB indicates that we ignore it. The fifth parameter is the depth to which we will clear it. In the scene that is rendered, pixels get a depth value between 0 and 1, of type float. One reason you may not want to clear all the way to the back, would be so you don’t need to re-render a background that is in the distance, because it cannot have changed and you know that beforehand. The final value, which is again zero, concerns stencil masking. This is used to omit an area from the process, but since we do not have any stencil we can use, and we don’t really want or need any stencils so far, we pass zero to indicate that.
Finally, after we have cleared the scene, we present the window, meaning that we send the buffer to the screen. This is the final step of the rendering process, and all the rendering is done between these two functions. The parameters of this function refer to which part of the back buffer is to be printed on the screen, passing NULL to all prints the entire back buffer, which generally is what you want. For a while there will certainly be no need to print only a part of it.

            Next we have the cleanUp() function:

void cleanUp(void)
{
if(pd3dDevice != NULL)
{
pd3dDevice->Release();
}
if(pD3D != NULL)
{
pD3D->Release();
}
}

            This is an easy one. You check if the device and the DirectX object exist, and if they do, you delete them respectively, using their own functions. Considering how it’s normal for a game to crash (normal, so to speak, I mean it won’t ruin the machine), I’m confident nothing bad would happen if you omit this part, but this way the code is more elegant and presentable.

            And finally we have the last function, iniDirect3D:

 

bool initDirect3D()
{
pD3D = NULL;
pd3dDevice = NULL;

            //Create DirectX object
if(NULL == (pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
{
return false;
}

            HWND hWnd;

            getHandle:
hWnd = GetActiveWindow();
if(GetParent(hWnd) != NULL)
goto getHandle;

            D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferHeight = 480;
d3dpp.BackBufferWidth = 600;
d3dpp.hDeviceWindow = hWnd;

            if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pd3dDevice)))
{
return false;
}
return true;
}

Now, this is a long one. I have to note that every time I read through a tutorial or a book, something might be different. This code is as recommended by many tutorials, but what I added is the part which deals with hWnd. hWind was taken for granted in most tutorials, but it’s best to play it safe. Let’s see the code:
We nullify the device and DirectX object. After that we try to create an object of version equal to that of the SDK, if that fails, we return.
Assuming we haven’t failed, we better get the handle to the active window beforehand. To do that, we request a handle to the active window with GetActiveWindow(). However, this returns a handle to an active window, which may be a child to another. Thus we check if it has a parent, if it does, we request for another one (the GetParent() part and the goto call).
Now that we have the window (I know, what if it fails to get it? Well, it shouldn’t, this is too fundamental an element of the Win32 to fail), we have to set the parameters under which the application will be running. These are of type D3DPRESENT_PARAMETERS and we store them in d3dpp. We set all the memory of d3dpp to zero, with ZeroMemory(&address, int length). Obviously we give it the location and length of d3dpp.
The next few lines are relatively straightforward. We set the windowed mode to true (thus it’s not fullscreen), when we swap the buffers we discard the old front buffer to write on it, we set an “UNKNOWN” format to the back buffer (it’s a good default format that keeps the specifics out of the way). We tell it to use one back buffer after that. This means that there will be one buffer we draw to and present in the next frame, other than the buffer used for drawing the current frame. The optimal technique when you are not sure if you can render two frames between two updates is triple buffering, which always has one more current frame ready to print, and is drawing on an older one, thus you always have a frame ready. Multiple buffers are used when you need to go back to a previous scene, for example when you do a replay. Chances are that NVidia ShadowPlay technology is based on a large number of buffers. Finally, you set the width and height of the screen and then pass it the window handle I so artistically found earlier.
In the end, we see if creating a device fails. The device is created through the DirectX object, and we tell it to use the default adapter, the reference device type (used during development for easier debugging and measuring of performance), the handle to the window it will be using, that we’ll be using vertex processing so it should have the software for that ready (in case the hardware does not support it), and finally we pass by reference the present parameters and the address of the pointer that will point to the device, so the pointer will be updated.

Now we have to call these functions in the code that runs, which is, as you’ll remember from Win32, _tWinMain (or similar). Before the existing while() loop, add

if( !initDirect3D() )
{
return false;
}

so that the program terminates if we fail to create a Direct3D device and object. Accordingly, modify the contents of the while() loop so they are like this:

while (true)
{
if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
render();
}
}

The entire loop should be just like the one we defined in the corresponding Win32Api tutorial, it checks if there is a message but doesn’t wait for it. If there is no message, we call render() to show our beautiful blue background. Later on we’ll also call the application’s main update() method.

You can download the project here.

You have now set up DirectX and it is functioning. In the next tutorial we will load and display a bitmap. I will also give simple code to make it move a bit, though I will not document it, as it has been documented in the Win32 message handling tutorials.

Note: I forgot to empty the while() from the GetMessage() function, just empty it and write "true" inside (without the quotes). Otherwise you'll have to close the program through the task manager.