28. GUI

 GUI系统将主要由按钮和静态文本控件对象构成。同样,可以指定和按钮、文本一起显示的背景图。GUI系统是2D的。这意味着指定对象位置时,用不到Z轴。另外,使用正交投影方式渲染GUI,这样可以根据像素位置指定屏幕位置,而不是使用标准单位来指定屏幕位置。回忆一下(0,0)对应的是屏幕的左上角。在Windows系统中,左上角同样是(0,0)。有了该信息,就可以轻松地确定玩家的鼠标指针是否在要创建的GUI系统的控件上或是正在按该控件。

       只要在规划按钮和文本位置时,记得左上角是(0,0),就能创建任何限制在系统范围内的GUI。当涉及到背景图时,没必要考虑它的位置,因为该图像是一个由两个三角形构成的全屏图像。只要系统知道当前程序的宽和高度,就可以显示一个全屏纹理图。

       想要具备的另一个特性是可以点击按钮控件。这实际上比点击图像更容易实现。由于按钮控件的位置是以像素指定的,因此为了确定鼠标指针是否在按钮上并按下该按钮,要做的全部工作就是检查当前鼠标指针的位置是否落在按钮区域内。因为Windows操作系统以像素指定鼠标位置且其屏幕左上角为(0,0),所以检查按钮的左侧坐标位置是否小于鼠标指针的X坐标,右侧坐标位置是否大于它的Y坐标,按钮高度是否小于它的Y坐标,按钮底部是否大于它的Y坐标,非常简单。如果4种情况都为真,那么可以确定鼠标指针就在按钮控件上。通过消息过程可以检查是否按下鼠标左键这个状态。如果按下了鼠标左键,那么可以确定玩家不仅仅是把鼠标指针放在了按钮上,而且在按该按钮。可以使用该信息更改按钮外观或是反映出按钮状态,并能够在按下按钮时做出一些动作。

   在开始编写代码之前,先列出GUI控件和界面所需的全部属性。GUI控件和界面的属性如下所示。

GUI控件属性:
■ 控件类型
■ 控件ID
■ 颜色
■ 起始X和Y坐标位置
■ 按钮的宽度和高度
■ 静态文本标签的显示文本
■ 背景图
■ 按钮正常、按下以及鼠标在其上面时显示的不同图像
■ 链表ID

GUI界面属性:
■ Direct3D设备指针
■ Direct3D字体对象列表
■ 存储的字体总数
■ 控件链表
■ 界面中存储的控件总数
■ 顶点缓存链表
■ 顶点缓存总表
■ 背景控件
■ 背景顶点控件
■ 窗口的宽度和高度

       控件由9个属性构成。控件类型用于存储控件类型ID。该值为1时指静态文本,为2时指按钮,为3时指背景图。静态文本只是显示在屏幕上的文本。按钮是一个按照鼠标指针在不同状态下采用不同纹理图像显示出的纹理矩形。不同状态指的是鼠标指针可以在按钮矩形框上、或按下按钮矩形框上、或在按钮矩形框外部。背景图则是一个显示在全屏上的单个纹理图像。

      下一个属性是控件ID。该ID用于给每个控件分配唯一的标识符,例如,如果一个按钮被按下,就可以知道这个被点击的按钮的ID。创建回调函数时,知道这一点很有用。回调函数对动作做出响应,如按钮被点击这样的动作。回调函数与Windows操作系统的消息过程函数很相似,它们也包含了要产生响应的对象ID。

    颜色、X坐标和Y坐标、宽度和高度等属性非常明了。颜色只是一个想要控件对象显示的简单值。X、Y坐标是控件左上角的起始位置。宽度和高度用于确定按钮的宽度和高度。由于背景图是一个全屏四方形且文本由字符长度决定,因此在这两种情况下使用控件时,不需要指定宽度和高度这样的属性。只有控件为按钮时才使用该信息,这样可以计算按钮是否被按下或鼠标指针是否在按钮上面。

      文本属性存储静态文本控件的实际文本内容。背景图属性存储背景图的Direct3D纹理对象。按钮的三个动作状态属性是三个Direct3D纹理对象,代表按钮的不同状态。如果俗话表指针不在按钮上,按钮状态是弹起的。如果鼠标指针在按钮上,而没有点击按钮,按钮状态是在其上的。如果鼠标指针指在按钮上,并点击按钮,按钮状态就是按下的。

      GUI控件的最后一个属性是链表ID。该链表ID保存Direct3D顶点缓存链表索引。对于按钮或索引,界面类将保存到D3D字体链表中。正在使用的控件类型将决定该属性代表的内容。如果是静态静态文本控件,那么该属性将索引保存到用于绘制该文本的D3D字体对象的数组中。如果是按钮,那么该属性将在存有该控件几何形状的顶点缓存中保存该索引。换而言之,链表ID就是一个数组索引。

  前面列举的GUI界面属性用于描述GUI系统。设备属性是Direct3D设备指针,该指针用于创建Direct3D字体对象、顶点缓存和纹理。字体链表是一个显示不同字体类型和大小的Direct3D字体对象链表。控件链表用于存储构成界面的GUI控件对象数组。顶点缓存链表是Direct3D顶点缓存对象链表。该链表存储系统中用到的全部按钮的顶点缓存。背景图属性存储背景图信息。由于只有一幅背景图,因此将其从控件链表中分出来。背景图的顶点缓存用于存储全屏背景四方形的三角形数据。窗口的宽度和高度这两个属性用于存储当前窗口分辨率下的宽度和高度。该属性用于创建背景图的几何形状。

  此时,系统唯一要补充的内容是处理控件动作的方法。例如,如果有人点击了按钮,就要有一个对该动作做出响应的方法。系统回调函数可以完成该工作。该函数有两个参数:控件ID和控件状态。所以,如果按钮被按下,该函数将按照发送给它的被按下的按钮ID和按钮状态运行。程序清单5.7给出了一个系统回调函数的示例。

