DXUT 主程序源文件结构分析
分析文件来自于DirectX Sample Browser下的 Pick例程:
第一部分:头文件及帮助编译的宏定义
//-------------------------------------------------------------------------------------- // File: Pick.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. //-------------------------------------------------------------------------------------- #include "DXUT.h" #include "DXUTcamera.h" #include "DXUTsettingsdlg.h" #include "SDKmesh.h" #include "SDKmisc.h" #include "resource.h" //#define DEBUG_VS // Uncomment this line to debug vertex shaders //#define DEBUG_PS // Uncomment this line to debug pixel shaders
这一部分出现的宏定义是帮助帮助编译器进行某些操作的,此处两个宏定义被注释了。没有被注释时的功能是对vertex shader 和pixel shader进行debug。这两个过程究竟是怎么进行的,暂时还没查到相关资料。
//TODO
//#define DEBUG_VS // Uncomment this line to debug vertex shaders //#define DEBUG_PS // Uncomment this line to debug pixel shaders
第二部分:定义和声明用户自定义的结构体以及用户预设宏定义
//-------------------------------------------------------------------------------------- // Vertex format //-------------------------------------------------------------------------------------- struct D3DVERTEX { D3DXVECTOR3 p; D3DXVECTOR3 n; FLOAT tu, tv; static const DWORD FVF; }; const DWORD D3DVERTEX::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1; struct INTERSECTION { DWORD dwFace; // mesh face that was intersected FLOAT fBary1, fBary2; // barycentric coords of intersection FLOAT fDist; // distance from ray origin to intersection FLOAT tu, tv; // texture coords of intersection }; // For simplicity's sake, we limit the number of simultaneously intersected // triangles to 16 #define MAX_INTERSECTIONS 16 #define CAMERA_DISTANCE 3.5f
在这部分定义和声明游湖自定义的相关简单的结构体,如例程中声明了一个灵活的定点格式的结构体和一个焦点结构体。
用户在这部分声明的宏定义带有自定性。
第三部分:全局变量声明区
//-------------------------------------------------------------------------------------- // Global variables //-------------------------------------------------------------------------------------- ID3DXFont* g_pFont = NULL; // Font for drawing text ID3DXSprite* g_pTextSprite = NULL; // Sprite for batching draw text calls ID3DXEffect* g_pEffect = NULL; // D3DX effect interface CDXUTXFileMesh g_Mesh; // The mesh to be rendered CModelViewerCamera g_Camera; // A model viewing camera DWORD g_dwNumIntersections; // Number of faces intersected INTERSECTION g_IntersectionArray[MAX_INTERSECTIONS]; // Intersection info LPDIRECT3DVERTEXBUFFER9 g_pVB; // VB for Picked triangles bool g_bShowHelp = true; // If true, it renders the UI control text bool g_bUseD3DXIntersect = true; // Whether to use D3DXIntersect bool g_bAllHits = true; // Whether to just get the first "hit" or all "hits" CDXUTDialogResourceManager g_DialogResourceManager; // manager for shared resources of dialogs CD3DSettingsDlg g_SettingsDlg; // Device settings dialog CDXUTDialog g_HUD; // dialog for standard controls CDXUTDialog g_SampleUI; // dialog for sample specific controls
第四部分:UI控件 ID声明区
//-------------------------------------------------------------------------------------- // UI control IDs //-------------------------------------------------------------------------------------- #define IDC_TOGGLEFULLSCREEN 1 #define IDC_TOGGLEREF 2 #define IDC_CHANGEDEVICE 3 #define IDC_USED3DX 4 #define IDC_ALLHITS 5
关于UI:思考了下自定义UI的设计方法,而且还看了看DXUT中自定义控件的设计文档,感觉学到了很多。比如控件一定是依附于对话框,设计控件类时从最基础的控件原型开始,其他它控件继承父类控件最终形成所需的控件。后面会写一个关于自定义控件写法的随笔。
第五部分:函数声明区
//-------------------------------------------------------------------------------------- // Forward declarations //-------------------------------------------------------------------------------------- bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext ); bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext ); HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext ); HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext ); void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext ); void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext ); LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext ); void CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext ); void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext ); void CALLBACK OnLostDevice( void* pUserContext ); void CALLBACK OnDestroyDevice( void* pUserContext ); void InitApp(); void RenderText(); HRESULT Pick(); bool IntersectTriangle( const D3DXVECTOR3& orig, const D3DXVECTOR3& dir, D3DXVECTOR3& v0, D3DXVECTOR3& v1, D3DXVECTOR3& v2, FLOAT* t, FLOAT* u, FLOAT* v );
后面有各函数的详细定义。
部分主要模块执行顺序:
函数名称 | 正常执行顺序 | 改变窗口大小 | 显示设备对话框 |
程序退出 |
InitApp | 1 | |||
OnCreateDevice | 2 | |||
OnResetDevice | 3 | 2 | 2 | |
OnFrameMove | 4 | |||
OnFrameRender | 5 | |||
MsgProc | ||||
KeyboardProc | ||||
OnGUIEvent | ||||
OnLostDevice | 6 | 1 | 1 | 1 |
OnDestroyDevice | 7 | 2 |
第六部分:程序入口函数
//-------------------------------------------------------------------------------------- // Entry point to the program. Initializes everything and goes into a message processing // loop. Idle time is used to render the scene. //-------------------------------------------------------------------------------------- INT WINAPI wWinMain( HINSTANCE, HINSTANCE, LPWSTR, int ) { // Enable run-time memory check for debug builds. #if defined(DEBUG) | defined(_DEBUG) _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif // Set the callback functions. These functions allow DXUT to notify // the application about device changes, user input, and windows messages. The // callbacks are optional so you need only set callbacks for events you're interested // in. However, if you don't handle the device reset/lost callbacks then the sample // framework won't be able to reset your device since the application must first // release all device resources before resetting. Likewise, if you don't handle the // device created/destroyed callbacks then DXUT won't be able to // recreate your device resources. DXUTSetCallbackD3D9DeviceAcceptable( IsDeviceAcceptable ); DXUTSetCallbackD3D9DeviceCreated( OnCreateDevice ); DXUTSetCallbackD3D9DeviceReset( OnResetDevice ); DXUTSetCallbackD3D9FrameRender( OnFrameRender ); DXUTSetCallbackD3D9DeviceLost( OnLostDevice ); DXUTSetCallbackD3D9DeviceDestroyed( OnDestroyDevice ); DXUTSetCallbackMsgProc( MsgProc ); DXUTSetCallbackKeyboard( KeyboardProc ); DXUTSetCallbackFrameMove( OnFrameMove ); DXUTSetCallbackDeviceChanging( ModifyDeviceSettings ); // Show the cursor and clip it when in full screen DXUTSetCursorSettings( true, true ); InitApp(); // Initialize DXUT and create the desired Win32 window and Direct3D // device for the application. Calling each of these functions is optional, but they // allow you to set several options which control the behavior of the framework. DXUTInit( true, true ); // Parse the command line and show msgboxes DXUTSetHotkeyHandling( true, true, true ); // handle the defaul hotkeys DXUTCreateWindow( L"Pick" ); DXUTCreateDevice( true, 640, 480 ); // Pass control to DXUT for handling the message pump and // dispatching render calls. DXUT will call your FrameMove // and FrameRender callback when there is idle time between handling window messages. DXUTMainLoop(); // Perform any application-level cleanup here. Direct3D device resources are released within the // appropriate callback functions and therefore don't require any cleanup code here. return DXUTGetExitCode(); }
程序中注释的翻译:
设置回调函数。这些函数允许DXUT通知设备的变化,用户输入的应用程序,和windows消息。回调是可选的,你只需要为你感兴趣的事件设置回调函数。然而,如果你不处理设备的复位/失去(reset和lost)回调函数的话,框架会无法复位你的设备,因为应用程序在复位前必须释放所有的设备资源。同样的,如果你不处理设备创建/销毁回调函数,DXUT不能重建你的设备资源。
这些函数的基本描述:
OnCreateDevice()用于初始化应用程序资源,如装载模型、纹理和效果文件的变异等,程序只执行一次
OnResetDevice()用于复位设备,在窗口调整时会调用,多次。
OnFrameMove()根据时间设置变换矩阵,使场景具有动画效果。
OnFrameRender()用于渲染。
OnLostDevice()在检测到设备丢失时释放部分资源。
OnDestroyDevice()释放相关资源,如字体等以组件方式释放
第七部分:应用程序初始化InitApp()
//-------------------------------------------------------------------------------------- // Initialize the app //-------------------------------------------------------------------------------------- void InitApp() { // Initialize dialogs g_SettingsDlg.Init( &g_DialogResourceManager ); g_HUD.Init( &g_DialogResourceManager ); g_SampleUI.Init( &g_DialogResourceManager ); g_HUD.SetCallback( OnGUIEvent ); int iY = 10; g_HUD.AddButton( IDC_TOGGLEFULLSCREEN, L"Toggle full screen", 35, iY, 125, 22 ); g_HUD.AddButton( IDC_TOGGLEREF, L"Toggle REF (F3)", 35, iY += 24, 125, 22 ); g_HUD.AddButton( IDC_CHANGEDEVICE, L"Change device (F2)", 35, iY += 24, 125, 22, VK_F2 ); g_HUD.AddCheckBox( IDC_USED3DX, L"Use D3DXIntersect", 35, iY += 24, 125, 22, g_bUseD3DXIntersect, VK_F4 ); g_HUD.AddCheckBox( IDC_ALLHITS, L"Show All Hits", 35, iY += 24, 125, 22, g_bAllHits, VK_F5 ); g_SampleUI.SetCallback( OnGUIEvent ); iY = 10; }
对用户UI进参数填充。HUD
第八部分:回调函数IsDeviceAcceptable()
//-------------------------------------------------------------------------------------- // Called during device initialization, this code checks the device for some // minimum set of capabilities, and rejects those that don't pass by returning false. //-------------------------------------------------------------------------------------- bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext ) { // Skip backbuffer formats that don't support alpha blending IDirect3D9* pD3D = DXUTGetD3D9Object(); if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, BackBufferFormat ) ) ) return false; // No fallback defined by this app, so reject any device that // doesn't support at least ps2.0 if( pCaps->PixelShaderVersion < D3DPS_VERSION( 2, 0 ) ) return false; return true; }
第九部分:回调函数ModifyDeviceSettings()
//-------------------------------------------------------------------------------------- // This callback function is called immediately before a device is created to allow the // application to modify the device settings. The supplied pDeviceSettings parameter // contains the settings that the framework has selected for the new device, and the // application can make any desired changes directly to this structure. Note however that // DXUT will not correct invalid device settings so care must be taken // to return valid device settings, otherwise IDirect3D9::CreateDevice() will fail. //-------------------------------------------------------------------------------------- bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext ) { assert( DXUT_D3D9_DEVICE == pDeviceSettings->ver ); HRESULT hr; IDirect3D9* pD3D = DXUTGetD3D9Object(); D3DCAPS9 caps; V( pD3D->GetDeviceCaps( pDeviceSettings->d3d9.AdapterOrdinal, pDeviceSettings->d3d9.DeviceType, &caps ) ); // If device doesn't support HW T&L or doesn't support 1.1 vertex shaders in HW // then switch to SWVP. if( ( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) == 0 || caps.VertexShaderVersion < D3DVS_VERSION( 1, 1 ) ) { pDeviceSettings->d3d9.BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING; } // Debugging vertex shaders requires either REF or software vertex processing // and debugging pixel shaders requires REF. #ifdef DEBUG_VS if( pDeviceSettings->d3d9.DeviceType != D3DDEVTYPE_REF ) { pDeviceSettings->d3d9.BehaviorFlags &= ~D3DCREATE_HARDWARE_VERTEXPROCESSING; pDeviceSettings->d3d9.BehaviorFlags &= ~D3DCREATE_PUREDEVICE; pDeviceSettings->d3d9.BehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; } #endif #ifdef DEBUG_PS pDeviceSettings->d3d9.DeviceType = D3DDEVTYPE_REF; #endif // For the first device created if its a REF device, optionally display a warning dialog box static bool s_bFirstTime = true; if( s_bFirstTime ) { s_bFirstTime = false; if( pDeviceSettings->d3d9.DeviceType == D3DDEVTYPE_REF ) DXUTDisplaySwitchingToREFWarning( pDeviceSettings->ver ); } return true; }
第十部分:回调函数OnCreateDevice()
//-------------------------------------------------------------------------------------- // This callback function will be called immediately after the Direct3D device has been // created, which will happen during application initialization and windowed/full screen // toggles. This is the best location to create D3DPOOL_MANAGED resources since these // resources need to be reloaded whenever the device is destroyed. Resources created // here should be released in the OnDestroyDevice callback. //-------------------------------------------------------------------------------------- HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext ) { WCHAR str[MAX_PATH]; HRESULT hr; V_RETURN( g_DialogResourceManager.OnD3D9CreateDevice( pd3dDevice ) ); V_RETURN( g_SettingsDlg.OnD3D9CreateDevice( pd3dDevice ) ); // Initialize the font V_RETURN( D3DXCreateFont( pd3dDevice, 15, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial", &g_pFont ) ); // Load the mesh with D3DX and get back a ID3DXMesh*. For this // sample we'll ignore the X file's embedded materials since we know // exactly the model we're loading. See the mesh samples such as // "OptimizedMesh" for a more generic mesh loading example. V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, TEXT( "scanner\\scannerarm.x" ) ) ); V_RETURN( g_Mesh.Create( pd3dDevice, str ) ); V_RETURN( g_Mesh.SetFVF( pd3dDevice, D3DVERTEX::FVF ) ); // Create the vertex buffer if( FAILED( pd3dDevice->CreateVertexBuffer( 3 * MAX_INTERSECTIONS * sizeof( D3DVERTEX ), D3DUSAGE_WRITEONLY, D3DVERTEX::FVF, D3DPOOL_MANAGED, &g_pVB, NULL ) ) ) { return E_FAIL; } // Define DEBUG_VS and/or DEBUG_PS to debug vertex and/or pixel shaders with the // shader debugger. Debugging vertex shaders requires either REF or software vertex // processing, and debugging pixel shaders requires REF. The // D3DXSHADER_FORCE_*_SOFTWARE_NOOPT flag improves the debug experience in the // shader debugger. It enables source level debugging, prevents instruction // reordering, prevents dead code elimination, and forces the compiler to compile // against the next higher available software target, which ensures that the // unoptimized shaders do not exceed the shader model limitations. Setting these // flags will cause slower rendering since the shaders will be unoptimized and // forced into software. See the DirectX documentation for more information about // using the shader debugger. DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE; #if defined( DEBUG ) || defined( _DEBUG ) // Set the D3DXSHADER_DEBUG flag to embed debug information in the shaders. // Setting this flag improves the shader debugging experience, but still allows // the shaders to be optimized and to run exactly the way they will run in // the release configuration of this program. dwShaderFlags |= D3DXSHADER_DEBUG; #endif #ifdef DEBUG_VS dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT; #endif #ifdef DEBUG_PS dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT; #endif // Read the D3DX effect file V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"Pick.fx" ) ); // If this fails, there should be debug output as to // they the .fx file failed to compile V_RETURN( D3DXCreateEffectFromFile( pd3dDevice, str, NULL, NULL, dwShaderFlags, NULL, &g_pEffect, NULL ) ); // Set effect variables as needed D3DXCOLOR colorMtrl( 1.0f, 1.0f, 1.0f, 1.0f ); D3DXVECTOR3 vLightDir( 0.1f, -1.0f, 0.1f ); D3DXCOLOR vLightDiffuse( 1,1,1,1 ); V_RETURN( g_pEffect->SetValue( "g_MaterialAmbientColor", &colorMtrl, sizeof( D3DXCOLOR ) ) ); V_RETURN( g_pEffect->SetValue( "g_MaterialDiffuseColor", &colorMtrl, sizeof( D3DXCOLOR ) ) ); V_RETURN( g_pEffect->SetValue( "g_LightDir", &vLightDir, sizeof( D3DXVECTOR3 ) ) ); V_RETURN( g_pEffect->SetValue( "g_LightDiffuse", &vLightDiffuse, sizeof( D3DXVECTOR4 ) ) ); V_RETURN( g_pEffect->SetTexture( "g_MeshTexture", g_Mesh.m_pTextures[0] ) ); // Setup the camera's view parameters D3DXVECTOR3 vecEye( -CAMERA_DISTANCE, 0.0f, 0.0f ); D3DXVECTOR3 vecAt ( 0.0f, 0.0f, 0.0f ); g_Camera.SetViewParams( &vecEye, &vecAt ); // Setup the world matrix of the camera // Change this to see how Picking works with a translated object D3DXMATRIX mWorld; D3DXMatrixTranslation( &mWorld, 0.0f, -1.7f, 0.0f ); g_Camera.SetWorldMatrix( mWorld ); return S_OK; }
第十一部分:回调函数OnFrameMove()
//-------------------------------------------------------------------------------------- // This callback function will be called once at the beginning of every frame. This is the // best location for your application to handle updates to the scene, but is not // intended to contain actual rendering calls, which should instead be placed in the // OnFrameRender callback. //-------------------------------------------------------------------------------------- void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext ) { // Rotate the camera about the y-axis D3DXVECTOR3 vFromPt = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ); D3DXVECTOR3 vLookatPt = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ); vFromPt.x = -cosf( ( float )fTime / 3.0f ) * CAMERA_DISTANCE; vFromPt.y = 1.0f; vFromPt.z = sinf( ( float )fTime / 3.0f ) * CAMERA_DISTANCE; // Update the camera's position based on time g_Camera.SetViewParams( &vFromPt, &vLookatPt ); }
第十二部分:回调函数OnFrameRender()
//-------------------------------------------------------------------------------------- // This callback function will be called at the end of every frame to perform all the // rendering calls for the scene, and it will also be called if the window needs to be // repainted. After this function has returned, DXUT will call // IDirect3DDevice9::Present to display the contents of the next buffer in the swap chain //-------------------------------------------------------------------------------------- void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext ) { HRESULT hr; D3DXMATRIXA16 mWorld; D3DXMATRIXA16 mView; D3DXMATRIXA16 mProj; D3DXMATRIXA16 mWorldViewProjection; // If the settings dialog is being shown, then // render it instead of rendering the app's scene if( g_SettingsDlg.IsActive() ) { g_SettingsDlg.OnRender( fElapsedTime ); return; } // Check for Picked triangles Pick(); // Clear the render target and the zbuffer V( pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB( 0, 45, 50, 170 ), 1.0f, 0 ) ); // Render the scene if( SUCCEEDED( pd3dDevice->BeginScene() ) ) { // Get the projection & view matrix from the camera class mWorld = *g_Camera.GetWorldMatrix(); mProj = *g_Camera.GetProjMatrix(); mView = *g_Camera.GetViewMatrix(); mWorldViewProjection = mWorld * mView * mProj; V( g_pEffect->SetTechnique( "RenderScene" ) ); // Update the effect's variables. Instead of using strings, it would // be more efficient to cache a handle to the parameter by calling // ID3DXEffect::GetParameterByName V( g_pEffect->SetMatrix( "g_mWorldViewProjection", &mWorldViewProjection ) ); V( g_pEffect->SetMatrix( "g_mWorld", &mWorld ) ); V( g_pEffect->SetFloat( "g_fTime", ( float )fTime ) ); UINT uPasses; V( g_pEffect->Begin( &uPasses, 0 ) ); // Set render mode to lit, solid triangles pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE ); // If a triangle is Picked, draw it if( g_dwNumIntersections > 0 ) { for( UINT uPass = 0; uPass < uPasses; ++uPass ) { V( g_pEffect->BeginPass( uPass ) ); // Draw the Picked triangle pd3dDevice->SetFVF( D3DVERTEX::FVF ); pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( D3DVERTEX ) ); pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, g_dwNumIntersections ); V( g_pEffect->EndPass() ); } // Set render mode to unlit, wireframe triangles pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME ); pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); } V( g_pEffect->End() ); // Render the mesh V( g_Mesh.Render( g_pEffect ) ); DXUT_BeginPerfEvent( DXUT_PERFEVENTCOLOR, L"HUD / Stats" ); // These events are to help PIX identify what the code is doing RenderText(); V( g_HUD.OnRender( fElapsedTime ) ); V( g_SampleUI.OnRender( fElapsedTime ) ); DXUT_EndPerfEvent(); V( pd3dDevice->EndScene() ); } }
第十三部分:函数RenderText()
//-------------------------------------------------------------------------------------- // Render the help and statistics text. This function uses the ID3DXFont interface for // efficient text rendering. //-------------------------------------------------------------------------------------- void RenderText() { // The helper object simply helps keep track of text position, and color // and then it calls pFont->DrawText( m_pSprite, strMsg, -1, &rc, DT_NOCLIP, m_clr ); // If NULL is passed in as the sprite object, then it will work however the // pFont->DrawText() will not be batched together. Batching calls will improves performance. CDXUTTextHelper txtHelper( g_pFont, g_pTextSprite, 15 ); // Output statistics txtHelper.Begin(); txtHelper.SetInsertionPos( 5, 5 ); txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) ); txtHelper.DrawTextLine( DXUTGetFrameStats( DXUTIsVsyncEnabled() ) ); txtHelper.DrawTextLine( DXUTGetDeviceStats() ); txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) ); if( g_dwNumIntersections < 1 ) { txtHelper.DrawTextLine( L"Use mouse to Pick a polygon" ); } else { WCHAR wstrHitStat[256]; for( DWORD dwIndex = 0; dwIndex < g_dwNumIntersections; dwIndex++ ) { swprintf_s( wstrHitStat, 256, L"Face=%d, tu=%3.02f, tv=%3.02f", g_IntersectionArray[dwIndex].dwFace, g_IntersectionArray[dwIndex].tu, g_IntersectionArray[dwIndex].tv ); txtHelper.DrawTextLine( wstrHitStat ); } } txtHelper.End(); }
第十四部分:消息处理函数MsgProc
//-------------------------------------------------------------------------------------- // Before handling window messages, DXUT passes incoming windows // messages to the application through this callback function. If the application sets // *pbNoFurtherProcessing to TRUE, then DXUT will not process this message. //-------------------------------------------------------------------------------------- LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext ) { // Always allow dialog resource manager calls to handle global messages // so GUI state is updated correctly *pbNoFurtherProcessing = g_DialogResourceManager.MsgProc( hWnd, uMsg, wParam, lParam ); if( *pbNoFurtherProcessing ) return 0; if( g_SettingsDlg.IsActive() ) { g_SettingsDlg.MsgProc( hWnd, uMsg, wParam, lParam ); return 0; } // Give the dialogs a chance to handle the message first *pbNoFurtherProcessing = g_HUD.MsgProc( hWnd, uMsg, wParam, lParam ); if( *pbNoFurtherProcessing ) return 0; *pbNoFurtherProcessing = g_SampleUI.MsgProc( hWnd, uMsg, wParam, lParam ); if( *pbNoFurtherProcessing ) return 0; // Pass all remaining windows messages to camera so it can respond to user input //g_Camera.HandleMessages( hWnd, uMsg, wParam, lParam ); switch( uMsg ) { case WM_LBUTTONDOWN: { // Capture the mouse, so if the mouse button is // released outside the window, we'll get the WM_LBUTTONUP message DXUTGetGlobalTimer()->Stop(); SetCapture( hWnd ); return TRUE; } case WM_LBUTTONUP: { ReleaseCapture(); DXUTGetGlobalTimer()->Start(); break; } case WM_CAPTURECHANGED: { if( ( HWND )lParam != hWnd ) { ReleaseCapture(); DXUTGetGlobalTimer()->Start(); } break; } } return 0; }
第十五部分:键盘响应函数
//-------------------------------------------------------------------------------------- // As a convenience, DXUT inspects the incoming windows messages for // keystroke messages and decodes the message parameters to pass relevant keyboard // messages to the application. The framework does not remove the underlying keystroke // messages, which are still passed to the application's MsgProc callback. //-------------------------------------------------------------------------------------- void CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext ) { if( bKeyDown ) { switch( nChar ) { case VK_F1: g_bShowHelp = !g_bShowHelp; break; } } }
第十六部分:GUI事件处理函数
//-------------------------------------------------------------------------------------- // Handles the GUI events //-------------------------------------------------------------------------------------- void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext ) { switch( nControlID ) { case IDC_TOGGLEFULLSCREEN: DXUTToggleFullScreen(); break; case IDC_TOGGLEREF: DXUTToggleREF(); break; case IDC_CHANGEDEVICE: g_SettingsDlg.SetActive( !g_SettingsDlg.IsActive() ); break; case IDC_USED3DX: g_bUseD3DXIntersect = !g_bUseD3DXIntersect; break; case IDC_ALLHITS: g_bAllHits = !g_bAllHits; break; } }
第十七部分:回调函数OnLostDevice()
//-------------------------------------------------------------------------------------- // This callback function will be called immediately after the Direct3D device has // entered a lost state and before IDirect3DDevice9::Reset is called. Resources created // in the OnResetDevice callback should be released here, which generally includes all // D3DPOOL_DEFAULT resources. See the "Lost Devices" section of the documentation for // information about lost devices. //-------------------------------------------------------------------------------------- void CALLBACK OnLostDevice( void* pUserContext ) { g_DialogResourceManager.OnD3D9LostDevice(); g_SettingsDlg.OnD3D9LostDevice(); if( g_pFont ) g_pFont->OnLostDevice(); if( g_pEffect ) g_pEffect->OnLostDevice(); SAFE_RELEASE( g_pTextSprite ); }
第十八部分:回调函数OnDestroyDevice()
//-------------------------------------------------------------------------------------- // This callback function will be called immediately after the Direct3D device has // been destroyed, which generally happens as a result of application termination or // windowed/full screen toggles. Resources created in the OnCreateDevice callback // should be released here, which generally includes all D3DPOOL_MANAGED resources. //-------------------------------------------------------------------------------------- void CALLBACK OnDestroyDevice( void* pUserContext ) { g_DialogResourceManager.OnD3D9DestroyDevice(); g_SettingsDlg.OnD3D9DestroyDevice(); g_Mesh.Destroy(); SAFE_RELEASE( g_pVB ); SAFE_RELEASE( g_pEffect ); SAFE_RELEASE( g_pFont ); }
第十九部分:主功能函数pick()
//-------------------------------------------------------------------------------------- // Checks if mouse point hits geometry the scene. //-------------------------------------------------------------------------------------- HRESULT Pick() { HRESULT hr; D3DXVECTOR3 vPickRayDir; D3DXVECTOR3 vPickRayOrig; IDirect3DDevice9* pD3Device = DXUTGetD3D9Device(); const D3DSURFACE_DESC* pd3dsdBackBuffer = DXUTGetD3D9BackBufferSurfaceDesc(); g_dwNumIntersections = 0L; // Get the Pick ray from the mouse position if( GetCapture() ) { const D3DXMATRIX* pmatProj = g_Camera.GetProjMatrix(); POINT ptCursor; GetCursorPos( &ptCursor ); ScreenToClient( DXUTGetHWND(), &ptCursor ); // Compute the vector of the Pick ray in screen space D3DXVECTOR3 v; v.x = ( ( ( 2.0f * ptCursor.x ) / pd3dsdBackBuffer->Width ) - 1 ) / pmatProj->_11; v.y = -( ( ( 2.0f * ptCursor.y ) / pd3dsdBackBuffer->Height ) - 1 ) / pmatProj->_22; v.z = 1.0f; // Get the inverse view matrix const D3DXMATRIX matView = *g_Camera.GetViewMatrix(); const D3DXMATRIX matWorld = *g_Camera.GetWorldMatrix(); D3DXMATRIX mWorldView = matWorld * matView; D3DXMATRIX m; D3DXMatrixInverse( &m, NULL, &mWorldView ); // Transform the screen space Pick ray into 3D space vPickRayDir.x = v.x * m._11 + v.y * m._21 + v.z * m._31; vPickRayDir.y = v.x * m._12 + v.y * m._22 + v.z * m._32; vPickRayDir.z = v.x * m._13 + v.y * m._23 + v.z * m._33; vPickRayOrig.x = m._41; vPickRayOrig.y = m._42; vPickRayOrig.z = m._43; } // Get the Picked triangle if( GetCapture() ) { LPD3DXMESH pMesh; g_Mesh.GetMesh()->CloneMeshFVF( D3DXMESH_MANAGED, g_Mesh.GetMesh()->GetFVF(), pD3Device, &pMesh ); LPDIRECT3DVERTEXBUFFER9 pVB; LPDIRECT3DINDEXBUFFER9 pIB; pMesh->GetVertexBuffer( &pVB ); pMesh->GetIndexBuffer( &pIB ); WORD* pIndices; D3DVERTEX* pVertices; pIB->Lock( 0, 0, ( void** )&pIndices, 0 ); pVB->Lock( 0, 0, ( void** )&pVertices, 0 ); if( g_bUseD3DXIntersect ) { // When calling D3DXIntersect, one can get just the closest intersection and not // need to work with a D3DXBUFFER. Or, to get all intersections between the ray and // the mesh, one can use a D3DXBUFFER to receive all intersections. We show both // methods. if( !g_bAllHits ) { // Collect only the closest intersection BOOL bHit; DWORD dwFace; FLOAT fBary1, fBary2, fDist; D3DXIntersect( pMesh, &vPickRayOrig, &vPickRayDir, &bHit, &dwFace, &fBary1, &fBary2, &fDist, NULL, NULL ); if( bHit ) { g_dwNumIntersections = 1; g_IntersectionArray[0].dwFace = dwFace; g_IntersectionArray[0].fBary1 = fBary1; g_IntersectionArray[0].fBary2 = fBary2; g_IntersectionArray[0].fDist = fDist; } else { g_dwNumIntersections = 0; } } else { // Collect all intersections BOOL bHit; LPD3DXBUFFER pBuffer = NULL; D3DXINTERSECTINFO* pIntersectInfoArray; if( FAILED( hr = D3DXIntersect( pMesh, &vPickRayOrig, &vPickRayDir, &bHit, NULL, NULL, NULL, NULL, &pBuffer, &g_dwNumIntersections ) ) ) { SAFE_RELEASE( pMesh ); SAFE_RELEASE( pVB ); SAFE_RELEASE( pIB ); return hr; } if( g_dwNumIntersections > 0 ) { pIntersectInfoArray = ( D3DXINTERSECTINFO* )pBuffer->GetBufferPointer(); if( g_dwNumIntersections > MAX_INTERSECTIONS ) g_dwNumIntersections = MAX_INTERSECTIONS; for( DWORD iIntersection = 0; iIntersection < g_dwNumIntersections; iIntersection++ ) { g_IntersectionArray[iIntersection].dwFace = pIntersectInfoArray[iIntersection].FaceIndex; g_IntersectionArray[iIntersection].fBary1 = pIntersectInfoArray[iIntersection].U; g_IntersectionArray[iIntersection].fBary2 = pIntersectInfoArray[iIntersection].V; g_IntersectionArray[iIntersection].fDist = pIntersectInfoArray[iIntersection].Dist; } } SAFE_RELEASE( pBuffer ); } } else { // Not using D3DX DWORD dwNumFaces = g_Mesh.GetMesh()->GetNumFaces(); FLOAT fBary1, fBary2; FLOAT fDist; for( DWORD i = 0; i < dwNumFaces; i++ ) { D3DXVECTOR3 v0 = pVertices[pIndices[3 * i + 0]].p; D3DXVECTOR3 v1 = pVertices[pIndices[3 * i + 1]].p; D3DXVECTOR3 v2 = pVertices[pIndices[3 * i + 2]].p; // Check if the Pick ray passes through this point if( IntersectTriangle( vPickRayOrig, vPickRayDir, v0, v1, v2, &fDist, &fBary1, &fBary2 ) ) { if( g_bAllHits || g_dwNumIntersections == 0 || fDist < g_IntersectionArray[0].fDist ) { if( !g_bAllHits ) g_dwNumIntersections = 0; g_IntersectionArray[g_dwNumIntersections].dwFace = i; g_IntersectionArray[g_dwNumIntersections].fBary1 = fBary1; g_IntersectionArray[g_dwNumIntersections].fBary2 = fBary2; g_IntersectionArray[g_dwNumIntersections].fDist = fDist; g_dwNumIntersections++; if( g_dwNumIntersections == MAX_INTERSECTIONS ) break; } } } } // Now, for each intersection, add a triangle to g_pVB and compute texture coordinates if( g_dwNumIntersections > 0 ) { D3DVERTEX* v; D3DVERTEX* vThisTri; WORD* iThisTri; D3DVERTEX v1, v2, v3; INTERSECTION* pIntersection; g_pVB->Lock( 0, 0, ( void** )&v, 0 ); for( DWORD iIntersection = 0; iIntersection < g_dwNumIntersections; iIntersection++ ) { pIntersection = &g_IntersectionArray[iIntersection]; vThisTri = &v[iIntersection * 3]; iThisTri = &pIndices[3 * pIntersection->dwFace]; // get vertices hit vThisTri[0] = pVertices[iThisTri[0]]; vThisTri[1] = pVertices[iThisTri[1]]; vThisTri[2] = pVertices[iThisTri[2]]; // If all you want is the vertices hit, then you are done. In this sample, we // want to show how to infer texture coordinates as well, using the BaryCentric // coordinates supplied by D3DXIntersect FLOAT dtu1 = vThisTri[1].tu - vThisTri[0].tu; FLOAT dtu2 = vThisTri[2].tu - vThisTri[0].tu; FLOAT dtv1 = vThisTri[1].tv - vThisTri[0].tv; FLOAT dtv2 = vThisTri[2].tv - vThisTri[0].tv; pIntersection->tu = vThisTri[0].tu + pIntersection->fBary1 * dtu1 + pIntersection->fBary2 * dtu2; pIntersection->tv = vThisTri[0].tv + pIntersection->fBary1 * dtv1 + pIntersection->fBary2 * dtv2; } g_pVB->Unlock(); } pVB->Unlock(); pIB->Unlock(); SAFE_RELEASE( pMesh ); SAFE_RELEASE( pVB ); SAFE_RELEASE( pIB ); } return S_OK; }
第二十部分:主功能函数IntersectTriangle()
//-------------------------------------------------------------------------------------- // Given a ray origin (orig) and direction (dir), and three vertices of a triangle, this // function returns TRUE and the interpolated texture coordinates if the ray intersects // the triangle //-------------------------------------------------------------------------------------- bool IntersectTriangle( const D3DXVECTOR3& orig, const D3DXVECTOR3& dir, D3DXVECTOR3& v0, D3DXVECTOR3& v1, D3DXVECTOR3& v2, FLOAT* t, FLOAT* u, FLOAT* v ) { // Find vectors for two edges sharing vert0 D3DXVECTOR3 edge1 = v1 - v0; D3DXVECTOR3 edge2 = v2 - v0; // Begin calculating determinant - also used to calculate U parameter D3DXVECTOR3 pvec; D3DXVec3Cross( &pvec, &dir, &edge2 ); // If determinant is near zero, ray lies in plane of triangle FLOAT det = D3DXVec3Dot( &edge1, &pvec ); D3DXVECTOR3 tvec; if( det > 0 ) { tvec = orig - v0; } else { tvec = v0 - orig; det = -det; } if( det < 0.0001f ) return FALSE; // Calculate U parameter and test bounds *u = D3DXVec3Dot( &tvec, &pvec ); if( *u < 0.0f || *u > det ) return FALSE; // Prepare to test V parameter D3DXVECTOR3 qvec; D3DXVec3Cross( &qvec, &tvec, &edge1 ); // Calculate V parameter and test bounds *v = D3DXVec3Dot( &dir, &qvec ); if( *v < 0.0f || *u + *v > det ) return FALSE; // Calculate t, scale parameters, ray intersects triangle *t = D3DXVec3Dot( &edge2, &qvec ); FLOAT fInvDet = 1.0f / det; *t *= fInvDet; *u *= fInvDet; *v *= fInvDet; return TRUE; }