DXUT中控制項、文本繪製、像機

來源:互聯網
上載者:User
  DXUT 攝像機

DXUT中的CModelViewerCamera 類可以簡單的的管理檢視變換和透視變換, 就像GUI 的功能一樣.

    CModelViewerCamera g_Camera;  // A model viewing camera

攝像機類提供的第一個功能是建立視圖和透視矩陣. 有了這個攝像機,沒有必要擔心這些矩陣. 反而, 你可以指定你在哪, 你看著什麼,還有視窗的大小. 然後, 把這些參數傳遞給攝像機對象, 它會在後台建立這些矩陣.

這裡我們設定攝像機的一部分視圖參數. 我們指定我們在哪,和我們看著什麼.

    // Initialize the camera
    D3DXVECTOR3 Eye( 0.0f, 0.0f, -800.0f );
    D3DXVECTOR3 At( 0.0f, 0.0f, 0.0f );
    g_Camera.SetViewParams( &Eye, &At );

接下來,我們指定攝像機的投影參數. 也就是說, 我們需要提供觀察角度,縱橫比, 和視圖截錐的近、遠裁剪面. 這些資訊跟之前的指南提供的一樣, 不同的是, 你不必擔心怎麼去建立矩陣.

    // Setup the camera's projection parameters
    float fAspectRatio = pBackBufferSurfaceDesc->Width / (FLOAT)pBackBufferSurfaceDesc->Height;
    g_Camera.SetProjParams( D3DX_PI/4, fAspectRatio, 0.1f, 5000.0f );
    g_Camera.SetWindow( pBackBufferSurfaceDesc->Width, p00BackBufferSurfaceDesc->Height );

另外, 這個攝像機還封裝了簡單的滑鼠反饋. 在這裡, 我們指定三個滑鼠按鍵給提供的滑鼠操作: 模型旋轉, 放縮, 還有攝像機旋轉.試著編譯工程並用每個按鍵體會一下各個操作.

    g_Camera.SetButtonMasks( MOUSE_LEFT_BUTTON, MOUSE_WHEEL, MOUSE_MIDDLE_BUTTON );

在這個按鈕組的基礎上, 攝像機會監聽這些輸入併產生相應效果. 為了響應使用者輸入, 需要加入一個監聽到MsgProc回呼函數(DXUT訊息處理函數).

    // Pass all remaining windows messages to camera so it can respond to user input
    g_Camera.HandleMessages( hWnd, uMsg, wParam, lParam );

最後,在把所有資料都輸入攝像機後, 就是取出真正的變換矩陣的時候了. 這裡我們用相關函數擷取投影矩陣和視圖矩陣. 攝像機對象會自己計算這些矩陣.

    g_pProjectionVariable->SetMatrix( (float*)g_Camera.GetProjMatrix() );
    g_pViewVariable->SetMatrix( (float*)g_Camera.GetViewMatrix() );

 

DXUT 對話方塊

使用者互動可以用CDXUTDialog來實現, 它在一個包含控制項的對話方塊裡接受使用者輸入,並且通過程式控制代碼傳遞它們. 首先, 對話方塊類要執行個體化, 然後每個單獨的控制項才能加入.

在這個指南裡, 有兩個對話方塊會被加入, 一個叫 g_HUD跟D3D10樣本共用相同的代碼, 另一個叫 g_SampleUI 用於顯示函數細節給這個指南. 第二個對話方塊用來控制模型的"胖瘦",就是設定一個變數傳遞給shaders.

    CDXUTDialog g_HUD;      // manages the 3D UI
    CDXUTDialog g_SampleUI; // dialog for sample specific controls

這些對話方塊被CDXUTDialogResourceManager控制著.這個管理程式會在對話方塊之間傳遞訊息並處理共用資源.

    CDXUTDialogResourceManager g_DialogResourceManager; // manager for shared resources of dialogs

最後, 一些回呼函數與GUI處理的實際訊息相關聯. 這個函數用於處理控制項間的互動.

    void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext );


 

對話方塊初始化