// 回调函数(由ProcessGUI()函数调用)
void GUICallback(int id, int state)
{
switch (id)
{
case BUTTON_ID_START:
if (state == UGP_BUTTON_DOWN)
StartApplication();
break;

case BUTTON_ID_QUIT:
if (state == UGP_BUTTON_DOWN)
QuitApplication();
break;
}
}

  

 对程序而言,回调函数就像消息过程。实际上游戏并不调用该函数,而是在该函数处理自身的时候,由系统调用该函数。当谈到回调函数时,没必要指明全部的控件或情况。只要指明要考虑的对象和条件出现时间,系统就会做出相应的处理。回调函数作为函数指针被发送到GUI系统,这样,在该函数可用时系统就可以调用它。在每一帧结尾的地方,对系统进行处理。同调用回调函数一样,也要设置全部的按钮状态和纹理。程序清单5.8列出了GUI系统头文件的全部代码。

GUI系统D3DGUI.h头文件的完整代码

#ifndef _UGP_D3D_GUI_H_
#define _UGP_D3D_GUI_H_

// Types of controls we support.
#define UGP_GUI_STATICTEXT 1 //静态文本
#define UGP_GUI_BUTTON 2 // 按钮
#define UGP_GUI_BACKDROP 3 //背景图

// Mouse button states.
#define UGP_BUTTON_UP 1
#define UGP_BUTTON_OVER 2
#define UGP_BUTTON_DOWN 3


// A structure for our custom vertex type
struct stGUIVertex
{
float x, y, z, rhw;
unsigned
long color;
float tu, tv;
};

/*
在顶点结构体中没有RHW时,Direct3D将执行视、投影、世界等变换以及进行光线计算,
之后你才能在窗口中得到你所绘制的物体。当顶点结构体中有RHW时,告知Direct3D使
用的顶点已经在屏幕坐标系中了,不再执行视图、投影、世界等变换和光线计算,因为D3DFVF_XYZRHW标志告诉
它顶点已经经过了这些处理,并直接将顶点进行光栅操作,任何用SetTransform进行的转换都对其无效。不过
这时的原点就在客户区的左上角了,其中x向右为正,y向下为正,而z的意义已经变为z-buffer的象素深度。
*/
// Our custom FVF, which describes our custom vertex structure
#define D3DFVF_GUI (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)


