Tuesday 25 October 2011

Getting started with a simple example DirectX 9.



First we need to understand what is needed from a basic application that implements directx.

We need a few bits of basic win32 api programming , we need to create an application window, this will be our gui to display to the user, later on we can make this full screen and show some basic objects and a box on screen. This will give our project a little more of a feel of a gaming environment.

In a basic c++ console application we use int main() as our application entry point, for a windowed application we use INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )

Within the winmain we need to register the window class and create the application window, to do this we first create an instance of the WNDCLASSEX structure and then register it. For more information: http://msdn.microsoft.com/en-us/library/windows/desktop/ms633577(v=vs.85).aspx

WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
"Direct3D Tutorial", NULL };

RegisterClassEx( &wc );



We then need to create the application window, to do so we reference the window class within our options.

HWND hWnd = CreateWindow( "Direct3D Tutorial", "Direct3D Tutorial 01: CreateDevice",
WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
GetDesktopWindow(), NULL, wc.hInstance, NULL );

Again for more information: http://msdn.microsoft.com/en-us/library/windows/desktop/ms632679(v=vs.85).aspx

This can’t run until we set up a message handler, this is done via a message handle in the form of a function pointer.

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

The basic function looks like this

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}


To summarise so far our code looks like the below :
#include


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShow)
{


WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0, 0, hInstance,NULL, NULL, (HBRUSH)(COLOR_WINDOW+1), NULL, "Direct3D Tutorial", NULL};

RegisterClassEx( &wc );

HWND hWnd = CreateWindow( "Direct3D Tutorial", "Direct3D Tutorial 01: CreateDevice",
WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
GetDesktopWindow(), NULL, wc.hInstance, NULL );

}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}

We add the dirextx 9 header file to the top of our project for the next parts.

#include

Next we will add a few lines of code to initiate dirextx, we first create 2 variables and set them to NULL for later use. The first is used to create the D3DDevice object and the second is our rendering device.


LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;

The next line actually creates the directx object
g_pD3D = Direct3DCreate9( D3D_SDK_VERSION )

Adding some small validation to the line above will allow the program to gracefully fail if directx is lacking from the system or failed to create the object in some way.

if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
return E_FAIL;


Next we create a d3d parameters object which we will specify how our directx 3d program will act.
First we wipe the memory with ZeroMemory, Windowed sets the program to a windowed mode, Swap effect relates to buffer swap behaviour, BackBufferFormat is set to D3DFMT_UNKNOWN which tells directx to use the current display mode format.

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

Now we attempt to actually create our render object by calling CreateDevice.

if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp, &g_pd3dDevice ) ) )
return E_FAIL;

D3DADAPTER_DEFAULT = the default adapter for the system it’s running on, most systems will only have 1 adapter but we will always use the default adapter here.

D3DEVTYPE_HAL = We will use the hardware adapter as opposed to software rendering.

hWnd is the instance of our window, The focus window alerts Direct3D when an application switches from foreground mode to background mode if full screen.

D3DCREATE_SOFTWARE_VERTEXPROCESSING is a behavioural flag, you can combine many flags separated by a pipe, this one states that vertex process should be done by software rather than that hardware, most cards support hardware vertex processing this can be set to mixed to switch between both. See http://msdn.microsoft.com/en-us/library/windows/desktop/bb172527(v=vs.85).aspx for a full list.

&d3dpp is the address of our parameter object that we have just set up.

&g_pd3dDevice is the address of our rendering object.

If we run the program now you won’t notice much happening, this is due to us not actually rendering the scene yet, we have only created the application window and initialised direct 3d.

Just under where we’ve called createdevice add the next two lines:

ShowWindow(hWnd, nShow);
UpdateWindow(hWnd);

These two lines make the window visible and UpdateWindow makes windows send a WM_PAINT message.

In order to render the scene we need to handle system messages so that we know when to render the scene, for this we create a message loop and wait for WM_PAINT message to be in the message queue. From MSDN : The WM_PAINT message is sent when the system or another application makes a request to paint a portion of an application's window.


Within our winmain we have to declare a MSG variable which is actually a typedef to a message pointer that windows will fill for us.

MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}

TranslateMessage converts the key messages in to character messages and passes them on to DispatchMessage which sends the message to the windows procedure (WndProc) we defined in our window class.
When WndProc is ran, currently our switch statement only checks for WM_QUIT messages, so we need to add a new case for WM_PAINT so that we can handle the message event.
case WM_PAINT:
ValidateRect( hWnd, NULL );
return 0;

Adding the additional case above to our switch results in our callback function looking like this:

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
break;
case WM_PAINT:

ValidateRect( hWnd, NULL );
return 0;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}

The ValidateRect function validates the client area within a rectangle by removing the rectangle from the update region of the specified window.
To render and display our scene we first need to clear the back buffer and give it a nice blue colour.

g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );


The first two parameters tell the function to clear the whole application window, more specifically the size and the address of the array of rectangles to clear, 0, NULL make the whole window clear.
D3DCLEAR_TARGET will only clear the render target and not the zbuffer or stencil buffer. See : http://msdn.microsoft.com/en-us/library/windows/desktop/bb172514(v=vs.85).aspx
(D3DCOLOR_XRGB(0,0,255)) Initializes a colour with the supplied red, green, and blue values and returns a D3DCOLOR
The last two parameters are ignored.
After we clear the scene we use the BeginScene() to start our rendering timeline and state that rendering is beginning, we then add our rendering code inside this and EndScene(),which states that rendering has finised. Rendering can only be done within this section.

We add the following code to the WM_PAINT case

g_pd3dDevice->BeginScene();




g_pd3dDevice->EndScene();


In order to shut down our application we must not only close our window but also deallocate any directx objects our program has used.

Within our Winmain under our while loop for getmessage write the following code:

if( g_pd3dDevice != NULL)
g_pd3dDevice->Release();
if( g_pD3D != NULL)
g_pD3D->Release();


Apart from the checks for NULL this just calls the Release method for our Render device.

Monday 24 October 2011

Setting Up Visual Studio 2010 for DirectX 9

Setting up VS210 for DirectX9 SDK

1.Grab the Direct X SDK : http://www.microsoft.com/download/en/details.aspx?id=6812
2.Install Visual Studio / Visual C++ (The express version is free!)

First things first, we need the correct tools for the job, and we need these tools to work with DirectX.

Visual Studio 2010 won’t work ‘out of the box’, a few things need to be changed. The SDK will be installed to your harddrive, mine is located at C:\Program Files\Microsoft DirectX SDK (June 2010). After installing VS2010 start up a new project, select project> Properties (ALT+F7), from here you’ll need to change the following:

Configuration Properties > VC++ Directories

Add the SDK Include folder to the Include Directories, proceed to do the same with the library directory as shown below. This can be done be simply selecting the drop down box and adding $(DXSDK_DIR)include & $(DXSDK_DIR)Lib respectively.



Linker > Input

Now we add the links to the static libraries for DirectX, add the d3d9.lib to your Addition Dependencies, d3dx9.lib I had to give an absolute reference to. You can set this for all projects by selecting “ALL CONFIGURATIONS” under the configuration drop down.



Configuration Properties > General

To avoid errors when passing multi-byte strings to a function which expects a unicode string set the character set – use multibyte character set.

Friday 21 October 2011

It Starts!

The start of the blog, well what to say?

I've always been intrigued by game development and through my time as a developer i've dabbled with a few simple projects. The main background to these small projects has been to investigate how something works or for projects within education.

The aim of this blog is to keep a running diary of progress, what i learn and hopefully become a source of information for new developers.