天行健 君子当自强而不息

【ZT】DXUTGUI控件的定制


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


最近在研究DXUT自带的控件库,按照SDK及例子做了些试验,总是那个固定的样子(可以看DXSDK中的例子,就是哪种效果),让人一眼就看出来界面是利用DXUTGUI实现的。我想要做出自己的效果,看来必须定制。

    定制包含两个方面,整个控件库风格的定制和特定控件实例本身的定制。

    我们先说整个UI风格的定制。

    我是从SDK的CUSTOMUI入手学习DXUT的。

    这个例子声明了一个全局的对话框资源管理对象CDXUTDialogResourceManager g_DialogResourceManager,然后用它分别初始化三个对话框。以SampleUI对话框为例,初始化语句在InitApp函数中: g_SamleUI.Init(&g_DialogResourceManager)。对Init函数的调用只有一个参数,另一个是默认的 bRegisterDialog=true。

    DXUT实现了按钮、列表框、可选按钮、编辑框等控件。一开始我以为控件是直接画出来的(这种感觉太愚蠢了),后来想想应当是用的纹理贴图。但是怎么也没有找到它所用的纹理文件在哪里,看来必须阅读DXUTGUI的源码了。

    从Init函数入手来研究DXUTGUI的资源管理是个不错的选择。我一路跟进去,发现按照示例程序那样初始化对话框时,会从内存中加载“皮肤”纹理。 DXUTGUI所用的内存纹理资源是DDS格式的,保存在DXUTRes.cpp的g_DXUTGUITextureSrcData数组内。这就是它的奥秘所在了。

    将这个纹理保存成bmp图片(256X256),就可以看到DXUTGUI控件的资源了。

    有了这个发现,我们就可以实现自己的风格了。只需两步:

    (1)仿照DXUTGUI自带的皮肤纹理制作自己的图片
    (2)在初始化对话框时选择三个参数的Init函数,指定纹理图片的路径。

    需要注意的是,我们所做的图片必须与DXUTGUI使用的图片规格一样,包括各种元素所对应的纹理区域等等,否则可能会一团糟。当然还有一个办法可以不和 DXUTGUI的图片规格保持一致:修改CDXUTDialog::InitDefaultElements函数。

    研究InitDefaultElements函数可以了解DXUTGUI是怎么使用纹理皮肤的,有助于我们实现自己的皮肤。

DXUTGUI提供的默认控件已经实现了透明效果和类似色彩键的效果。在D3D中没有直接的色彩键(direct draw中有)功能,不过可以利用alpha通道实现类似的效果,只是需要图片具有alpha通道。

    DXUTGUI的控件纹理正是这样实现的,用photoshop打开保存下来的纹理图片,可以看到其alpha通道的图片。

    要在D3D9中实现透明和颜色过滤功能,需要两个步骤。

    (1)定义FVF结构体,包含顶点颜色域。定义FVF标记,使其包含D3DFVF_DIFFUSE。

    struct CustomVertext{
        float x,y,z,h;
        DWORD color;
    };

    #define CUSTOMFVF   D3DFVF_XYZRHW | D3DFVF_DIFFUSE


    (2)设置渲染状态:

    pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
    pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
    pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );   
    pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
    pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );


    透明效果的实现是通过顶点颜色的alpha值(0完全透明,255不透明)实现的,而颜色过滤是通过纹理的alpha通道实现的,两者的乘积可以实现“透明+过滤”效果。这样就可以实现不规则且透明的控件。

    对应在DXUTGUI中,如果要设置某一个控件的透明度,可以调用该控件的GetElement函数,获取CDXUTElement类型的指针,调用其SetTexture函数实现。

    要统一设置某一类控件的透明度,可以调用CDXUTDialog::GetDefaultElement获取该类控件的分子对象的指针,修改其TextureColor成员的alpha通道(或者调用SetTexture函数)。

    知道了DXUTGUI如何实现上述效果,我们就有了定制UI的基础。通过提供具有alpha通道的图片给DXUTGUI使用,就可以随心所欲的实现各种效果的控件了。但如何让DXUTGUI为某一个控件(如一个按钮)使用我们自己的图片,还需要做进一步的挖掘和实现。


