本篇是
创建游戏内核(10)的续篇,其中涉及到的顶点缓存知识请参阅D3D中基本立体面的绘制。
顶点和VERTEX_BUFFER
顶点是一个麻烦的东西,有时对它束手无策,通过提供一种快速创建、设置以及渲染顶点集合的方法,VERTEX_BUFFER类能够减少此烦恼。
来看看VERTEX_BUFFER类的定义:
class VERTEX_BUFFER
{
private:
GRAPHICS* _graphics; // pointer to graphics object
IDirect3DVertexBuffer9* _vertex_buffer; // pointer to Direct3D vertex buffer object
DWORD _num_vertices; // number of vertex
DWORD _vertex_size; // vertex size
DWORD _fvf; // flexible vertex format
BOOL _is_locked; // flag indicates if vertex buffer has been locked
char* _ptr; // pointer to vertex data buffer
public:
VERTEX_BUFFER();
~VERTEX_BUFFER();
IDirect3DVertexBuffer9* Get_Vertex_buffer_COM();
unsigned long Get_Vertex_Size();
unsigned long Get_Vertex_FVF();
unsigned long Get_Num_Vertices();
BOOL Create(GRAPHICS* graphics, unsigned long num_vertices, unsigned long vertex_size, DWORD fvf);
void Free();
BOOL Is_Loaded();
BOOL Set(unsigned long first_vertex, unsigned long num_vertices, void* vertex_list);
BOOL Render(unsigned long first_vertex, unsigned long num_primitives, DWORD type);
BOOL Lock(unsigned long first_vertex = 0, unsigned long num_vertices = 0);
BOOL Unlock();
void* Get_Ptr();
};
接着是类VERTEX_BUFFER的实现:
//---------------------------------------------------------------------------
// Constructor, initialize data member.
//---------------------------------------------------------------------------
VERTEX_BUFFER::VERTEX_BUFFER()
{
_graphics = NULL;
_vertex_buffer = NULL;
_ptr = NULL;
_num_vertices = 0;
_fvf = 0;
_is_locked = FALSE;
}
//---------------------------------------------------------------------------
// Destructor, free vertex buffer resource.
//---------------------------------------------------------------------------
VERTEX_BUFFER::~VERTEX_BUFFER()
{
Free();
}
//---------------------------------------------------------------------------
// Get pointer to vetex buffer.
//---------------------------------------------------------------------------
IDirect3DVertexBuffer9* VERTEX_BUFFER::Get_Vertex_buffer_COM()
{
return _vertex_buffer;
}
//---------------------------------------------------------------------------
// Gets the size of a vertex for a flexible vertex format (FVF)
//---------------------------------------------------------------------------
unsigned long VERTEX_BUFFER::Get_Vertex_Size()
{
return D3DXGetFVFVertexSize(_fvf);
}
//---------------------------------------------------------------------------
// Get flexible vertex format.
//---------------------------------------------------------------------------
unsigned long VERTEX_BUFFER::Get_Vertex_FVF()
{
return _fvf;
}
//---------------------------------------------------------------------------
// Get number of vertex.
//---------------------------------------------------------------------------
unsigned long VERTEX_BUFFER::Get_Num_Vertices()
{
return _num_vertices;
}
//---------------------------------------------------------------------------
// Create vertex buffer.
//---------------------------------------------------------------------------
BOOL VERTEX_BUFFER::Create(GRAPHICS* graphics, unsigned long num_vertices, unsigned long vertex_size, DWORD fvf)
{
// free vertex buffer resource first
Free();
// Check condition
if((_graphics = graphics) == NULL)
return FALSE;
if(_graphics->Get_Device_COM() == NULL)
return FALSE;
if(!(_num_vertices = num_vertices) || !(_fvf = fvf) || !(_vertex_size = vertex_size))
return FALSE;
// create vertex buffer now
if(FAILED(_graphics->Get_Device_COM()->CreateVertexBuffer(
_num_vertices * _vertex_size, 0, _fvf, D3DPOOL_MANAGED, &_vertex_buffer, NULL)))
return FALSE;
return TRUE;
}
//---------------------------------------------------------------------------
// Free vertex buffer resource, reset data member.
//---------------------------------------------------------------------------
void VERTEX_BUFFER::Free()
{
Unlock();
Release_COM(_vertex_buffer);
_graphics = NULL;
_ptr = NULL;
_num_vertices = 0;
_fvf = 0;
_is_locked = FALSE;
}
//---------------------------------------------------------------------------
// Copy vertex data from VertexList to vertex buffer.
//---------------------------------------------------------------------------
BOOL VERTEX_BUFFER::Set(unsigned long first_vertex, unsigned long num_vertices, void* vertex_list)
{
// check condition
if(_graphics == NULL || vertex_list == NULL || _vertex_buffer == NULL)
return FALSE;
if(_graphics->Get_Device_COM() == NULL)
return FALSE;
// lock the vertex buffer
if(! Lock(first_vertex, num_vertices))
return FALSE;
// copy vertices to vertex buffer
memcpy(_ptr, vertex_list, num_vertices * _vertex_size);
// unlock vertex buffer
return Unlock();
}
//---------------------------------------------------------------------------
// Render vertex buffer into display.
//---------------------------------------------------------------------------
BOOL VERTEX_BUFFER::Render(unsigned long first_vertex, unsigned long num_primitives, DWORD type)
{
if(_graphics->Get_Device_COM() == NULL || _vertex_buffer == NULL)
return FALSE;
// binds a vertex buffer to a device data stream
_graphics->Get_Device_COM()->SetStreamSource(0, _vertex_buffer, 0, _vertex_size);
// sets the current vertex stream declaration
_graphics->Get_Device_COM()->SetFVF(_fvf);
// Renders a sequence of nonindexed, geometric primitives of the specified type from the current set
// of data input streams.
_graphics->Get_Device_COM()->DrawPrimitive((D3DPRIMITIVETYPE) type, first_vertex, num_primitives);
return TRUE;
}
//---------------------------------------------------------------------------
// Lock vertex buffer from specified position with specified length.
//---------------------------------------------------------------------------
BOOL VERTEX_BUFFER::Lock(unsigned long first_vertex, unsigned long num_vertices)
{
if(_vertex_buffer == NULL)
return FALSE;
// locks a range of vertex data and obtains a pointer to the vertex buffer memory
if(FAILED(_vertex_buffer->Lock(first_vertex * _vertex_size, num_vertices * _vertex_size, (void**)&_ptr, 0)))
return FALSE;
_is_locked = TRUE;
return TRUE;
}
//---------------------------------------------------------------------------
// Unlock vertex buffer.
//---------------------------------------------------------------------------
BOOL VERTEX_BUFFER::Unlock()
{
if(_vertex_buffer == NULL)
return FALSE;
if(FAILED(_vertex_buffer->Unlock()))
return FALSE;
_is_locked = FALSE;
return TRUE;
}
//---------------------------------------------------------------------------
// Check whether vertex buffer has created successfully.
//---------------------------------------------------------------------------
BOOL VERTEX_BUFFER::Is_Loaded()
{
return _vertex_buffer ? TRUE : FALSE;
}
//---------------------------------------------------------------------------
// Ger pointer to vertex data.
//---------------------------------------------------------------------------
void* VERTEX_BUFFER::Get_Ptr()
{
return (void*)_ptr;
}
首先必须使用VERTEX_BUFFER::Create函数创建顶点缓冲,此函数的参数有GRAPHICS对象、用于分配空间的顶点数、单个顶点的大小(单位是字节)、可变顶点格式(FVF)。由此可以看出,要使用这个类,还需要创建一个顶点结构体。
要记得在处理完类的实例后,调用VERTEX_BUFFER::Free函数释放它。但是,在释放类的实例之前,需要调用
VERTEX_BUFFER::Set函数给缓冲区填充要使用的顶点信息。VERTEX_BUFFER::Set函数的参数包括要设置的第一个顶点的索引、要设置的顶点数目、一个指向自定义顶点结构体数组的指针。
经过以上的准备,就可以使用VERTEX_BUFFER::Render函数渲染多边形了,注意可以指定开始绘制的第一个顶点以及要绘制的图元(多边形的面)的总数目。
type参数的含义如下表所示:
标志 |
说明 |
D3DPT_POINTLIST |
将顶点通过单独的点集合方式进行渲染。 |
D3DPT_LINELIST |
将顶点通过单独的线段集合方式进行渲染。 |
D3DPT_LINESTRIP |
一连串的线段,每条线段都是从前一个顶点到当前顶点绘制而成。 |
D3DPT_TRIANGLELIST |
三角形列,其中每个三角形都有三个独享的顶点。 |
D3DPT_TRIANGLESTRIP |
三角形带,每个顶点都使用前面两个顶点来形成一个面。 |
D3DPT_TRIANGLEFAN |
三角形扇,每个顶点都使用一个中心顶点和另外两个顶点形成一个面。 |
其中三角形列、带、扇示意图如下:
下面给出测试代码:
点击下载源码和工程
/*****************************************************************************
PURPOSE:
Test for class VERTEX_BUFFER.
*****************************************************************************/
#include "Core_Global.h"
#pragma warning(disable : 4996)
//===========================================================================
// Defines class APP which public inherits from class APPLICATION.
//===========================================================================
class APP : public APPLICATION
{
private:
GRAPHICS _graphics;
VERTEX_BUFFER _vertex_buffer;
// The 2D vertex format and descriptor
typedef struct
{
float x, y, z; // 2D coordinates
float rhw; // rhw
D3DCOLOR diffuse; // diffuse color component
} VERTEX;
#define VERTEX_FVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)
public:
BOOL Init();
BOOL Shutdown();
BOOL Frame();
};
//-----------------------------------------------------------------------------
// Initialize graphics, set display mode, set vertex buffer.
//-----------------------------------------------------------------------------
BOOL APP::Init()
{
// initialize vertex data
VERTEX verts[] = {
{ 100.0f, 100.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(0,64,128,255) },
{ 300.0f, 100.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(0,64,128,255) },
{ 100.0f, 300.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(0,64,128,255) },
{ 300.0f, 300.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(0,64,128,255) },
{ 50.0f, 150.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(128,0,0,128) },
{ 350.0f, 150.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(128,0,0,128) },
{ 50.0f, 350.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(128,0,0,128) },
{ 350.0f, 350.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(128,0,0,128) }
};
// initialize graphics
if (! _graphics.Init())
return FALSE;
// set display mode for graphics
if(! _graphics.Set_Mode(Get_Hwnd(), TRUE, FALSE, 400, 400, 16))
return FALSE;
// create the vertex buffer
_vertex_buffer.Create(&_graphics, 8, sizeof(VERTEX), VERTEX_FVF);
// Copy vertex data from VertexList to vertex buffer.
_vertex_buffer.Set(0, 8, verts);
return TRUE;
}
//-----------------------------------------------------------------------------
// Release all d3d resource.
//-----------------------------------------------------------------------------
BOOL APP::Shutdown()
{
// Free vertex buffer resource, reset data member.
_vertex_buffer.Free();
return TRUE;
}
//-----------------------------------------------------------------------------
// Render a frame.
//-----------------------------------------------------------------------------
BOOL APP::Frame()
{
// clear display with specified color
_graphics.Clear_Display(D3DCOLOR_RGBA(0, 0, 0, 255));
// begin scene
if(_graphics.Begin_Scene())
{
// 1) disable alpha blending first
_graphics.Enable_Alpha_Blending(FALSE);
// Render vertex buffer into display.
_vertex_buffer.Render(0, 2, D3DPT_TRIANGLESTRIP);
// 2) enable alpha blending now
_graphics.Enable_Alpha_Blending(TRUE);
// Render vertex buffer into display.
_vertex_buffer.Render(4, 2, D3DPT_TRIANGLESTRIP);
// end the scene
_graphics.End_Scene();
}
// display video buffer
_graphics.Display();
return TRUE;
}
int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
APP app;
return app.Run();
}
运行截图: