DXUT框架概览

 
概要
DXUT 是一个建立在 Direct3D API 之上的,被大部分 Direct3D 指南和例子所使用的框架。 它的目标是创建 Direct3D 例子、原型和工具,更容易的制作出坚固、专业的游戏。
DXUT
DXUT 为创建窗口,创建(或选择)一个 Direct3D 设备和处理 Windows 消息提供了一个简单的过程。它能减少你在处理这些基本任务时所花费的时间。
DXUT 也是 Direct3D 10 的重要组成部分。 DXUT 的核心组件包括创建标准窗口,创建设备和函数管理。 DXUT 的可选组件包括摄像机操作,图形用户界面系统和网格处理等一系列函数。这篇指南对 DXUT 的核心组件进行了解释,并着重介绍了设备创建,主循环和简单的键盘输入。
DXUT 提供了能够供用户使用的一系列回调函数。在程序执行时 DXUT 在相应的逻辑点上调用这些回调函数。你可以在这些回调函数里编写代码来构建应用程序,而 DXUT 则在这期间负责管理窗口和设备这些程序运行时的必要工作。
DXUT 同时支持 Direct3D 9 Direct3D 10 的应用程序。程序运行时 DXUT 自动为系统提供最佳的运行环境。但是本指南只对 Direct3D 10 的渲染方式进行介绍。
设置回调函数
第一步是编写程序的 WinMain 主函数。 WinMain WIN32 应用程序的入口函数,初始化工作是在这里完成的,然后进入消息循环。在 DXUT 框架中 WinMain 起到类似的作用。
首先,设置应用程序的回调函数。程序运行时 DXUT 会根据相应的触发事件调用这些函数。例如创建设备,创建交换链,键盘输入,画面移动和画面渲染等。
    // Direct3D10 callbacks
    DXUTSetCallbackD3D10DeviceAcceptable( IsD3D10DeviceAcceptable );
    DXUTSetCallbackD3D10DeviceCreated( OnD3D10CreateDevice );
    DXUTSetCallbackD3D10SwapChainResized( OnD3D10ResizedSwapChain );
    DXUTSetCallbackD3D10SwapChainReleasing( OnD3D10ReleasingSwapChain );
    DXUTSetCallbackD3D10DeviceDestroyed( OnD3D10DestroyDevice );
    DXUTSetCallbackD3D10FrameRender( OnD3D10FrameRender );
 
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackKeyboard( KeyboardProc );
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
然后对程序进行额外的 DXUT 设置,例子如下所示:
    // Show the cursor and clip it when in full screen
    DXUTSetCursorSettings( true, true );
最后,调用初始化函数。本指南与其他指南相比不同之处在于你只需要关注在初始化时程序的特定代码。因为相应的设备和窗口的初始化工作都已交给 DXUT 来完成了。这些特定代码可以写在任何关联的回调函数之中。 DXUT 会在初始化过程中调用它们。
    // 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 options that control the behavior of the framework.
    DXUTInit( true, true ); // Parse the command line, handle the default hotkeys, and show msgboxes
    DXUTCreateWindow( L"Tutorial8" );
    DXUTCreateDevice( true, 640, 480 );
使用DXUTTrace做调试
DXUTTrace 是一个宏,利用它可以输出应用程序的调试结果。它与标准的调试输出函数非常相似,并且允许输出变量,例如:
    DXUTTRACE( L"Hit points left: %d/n", nHitPoints );
在本指南中,它会被放在下一个函数的入口处来报告当前程序状态。举例说明,在 OnD3D10ResizedSwapChain() 函数开头报告消息:“ SwapChain Reset called ”。
    DXUTTRACE( L"SwapChain Reset called/n" );