struct stGUIControl
{
//控件类型
int m_type;

//控件id
int m_id;

//控件颜色
unsigned long m_color;

// 如果控件类型是静态文本,则是GUISystem中字体链表中的索引;否则是定点缓存链表中的索引。
int m_listID;

//控件左上角的坐标位置
float m_xPos, m_yPos;

// 按钮的宽度和高度(仅对按钮有效,静态文本,背景图时无效)
float m_width, m_height;

// 静态文本的文字内容(仅对静态文本有效)
char *m_text;

// 背景图用的纹理 (仅对背景图有效)
LPDIRECT3DTEXTURE9 m_backDrop;

// 按钮弹起,按下,鼠标移动到按钮上时分别显示的图片(仅对按钮有效)
LPDIRECT3DTEXTURE9 m_upTex, m_downTex, m_overTex;
};


class CD3DGUISystem
{
public:
CD3DGUISystem(LPDIRECT3DDEVICE9 device,
int w, int h);
~CD3DGUISystem() { Shutdown(); }

// 创建字体
// @fontName 新创建字体的名字
// @size 新创建字体对象的大小
// @fontID 新创建字体的ID指针
bool CreateFont(char *fontName, int size, int *fontID);

// 创建用于保存背景图几何形状的顶点缓存,并加载要映射到表面上的纹理图像
// @fileName 要加载的纹理图像文件名
bool AddBackdrop(char *fileName);

// 添加静态文本控件
// @id 控件ID
// @text 想要显示的文本内容
// @x 文本的起始x坐标
// @y 文本的起始y坐标
// @color 文本颜色
// @fontID 文本要是用的字体的id(在字体链表中的索引)
bool AddStaticText(int id, char *text, float x, float y, unsigned long color, int fontID);

// 添加按钮控件
// @id 控件id
// @x 左上角的x坐标
// @y 左上角的y坐标
// @up 按钮弹起时显示的纹理
// @over 鼠标在按钮上时显示的纹理
// @down 按钮按下时显示的纹理
bool AddButton(int id, float x, float y, char *up, char *over, char *down);

void Shutdown();


LPD3DXFONT GetFont(
int id)
{
if(id < 0 || id >= m_totalFonts) return NULL;
return m_fonts[id];
}

stGUIControl
*GetGUIControl(int id)
{
if(id < 0 || id >= m_totalControls) return NULL;
return &m_controls[id];
}

LPDIRECT3DVERTEXBUFFER9 GetVertexBuffer(
int id)
{
if(id < 0 || id >= m_totalBuffers) return NULL;
return m_vertexBuffers[id];
}

int GetTotalFonts() { return m_totalFonts; }
int GetTotalControls() { return m_totalControls; }
int GetTotalBuffers() { return m_totalBuffers; }
int GetWindowWidth() { return m_windowWidth; }
int GetWindowHeight() { return m_windowHeight; }
LPDIRECT3DDEVICE9 GetDevice() {
return m_device; }
stGUIControl
*GetBackDrop() { return &m_backDrop; }
LPDIRECT3DVERTEXBUFFER9 GetBackDropBuffer() {
return m_backDropBuffer; }
bool UseBackDrop() { return m_useBackDrop; }
void SetWindowSize(int w, int h) { m_windowWidth = w; m_windowHeight = h; }

private:
// D3D设备,用于创建Direct3D字体对象、顶点缓存和纹理。
LPDIRECT3DDEVICE9 m_device;

// Direct3D字体对象链表
LPD3DXFONT *m_fonts;

// GUI控件对象数组
stGUIControl *m_controls;

//顶点缓存链表是Direct3D顶点缓存对象链表,该链表存储系统中用到的全部按钮的顶点缓存.
LPDIRECT3DVERTEXBUFFER9 *m_vertexBuffers;

//背景图属性存储背景图信息。由于只有一幅背景图,因此将其从控件链表中分出来。
stGUIControl m_backDrop;

//背景图的顶点缓存用于存储全屏背景四方形的三角形数据。
LPDIRECT3DVERTEXBUFFER9 m_backDropBuffer;

// 是否使用背景图
bool m_useBackDrop;

// 字体总数
int m_totalFonts;

// 控件总数
int m_totalControls;

// 顶点缓存总数
int m_totalBuffers;

// 窗口宽度、高度
int m_windowWidth;
int m_windowHeight;
};


