d3dUtility.cpp #include "d3dUtility.h" /********************************************************************* InitD3D 该函数对应用程序主窗口进行了初始化并执行了Direct3D初始化过程。 如果该函数成功返回,将得到一个指向已创建好的IDrect3DDevice9接口的指针。 该函数允许我们指定窗口的尺寸以及运行模式(窗口模式或全屏模式)。 *********************************************************************/ bool d3d::InitD3D( HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9** device) { // Create the main application window. WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)d3d::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 = L"Direct3D9App"; if( !RegisterClass(&wc) ) { ::MessageBox(0, L"RegisterClass() - FAILED", 0, 0); return false; } HWND hwnd = 0; hwnd = ::CreateWindow(L"Direct3D9App", L"Direct3D9App", WS_EX_TOPMOST, 0, 0, width, height, 0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/); if( !hwnd ) { ::MessageBox(0, L"CreateWindow() - FAILED", 0, 0); return false; } ::ShowWindow(hwnd, SW_SHOW); ::UpdateWindow(hwnd); // Init D3D: HRESULT hr = 0; // Step 1: 获取接口IDirect3D9的指针 /************************************************************************** 要初始化IDirect3D,首先必须获取指向接口IDirect3D9的指针。使用一个专门的Direct3D函数 可以很容易做到。 函数Direct3DCreate9的参数必须是D3D_SDK_VERSION,只有如此方能保证应用程序使用正确的头 文件。如果该函数调用失败,将返回一个NULL指针。 上述IDirect3D9对象主要有两个用途:设备枚举device enumeration以及创建IDirect3DDevice9 类型的对象。设备枚举是指获取系统中可用的每块图形卡的性能、显示模式display mode、格式 及其他信息。 **************************************************************************/ IDirect3D9* d3d9 = 0; d3d9 = Direct3DCreate9(D3D_SDK_VERSION); if( !d3d9 ) { ::MessageBox(0, L"Direct3DCreate9() - FAILED", 0, 0); return false; } // Step 2: 校验硬件顶点运算 /************************************************************************** 创建一个代表主显卡的IDirect3DDevice9类型对象时,必须指定使用该对象进行顶点运算的类型。如果可以,我们希望使用硬件顶点运算,但是由于并非所有的显卡都支持硬件顶点运算,我们必须首先检查图形卡是否支持该类型的运算。 要进行检查,必须先根据主显卡的性能参数初始化一个IDirect3DDevice9类型的对象。 Adapter 指定物理显卡的序号; DeviceType 指定设备类型; pCaps 返回已初始化的设备性能结构实例。 **************************************************************************/ D3DCAPS9 caps; d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps); int vp = 0; if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; else vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; // Step 3: 填充D3DPRESENT_PARAMETER结构 /************************************************************************** 该结构用于指定所要创建的IDirect3DDevice9类型对象的一些特性,该结构定义如下: typedef struct _D3DPRESENT_PARAMETERS_ { UNIT BackBufferWidth; //后台缓存中表面的宽度,单位为像素 UNIT BackBufferHeight; //后台缓存中表面的高度,单位为像素 D3DFORMAT BackBUfferFormat; //后台缓存中的像素格式 UINT BackBufferCount; //所需使用的后台缓存的个数,通常指定为,表明我们仅需要一个后台缓存 D3DMULTISAMPLE_TYPE MultiSampleType; //后台缓存所使用的多重采样类型 DWORD MultiSampleQuality; //多重采样的质量水平 D3DSWAPEFFECT SwapEffect; //D3DSWAPEFFECT枚举类型的一个成员。该枚举类型指定了交换链中的缓存的页面置换方式。指定为: //D3DSWAPEFFECT_DISCARD时效率最高 HWND hDeviceWindow; //与设备相关的窗口句柄。指定了所要进行绘制的应用程序窗口 BOOL Windowed;//为true时,表示窗口模式。为false时表示全屏模式 BOOL EnableAutoDepthStencil; //设为true,则Direct3D自动创建并维护深度缓存或模版缓存 D3DFORMAT AutoDepthStencilFormat; //深度缓存或模版缓存的像素格式 DWORD Flags; //一些附加的特性。可以指定为(无标志)或D3DPRESENTFLAG集合中的一个成员,其中两个成员较为常用: D3DPRESENTFLAG_LOCKBALE_DEPTHBUFFER 指定可锁定的后台缓存。注意使用一个可锁定的后台缓存会降低性能 D3DPRESENTFLAG)DISCARD)DEPTHBUFFER 指定当下一个后台缓存提交时,哪个深度后模版缓存将被丢弃。这样可以提高性能。 UINT FullScreen_RefreshRateInHz; //刷新频率 UINT PresentationInterval; //D3DPRESENT集合的一个成员。 }D3DRESENT_PARAMETERS; **************************************************************************/ 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; // Step 4: 创建IDirect3DDevice9接口 /************************************************************************** 可用如下方法创建IDirect3DDevice9类型的对象。 HRESULT IDirect3D9::CreateDevice { UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9 **ppReturnedDeviceInterface }; Adapter 指定我们希望用已创建的IDirect3DDevice9对象代表哪块物理显卡 DeviceType 指定需要使用的设备类型 hFocusWindow 与设备相关的窗口句柄。通常情况下是指设备所要进行绘制的目标窗口,为了达到预期的目的,该句柄与D3DPRESENT_PARAMETERS结构的数据成员d3dpp.hDeviceWindow应为同一句柄 BehaviorFlags 该参数可为D3DCREATE_HARDWARE_VERTEXPROCESSING或D3DCREATE_SOFTWAVE_VERTEXPROCESSING PresentationParameters 一个已经完成初始化的D3DPRESENT_PARAMETERS类型的实例,该实例定义了设备的一些特性。 ppReturnedDeviceInterface 返回所创建的设备 **************************************************************************/ hr = d3d9->CreateDevice( D3DADAPTER_DEFAULT, // primary adapter deviceType, // device type hwnd, // window associated with device vp, // vertex processing &d3dpp, // present parameters device); // return created device if( FAILED(hr) ) { // try again using a 16-bit depth buffer d3dpp.AutoDepthStencilFormat = D3DFMT_D16; hr = d3d9->CreateDevice( D3DADAPTER_DEFAULT, deviceType, hwnd, vp, &d3dpp, device); if( FAILED(hr) ) { d3d9->Release(); // done with d3d9 object ::MessageBox(0, L"CreateDevice() - FAILED", 0, 0); return false; } } d3d9->Release(); // done with d3d9 object return true; } /****************************************************************************** EnterMsgLoop 该函数封装了应用程序的消息循环。它接收一个指向显示函数display function的函数指针。该显示函数就是实现绘制功能的那个函数。该消息循环函数需要知道使用哪个显示函数,这样方可对其进行调用,并在空闲处理期间显示场景。 ******************************************************************************/ int d3d::EnterMsgLoop( bool (*ptr_display)(float timeDelta) ) { MSG msg; ::ZeroMemory(&msg, sizeof(MSG)); static float lastTime = (float)timeGetTime(); 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; ptr_display(timeDelta); lastTime = currTime; } } return msg.wParam; } d3dInit.cpp #include "d3dUtility.h" // // Globals // IDirect3DDevice9* Device = 0; // // Framework Functions // /*************************************************************************************** 所需进行的全部设置和初始化都在该函数中进行,如分配资源、检查设备性能、设备应用程序的状态。 ***************************************************************************************/ bool Setup() { // Nothing to setup in this sample. return true; } /*************************************************************************************** 该函数用于释放在Setup函数中分配的任何资源,例如分配的存储单元。 ***************************************************************************************/ void Cleanup() { // Nothing to cleanup in this sample. } /************************************************************************************** 在该函数中应实现全部的绘制代码以及相邻帧之间应执行的操作,例如更新物体的位置。参数 timeDelta是相邻帧间的时间差,只要用于将动画与显示器的刷新频率保持同步。 **************************************************************************************/ bool Display(float timeDelta) { if( Device ) // Only use Device methods if we have a valid device. { // Instruct the device to set each pixel on the back buffer black - // D3DCLEAR_TARGET: 0x00000000 (black) - and to set each pixel on // the depth buffer to a value of 1.0 - D3DCLEAR_ZBUFFER: 1.0f. Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0); /***************************************************************************************** Display函数中调用了IDirect3DDevice9::Clear函数,后者将后台缓存和深度或模版分别置为黑色和1.0.注意, 如果应用程序不被暂停执行,仅有绘制代码会执行。函数IDirect3DDevice9::Clear的声明如下: HRESULT IDirect3DDevice9::Clear { DWORD Count, //pRect数组中矩形的数目 const D3DRECT *pRects, //所要执行清除操作的屏幕矩形数组。该参数允许我们只对表面的部分区域进行清除操作。 DWORD Flags, //指定所要清除的表面。我们可清除下列表面中的一个或多个。 D3DCOLOR Color, //指定将绘制目标体设置为何种颜色 float z, //深度缓存所要设定的值 DWORD Stencil //模版缓存所要设定的值 }; *****************************************************************************************/ // Swap the back and front buffers. Device->Present(0, 0, 0, 0); } return true; } // // WndProc // LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_DESTROY: ::PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); break; } return ::DefWindowProc(hwnd, msg, wParam, lParam); } // // WinMain // /****************************************************************************************** WinMain函数执行下列步骤: 初始化主显示窗口及Direct3D 调用Setup函数对应用程序进行设置 将Display函数作为显示函数,进入消息循环 执行清除操作,最终释放IDirect3DDevice9接口对象 ******************************************************************************************/ int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { if(!d3d::InitD3D(hinstance, 640, 480, true, D3DDEVTYPE_HAL, &Device)) { ::MessageBox(0, L"InitD3D() - FAILED", 0, 0); return 0; } if(!Setup()) { ::MessageBox(0, L"Setup() - FAILED", 0, 0); return 0; } d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); return 0; } d3dUtility.h #ifndef __d3dUtilityH__ #define __d3dUtilityH__ #include <d3dx9.h> #include <string> namespace d3d { bool InitD3D( HINSTANCE hInstance, // [in] Application instance. int width, int height, // [in] Backbuffer dimensions. bool windowed, // [in] Windowed (true)or full screen (false). D3DDEVTYPE deviceType, // [in] HAL or REF IDirect3DDevice9** device);// [out]The created device. int EnterMsgLoop( bool (*ptr_display)(float timeDelta)); LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); template<class T> void Release(T t) { if( t ) { t->Release(); t = 0; } } template<class T> void Delete(T t) { if( t ) { delete t; t = 0; } } } #endif // __d3dUtilityH__