天行健 君子当自强而不息

【ZT】再看DXUT框架

From: http://blog.csdn.net/MikeFeng/

     如果说Win32 API SDK中GDI部分是主旋律,那么MFC就是一个流行歌曲作家发挥之后完成的作品,而DXUT的图形框架则继承了Platform SDK中的风格,代码行间给人一种高贵的感觉。因为GDI函数都是C API,不利于代码的重用,DXUT框架则把他们包装成了C++的类,以便于用户继承更改控件特性。

  先了解一下DXUT中关于控件部分的设计架构比较好。这次看的代码大部分集中在DXUTgui.h和DXUTgui.cpp中。控件类的继承关系图是这样的:
  常用的控件大致都已经囊括在内,如果有特殊需要的话可以依葫芦画瓢从CDXUTControl继承。和这些控件有关系的还有几个重要的类,一个是CDXUTDialog,这个类负责纪录一个对话框的所有属性以及它上面的所有控件信息。另一个是 CDXUTDialogResourceManager,这个类保存了所有注册过的对话框链表,以及这些对话框共享的资源。另外CDXUTElement 这个类保存了需要渲染的元素信息,经常会在渲染函数中用到,最后一个类是可动态增长的链表类CGrowableArray< TYPE >。这个模版类写的不错,不光能用在DXUT框架中,还可以用于很多其他场合。

  具体的代码太多了,这里挑重要的讲解。
 
1.CDXUTDialog的Add系列函数。
 
  在初始化一个CDXUTDialog之后就是往这个对话框中添加控件了。在以前的例子中通常是在InitApp函数中调用对话框的Add系列函数来给对话框添加控件的。例如

g_HUD.Init( &g_DialogResourceManager );
g_HUD.SetCallback( OnGUIEvent ); int iY = 10;
g_HUD.AddButton( IDC_TOGGLEFULLSCREEN, L"Toggle full screen", 35, iY, 125, 22 );

  前面两句分别是初始化和设定消息处理回调函数,最后一句是Add系列函数,这个用来在对话框特定位置添加一个特定控件。
 
2.CDXUTDialog::MsgProc
 
  这个函数是处理对话框消息的。具体处理的消息包括该对话框的移动消息,优先处理焦点控件的消息,对话框大小和移动消息,获得焦点消息,键盘、鼠标消息,以及鼠标丢失消息。了解这些有助于我们充分利用这些功能,并添加自己想要的功能。
 
3.控件的初始化和显示
 
  每个控件都有其特定的属性。如果程序为每种控件定义一些默认属性,可以省去我们重复定义的很多麻烦。了解这些我们可以在此基础上修改这些默认属性,使你的控件更加个性化。在DXUT中控件的默认属性是按照这样的步骤定义和应用的。

①     在初始化对话框的函数CDXUTDialog::Init中调用InitDefaultElements()为每种控件设定默认属性。
②     将这些默认属性添加到CDXUTDialog::m_DefaultElements中
③     用户代码调用Add系列函数添加控件。这个Add函数调用CDXUTDialog::AddControl函数,并完成这个控件的一些设置。
④     CDXUTDialog::AddControl函数调用CDXUTDialog::InitControl初始化控件,并将该控件添加至对话框的CDXUTDialog::m_Control控件列表中。
⑤     CDXUTDialog::InitControl函数中遍历对话框的默认控件列表,并找到和要添加控件类型相同的默认控件,获得它的属性并将其设定到这个控件对象中。这个操作由CDXUTControl::SetElement函数来完成。
⑥     在CDXUTControl的继承类(例如CDXUTButton)的Render函数中使用这些属性并且通过CDXUTDialog的DrawSprite函数画出图形,用CDXUTDialog的DrawText画出文字。

  要做一个漂亮的界面肯定少不了对控件的背景进行设置,例如用来显示文字的static控件,如果能用圆形表示那多好。但遗憾的是CDXUT 框架中没有给我们提供这些功能,需要我们自己去实现。它甚至没有给我们提供映射纹理的功能,而仅仅是提供了修改控件背景和前景字体颜色的功能。这些信息都放在空间的m_Elements属性中,并由上述过程初始化。

  控件的显示在上面第六步有说明,就是调用DrawSprite和DrawText来实现。
 