// @gui 需要处理的GUI
// @LMBDown 左键是都被按下
// @mouseX 以像素为单位的指针的x坐标
// @mouseY 以像素为单位的指针的y坐标
// @funcPtr 回调函数指针
// @id 回调函数需要的控件id
// @state 回调函数需要的控件状态
void ProcessGUI(CD3DGUISystem *gui, bool LMBDown, int mouseX, int mouseY, void(*funcPtr)(int id, int state));

#endif

  

GUI系统D3DGUI.cpp头文件的完整代码

#include<d3d9.h>
#include
<d3dx9.h>
#include
"D3DGUI.h"


CD3DGUISystem::CD3DGUISystem(LPDIRECT3DDEVICE9 device,
int w, int h)
{
m_fonts
= NULL;
m_controls
= NULL;
m_vertexBuffers
= NULL;

m_totalFonts
= m_totalControls = m_totalBuffers = 0;
m_windowWidth
= m_windowHeight = 0;
m_useBackDrop
= false;

m_device
= device;
m_windowWidth
= w; m_windowHeight = h;

memset(
&m_backDrop, 0, sizeof(stGUIControl));
}


/*
之所以需要字体链表和该函数,是因为这样做允许程序员将不同类型和大小的字体汇合在一起,
而不必在每次想要使用不同字体时单独重新创建一个字体对象。
就目的而言,字体ID是字体对象的数组索引。每次创建一种新字体,就要将数组中新字体对象
的位置返回给指针。这样可以在渲染字体时得到需要的字体对象。
*/
bool CD3DGUISystem::CreateFont(char *fontName, int size, int *fontID)
{
if(!m_device) return false;

if(!m_fonts)
{
m_fonts
= new LPD3DXFONT[1];
if(!m_fonts) return false;
}
else
{
//copy and swap
LPD3DXFONT *temp;
temp
= new LPD3DXFONT[m_totalFonts + 1];
if(!temp) return false;
memcpy(temp, m_fonts,
sizeof(LPD3DXFONT) * m_totalFonts);
delete[] m_fonts;
m_fonts
= temp;
}

if(FAILED(D3DXCreateFont(m_device, size, 0, 0, 1, 0,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH
| FF_DONTCARE, fontName,
&m_fonts[m_totalFonts]))) return false;

if(!m_fonts[m_totalFonts]) return false;

if(fontID)
*fontID = m_totalFonts;
m_totalFonts
++;

return true;
}

//背景图是全屏的,并且该信息可以以像素为单位指定,
// 而不是过去在创建几何图形时用的单位。由于整个场景是二维的,因此这样做很有意义。
bool CD3DGUISystem::AddBackdrop(char *fileName)
{
if(!fileName) return false;

m_backDrop.m_type
= UGP_GUI_BACKDROP;

// Load the texture image from file.
if(D3DXCreateTextureFromFile(m_device, fileName, &m_backDrop.m_backDrop) != D3D_OK)
return false;

// Get window width and height so we can create a fullscreen quad.
float w = (float)m_windowWidth;
float h = (float)m_windowHeight;

stGUIVertex obj[]
=
{
{w,
0, 0.0f, 1, D3DCOLOR_XRGB(255,255,255), 1.0f, 0.0f},
{w, h,
0.0f, 1, D3DCOLOR_XRGB(255,255,255), 1.0f, 1.0f},
{
0, 0, 0.0f, 1, D3DCOLOR_XRGB(255,255,255), 0.0f, 0.0f},
{
0, h, 0.0f, 1, D3DCOLOR_XRGB(255,255,255), 0.0f, 1.0f},
};

// Create the vertex buffer.
if(FAILED(m_device->CreateVertexBuffer(sizeof(obj), 0, D3DFVF_GUI,
D3DPOOL_DEFAULT,
&m_backDropBuffer, NULL))) return false;

// Fill the vertex buffer.
void *ptr;
if(FAILED(m_backDropBuffer->Lock(0, sizeof(obj), (void**)&ptr, 0))) return false;
memcpy(ptr, obj,
sizeof(obj));
m_backDropBuffer
->Unlock();

m_useBackDrop
= true;

return true;
}

