首页 D3D和MFC的整合

D3D和MFC的整合

举报
开通vip

D3D和MFC的整合 Frank Luna www.moon-labs.com - 1 - Integrating Direct3D 9.0 with MFC Using Visual Studio .Net (7.0) By Frank Luna www.moon-labs.com Created on March 16, 2003 (Original Direct3D 8.1 Version Created on April 8, 2002 and is Available at http:...

D3D和MFC的整合
Frank Luna www.moon-labs.com - 1 - Integrating Direct3D 9.0 with MFC Using Visual Studio .Net (7.0) By Frank Luna www.moon-labs.com Created on March 16, 2003 (Original Direct3D 8.1 Version Created on April 8, 2002 and is Available at http://www.gamedev.net/reference/articles/article1778.asp) 1.1 Introduction There is often some difficulty integrating the DirectX APIs with the Microsoft Foundation Classes (MFC). The DirectX SDK provides several samples using DirectX with MFC, but none of them use the desired document/view architecture or the application wizards. This paper shows the reader, step by step, how to set up a simple Direct3D 9.0 application in the MFC framework using the wizards and the document/view architecture in Visual Studio .Net. It is assumed that the reader has some experience with both Direct3D and MFC but not both of them together. The project we will build is simple; we will load a teapot into a mesh data structure and rotate it about the y-axis. The final rendering will look something like the picture below – figure (1) – but will be animated. Figure 1: Screenshot of the application we will develop in this paper. Frank Luna www.moon-labs.com - 2 - 1.2 Creating the Project This paper will move at a slow pace and attempt to walk you through it one step at a time. We will start by creating a new project in Visual Studio .Net (VS7). Select the MFC Application icon as shown in figure (2) and enter a project name such as “Direct3D9MFC.” Then press the OK button. Figure 2: Select an MFC Application template for this project. The next dialog box that appears allows you to configure your application. Select the Application Type tab on the left as shown in figure (3). Then select Single document as the application type and be sure that the Document/View architecture support checkbox is checked. The rest of the settings can be left to the default so press the Finish button to create the application. Frank Luna www.moon-labs.com - 3 - Figure 3: Application creation settings. Specify how you want your application to be created using these dialogs. For this demonstration we want a Single Document Interface and we want to use the Document/View Architecture. After pressing Finish the application template should have been created successfully. Just to make sure, you may want to compile, build, and execute your project before continuing. Now, before we move on to integrating Direct3D with MFC, we should link the Direct3D library files “d3d9.lib” and “d3dx9.lib” into our project. In addition, we will link in “winmm.lib” (Windows Multimedia Library) for timer functions. In VS7 you can specify the library files to link in by going to the menu and selecting Project- >Properties->Linker->Input Folder and then entering the library names as shown in figure (4). Frank Luna www.moon-labs.com - 4 - Figure 4: Link “d3d9.lib”, “d3dx9.lib” and “winmm.lib” into your application. 1.3 Integrating Direct3D with MFC We will integrate Direct3D with MFC by having the CDirect3D9MFCView (CView) contain our Direct3D related interfaces. This makes sense because CDirect3D9MFCView is responsible for drawing data and will need direct access to Direct3D to do that drawing. 1.3.1 CDirect3D9MFCView Data Additions We will begin by adding the following variables to the CDirect3D9MFCView class: #include class CDirect3D9MFCView : public CView { ... private: IDirect3DDevice9* _device; D3DPRESENT_PARAMETERS _d3dpp; ID3DXMesh* _teapot; }; Frank Luna www.moon-labs.com - 5 - You should already be familiar with the first two variable types, _device and _d3dpp, and know what they do or what they are used for; as they are fundamental Direct3D types that you should know before attempting to read this paper. Therefore I will not elaborate on them. The third variable, _teapot, is simply a mesh data structure that will store the geometry of the teapot we are going to render in this paper’s sample program. Remember that you will need to include d3dx9.h at the top of your CDirect3D9MFCView class. Note that the constructor and destructor for CDirect3D9MFCView are implemented as follows: CDirect3D9MFCView::CDirect3D9MFCView() : _device(0), _teapot(0) { ::ZeroMemory(&_d3dpp, sizeof(_d3dpp)); } CDirect3D9MFCView::~CDirect3D9MFCView() { if( _teapot ) _teapot->Release(); if( _device ) _device->Release(); } 1.3.2 CDirect3D9MFCView Method Additions Now add the following methods to the CDirect3D9MFCView class: class CDirect3D9MFCView : public CView { ... private: HRESULT initD3D( HWND hwnd, int width, int height, bool windowed, D3DDEVTYPE deviceType); HRESULT setup(int width, int height); HRESULT cleanup(); public: HRESULT update(float timeDelta); HRESULT render(); }; Frank Luna www.moon-labs.com - 6 - • The initD3D method is responsible for initializing Direct3D, that is, obtaining a pointer to an IDirect3DDevice9 interface based on the arguments passed in. For simplicity, the sample application code leaves out device enumeration and selects a “safe configuration” when initializing Direct3D. The corresponding sample program to this paper implements this method as follows: HRESULT CDirect3D9MFCView::initD3D(HWND hwnd, int width, int height, bool windowed, D3DDEVTYPE deviceType) { HRESULT hr = 0; // Step 1: Create the IDirect3D9 object. IDirect3D9* d3d9 = 0; d3d9 = Direct3DCreate9(D3D_SDK_VERSION); if( !d3d9 ) { ::MessageBox(0, "Direct3DCreate9() - FAILED", 0, 0); return E_FAIL; } // Step 2: Check for hardware vp. 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: Fill out the D3DPRESENT_PARAMETERS structure. _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: Create the device. Frank Luna www.moon-labs.com - 7 - 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 safer configuration. _d3dpp.AutoDepthStencilFormat = D3DFMT_D16; hr = d3d9->CreateDevice( D3DADAPTER_DEFAULT, deviceType, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &_d3dpp, &_device); if( FAILED(hr) ) { d3d9->Release(); // done with d3d9 object ::MessageBox(0, "CreateDevice() - FAILED", 0, 0); return hr; } } d3d9->Release(); // done with d3d9 object return S_OK; } • The setup method is responsible for performing application specific code that is dependant upon Direct3D. This includes, allocating Direct3D resources, checking device capabilities, and setting device states. The corresponding sample program to this paper implements this method as follows: HRESULT CDirect3D9MFCView::setup(int width, int height) { if( _device ) { // Set view matrix. D3DXMATRIX V; D3DXVECTOR3 pos (0.0f, 0.0f, -6.0f); D3DXVECTOR3 target (0.0f, 0.0f, 0.0f); D3DXVECTOR3 up (0.0f, 1.0f, 0.0f); D3DXMatrixLookAtLH(&V, &pos, &target, &up); _device->SetTransform(D3DTS_VIEW, &V); Frank Luna www.moon-labs.com - 8 - // Create the teapot. if( !_teapot) D3DXCreateTeapot(_device, &_teapot, 0); // Use wireframe mode and turn off lighting. _device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); _device->SetRenderState(D3DRS_LIGHTING, false); // Size the viewport based on window dimensions. D3DVIEWPORT9 vp = {0, 0, width, height, 0.0f, 1.0f}; _device->SetViewport( &vp ); // Set the projection matrix based on the // window dimensions. D3DXMATRIX P; D3DXMatrixPerspectiveFovLH( &P, D3DX_PI * 0.25f,//45-degree field of view (float)width / (float)height, 1.0f, 1000.0f); _device->SetTransform(D3DTS_PROJECTION, &P); } return S_OK; } • The cleanup method is used to cleanup any resources that need to be freed before calling IDirect3DDevice9::Reset. In the corresponding sample program to this paper, this method’s definition is empty since there is nothing we need to cleanup before calling IDirect3DDevice9::Reset. Recall that only certain resources need to be released prior to IDirect3DDevice9::Reset, such as those in the D3DPOOL_DEFAULT memory pool. HRESULT CDirect3D9MFCView::cleanup() { // Nothing to Destroy. return S_OK; } • In the update method you will perform operations that need to be done on a frame-by-frame basis, such as, animation, collision detection, and testing for user input. Note that the time between frames is to be passed into this function as an argument. In this way you can sync operations based on time. The corresponding sample program to this paper implements this method as follows: HRESULT CDirect3D9MFCView::update(float timeDelta) { if( _device ) { Frank Luna www.moon-labs.com - 9 - // // Spin the teapot around the y-axis. // static float angle = 0.0f; D3DXMATRIX yRotationMatrix; D3DXMatrixRotationY(&yRotationMatrix, angle); _device->SetTransform(D3DTS_WORLD, &yRotationMatrix); angle += timeDelta; if(angle >= D3DX_PI * 2.0f) angle = 0.0f; } return S_OK; } • In the render method you perform your drawing commands, which also occur on a frame-by-frame basis. The corresponding sample program to this paper implements this method as follows: HRESULT CDirect3D9MFCView::render() { if( _device ) { // // Draw the scene. // _device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0); _device->BeginScene(); // Draw the teapot. _teapot->DrawSubset(0); _device->EndScene(); _device->Present(0, 0, 0, 0); } return S_OK; } 1.3.3 Creating the Direct3D Interfaces At this point, we need to figure out where to actually create our Direct3D interfaces. That is, where should we call initD3D? It doesn’t really matter where, except that we need to do it after we have a valid window handle and that we need to do it before we attempt to use the interfaces. I have elected to do it by overriding the Frank Luna www.moon-labs.com - 10 - CView::OnInitialUpdate method. To override this method, select the Overrides button on the properties tab for CDirect3DMFCView in the lower right corner of VS7 (figure (5)). Find the OnInitialUpdate method from the list, select it, then select the down arrow, and click Add. Figure 5: Select the Overrides button and find and select the “OnInitialUpdate” item from the list, then select the down arrow, and choose “Add”. In the sample program, OnInitialUpdate is implemented as follows: void CDirect3D9MFCView::OnInitialUpdate() { CView::OnInitialUpdate(); CRect rect; GetClientRect(&rect); // Initialize Direct3D (e.g. acquire a IDirect3DDevice9 poniter). HRESULT hr = initD3D( GetSafeHwnd(), rect.right, rect.bottom, true, D3DDEVTYPE_HAL); if(FAILED(hr)) { MessageBox("initD3D() - Failed", "Error"); ::PostQuitMessage(0); } Frank Luna www.moon-labs.com - 11 - // Setup the application. hr = setup(rect.right, rect.bottom); if(FAILED(hr)) { MessageBox("setup() - Failed", "Error"); ::PostQuitMessage(0); } } 1.3.4 Handling WM_SIZE We have yet to define what is to occur when the window is resized. Since several Direct3D components are dependant on the size of the window, namely the backbuffer, viewport, and projection matrix, we ought to reset these components whenever the size of the window changes. We do this by handling the WM_SIZE message. Locate the Messages button on the properties tab for CDirect3DMFCView in the lower right corner of VS7, which is next to the Overrides button shown in figure (5). Find the WM_SIZE message and select it, then select the down arrow, and click the Add OnSize item. In the sample program, OnSize is implemented as follows: void CDirect3D9MFCView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); if( _device ) { HRESULT hr = 0; // On a resize we must change the dimensions of the // back buffers to match the new window size. _d3dpp.BackBufferWidth = cx; _d3dpp.BackBufferHeight = cy; // We are about to call Reset, free any resources // that need to be freed prior to a Reset. hr = cleanup(); if(FAILED(hr)) { MessageBox("destroy() - Failed", "Error"); ::PostQuitMessage(0); } // Reset the flipping chain with the new window dimensions. // Note that all device states are reset to the default // after this call. hr = _device->Reset(&_d3dpp); if(FAILED(hr)) { MessageBox("Reset() - Failed", "Error"); ::PostQuitMessage(0); } // Reinitialize resource and device states since we Frank Luna www.moon-labs.com - 12 - // Reset everything. hr = setup(cx, cy); if(FAILED(hr)) { MessageBox("setup() - Failed", "Error"); ::PostQuitMessage(0); } } } 1.3.5 Handling WM_ERASEBKGND While we are on the topic of message handlers we should write a handler for the WM_ERASEBKGND message. Basically, we want to handle this function and have it do nothing, which prevents MFC from erasing the background every time we redraw it. Stopping MFC from erasing the background is desired since we are using Direct3D for graphics, that is, we use Direct3D to erase the background and we don’t want MFC and Direct3D to conflict with each other. Furthermore, a sort of flicker will occur if we let MFC erase the background. You can override this method the same way we overrode the WM_SIZE method, this time look for WM_ERASEBKGND. BOOL CDirect3D9MFCView::OnEraseBkgnd(CDC* pDC) { return FALSE; } 1.3.6 Updating and Rendering We now need to turn our attention to integrating the update and render methods into the MFC framework. In a regular Win32 application we normally update and render during idle time. We take the same approach here by overriding CWinApp::OnIdle. You can override CWinApp::OnIdle the same way we overrode CView::OnInitialUpdate, however, be sure that you are selecting methods to override from your application class (e.g. CDirect3D9MFCApp) and not the view class, since OnIdle is a method of CWinApp. In the sample, we implement CDirect3D9MFCApp::OnIdle as follows: BOOL CDirect3D9MFCApp::OnIdle(LONG lCount) { CWinApp::OnIdle(lCount); CWnd* mainFrame = AfxGetMainWnd(); CDirect3D9MFCView* cview = (CDirect3D9MFCView*)mainFrame->GetWindow(GW_CHILD); // Save last time. static float lastTime = (float)timeGetTime(); // Compute time now. float currentTime = (float)timeGetTime(); Frank Luna www.moon-labs.com - 13 - // Compute the difference: time elapsed in seconds. float deltaTime = (currentTime - lastTime) * 0.001f; // Last time is now current time. lastTime = currentTime; cview->update(deltaTime); cview->render(); return TRUE; } First we call the parent implementation of CWinApp::OnIdle so that MFC will take care of what it needs to do with idle time, like updating user interface components. Next we get a pointer to our main window, which is the main frame. Then from the main frame we get a pointer to the view class. The next four lines of code are used to compute the time elapsed between frames. Note that you will need to include mmsystem.h for the timeGetTime function. Now we can call our update and render methods using the view class pointer we obtained. Finally, we return a non-zero value because we want MFC to keep on running our idle method as long as the message queue is empty. If we did not return zero, it would indicate to MFC that we don’t want to do any further idle processing until the next message is processed. This is the last step, and at this point you should be done. Compile, build, and execute your program. If it does not work, compare your code to the corresponding sample source code for this article. 1.4 Summary 1. Integrate Direct3D with MFC by having the CDirect3D9MFCView (CView) class contain the Direct3D related interfaces. This makes sense because CDirect3D9MFCView is responsible for drawing data and will need direct access to Direct3D to do that drawing. 2. To initialize Direct3D we need a valid handle to the window we are going to draw onto, therefore initialize Direct3D in a place where such a handle is available, such as CDirect3D9MFCView::OnInitialUpdate. 3. Handle the WM_SIZE message to update Direct3D components that are dependant upon the window size, such as the backbuffer, viewport, and the projection matrix. 4. Handle the WM_ERASEBKGND message to prevent MFC from updating the background when your window is redrawn. Frank Luna www.moon-labs.com - 14 - 5. Override CWinApp::OnIdle and implement the code that you want executed during idle time, such as updating and rendering the 3D scene. I hope you have found this article useful. As we have learned, integrating Direct3D and MFC is not difficult; it is simply a matter of knowing where Direct3D fits into the MFC framework. You can leave feedback and or corrections either on the message forums at www.moon-labs.com or email me directly at frank@moon-labs.com.
本文档为【D3D和MFC的整合】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_237443
暂无简介~
格式:pdf
大小:446KB
软件:PDF阅读器
页数:0
分类:互联网
上传时间:2011-07-21
浏览量:17