既然已經介紹了許多有用的東西, 那麼需要被初始化, 這個指南將這些模組的真正初始化轉移到一個單獨的函數, 叫 InitApp().

每個對話方塊的控制項被初始化就是在這個函數裡. 每個對話方塊都需要調用它的初始化函數, 並傳遞給資源管理者來指定把控制項供給誰. 同樣, 它們設定回呼函數來處理GUI 響應. 既然這樣, 這個相關的回呼函數是 OnGUIEvent.

    g_HUD.Init( &g_DialogResourceManager );
    g_SampleUI.Init( &g_DialogResourceManager );
    g_HUD.SetCallback( OnGUIEvent );
    g_SampleUI.SetCallback( OnGUIEvent );

在它們被初始化後, 每個都對話方塊可以加入它們想要使用的控制項. 對話方塊 HUD加入了3個按鈕用於準系統: 切換全屏, 切換引用 (軟體) 渲染器, 和改變裝置.要加入一個按鈕, 你要指定你想要使用的IDC 標識符, 一個用於顯示的字串, 座標, 寬和高, 還有可選的與它關聯的熱鍵. 熱鍵可以用鍵盤快速地開關這些按鈕.

注意指定的座標與對話方塊的定位相關.

    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 );

同樣對於sample UI, 加入了這些控制項,一個靜態文本,一個滑塊, 還有一個複選按鈕.
傳遞給靜態文本的參數是IDC 標識符,字串,座標, 和寬高.
滑塊的參數是IDC 標識符,字串,座標, 和寬高, 還有滑塊的最大最小值, 最後是儲存結果的變數.
複選按鈕包括IDC標識符,一個字串標籤,wf座標, 寬高,還有儲存結果的布爾值.

    iY = 10;
    WCHAR sz[100];
    iY += 24;
    StringCchPrintf( sz, 100, L"Puffiness: %0.2f", g_fModelPuffiness ); 
    g_SampleUI.AddStatic( IDC_PUFF_STATIC, sz, 35, iY += 24, 125, 22 );
    g_SampleUI.AddSlider( IDC_PUFF_SCALE, 50, iY += 24, 100, 22, 0, 2000, (int)(g_fModelPuffiness*100.0f) );
    
    iY += 24;
    g_SampleUI.AddCheckBox( IDC_TOGGLESPIN, L"Toggle Spinning", 35, iY += 24, 125, 22, g_bSpinning );

一旦初始化了, 這個對話方塊要顯示在螢幕上. 這個由 OnD3D10ResizedSwapChain 調用來完成,螢幕座標每次改變這個交換鏈結會重建 (可能由於視窗大小的變化).

    g_HUD.SetLocation( pBackBufferSurfaceDesc->Width-170, 0 );
    g_HUD.SetSize( 170, 170 );
    g_SampleUI.SetLocation( pBackBufferSurfaceDesc->Width-170, pBackBufferSurfaceDesc->Height-300 );
    g_SampleUI.SetSize( 170, 300 );

對話方塊的最後一步在哪需要鑒別它們自己是 OnD3D10FrameRender 函數. 一個對話方塊如果你不繪製它並且使用者看不到不太好吧?

    //
    // Render the UI
    //
    g_HUD.OnRender( fElapsedTime ); 
    g_SampleUI.OnRender( fElapsedTime );

 

資源管理程式初始化

資源管理程式在每次回調初始化和銷毀時需要被初始化. 這是因為在每次裝置被重建的時候 GUI需被重建, 或者交換鏈結被重建的時候.  CDXUTDialogResourceManager 類包含符合每個這樣的回調的函數, 具有一模一樣的名字. 因此, 它僅僅是在合適的地方插入代碼來調用它們.

    V_RETURN( g_DialogResourceManager.OnD3D10CreateDevice( pd3dDevice ) );
    V_RETURN( g_DialogResourceManager.OnD3D10ResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc ) );
    g_DialogResourceManager.OnD3D10ReleasingSwapChain();
    g_DialogResourceManager.OnD3D10DestroyDevice();



響應GUI事件

