Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
97 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

  Contents

 DirectX Graphics
 Basic Application
 Drawing Triangles
 Indexed Triangles
 Adding Texture
 Full Screen
 Graphics


 Get the source
 Printable version

 


Our Basic Application (dxtest.cpp and d3d1.cpp)

I will present several demos along the way. To make things simple, each of the demos will share the same basic application.

The base application I will start with is a simple Win32 skeleton that is around 90 lines long. All it does is create a window and call the DirectX functions that I will define a bit later. These functions are InitDirect3D, ShutdownDirect3D, and DrawScene. This shell can be used with all the sample demos I will work with in this article, so I separated it from the rest of the code. This code is nothing you can’t find in Petzold, so I won’t reproduce it here. It is included in the code pack that comes with this article.

All the demos share the same ShutdownDirect3D function and related variables:

#define HELPER_RELEASE(x) { if(x) { (x)->Release(); (x) = NULL; }} IDirect3D8 * pID3D = NULL; IDirect3DDevice8 * pID3DDevice = NULL; IDirect3DVertexBuffer8 * pStreamData = NULL; IDirect3DIndexBuffer8 * pIndexBuffer = NULL; IDirect3DTexture8 * pTexture = NULL; void ShutdownDirect3D() { HELPER_RELEASE(pTexture); HELPER_RELEASE(pIndexBuffer); HELPER_RELEASE(pStreamData); HELPER_RELEASE(pID3DDevice); HELPER_RELEASE(pID3D); }

First I declared all the interfaces I will use. Take note that not all the demos will use all the interfaces. They are simply included to simplify and unify the code base.

ShutdownDirect3D simply releases all these interfaces. In the future, you may need to add extra code to shutdown these interfaces, but for now this is all we will need.

Let us start our initialization code, which is located in the InitDirect3D function. InitDirect3D and DrawScene are functions that you will change as we go on, so be sure to experiment with them. IDirect3D is the first interface that you need to instantiate. To do this you write:

IDirect3D8 * pID3D = Direct3DCreate8(D3D_SDK_VERSION);

Unlike most DirectX methods, there is no return code that you need to check here. You do, however, need to check that your pointer is non-NULL before you reference it.

Your next step would usually be to create a device interface. You can’t do this until you first call the GetAdapterDisplayMode method to get some needed information:

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

This will get the parameters of the current display mode. The parameter you are looking for is the surface format. You can use this to build a D3DPRESENT_PARAMETERS structure:

D3DPRESENT_PARAMETERS present; ZeroMemory(&present, sizeof(present)); present.SwapEffect = D3DSWAPEFFECT_COPY; present.Windowed = TRUE; present.BackBufferFormat = d3ddm.Format;

D3DPRESENT_PARAMETERS describes information such as the format of a display’s surfaces, the type of swapping mechanism, and whether the app is windowed or full screen.

In this example, surface copying is used instead of page flipping because the app is windowed. The back buffer is set to match the surface of the current video mode. A surface represents an area that can be drawn upon. Surfaces have properties like resolution and color depth. It is important that our back buffer and our primary buffer match in these properties.

You can now create an IDirect3DDevice8 interface:

pID3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present, &pID3DDevice);

This function has six parameters, but luckily, none of them are complex. D3DADAPTER_DEFAULT tells Direct3D to use the primary monitor. This is only an issue if you are using a multi-monitor system. You can use a secondary monitor by specifying the number of the monitor you wish to use. Calling GetAdapterCount on the IDirect3D interface will return the count of adapters in the system.

The next parameter, D3DDEVTYPE_HAL, tells Direct3D to use the HAL for display purposes. Other options include D3DDEVTYPE_REF and D3DDEVTYPE_SW, which are the reference software rasterizer and a user specified software rasterizer respectively. Usually you will want to use D3DDEVTYPE_HAL, but you may want to use reference rasterizer for some testing purposes. You certainly should ship with the HAL version.

You then specify the focus window. For a full screen application, you need to make this a top-level window.

D3DCREATE_SOFTWARE_VERTEXPROCESSING specifies the type of vertex processing. You can also use hardware, or a combination, but I chose software for maximum compatibility. You will want to use hardware vertex processing if you want hardware assisted T & L.

The last two parameters are simple. You pass in the structure that you built above, and you are returned the IDirect3DDevice8 interface. If the method returns D3DERR_NOTAVAILABLE, then you passed in valid parameters, but the device does not support them.

The nicest part about this method is that it automatically creates all your needed back buffers and depth buffers. Clipping is automatically enabled, as is backface culling. Lighting is also enabled, and since you will specify our own vertex colors later, you want to disable this:

pID3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

That is the end of our InitDirect3D function. Let’s see it in its entirety:

HRESULT InitDirect3D(HWND hwnd) { pID3D = Direct3DCreate8(D3D_SDK_VERSION); HRESULT hr; do { // we need the display mode so we can get // the properties of our back buffer D3DDISPLAYMODE d3ddm; hr = pID3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm); if(FAILED(hr)) break; D3DPRESENT_PARAMETERS present; ZeroMemory(&present, sizeof(present)); present.SwapEffect = D3DSWAPEFFECT_COPY; present.Windowed = TRUE; present.BackBufferFormat = d3ddm.Format; hr = pID3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present, &pID3DDevice); if(FAILED(hr)) break; hr = pID3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE); } while(0); return hr; }

We now turn our attention to the DrawScene function. For our first exercise, I want to just get something on the screen. Once we get to this point, adding to it should be trivial.

This is the DrawScene function we will start with:

HRESULT DrawScene() { HRESULT hr; do { // clear back buffer hr = pID3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0,63,0,0), 0, 0); if(FAILED(hr)) break; // start drawing hr = pID3DDevice->BeginScene(); if(FAILED(hr)) break; // Put all drawing code here hr = pID3DDevice->EndScene(); if(FAILED(hr)) break; // flip back buffer to front hr = pID3DDevice->Present(NULL, NULL, NULL, NULL); } while(0); return hr; }

This code is pretty simple, if you look beyond all the error handling. Clear will flood fill the buffers you specify. You can fill the z-buffer, the back buffer, or the stencil buffer. In this example, you want to fill the back buffer with the color green. So we set the flags to D3DCLEAR_TARGET, and the color to green.

BeginScene and EndScene don’t do anything in this example, but we will be using them in future versions. These functions will wrap all of our primitive drawing routines.

The Present function will cycle to the next back buffer. Since we only have one back buffer and one front buffer, the buffers simply flip. The back buffer will be displayed and we can now draw on the front buffer (actually, since we not doing page flipping, we are actually still drawing on the back buffer, but the concept is the same).

If you now run the program, you should get a window that is filled green. If everything worked okay, we can start writing code to draw triangles, the primitive that is the heart of game programming.




Next : Drawing Triangles