定制控件

     DXUTGUI的控件库默认使用内置的纹理资源,这个纹理资源可以在CDXUTDialog的Init函数中指定为我们自己的纹理资源(通常可以用一个图片文件来替代)。

    研究CDXUTDialog的InitDefaultElements函数可以发现,DXUTGUI为每种控件定义了若干元素,这些元素保存在 m_DefaultElements数组中。当增加一个新的控件时,比较控件类型,将该类型的元素集从DefaultElements取出,传递给该控件,该控件生成自己的元素实例并保存起来。

    我们发现纹理资源保存在CDXUTDialogResourceManager的成员变量m_TextureCache中。m_TextureCache 是一个动态数组,可以保存任意的纹理资源,如一个按钮的图片纹理,一个列表框的背景纹理等。只需要调用CDXUTDialg::SetTexture函数,指定一个ID和纹理文件名即可。

    纹理有了保存的地方,接下来只需要让控件使用我们自己的纹理就可以进行定制了。而控件的定制分为三类:单个控件的定制、一类控件的定制、生成新控件类型。下面一一说明怎么来实现。
 

    一、单个控件定制

    单个控件的定制比较简单,以按钮为例,需要三步:

    (1)CDXUTDialog::AddButton生成按钮pBtn
    (2)CDXUTDialog::SetTexture,生成该按钮的纹理,记录纹理序号nTexture
    (3)pBtn->GetElement获取CDXUTElement指针pElem,pElem->SetTexture修改该控件所用纹理为nTexture。

    上面的定制受限于DXUTGUI,需要根据其所实现的控件的渲染方法来生成自己的纹理资源,还要查看InitDefaultElements来决定怎么调用CDXUTElement::SetTexture和CDXUTElement::SetFont。

 

    二、单类控件的定制

    某一类控件的定制需要更改该类控件的默认元素,这个可以通过CDXUTDialog::SetDefaultElement来实现。需要两步完成:

    (1)CDXUTDialog::SetTexture,生成该类控件的纹理,记录纹理序号nTexture
    (2)CDXUTDialog::GetDefaultElement或者默认元素对象的指针pElem,然后pElem->SetTexture修改。

    第(2)步也还有另一种实现方法。声明CDXUTElement对象,设置其成员,然后调用CDXUTDialog:: SetDefaultElement,改写初始化时生成的默认元素集。无论怎样,都需要了解InitDefaultElements函数中做了什么。

 

    三、生成新控件类型

    生成新控件并使用定制的纹理,需要以下几步:

    (1)实现控件类
    (2)加载资源
    (3)为新类型控件生成默认元素集
    (4)生成控件实例,添加到对话框

    我们不改变DXUT自己的文件,一切都在我们自己的文件中实现。

    (1)DXUTGUI提供的控件不一定能满足我们需要,有时候需要自己实现新的控件,如图片按钮。我们可以从CDXUTControl派生,也可以从某个特定的控件类派生。下面我们以图片按钮的实现为例来说明,先看代码。
