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;
}
原文地址:https://www.cnblogs.com/moniza/p/3570853.html