设备管理和初始化
DXUT 提供了多种方法来创建和配置窗口和 Direct3D 设备。下面列出了这些方法的一部分,它们对一个中等复杂程度的 Direct3D 程序来说已经足够了。
在之前的内容里, InitDevice() leanupDevice() 就包含了下面这些方法,来发挥相应的作用。
·   IsD3D10DeviceAcceptable Callback
·   ModifyDeviceSettings Callback
·   OnD3D10CreateDevice Callback
·   OnD3D10DestroyDevice Callback
由于不是所有的资源都在同一时间创建,我们能够最大限度的减少重复调用它们的次数。我们根据上下文依赖性重复创建唯一的资源来达到这一目的。每一次调整窗口大小时,所有资源到要重建。
IsD3D10DeviceAcceptable函数
该函数用来查找系统中是否有可用的设备,它允许程序接受或拒绝设备访问。例如,应用程序能够拒绝所有 REF 设备,或拒绝全屏显示设备的使用。在本指南中,所有设备都是可用的,因为我们不需要任何的高级功能。
ModifyDeviceSettings函数
该函数可在 DXUT 创建设备之后直接更改任何设备的设置。在本指南中,这个函数不做任何事情,因为我们不需要任何的高级功能。
OnD3D10CreateDevice函数
该函数在 Direct3D10 设备创建之后调用。设备创建后,程序可以用该函数分配资源,设置缓冲和处理其它一些工作。在本指南中,设备和交换链的创建代码被省略了,这些功能由 DXUT 来实现。 OnD3D10CreateDevice 函数用来创建效果,顶点 / 索引缓冲,纹理和变换矩阵。
OnD3D10DestroyDevice函数
该函数在 DXUT 释放 ID3D10Device 之后调用。它用来释放被设备使用完了的资源。在本指南中 OnD3D10DestroyDevice 用语是放在 OnD3D10CreateDevice 中创建的资源。这些资源包扩顶点 / 索引缓冲,版面,纹理材质和效果。
渲染
对于渲染来讲, DXUT 提供了两个回调函数对程序进行渲染。第一个是 OnFrameMove ,他在每帧画面渲染之前调用。随着应用程序推进。第二个是 OnD3D10FrameRender ,为 DXUT 提供渲染。这两个函数在 Render() 逻辑里分担工作。 OnFrameMove 用于更新所有的活动矩阵。 OnD3D10FrameRender 包括了渲染调用。
OnFrameMove函数
该函数在一帧画面渲染之后调用。 它被用于处理世界坐标系状态。它的更新频率取决于系统速度。在一个较快的系统中,它每秒钟调用一次。这意味着任何更新状态的代码都由时间来调节。否则,在一个较快的系统和一个较慢的系统中效果是不一样的。 在本指南中,无时无刻不在调用这个函数,例如世界坐标系状态每更新一次,网格颜色调整等。
OnD3D10FrameRender函数
该函数在屏幕刷新的时候调用,在这个函数内部进行效果应用,资源整合和绘制场景。在本指南中,调用这个函数的功能还包括清空和格式缓存,安装矩阵与绘制立方体。
消息处理
消息处理是所有 Windows 应用程序的固有特性。 DXUT 将为高级应用程序处理这些消息。
MsgProc函数
当收到 Windows 的消息时 DXUT 就会调用该函数。它会让程序以相应的方式来处理消息。在本指南中,不存在额外的消息处理。
KeyboardProc函数
当有键盘输入的时候 DXUT 会调用该函数,它可以为按键命令实现简单的功能。他不会在本指南中使用,因为键盘交互并非不可或缺。不过你可以用比如 F1 键来测试该函数,编写代码让旋转的立方体停止转动。
网格
用代码罗列出每个顶点是一项单调而且乏味的方法。取而代之,框架已经提供了为你的程序架构模型的方法。
网格是预定义顶点数据的集合,它包括常规向量,颜色,材质和纹理坐标等一些额外信息。只要知道了网格文件的格式,他就能被导入和渲染。
网格用于将二进制源文件和程序代码分开维护,所以源文件能够重复使用。因此网格作为独立文件存在。它们极少产生于应用程序之中,而是由 3D 模型制作软件创建。在之前的指南里提到的立方体也可以分离保存为单独的网格文件,然后重载。但是要导入一个尺寸复杂得多的网格文件必修要更加高效。其中一种有效的手段就是只导入纹理坐标,从而使网格文件与应用程序完美配合起来。
DXUT 利用 CDXUTMesh10 这个类来处理网格,它是 D3DX 网格类的包装类。这个类包括了导入,渲染和销毁网格的函数。我们采用的网格文件格式是扩展名为 .X 的文件。有许多可以免费获得的文件转换器能够把其它格式的网格文件转换为 .X 格式。
创建网格
想要导入模型,我们首先要创建一个 CDXUTMesh10 的对象。取名为 g_Mesh ,但一般情况下为了便于关联,对象名要与网格源文件名一致。
    CDXUTMesh10 g_Mesh;