class CDXUTImageButton : public CDXUTButton
{
public:
    CDXUTImageButton(CDXUTDialog *pDialog = NULL ):CDXUTButton(pDialog)
    {
        m_Type = (DXUT_CONTROL_TYPE)(DXUT_CONTROL_SCROLLBAR + 1);
    };
    ~CDXUTImageButton(void)...{};
    virtual void Render( float fElapsedTime )
    {    
int nOffsetX = 0;
    int nOffsetY = 0;
    DXUT_CONTROL_STATE iState = DXUT_STATE_NORMAL;
    int iIndex = 0;
    if( m_bVisible == false )
    {
        iState = DXUT_STATE_HIDDEN;
    }
    else if( m_bEnabled == false )
    {
        iState = DXUT_STATE_DISABLED;
        iIndex = 2;
    }
    else if( m_bPressed )
    {
        iState = DXUT_STATE_PRESSED;
        iIndex = 1;
    }
    else if( m_bMouseOver )
    {
        iState = DXUT_STATE_MOUSEOVER;
        iIndex = 3;
    }
    else if( m_bHasFocus )
    {
        iState = DXUT_STATE_FOCUS;
        iIndex = 3;
    }
    // Main button
    CDXUTElement *pElement = m_Elements.GetAt( iIndex );
    float fBlendRate = ( iState == DXUT_STATE_PRESSED ) ? 0.0f : 0.8f;
    // Blend current color
    pElement->TextureColor.Blend( iState, fElapsedTime, fBlendRate );
    m_pDialog->DrawSprite( pElement, &m_rcBoundingBox, 0.8f );
};

    我们需要为CDXUTImageButton指定一个控件类型,取DXUT_CONTROL_SCROLLBAR + 1。同时改写CDXUTButton的Render函数,依据按钮状态取不同的纹理元素进行绘制。我们所提供的图片具有四个状态(顺序):正常态、下压态、禁止态、悬停态,对应按钮的四个状态。

    (2)有了图片按钮类,我们需要将按钮的资源加载进来。可以用CDXUTDialog::SetTexture实现。
    (3)四次调用CDXUTDialog::SetDefaultElement,为图片按钮设置四个元素。
    (4)分配CDXUTImageButton对象,调用CDXUTDialog::AddControl,然后设置该按钮的ID、TEXT、位置、大小等元素。
    (2)、(3)、(4)步的示例代码:
    //init custom button, normal way
    int iTexture = g_SampleUI.SetTexture(IDC_BUTTON_CUSTOM_1, L"play.tga");
    CDXUTElement elem;
    elem.iTexture = IDC_BUTTON_CUSTOM_1;
    elem.iFont = 0;
    RECT rc = {0};
    for(int i=0; i<4; i++)
    {
        SetRect(&rc, i*64, 0, (i+1)*64, 28);
        elem.SetTexture(IDC_BUTTON_CUSTOM_1, &rc, D3DCOLOR_ARGB(128, 255, 255, 255));
        g_SampleUI.SetDefaultElement(DXUT_CONTROL_SCROLLBAR+1, i, &elem);
    }
    CDXUTImageButton *imgbtn = new CDXUTImageButton(&g_SampleUI);
    g_SampleUI.AddControl(imgbtn);
    imgbtn->SetID(IDC_BUTTON_CUSTOM_1);
    imgbtn->SetText(L"CustomStyle");
    imgbtn->SetSize(64, 27);
    imgbtn->SetLocation(5, 5);

   
    如果改动DXUTGUI的源码,则可以在枚举类型DXUT_CONTROL_TYPE中添加DXUT_CONTROL_IMAGEBUTTON项,同时将上面的for循环设置默认元素集部分加入到InitDefaultElements函数中,给CDXUTDialog添加AddImageButton函数。那么生成按钮的代码看起来会相对简洁一些,它可能是这个样子:

g_SampleUI.AddImageButton(IDC_BUTTON_CUSTOM_1, L"CustomStyle", 5, 5, 64, 27);

    好了,DXUTGUI控件定制到此为止。

posted on 2008-05-18 15:53 lovedday 阅读(2818) 评论(3)  编辑 收藏 引用 所属分类: ■ DXUT Research

评论

# re: 【ZT】DXUTGUI控件的定制 2008-06-19 14:08 fly

你好,看了你的这篇文章,觉得很好,但是照搬你的代码,编译老是有错误,我还是个初学者,能将你的源程序发给我一份马,不胜感激,谢谢!!!我的邮箱:taxueliuyun@sina.com  回复  更多评论   

# re: 【ZT】DXUTGUI控件的定制[未登录] 2009-08-06 15:03 nancy

@fly
有没有写好的例子参考一下啊,我的邮箱957712059@qq.com  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论