//可以有一个或多个使用相同或不同字体对象的静态文本。
//对大部分静态文本而言,可能将使用相同的字体对象,但也可以将字体对象组合起来使用。
bool CD3DGUISystem::AddStaticText(int id, char *text, float x, float y, unsigned long color, int fontID)
{
if(!text || fontID < 0 || fontID >= m_totalFonts) return false;

// Create a blank control.
if(!m_controls)
{
m_controls
= new stGUIControl[1];
if(!m_controls) return false;
memset(
&m_controls[0], 0, sizeof(stGUIControl));
}
else
{
stGUIControl
*temp;
temp
= new stGUIControl[m_totalControls + 1];
if(!temp) return false;
memset(temp,
0, sizeof(stGUIControl) * (m_totalControls + 1));
memcpy(temp, m_controls,
sizeof(stGUIControl) * m_totalControls);
delete[] m_controls;
m_controls
= temp;
}

// Fill it with all the info we need for static text.
m_controls[m_totalControls].m_type = UGP_GUI_STATICTEXT;
m_controls[m_totalControls].m_id
= id;
m_controls[m_totalControls].m_color
= color;
m_controls[m_totalControls].m_xPos
= x;
m_controls[m_totalControls].m_yPos
= y;
m_controls[m_totalControls].m_listID
= fontID;

// Copy text data.
int len = strlen(text);
m_controls[m_totalControls].m_text
= new char[len + 1];
if(!m_controls[m_totalControls].m_text) return false;
memcpy(m_controls[m_totalControls].m_text, text, len);
m_controls[m_totalControls].m_text[len]
= '\0';

// Increment total.
m_totalControls++;
return true;
}


bool CD3DGUISystem::AddButton(int id, float x, float y, char *up, char *over, char *down)
{
if(!up || !over || !down) return false;

if(!m_controls)
{
m_controls
= new stGUIControl[1];
if(!m_controls) return false;
memset(
&m_controls[0], 0, sizeof(stGUIControl));
}
else
{
stGUIControl
*temp;
temp
= new stGUIControl[m_totalControls + 1];
if(!temp) return false;
memset(temp,
0, sizeof(stGUIControl) * (m_totalControls + 1));
memcpy(temp, m_controls,
sizeof(stGUIControl) * m_totalControls);
delete[] m_controls;
m_controls
= temp;
}

// Set all the data needed to render/process a button.
m_controls[m_totalControls].m_type = UGP_GUI_BUTTON;
m_controls[m_totalControls].m_id
= id;
m_controls[m_totalControls].m_xPos
= x;
m_controls[m_totalControls].m_yPos
= y;
m_controls[m_totalControls].m_listID
= m_totalBuffers;

// Load the texture image from file.
if(D3DXCreateTextureFromFile(m_device, up, &m_controls[m_totalControls].m_upTex) != D3D_OK)
{
return false;
}

if(D3DXCreateTextureFromFile(m_device, over, &m_controls[m_totalControls].m_overTex) != D3D_OK)
{
return false;
}

if(D3DXCreateTextureFromFile(m_device, down, &m_controls[m_totalControls].m_downTex) != D3D_OK)
{
return false;
}

unsigned
long white = D3DCOLOR_XRGB(255,255,255);

// Get width and height of the image so we use that size.
D3DSURFACE_DESC desc;
m_controls[m_totalControls].m_upTex
->GetLevelDesc(0, &desc);

float w = (float)desc.Width;
float h = (float)desc.Height;

m_controls[m_totalControls].m_width
= w;
m_controls[m_totalControls].m_height
= h;

stGUIVertex obj[]
=
{
{w
+ x, 0 + y, 0.0f, 1, white, 1.0f, 0.0f},
{w
+ x, h + y, 0.0f, 1, white, 1.0f, 1.0f},
{
0 + x, 0 + y, 0.0f, 1, white, 0.0f, 0.0f},
{
0 + x, h + y, 0.0f, 1, white, 0.0f, 1.0f},
};

// Create the vertex buffer.
if(!m_vertexBuffers)
{
m_vertexBuffers
= new LPDIRECT3DVERTEXBUFFER9[1];
if(!m_vertexBuffers) return false;
}
else
{
LPDIRECT3DVERTEXBUFFER9
*temp;
temp
= new LPDIRECT3DVERTEXBUFFER9[m_totalBuffers + 1];
if(!temp) return false;
memcpy(temp, m_vertexBuffers,
sizeof(LPDIRECT3DVERTEXBUFFER9) * m_totalBuffers);
delete[] m_vertexBuffers;
m_vertexBuffers
= temp;
}

if(FAILED(m_device->CreateVertexBuffer(sizeof(obj), 0,
D3DFVF_GUI, D3DPOOL_DEFAULT,
&m_vertexBuffers[m_totalBuffers], NULL)))
{
return false;
}

// Fill the vertex buffer.
void *ptr;

if(FAILED(m_vertexBuffers[m_totalBuffers]->Lock(0, sizeof(obj), (void**)&ptr, 0)))
{
return false;
}

memcpy(ptr, obj,
sizeof(obj));
m_vertexBuffers[m_totalBuffers]
->Unlock();

// Increase.
m_totalBuffers++;

// Increase.
m_totalControls++;

return true;
}