當全部初始化完成後, 我們最後可以開始寫代碼來處理GUI互動了. 在我們初始化對話方塊的時候,我們設定這些對話方塊的回呼函數做為OnGUIEvent. 現在我們將要建立 OnGUIEvent 函數, 它會監聽並處理GUI 相關事件(被架構調用的).

這是一個簡單的對每個IDC標識符包含一個case代碼塊的函數,它在對話方塊建立時被監聽. 在每個case塊中會是處理代碼,假設使用者以某種方式控制.這裡的處理控制的代碼跟 Win32 代碼很像.

跟HUD 相關的控制實際上調用DXUT內建的函數. 有一個DXUT 函數關係到切換全屏, 綁定引用軟體渲染器, 並且更改裝置設定(它會調用下面提到的3D設定對話方塊).

SampleUI 對話方塊包含自訂的代碼操作跟滑塊相關聯的變數. 它會收集數值, 更新與它相關的文本, 並把數值傳遞給滑塊.

    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_D3DSettingsDlg.SetActive( !g_D3DSettingsDlg.IsActive() ); break;
    
            case IDC_TOGGLESPIN:
            {
                g_bSpinning = g_SampleUI.GetCheckBox( IDC_TOGGLESPIN )->GetChecked();
                break;
            }
    
            case IDC_PUFF_SCALE: 
            {
                g_fModelPuffiness = (float) (g_SampleUI.GetSlider( IDC_PUFF_SCALE )->Getvalue() * 0.01f);
    
                WCHAR sz[100];
                StringCchPrintf( sz, 100, L"Puffiness: %0.2f", g_fModelPuffiness ); 
                g_SampleUI.GetStatic( IDC_PUFF_STATIC )->SetText( sz );
    
                g_pPuffiness->SetFloat( g_fModelPuffiness );
                break;
            }
        }        
    }

 


更新訊息處理

既然現在我們有對話方塊訊息和使用者互動, 那就會有傳遞給應用程式的訊息需要被解析, 如果適用的話.那些代碼會在DXUT提供的 MsgProc 回呼函數中被處理. 在之前的指南裡, 這段是空白因為沒有訊息需要被處理. 但是現在,我們要確保發送給資源管理程式和對話方塊的訊息被適當地發送.
不需要專門的訊息處理代碼; 我們只需要對每個對話方塊調用MsgProcs來確保訊息被處理了.這通過調用每個單獨的類的相應的 MsgProc 函數來完成. 應該注意到這個函數提供了一個標記通報給架構,不需要更多的處理, 因此,可以退出.

    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_D3DSettingsDlg.IsActive() )
        {
            g_D3DSettingsDlg.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;
    
        if( uMsg == WM_CHAR && wParam == '1' )
            DXUTToggleFullScreen();
    
        return 0;
    }

 


3D設定對話方塊

有一個專門的內建對話方塊用來控制D3DDevice的設定. DXUT提供的這個對話方塊就是CD3DSettingsDlg. 它的功能像一個自訂對話方塊,但是它會提供給使用者所有的他們需要修改的設定選項.

    CD3DSettingsDlg         g_D3DSettingsDlg;       // Device settings dialog

初始化就像其它對話方塊一樣, 你可以調用初始化函數. 然而, 每次Direct3D 改變它的交換鏈結或者裝置,這個對話方塊必須被更新.因此, 它必須包含一個調用在OnD3D10CreateDevice 和OnD3D10ResizedSwapChain裡, 相對應地命名, 來反映這些改變. 同樣地,銷毀對象的改變必須被通報, 因而, 需要在 OnD3D10DestroyDevice裡調用.

    g_D3DSettingsDlg.Init( &g_DialogResourceManager );
    
    V_RETURN( g_D3DSettingsDlg.OnD3D10CreateDevice( pd3dDevice ) );
    V_RETURN( g_D3DSettingsDlg.OnD3D10ResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc ) );
    
    g_D3DSettingsDlg.OnD3D10DestroyDevice();

