In order to have the computer draw awsome images for you, you need a framework. There are several APIs (Application programming interfaces) out there for game/graphics programming. Some include OpenGL, Allegro, Dark GDK, and of course DirectX. This is microsoft's API that they developed and that has pretty much dominated the commercial video game API scene (especially on PC's.) Since this is a tutorial on some beginning use of DirectX on a PC, I won't be going into MS's XNA or other game programming API's since:
a. they are closely guarded secrets
b. I don't know sqaut about them
Alright so let's delve into some fun!
(This tutorial assumes you know how to set up a basic windows app including WinMain, callback, etc...)
DirectX relates to your hardware and software in this manner:
Application --> Direct3D --> HAL (Hardware Abstraction Layer) --> Graphics Device (such as an Nvidia 8800 GTX, etc...)
Your application utilizes DirectX which takes care of the low level HAL and graphics device functions. Back in the earlier days each game/app coder had to write drivers for each type of device. Consider DirectX to be the C++ code instead of having everything in assembly (although it can be used to make algorithms more effcient, so on and so forht).
First, we must intialize DirectX in our program so we can use all of its goodies. This code utilizes a 'game loop' to illustrate where it would go in the code.
CODE
/* KYA DirectX tutorial
* 3-3-08
*/
//header files to include
#include <d3d9.h>
#include <time.h>
//application title
#define APPTITLE "Direct3D_Windowed"
//forward declarations
LRESULT WINAPI WinProc(HWND,UINT,WPARAM,LPARAM);
ATOM MyRegisterClass(HINSTANCE);
int Game_Init(HWND);
void Game_Run(HWND);
void Game_End(HWND);
//Direct3D objects
LPDIRECT3D9 d3d = NULL;
LPDIRECT3DDEVICE9 d3ddev = NULL;
//window event callback function
LRESULT WINAPI WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_DESTROY:
Game_End(hWnd);
PostQuitMessage(0);
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
//helper function to set up the window properties
ATOM MyRegisterClass(HINSTANCE hInstance)
{
//create the window class structure
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
//fill the struct with info
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = APPTITLE;
wc.hIconSm = NULL;
//set up the window with the class info
return RegisterClassEx(&wc);
}
//entry point for a Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// declare variables
MSG msg;
// register the class
MyRegisterClass(hInstance);
// initialize application
HWND hWnd;
//create a new window
hWnd = CreateWindow(
APPTITLE, //window class
APPTITLE, //title bar
WS_OVERLAPPEDWINDOW, //window style
CW_USEDEFAULT, //x position of window
CW_USEDEFAULT, //y position of window
500, //width of the window
400, //height of the window
NULL, //parent window
NULL, //menu
hInstance, //application instance
NULL); //window parameters
//was there an error creating the window?
if (!hWnd)
return FALSE;
//display the window
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
//initialize the game
if (!Game_Init(hWnd))
return 0;
// main message loop
int done = 0;
while (!done)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
//look for quit message
if (msg.message == WM_QUIT)
{
MessageBox(hWnd, "Received WM_QUIT message", "WinMain", MB_OK);
done = 1;
}
//decode and pass messages on to WndProc
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
//process game loop (else prevents running after window is closed)
Game_Run(hWnd);
}
return msg.wParam;
}
int Game_Init(HWND hwnd)
{
//display init message
MessageBox(hwnd, "Program is about to start", "Game_Init", MB_OK);
//initialize Direct3D
d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (d3d == NULL)
{
MessageBox(hwnd, "Error initializing Direct3D", "Error", MB_OK);
return 0;
}
//set Direct3D presentation parameters
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
//create Direct3D device
d3d->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hwnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&d3ddev);
if (d3ddev == NULL)
{
MessageBox(hwnd, "Error creating Direct3D device", "Error", MB_OK);
return 0;
}
//set random number seed
srand(time(NULL));
//return okay
return 1;
}
void Game_Run(HWND hwnd)
{
//make sure the Direct3D device is valid
if (d3ddev == NULL)
return;
//clear the backbuffer to black
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0), 1.0f, 0);
//start rendering
if (d3ddev->BeginScene())
{
//stop rendering
d3ddev->EndScene();
}
//display the back buffer on the screen
d3ddev->Present(NULL, NULL, NULL, NULL);
}
void Game_End(HWND hwnd)
{
//display close message
MessageBox(hwnd, "Program is about to end", "Game_End", MB_OK);
//release the Direct3D device
if (d3ddev != NULL)
d3ddev->Release();
//release the Direct3D object
if (d3d != NULL)
d3d->Release();
}
Wow, that was alot of code! Let's break it down:
First, you need to link d3d9.lib to your project. Feel free to do it under your project settings or pragma it in whatever you want. Secondly, I compiled all the code in this tutorial using MSVS 2005. Provided you have the DirectX SDK and know your way around your compiler, this should compile for darn near everything.
OK Let's look at the windows portions first:
CODE
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// declare variables
MSG msg;
// register the class
MyRegisterClass(hInstance);
// initialize application
HWND hWnd;
//create a new window
hWnd = CreateWindow(
APPTITLE, //window class
APPTITLE, //title bar
WS_OVERLAPPEDWINDOW, //window style
CW_USEDEFAULT, //x position of window
CW_USEDEFAULT, //y position of window
500, //width of the window
400, //height of the window
NULL, //parent window
NULL, //menu
hInstance, //application instance
NULL); //window parameters
//was there an error creating the window?
if (!hWnd)
return FALSE;
//display the window
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
//initialize the game
if (!Game_Init(hWnd))
return 0;
// main message loop
int done = 0;
while (!done)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
//look for quit message
if (msg.message == WM_QUIT)
{
MessageBox(hWnd, "Received WM_QUIT message", "WinMain", MB_OK);
done = 1;
}
//decode and pass messages on to WndProc
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
//process game loop (else prevents running after window is closed)
Game_Run(hWnd);
}
return msg.wParam;
}
Here we set up our window, various properties, so that we can color, paint, etc... on our window later.
Assuming that everyone understands how windows programs are set up let's discuss the game functions called using some directX features:
Game_Init sets up our 'game' using directX. Notice the DX9 intialization. Usually this creates a pointer to a dirextX9 object that will be referenced throughout the life of a program:
CODE
//initialize Direct3D
d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (d3d == NULL)
{
MessageBox(hwnd, "Error initializing Direct3D", "Error", MB_OK);
return 0;
}
In case there is some sort of error we have the message box tell us so we don't pull our hair out thinking it is a software issue. The most important part of all this code is this:
CODE
//create Direct3D device
d3d->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hwnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&d3ddev);
At this point, we won't delve into all of these various parameters, but know that there are many different routes we can take with DX9 objects, and can custom tailor it to our needs (such as bump mapping, lighting, texturing, etc...)
Next we have the Game_Run function:
CODE
void Game_Run(HWND hwnd)
{
//make sure the Direct3D device is valid
if (d3ddev == NULL)
return;
//clear the backbuffer to black
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0), 1.0f, 0);
//start rendering
if (d3ddev->BeginScene())
{
//stop rendering
d3ddev->EndScene();
}
//display the back buffer on the screen
d3ddev->Present(NULL, NULL, NULL, NULL);
}
This is the function that is ran every 'loop' of the program. It is barebones here -- i.e. it does much of nothing, but illustrates how to use directx in a possible game program. the D3D device renders the scene, then ends the scene each time the method is ran. Consecutive images appear as animations to the human eye and thus we are entertained. See this segment:
CODE
//clear the backbuffer to black
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0), 1.0f, 0);
Just like the WinAPI functions such as BitBLT, DirectX draws to an offscreen buffer before the image is sent to your screen (which could be a hardware tutorial all on its own).
After creating objects/devices you must clean up after them! Forgetting to do so can lead to a nasty memory leak later on down the road:
CODE
void Game_End(HWND hwnd)
{
//display close message
MessageBox(hwnd, "Program is about to end", "Game_End", MB_OK);
//release the Direct3D device
if (d3ddev != NULL)
d3ddev->Release();
//release the Direct3D object
if (d3d != NULL)
d3d->Release();
}
When running the program, a small window should pop up with a message box that states the 'game is about to start. The window should turn a shade of green and then when you press the 'X' in the upper right corner a message box will state the the 'game' is ending. Hopefully this helped you on your quest to learn more about directX. This barely scratched the surface on what directX can do or what you can do with its features. If this is somewhat well received I'll sit down and write a few more on vertex/matrix processing, some shaders, etc... We can write our D3D functions to create a reusable framework in applications over and over. Never reinvent the wheel!
--KYA