270 likes | 517 Views
3D Game Architecture using Direct3D. Bryan Duggan Hugh McAtamney. Purpose. To introduce the fundamental concepts of Direct3D programming Load & render a world Move the camera around Render and move the game characters Not an in-depth DirectX course!. What we will cover. This class
E N D
3D Game Architecture using Direct3D Bryan Duggan Hugh McAtamney
Purpose • To introduce the fundamental concepts of Direct3D programming • Load & render a world • Move the camera around • Render and move the game characters • Not an in-depth DirectX course!
What we will cover • This class • Program architecture • Initialising Direct3D and obtaining a IDirect3DDevice9 object • The rendering pipeline • Local space • World space • View space • Projection • Next class • Vertex & index buffers • Colour • Texturing • Meshes
What we will not cover • Lighting • Stencils • Terrain rendering • Particle systems • HLSL • Vertex shaders • Pixel shaders • The effects framework
Basic Architecture of a 3D Game main() { initialise_direct3D(); set_up_the_world(); while(! user_presses_ESC) { if (event_happens) { process_the_event(); } else { update_the_world(elapsedTime); draw_the_world(); } } cleanup(); }
Windows Program architecture • The entry point for all windows programs is called: • int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine,int showCmd) • Its job is to: • Register a WNCLASS struct specifying the icon, window name, handler for events etc. • Create a Window • Show the Window • Update the Window • Enter the message loop
Step 1 WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = _hInstance; wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = "Direct3D9App"; RegisterClass(&wc);
Step 2, 3, 4 hwnd = ::CreateWindow("Direct3D9App", "Direct3D9App", WS_EX_TOPMOST, 0, 0, _width, _height, 0 /*parent hwnd*/, 0 /* menu */, _hInstance, 0 /*extra*/); ::ShowWindow(hwnd, SW_SHOW); ::UpdateWindow(hwnd);
Step 5 while(msg.message != WM_QUIT) { if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } else { float currTime = (float)timeGetTime(); float timeDelta = (currTime - lastTime)*0.001f; update(timeDelta); display(timeDelta); lastTime = currTime; } }
Handling events • In Java events are handled by the handleEvent callback • In Windows, events are sent to a callback function registered as the callback for the window in the WNDCLASS struct • It has the signature: • LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) • Where: • hwnd is the window handle • msg is the event • wParam & lParam are the message parameters. Values depend on the msg parameter • This function gets called thousands of times a second and so needs to be very efficient
Initialising Direct3D • Is a (complicated) process involving: • Creation of an IDirect3D9 object • Checking the device capabilities • Filling out a D3DPRESENT_PARAMETERS structure specifying the parameters of the device we want • Creating an IDirect3DDevice9 object • Releasing the IDirect3D9
Step 1 IDirect3D9 * d3d9 = 0; d3d9 = Direct3DCreate9(D3D_SDK_VERSION); • Used to find out information about the capabilities of the system • Also to get the IDirect3DDevice9 • Always use D3D_SDK_VERSION to make sure you compile against the correct
Step 2 D3DCAPS9 caps; d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps); if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; else vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; • When we create an IDirect3DDevice9 we need to specify to “use hardware vertex processing” • Need to query the hardware to see if this is supported
Step 3 D3DPRESENT_PARAMETERS d3dpp; d3dpp.BackBufferWidth = _width; d3dpp.BackBufferHeight = _height; d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; d3dpp.BackBufferCount = 1; d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp.MultiSampleQuality = 0; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = hwnd; d3dpp.Windowed = _windowed; d3dpp.EnableAutoDepthStencil = true; d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; d3dpp.Flags = 0; d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; • Used to create the IDirect3DDevice9 with the requested parameters
Step 4 hr = d3d9->CreateDevice( D3DADAPTER_DEFAULT, // primary adapter D3DDEVTYPE_HAL, // _device type hwnd, // window associated with _device vp, // vertex processing &d3dpp, // present parameters & _device); // return created _device
The DalekWorld Architecture • The base class Direct3DBase handles: • The creation of the window • The creation of the IDirect3DDevice9 • Registering a message handler • Setting up the virtual camera • The message loop • Cleanup
The derived class DalekWorld • The derived class must: • Extend the base class Direct3DBase • Have a WinMain function that must construct an instance and call the main() member function • Override the handleEvent member function to handle any events
It may optionally: • Set the width, height, windowed option and window title in the constructor • Override the update member function • Override the draw member function • Override the cleanup member function
The rendering pipeline Local Space World Space View Space Backface Culling Lighting Clipping Projection Viewport Space Rasterization
Relation to matrices • Several of the stages transform geometry from one co-ordinate system to another • Use transformation matrices that we must set up • Use: • IDirect3DDevice::SetTransform
Local Space • The co-ordinate system we define the objects triangle list • Build all models around their own co-ordinates • Construct models without regard to position and orientation
World Space • World Transform brings • Objects into the world • Sets up the relationship between objects in the scene • Translations rotations and scaling • May be a different transform for each object in the scene • Use: • IDirect3DDevice::SetTransform(D3DTS_WORLD, &matrix);
View Space • View Space transform: • Translates the camera to the origin, looking down the positive Z-Axis • We don’t need to calculate it!! DirectX has an API to do this: • D3DXMatrixLookAtLH( D3DXMATRIX * pOut , D3DXVECTOR3 * eye , D3DXVECTOR3 * at , D3DXVECTOR3 * up) • Up is usually (0,1,0) • Use: • IDirect3DDevice::SetTransform(D3DTS_VIEW, &matrix);
Backface culling • All polygons have 2 sides. A front (visible) side • And a back (invisible) side • DirectX can cull the backfaces to reduce processing • Triangles with vertices specified in a clockwise winding order are considered front facing • Use: _ device->SetRenderState(D3DRS_CULLMODE, value) Where value is: • D3DCULL_NONE • D3DCULL_CW • D3DCULL-CCW
Projection • Transforming a 3D Scene to a 2D one • Uses perspective projection • Far away objects are smaller • Defines our frustum
The projection matrix • Is complex to derive, but directX has an API to do it: D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( &proj, D3DX_PI * 0.5f, // 90 - degree (float)_width / (float)_height, 1.0f, 1000.0f); _device->SetTransform(D3DTS_PROJECTION, &proj);
The Viewport transform • We can optionally transform to a viewport in the display window: typedef struct _D3DVIEWPORT9 { DWORD X; DWORD Y; DWORD Width; DWORD Height; float MinZ; float MaxZ; } D3DVIEWPORT9; We set the viewport using: _device->SetViewport(&vp)