/*
Shutdown()函数开始先释放所有与背景图相关的数据,然后释放
系统所使用的全部D3D对象,然后再清除所有的顶点缓存,最后释放的是控件本身。
*/
void CD3DGUISystem::Shutdown()
{
if(m_useBackDrop)
{
if(m_backDrop.m_backDrop)
m_backDrop.m_backDrop
->Release();

if(m_backDropBuffer)
m_backDropBuffer
->Release();
}
m_backDrop.m_backDrop
= NULL;
m_backDropBuffer
= NULL;

for(int i = 0; i < m_totalFonts; i++)
{
if(m_fonts[i])
{
m_fonts[i]
->Release();
m_fonts[i]
= NULL;
}
}
if(m_fonts) delete[] m_fonts;
m_fonts
= NULL;
m_totalFonts
= 0;

for(int i = 0; i < m_totalBuffers; i++)
{
if(m_vertexBuffers[i])
{
m_vertexBuffers[i]
->Release();
m_vertexBuffers[i]
= NULL;
}
}
if(m_vertexBuffers) delete[] m_vertexBuffers;
m_vertexBuffers
= NULL;
m_totalBuffers
= 0;

for(int i = 0; i < m_totalControls; i++)
{
if(m_controls[i].m_backDrop)
{
m_controls[i].m_backDrop
->Release();
m_controls[i].m_backDrop
= NULL;
}

if(m_controls[i].m_upTex)
{
m_controls[i].m_upTex
->Release();
m_controls[i].m_upTex
= NULL;
}

if(m_controls[i].m_downTex)
{
m_controls[i].m_downTex
->Release();
m_controls[i].m_downTex
= NULL;
}

if(m_controls[i].m_overTex)
{
m_controls[i].m_overTex
->Release();
m_controls[i].m_overTex
= NULL;
}

if(m_controls[i].m_text)
{
delete[] m_controls[i].m_text;
m_controls[i].m_text
= NULL;
}
}

if(m_controls) delete[] m_controls;
m_controls
= NULL;
m_totalControls
= 0;
}