4.对话框的初始化和显示
 
  DXUT框架中的对话框和GDI的有相似的地方,又有不同的地方。最大的不同就在于DXUT框架中的对话框是通过Draw*函数画出来的,因此每一桢都需要进行渲染,并且这些对话框是在主窗口内;而GDI的对话框是弹出式的,因此不属于原窗口。对话框的初始化在CDXUTDialog:: Init系列函数中。这个函数除了注册给CDXUTDialogResourceManager之外还进行了纹理的设置。在默认情况下框架在 OnCreateDevice时调用CDXUTDialogResourceManager的CreateTexture函数从内存创建纹理,而如果在 CDXUTDialog::Init函数中指定了纹理路径,或者指定使用资源中的纹理时,程序就从指定地点获得纹理。对话框采用一个列表来维护它的纹理,在通常情况下只有这个列表中只有一个纹理,除非用户调用相关函数手动添加。这个纹理是在显示控件的时候作为参数传给DrawSprite的,因此并不会用来作为对话框背景。
 
  对话框的显示使用CDXUTDialog::OnRender函数来实现。

  这个函数很关键,研究这个函数可以知道一个对话框是如何渲染的。和gdi应用程序不一样,DXUT框架是通过Draw*函数将控件画出来的,因此您可以按照喜欢的方式自己设定按钮等控件的样子。可以想象CDXUTDialog::OnRender的主要任务就是调用每个控件的 OnRender函数。在调用这些OnRender函数之前可以先将这个对话框的背景画好。因此这个函数的伪代码看起来就是这样子的:

  ①进行一些判断;
  ②为对话框背景设定渲染状态和纹理阶段状态;
  ③禁用顶点shader和像素shader,并且画出对话框背景;
  ④再次设定纹理阶段状态,为画控件做准备;
  ⑤调用各控件的Render函数画出各控件,对于获得焦点的控件做特殊处理;
  这个函数使用了状态块来记录设定过的状态,以备后用。
 
5.CD3DSettingDlg
 
  研究CD3DSettingDlg类可以学会如何使用上面的这些框架实现一个自己的对话框。我们可以在一个对话框上画另一个对话框,也可以选择在整个画面中只画一个对话框。这些可以在主回调函数OnFrameRender中设定。例如在处理CD3DSettingDlg对话框的Active 属性值被设定为true的时候不处理其他对话框的渲染。

    if( g_SettingsDlg.IsActive() )
    {
        g_SettingsDlg.OnRender( fElapsedTime );
        return;
    }

  在主回调函数MsgProc处理消息的时候,如果CD3DSettingDlg已经处理过则不再传递给其他函数。

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

  CD3DSettingDlg::Init                            初始化
  CD3DSettingDlg::CreateControls         添加控件
  CD3DSettingDlg::OnCreateDevice       设定消息处理回调函数
  CD3DSettingDlg::StaticOnEvent          消息处理回调函数,调用OnEvent
  CD3DSettingDlg::OnEvent                   消息处理函数
  CD3DSettingDlg::OnRender                 渲染

  ……

  在游戏中经常要进行游戏状态的切换,用户通过输入来发出某个游戏状态切换时,我们就可以重新绘制场景,而设计一个类似于 CD3DSettingDlg来实现是一个不错的注意。由于CreateControls函数和OnEvent函数在不同的场景中代码不同,因此可以在基类中将其设为virtual。可以根据需要将OnRender, OnXXX系列函数设为虚函数,以便子类实现相应功能。

posted on 2008-05-18 20:29 lovedday 阅读(1421) 评论(0)  编辑 收藏 引用 所属分类: ■ DXUT Research


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论