在渲染的這一邊, 因為對話方塊的出現可以被改變,它通過一個叫 IsActive()的標記來轉換. 如果這個標記被設成false, 那麼這個面板將不會被渲染.這個面板的改變是受上面說過的HUD對話方塊處理的. 標記The IDC_CHANGEDEVICE與HUD 控制項相關, 就像上面提到的那樣.

    if( g_D3DSettingsDlg.IsActive() )
    {
        g_D3DSettingsDlg.MsgProc( hWnd, uMsg, wParam, lParam );
        return 0;
    }

一旦這些初始步驟都完成了, 你就可以在你的程式中加入這個對話方塊. 試著編譯這個指南並與更改設定的面板進行互動來觀察它的效果. 真正的D3DDevice 或交換鏈結的重構在DXUT內部完成.
 


文本渲染

一個程式不會有趣如果使用者不知道幹什麼. 所以就DXUT包含了一個工具類用於在螢幕上繪製2D 文本用來給使用者反饋. 這個類CDXUTD3D10TextHelper允許你在螢幕的任意位置繪製一行行的文本, 並有簡單的字串輸入. 開始之前, 我們要執行個體化這個類.  既然文本渲染與大多數初始化過程獨立, 在這個指南裡我們讓大部分代碼在RenderText10裡.

    CDXUTD3D10TextHelper txtHelper( g_pFont, g_pSprite, 15 );

 

初始化

我們傳入的第一個參數是我們要繪製的字型. 字型的類型是D3DX提供的ID3DXFont. 要初始化這個字型, 你調用 D3DX10CreateFont, 並且你要傳入裝置, 高度, 寬度, 重量, mip層次 (一般取 1), 斜體, 字元集, 精度, 品質,  傾斜度和家族, 字型名稱, 還有你的對象的指標. 雖然它看起來像很多字元, 只有開始的4個和最後的2個真正有意義.

    V_RETURN( D3DX10CreateFont( pd3dDevice, 15, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, 
                                OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
                                L"Arial", &g_pFont ) );

第2個參數需要我們初始化一個ID3DXSprite 類. 要做這個的話, 我們可以調用 D3DX10CreateSprite (notice the trend here?). 這個函數只需要裝置和對象的指標做為參數.

    // Initialize the sprite
    V_RETURN( D3DX10CreateSprite( pd3dDevice, &g_pSprite ) );

當然, 像其它對象一樣, 字型和精靈在我們完成時必須被銷毀. 這可以用常見的 SAFE_RELEASE 宏完成.

    SAFE_RELEASE( g_pFont );
    SAFE_RELEASE( g_pSprite );

 

渲染

在這個樣本裡包含的文本裡有渲染的統計資訊. 另外, 還有一個地區顯示怎樣用滑鼠操作模型.

渲染必須在OnD3D10FrameRender裡調用, 而且在這個指南裡, 它是在幀渲染調用裡調用RenderText10來完成的.
第一個地區一直在繪製, 因此, 它最先完成. 第一個文本渲染調用是Begin(). 它通知引擎我要開始輸出文本到螢幕. 在那之後, 我們設定好“指標”的位置, 文本的顏色, 我們就準備繪製了.

輸出一個字串文本要調用DrawTextLine來完成. 你傳入字串, 它就會在當前位置輸出. 它在文本書寫的同時增加指標, 因此, 如果q包含了一個/n, 它會自動移動指標到下一行.

    // Output statistics
    txtHelper.Begin();
    txtHelper.SetInsertionPos( 2, 0 );
    txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
    txtHelper.DrawTextLine( DXUTGetFrameStats() );
    txtHelper.DrawTextLine( DXUTGetDeviceStats() );

還有另一個方法來輸出文本, 就好似通常使用的printf. 你用特殊的字元來格式化字串, 然後插入變數到字串裡. 這通過DrawFormattedTextLine來實現.

    txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
    txtHelper.DrawFormattedTextLine( L"fTime: %0.1f  sin(fTime): %0.4f", fTime, sin(fTime) );

既然協助繪製用一種相似的方式, 所以不需要涵蓋那部分代碼了. 一定要注意你能在任何時候通過調用SetInsertionPos來重新設定指標的位置.
最後, 當你對文本輸出滿意後, 調用 End() 來通知引擎你完成了.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.