void ProcessGUI(CD3DGUISystem *gui, bool LMBDown, int mouseX, int mouseY, void(*funcPtr)(int id, int state))
{
if(!gui) return;

LPDIRECT3DDEVICE9 device
= gui->GetDevice();
if(!device) return;

// 先渲染背景图。因为该控件位于个控件的最下方,所以要在绘制
// 其他控件之前先绘制它,这样可以在它上面绘制其他控件。
// 由于只与二维空间打交道,因此渲染GUI时,可以避免考虑深度测试这样的问题.
stGUIControl *backDrop = gui->GetBackDrop();
LPDIRECT3DVERTEXBUFFER9 bdBuffer
= gui->GetBackDropBuffer();

// Only can draw if we have created it.
if(gui->UseBackDrop() && backDrop && bdBuffer)
{
device
->SetTexture(0, backDrop->m_backDrop);
device
->SetStreamSource(0, bdBuffer, 0, sizeof(stGUIVertex));
device
->SetFVF(D3DFVF_GUI);
device
->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
device
->SetTexture(0, NULL);
}

//循环控件列表,并按照每个控件类型对其加以渲染。在循环语句中有一
//个switch语句,来检查正在处理的控件类型。如果是静态文本控件,就要获取
//该文本使用的D3D字体对象,设置文本位置,并调用LPD3DXFONT的成员函数DrawText()显示该文本。
// Objects used to display text.
LPD3DXFONT pFont = NULL;
RECT fontPosition
= {0, 0, (long)gui->GetWindowWidth(), (long)gui->GetWindowHeight()};

// Object used to render buttons;
LPDIRECT3DVERTEXBUFFER9 pBuffer = NULL;
int status = UGP_BUTTON_UP;

// Loop through all controls and display them.
for(int i = 0; i < gui->GetTotalControls(); i++)
{
// Get the current control.
stGUIControl *pCnt = gui->GetGUIControl(i);
if(!pCnt) continue;

// Take action depending on what type it is.
switch(pCnt->m_type)
{
case UGP_GUI_STATICTEXT:
// Get font object this text uses.
pFont = gui->GetFont(pCnt->m_listID);
if(!pFont) continue;

// Set text position.
fontPosition.left = pCnt->m_xPos;
fontPosition.top
= pCnt->m_yPos;

// Display text.
pFont->DrawText(NULL, pCnt->m_text, -1, &fontPosition, DT_LEFT, pCnt->m_color);
break;

case UGP_GUI_BUTTON:
status
= UGP_BUTTON_UP;

// Get vertex buffer object this button uses.
pBuffer = gui->GetVertexBuffer(pCnt->m_listID);
if(!pBuffer) continue;

// Set alpha transparency on for the texture image.
device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
device
->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device
->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

// Check if we are over the button with the mouse or pressing it.
if(mouseX > pCnt->m_xPos && mouseX < pCnt->m_xPos + pCnt->m_width &&
mouseY
> pCnt->m_yPos && mouseY < pCnt->m_yPos + pCnt->m_height)
{
if(LMBDown)
status
= UGP_BUTTON_DOWN;
else
status
= UGP_BUTTON_OVER;
}

// Depending on the mouse state will depend on it's texture.
if(status == UGP_BUTTON_UP) device->SetTexture(0, pCnt->m_upTex);
if(status == UGP_BUTTON_OVER) device->SetTexture(0, pCnt->m_overTex);
if(status == UGP_BUTTON_DOWN) device->SetTexture(0, pCnt->m_downTex);

// Render button.
device->SetStreamSource(0, pBuffer, 0, sizeof(stGUIVertex));
device
->SetFVF(D3DFVF_GUI);
device
->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

// Turn off alpha.
device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
break;
}

// Process control by calling the callback function for it.
if(funcPtr)
funcPtr(pCnt
->m_id, status);
}
}

  

main.cpp

#include<d3d9.h>
#include
<d3dx9.h>
#include
"D3DGUI.h"

#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "D3D GUI Demo"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480

// Function Prototypes...
bool InitializeD3D(HWND hWnd, bool fullscreen);
bool InitializeObjects();
void RenderScene();
void Shutdown();


// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice
= NULL;

// DirectX gui object.
CD3DGUISystem *g_gui = NULL;

// Mouse state information.
bool LMBDown = false; // 鼠标左键是否被按下
int mouseX = 0, mouseY = 0; //鼠标的指针位置(像素为单位)

// Font ids used to create font objects.
// 字体类型id
int arialID = -1;
int timesID = -1;

// ids for our GUI controls.
// 控件id
#define STATIC_ID_1 1
#define STATIC_ID_2 2
#define BUTTON_ID_1 3
#define BUTTON_ID_2 4
#define BUTTON_ID_3 5
#define BUTTON_ID_4 6


LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(
0);
return 0;
break;

case WM_KEYUP:
if(wParam == VK_ESCAPE) PostQuitMessage(0);
break;

case WM_LBUTTONDOWN:
LMBDown
= true;
break;

case WM_LBUTTONUP:
LMBDown
= false;
break;

case WM_MOUSEMOVE:
mouseX
= LOWORD (lParam);
mouseY
= HIWORD (lParam);
break;
}

return DefWindowProc(hWnd, msg, wParam, lParam);
}


int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prevhInst, LPSTR cmdLine, int show)
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
WINDOW_CLASS, NULL };
RegisterClassEx(
&wc);

// Create the application's window
HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW,
100, 100, WINDOW_WIDTH, WINDOW_HEIGHT,
GetDesktopWindow(), NULL, wc.hInstance, NULL);

