This page is a set of notes describing how to initialise Direct3D and enter a render loop. It assumes you already have a program that can display a window. If not take a look at the section about Windows API programming.
Note that there is a demo application with source code showing off a working Direct3D application here: Cube Demo
Since you want to use Direct3D code you must link to the Direct3D library and include its header file. In Viz go to the menu project - properties and click linker then input. There will be a list of library files your program is already linking to under dependencies. Add to the beginning of this list the Direct3D library: d3d9.lib.
You also need to include the function definitions and data structures used by the library so add an include for d3d9.h at the top of your main.cpp source file.
You can download the source code for a very simple demo that draws a triangle using Direct3D here: D3dTriangle.zip. While very simple it should help to show how Direct3D can be initialised and used to draw a primitive.
There are two main COM objects you need to create in order to initialise Direct3D. You need to obtain a pointer to the main Direct3D object and use it to obtain a device pointer. It is the device pointer through which you do all your graphic operations. It is common to create a new function in which you write this set-up code. You should call it after creating your window and just before you display it. Make sure you always check for error conditions and exit your program if one occurs. See the notes on tracking DirectX bugs.
Note: The Direct3D object and device pointers are used throughout your code so it is common to make them available globally. As usual when declaring a pointer assign it initially to NULL e.g.
LPDIRECT3D9 d3dObject=NULL;
LPDIRECT3DDEVICE9 d3dDevice=NULL;
The above creates a variable named d3dObject with the type LPDIRECT3D9. This is a typedef (synonym) for IDirect3D9* i.e. it is a pointer to the IDirect3D9 class. The 'I' in the name is used to indicate it provides interfaces (functions). The above lines could equally be written like this:
IDirect3D9* d3dObject=NULL;
IDirect3DDevice9* d3dDevice=NULL;
To create an instance of the object you call the API function as shown below:
d3dObject=Direct3DCreate9(D3D_SDK_VERSION);
You should check to see if the pointer is valid (not NULL) before proceeding. If it is NULL you should exit your program with an error as Direct3D could not be initialised. The version define is passed in simply so that a check is made that you are using the correct headers with the correct libraries.
Once you have obtained a pointer to the Direct3D object you can use it to obtain a device pointer. The device pointer is used throughout your graphics code:
IDirect3DDevice9* d3dDevice=NULL;
The above creates a variable named d3dDevice with the type IDirect3DDevice9*. Note sometimes you see this written as LPDIRECT3DDEVICE9 which is just a synonym for IDirect3DDevice9* a pointer to a IDirect3DDevice9 class. To create an instance of the device we use our main Direct3D object pointer which provides the following function (the definition is shown):
HRESULT IDirect3D9::CreateDevice(UINT adapter, D3DDEVTYPE deviceType, HWND focusWindow,DWORD behaviourFlags, D3DPRESENT_PARAMETERS *presentationParameters, IDirect3DDevice9** device);
Note: nearly every DirectX function returns a code indicating success or failure. The code is of type HRESULT and the API provides you with two macros that you can use to determine the success of the call. The two macros are SUCCEEDED and FAILED. Be careful not to test against true and false, you must use the macros. If you wish to find out more information about a return error look at this answer here: Dx Errors
The CreateDevice function takes a number of parameters that allow you to specify the kind of device you require. The parameters are:
You must create a variable to hold the above structure (D3DPRESENT_PARAMETERS). It is good practice to initialise it to 0 to start with as that is the default for most parameters. You can use the function ZeroMemory to set all values to 0. This function takes a pointer to the area of memory as its first parameter then the number of bytes you want to set to zero, which is normally the size of the structure so you can use sizeof to get that value.
D3DPRESENT_PARAMETERS presParams;
ZeroMemory(&presParams,sizeof(presParams));
For running in a window an example is:
presParams.Windowed=TRUE;
presParams.SwapEffect=D3DSWAPEFFECT_DISCARD;
presParams.BackBufferFormat=D3DFMT_UNKNOWN;
presParams.PresentationInterval=D3DPRESENT_INTERVAL_ONE;
Then an example of calling the CreateDevice function is:
HRESULT hr=d3dObject->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &presParams, &d3dDevice);
The final parameter to the CreateDevice call is the address of a device pointer (IDirect3DDevice9*). You declare the pointer and pass the address of it to the CreateDevice call. Direct3D then creates the device object and points your pointer at it.
Note: if you find device creation is failing try changing from hardware vertex processing to software. It may be your video card cannot do hardware vertex processing at all or at least not in a window with your current desktop settings.
Important: the device pointer is just like an instance of a class you may have written, there is nothing mystical about it. It provides a set of functions that you can use to control Direct3D. DirectX uses this pattern over and over again so if you understand how this works the rest should be easy :)
Once you have obtained your device pointer you can use it to display graphics. The render loop will always be the same:
The device provides the functions you will use in your render loop.
The definition of the clear function is:
HRESULT IDirect3DDevice9::Clear(DWORD count, const D3DRECT *pRects, DWORD flags, D3DCOLOR color, float z, DWORD stencil)
The back buffer can be cleared to a set colour. This function is multipurpose and also allows you to clear the stencil and z buffer: The parameters are:
Example - to clear the back buffer to green :
d3dDevice->Clear(0,NULL,D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0),1.0f,0)
HRESULT IDirect3DDevice9::BeginScene()
Note: remember these functions are provided by the device so to call them you need to use the device pointer e.g.
gD3dDevice->BeginScene();
HRESULT IDirect3DDevice9::EndScene()
Once you have rendered your scene you need to present it. The definition of the function is shown below:
HRESULT IDirect3DDevice9::Present(CONST RECT *pSourceRect, CONST RECT *pDestRect, HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion );
You can pass in regions of the screen to render, however the default is to use the whole screen so the call is simply:
d3dDevice->Present( NULL, NULL, NULL, NULL );
Once all these calls are in place you should be able to initialise Direct3D and clear the screen to a colour. Note that to get Direct3D debug messages in the output pane you need to have Direct3D set to debug mode (via the DirectX control pane; - now under the All Programs directory). To get the most amount of debug messages you also need to define D3D_DEBUG_INFO. Place this is your project settings under preprocessor definitions for your debug build only.
Being a well behaved Windows application requires waiting for Windows to send our application messages and only to do work when we receive messages. This is required so other applications can get processor time and Windows runs smoothly. However for games this is not so good. We want our game to run as fast as possible and not have to wait for Windows. So to become badly behaved we need to change the message loop so we do our work whenever there are no Windows messages waiting for us. An example of the changed message loop code is show below. Once you have made this change you will see that when you ALT-TAB away from your application everything runs really slow, this is because we are 'hogging' the operating system time - we are being badly behaved!
Your new message loop in WinMain will look like this:
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
.. update our game, render etc.
}
Advanced Note: when you do this you really do hog all the computer resources! In order to give a bit of time to other applications you can do a Sleep(0) each update (allows other threads to carry out essential task). Also when your game is minimised or not in focus (if running in a window) it is a good idea to stop rendering and give plenty of time to other applications. For how to do this see the answer on the Direct3D FAQ page: everything runs slowly...