接下来,我们调用创建函数读取并加载 X 文件到对象中。创建函数要求我们通过 D3D10Device X 文件的文件名关系到我们的网格,界面和界面上的元素数。还有一个名叫优化的附加参数,通过它调用 D3DX 函数来重新安排网格纹理和顶点。能够提高渲染时的性能。
下面要做的是,我们导入一个叫“ tiny.x ”的文件。定点格式为这个网格所包括的顶点坐标,法线和纹理坐标。我们必须指定输入界面进行匹配,比如下面的例子所示。
    // Define the input layout
    const D3D10_INPUT_ELEMENT_DESC layout[] =
    {
        { L"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { L"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { L"TEXCOORD0", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    };
界面定义完之后,我们调用创建函数导入模型。最后一个参数是顶点格式的元素个数,因为我们有坐标,法线和纹理坐标,所以指定为 3
    // Load the mesh
    V_RETURN( g_Mesh.Create( pd3dDevice, L"tiny.x", (D3D10_INPUT_ELEMENT_DESC*)layout, 3 ) );
如果没有错误产生,对象 g_Mesh 现在已经包含了我们新导入网格的顶点和索引缓冲,以及纹理和材质信息,接下来我们进行渲染。
渲染网格
在之前的指南中,由于我们明确的设置了顶点缓冲和索引缓冲,我不得不在每帧画面显示之前都要适当的分配它们。而对于一个网格来说这些东西都是抽象的,因此,我们必须提唯一的技术效果来渲染网格,并让它完成所有工作。
与之前的指南不同,我们去掉了 OnD3DFrameRender 中的部分内容而换上了渲染立方体的代码。在这里我们调用网格渲染函数。在网格渲染之前设置当前输入界面是一种推荐的做法。这就确保了在汇编程序中的网格与显示界面相匹配。
    //
    // Set the Vertex Layout
    //
    pd3dDevice->IASetInputLayout( g_pVertexLayout );
实际上真正完成渲染工作的是 CDXUTMesh10 类中的渲染函数。渲染会在分配缓冲区之后进行。为了达到渲染的目的,我们要用到 D3D10Device 设备对象,效果和技术缓冲。
    //
    // Render the mesh
    //
    g_Mesh.Render( pd3dDevice, g_pEffect, g_pTechnique );
如果世界矩阵在模型中设置好了,我们现在就可以看到之前例子中的立方体已被更复杂的网格模型所取代。需要注意的是该网格要比那个立方体大得多。试着调整世界矩阵以便让它在屏幕上完全显示出来。
销毁网格
像所有的对象一样, CDXUTMesh10 对象必须在使用之后销毁。这就需要调用 Destroy 函数。
    g_Mesh.Destroy();
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, pBackBufferSurfaceDesc->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 Direct3D 10 示例共享相同的代码,另一个叫 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 标识符,一个字符串标签,坐标, 宽高,还有存储结果的布尔值。
    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 这个函数只需要设备和对象的指针做为参数。
    // Initialize the sprite
    V_RETURN( D3DX10CreateSprite( pd3dDevice, MAX_SPRITES, &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() 来通知引擎你完成了。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值