// Initialize Direct3D
if(InitializeD3D(hWnd, false))
{
// Show the window
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);

// Enter the message loop
MSG msg;
ZeroMemory(
&msg, sizeof(msg));

while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(
&msg);
DispatchMessage(
&msg);
}
else
RenderScene();
}
}

// Release any and all resources.
Shutdown();

// Unregister our window.
UnregisterClass(WINDOW_CLASS, wc.hInstance);
return 0;
}


bool InitializeD3D(HWND hWnd, bool fullscreen)
{
D3DDISPLAYMODE displayMode;

// Create the D3D object.
g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
if(g_D3D == NULL) return false;

// Get the desktop display mode.
if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))
return false;

// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(
&d3dpp, sizeof(d3dpp));

if(fullscreen)
{
d3dpp.Windowed
= FALSE;
d3dpp.BackBufferWidth
= WINDOW_WIDTH;
d3dpp.BackBufferHeight
= WINDOW_HEIGHT;
}
else
d3dpp.Windowed
= TRUE;
d3dpp.SwapEffect
= D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat
= displayMode.Format;

// Create the D3DDevice
if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING
|
D3DCREATE_PUREDEVICE,
&d3dpp, &g_D3DDevice)))
{
return false;
}

// Initialize any objects we will be displaying.
if(!InitializeObjects()) return false;

return true;
}


bool InitializeObjects()
{
// Set the image states.
g_D3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_NONE);
g_D3DDevice
->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
g_D3DDevice
->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);

// Create the GUI system.
g_gui = new CD3DGUISystem(g_D3DDevice, WINDOW_WIDTH, WINDOW_HEIGHT);
if(!g_gui) return false;

// Add background image.
if(!g_gui->AddBackdrop("backdrop.jpg")) return false;

// Add two types of fonts to the system.
if(!g_gui->CreateFont("Arial", 18, &arialID)) return false;
if(!g_gui->CreateFont("Times New Roman", 18, &timesID)) return false;

// Add the static text to the GUI.
if(!g_gui->AddStaticText(STATIC_ID_1, "Main Menu Version: 1.0",
450, 100, D3DCOLOR_XRGB(255,255,255), arialID)) return false;

if(!g_gui->AddStaticText(STATIC_ID_2, "Chapter 4 - GUI Demo",
450, 340, D3DCOLOR_XRGB(255,255,255), timesID)) return false;

// Add 3 buttons to the GUI.
if(!g_gui->AddButton(BUTTON_ID_1, 50, 200, "startUp.png",
"StartOver.png", "startDown.png")) return false;

if(!g_gui->AddButton(BUTTON_ID_2, 50, 232, "loadUp.png",
"loadOver.png", "loadDown.png")) return false;

if(!g_gui->AddButton(BUTTON_ID_3, 50, 264, "optionsUp.png",
"optionsOver.png", "optionsDown.png")) return false;

if(!g_gui->AddButton(BUTTON_ID_4, 50, 296, "quitUp.png",
"quitOver.png", "quitDown.png")) return false;

return true;
}


void GUICallback(int id, int state)
{
switch(id)
{
case BUTTON_ID_1:
case BUTTON_ID_2:
case BUTTON_ID_3:
// Demonstration. No need to do anything.
break;


case BUTTON_ID_4:
// If the quit button is pressed, quit app.
if(state == UGP_BUTTON_DOWN)
PostQuitMessage(
0);
break;
}
}


void RenderScene()
{
// Clear the backbuffer.
g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);

// Begin the scene. Start rendering.
g_D3DDevice->BeginScene();

// Process and render our gui system.
ProcessGUI(g_gui, LMBDown, mouseX, mouseY, GUICallback);

// End the scene. Stop rendering.
g_D3DDevice->EndScene();

// Display the scene.
g_D3DDevice->Present(NULL, NULL, NULL, NULL);
}


void Shutdown()
{
if(g_D3DDevice != NULL) g_D3DDevice->Release(); g_D3DDevice = NULL;
if(g_D3D != NULL) g_D3D->Release(); g_D3D = NULL;
if(g_gui) { g_gui->Shutdown(); delete g_gui; g_gui = NULL; }
}

  


  


原文地址:https://www.cnblogs.com/kex1n/p/2162472.html