From: http://blog.csdn.net/foruok/
最近在研究DXUT自帶的控件庫(kù),按照SDK及例子做了些試驗(yàn),總是那個(gè)固定的樣子(可以看DXSDK中的例子,就是哪種效果),讓人一眼就看出來(lái)界面是利用DXUTGUI實(shí)現(xiàn)的。我想要做出自己的效果,看來(lái)必須定制。
定制包含兩個(gè)方面,整個(gè)控件庫(kù)風(fēng)格的定制和特定控件實(shí)例本身的定制。
我們先說(shuō)整個(gè)UI風(fēng)格的定制。
我是從SDK的CUSTOMUI入手學(xué)習(xí)DXUT的。
這個(gè)例子聲明了一個(gè)全局的對(duì)話框資源管理對(duì)象CDXUTDialogResourceManager
g_DialogResourceManager,然后用它分別初始化三個(gè)對(duì)話框。以SampleUI對(duì)話框?yàn)槔跏蓟Z(yǔ)句在InitApp函數(shù)中:
g_SamleUI.Init(&g_DialogResourceManager)。對(duì)Init函數(shù)的調(diào)用只有一個(gè)參數(shù),另一個(gè)是默認(rèn)的
bRegisterDialog=true。
DXUT實(shí)現(xiàn)了按鈕、列表框、可選按鈕、編輯框等控件。一開(kāi)始我以為控件是直接畫(huà)出來(lái)的(這種感覺(jué)太愚蠢了),后來(lái)想想應(yīng)當(dāng)是用的紋理貼圖。但是怎么也沒(méi)有找到它所用的紋理文件在哪里,看來(lái)必須閱讀DXUTGUI的源碼了。
從Init函數(shù)入手來(lái)研究DXUTGUI的資源管理是個(gè)不錯(cuò)的選擇。我一路跟進(jìn)去,發(fā)現(xiàn)按照示例程序那樣初始化對(duì)話框時(shí),會(huì)從內(nèi)存中加載“皮膚”紋理。
DXUTGUI所用的內(nèi)存紋理資源是DDS格式的,保存在DXUTRes.cpp的g_DXUTGUITextureSrcData數(shù)組內(nèi)。這就是它的奧秘所在了。
將這個(gè)紋理保存成bmp圖片(256X256),就可以看到DXUTGUI控件的資源了。
有了這個(gè)發(fā)現(xiàn),我們就可以實(shí)現(xiàn)自己的風(fēng)格了。只需兩步:
(1)仿照DXUTGUI自帶的皮膚紋理制作自己的圖片
(2)在初始化對(duì)話框時(shí)選擇三個(gè)參數(shù)的Init函數(shù),指定紋理圖片的路徑。
需要注意的是,我們所做的圖片必須與DXUTGUI使用的圖片規(guī)格一樣,包括各種元素所對(duì)應(yīng)的紋理區(qū)域等等,否則可能會(huì)一團(tuán)糟。當(dāng)然還有一個(gè)辦法可以不和
DXUTGUI的圖片規(guī)格保持一致:修改CDXUTDialog::InitDefaultElements函數(shù)。
研究InitDefaultElements函數(shù)可以了解DXUTGUI是怎么使用紋理皮膚的,有助于我們實(shí)現(xiàn)自己的皮膚。
DXUTGUI提供的默認(rèn)控件已經(jīng)實(shí)現(xiàn)了透明效果和類(lèi)似色彩鍵的效果。在D3D中沒(méi)有直接的色彩鍵(direct
draw中有)功能,不過(guò)可以利用alpha通道實(shí)現(xiàn)類(lèi)似的效果,只是需要圖片具有alpha通道。
DXUTGUI的控件紋理正是這樣實(shí)現(xiàn)的,用photoshop打開(kāi)保存下來(lái)的紋理圖片,可以看到其alpha通道的圖片。
要在D3D9中實(shí)現(xiàn)透明和顏色過(guò)濾功能,需要兩個(gè)步驟。
(1)定義FVF結(jié)構(gòu)體,包含頂點(diǎn)顏色域。定義FVF標(biāo)記,使其包含D3DFVF_DIFFUSE。
struct CustomVertext{
float x,y,z,h;
DWORD color;
};
#define CUSTOMFVF D3DFVF_XYZRHW | D3DFVF_DIFFUSE
(2)設(shè)置渲染狀態(tài):
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 );
透明效果的實(shí)現(xiàn)是通過(guò)頂點(diǎn)顏色的alpha值(0完全透明,255不透明)實(shí)現(xiàn)的,而顏色過(guò)濾是通過(guò)紋理的alpha通道實(shí)現(xiàn)的,兩者的乘積可以實(shí)現(xiàn)“透明+過(guò)濾”效果。這樣就可以實(shí)現(xiàn)不規(guī)則且透明的控件。
對(duì)應(yīng)在DXUTGUI中,如果要設(shè)置某一個(gè)控件的透明度,可以調(diào)用該控件的GetElement函數(shù),獲取CDXUTElement類(lèi)型的指針,調(diào)用其SetTexture函數(shù)實(shí)現(xiàn)。
要統(tǒng)一設(shè)置某一類(lèi)控件的透明度,可以調(diào)用CDXUTDialog::GetDefaultElement獲取該類(lèi)控件的分子對(duì)象的指針,修改其TextureColor成員的alpha通道(或者調(diào)用SetTexture函數(shù))。
知道了DXUTGUI如何實(shí)現(xiàn)上述效果,我們就有了定制UI的基礎(chǔ)。通過(guò)提供具有alpha通道的圖片給DXUTGUI使用,就可以隨心所欲的實(shí)現(xiàn)各種效果的控件了。但如何讓DXUTGUI為某一個(gè)控件(如一個(gè)按鈕)使用我們自己的圖片,還需要做進(jìn)一步的挖掘和實(shí)現(xiàn)。
定制控件
DXUTGUI的控件庫(kù)默認(rèn)使用內(nèi)置的紋理資源,這個(gè)紋理資源可以在CDXUTDialog的Init函數(shù)中指定為我們自己的紋理資源(通常可以用一個(gè)圖片文件來(lái)替代)。
研究CDXUTDialog的InitDefaultElements函數(shù)可以發(fā)現(xiàn),DXUTGUI為每種控件定義了若干元素,這些元素保存在
m_DefaultElements數(shù)組中。當(dāng)增加一個(gè)新的控件時(shí),比較控件類(lèi)型,將該類(lèi)型的元素集從DefaultElements取出,傳遞給該控件,該控件生成自己的元素實(shí)例并保存起來(lái)。
我們發(fā)現(xiàn)紋理資源保存在CDXUTDialogResourceManager的成員變量m_TextureCache中。m_TextureCache
是一個(gè)動(dòng)態(tài)數(shù)組,可以保存任意的紋理資源,如一個(gè)按鈕的圖片紋理,一個(gè)列表框的背景紋理等。只需要調(diào)用CDXUTDialg::SetTexture函數(shù),指定一個(gè)ID和紋理文件名即可。
紋理有了保存的地方,接下來(lái)只需要讓控件使用我們自己的紋理就可以進(jìn)行定制了。而控件的定制分為三類(lèi):?jiǎn)蝹€(gè)控件的定制、一類(lèi)控件的定制、生成新控件類(lèi)型。下面一一說(shuō)明怎么來(lái)實(shí)現(xiàn)。
一、單個(gè)控件定制
單個(gè)控件的定制比較簡(jiǎn)單,以按鈕為例,需要三步:
(1)CDXUTDialog::AddButton生成按鈕pBtn
(2)CDXUTDialog::SetTexture,生成該按鈕的紋理,記錄紋理序號(hào)nTexture
(3)pBtn->GetElement獲取CDXUTElement指針pElem,pElem->SetTexture修改該控件所用紋理為nTexture。
上面的定制受限于DXUTGUI,需要根據(jù)其所實(shí)現(xiàn)的控件的渲染方法來(lái)生成自己的紋理資源,還要查看InitDefaultElements來(lái)決定怎么調(diào)用CDXUTElement::SetTexture和CDXUTElement::SetFont。
二、單類(lèi)控件的定制
某一類(lèi)控件的定制需要更改該類(lèi)控件的默認(rèn)元素,這個(gè)可以通過(guò)CDXUTDialog::SetDefaultElement來(lái)實(shí)現(xiàn)。需要兩步完成:
(1)CDXUTDialog::SetTexture,生成該類(lèi)控件的紋理,記錄紋理序號(hào)nTexture
(2)CDXUTDialog::GetDefaultElement或者默認(rèn)元素對(duì)象的指針pElem,然后pElem->SetTexture修改。
第(2)步也還有另一種實(shí)現(xiàn)方法。聲明CDXUTElement對(duì)象,設(shè)置其成員,然后調(diào)用CDXUTDialog::
SetDefaultElement,改寫(xiě)初始化時(shí)生成的默認(rèn)元素集。無(wú)論怎樣,都需要了解InitDefaultElements函數(shù)中做了什么。
三、生成新控件類(lèi)型
生成新控件并使用定制的紋理,需要以下幾步:
(1)實(shí)現(xiàn)控件類(lèi)
(2)加載資源
(3)為新類(lèi)型控件生成默認(rèn)元素集
(4)生成控件實(shí)例,添加到對(duì)話框
我們不改變DXUT自己的文件,一切都在我們自己的文件中實(shí)現(xiàn)。
(1)DXUTGUI提供的控件不一定能滿足我們需要,有時(shí)候需要自己實(shí)現(xiàn)新的控件,如圖片按鈕。我們可以從CDXUTControl派生,也可以從某個(gè)特定的控件類(lèi)派生。下面我們以圖片按鈕的實(shí)現(xiàn)為例來(lái)說(shuō)明,先看代碼。
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指定一個(gè)控件類(lèi)型,取DXUT_CONTROL_SCROLLBAR + 1。同時(shí)改寫(xiě)CDXUTButton的Render函數(shù),依據(jù)按鈕狀態(tài)取不同的紋理元素進(jìn)行繪制。我們所提供的圖片具有四個(gè)狀態(tài)(順序):正常態(tài)、下壓態(tài)、禁止態(tài)、懸停態(tài),對(duì)應(yīng)按鈕的四個(gè)狀態(tài)。
(2)有了圖片按鈕類(lèi),我們需要將按鈕的資源加載進(jìn)來(lái)。可以用CDXUTDialog::SetTexture實(shí)現(xiàn)。
(3)四次調(diào)用CDXUTDialog::SetDefaultElement,為圖片按鈕設(shè)置四個(gè)元素。
(4)分配CDXUTImageButton對(duì)象,調(diào)用CDXUTDialog::AddControl,然后設(shè)置該按鈕的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);
如果改動(dòng)DXUTGUI的源碼,則可以在枚舉類(lèi)型DXUT_CONTROL_TYPE中添加DXUT_CONTROL_IMAGEBUTTON項(xiàng),同時(shí)將上面的for循環(huán)設(shè)置默認(rèn)元素集部分加入到InitDefaultElements函數(shù)中,給CDXUTDialog添加AddImageButton函數(shù)。那么生成按鈕的代碼看起來(lái)會(huì)相對(duì)簡(jiǎn)潔一些,它可能是這個(gè)樣子:
g_SampleUI.AddImageButton(IDC_BUTTON_CUSTOM_1, L"CustomStyle", 5,
5, 64, 27);
好了,DXUTGUI控件定制到此為止。