第十三回 顶点格式
这一回介绍一下一个自定义的顶点描述格式: FVFEx。
最早D3D是用FVF来描述一个vertex buffer里的顶点格式的,因为那时候的顶点中的element的类型就那么几种,可以写成固定的形式,但是后来D3D为了允许用户自己定义顶点的element类型,采用了非常难用的vertex declaration来定义一个vb的顶点格式,当然这也没有办法,D3D毕竟要考虑所有用户的需求.不过,在我看来,一个engine中会用到的顶点element的类型仍然是非常有限的(在目前的engine中,除了地表用到的几个特殊的顶点数据外,大部分的顶点数据都可以用最原始的FVF格式来描述),而vertex declaration这个东西无论是创建,修改,解析都不方便,所以我自己定义了一套类似于FVF系统的格式,我称它为FVFEx系统,它使用一个unsigned int64来表示一个顶点格式,这样最多可以支持64种不同的顶点element类型,engine中用到每个顶点element都被分配到一个特定的位,定义在一个头文件里,像这样:
typedef unsigned __int64 FVFEx;
#define FVFEX_MASK(v) (((FVFEx)0x1)<<(v))
#define FVFEX_NULL 0x0
#define FVFEX_XYZ0 FVFEX_MASK(0)
#define FVFEX_XYZ1 FVFEX_MASK(1)
#define FVFEX_NORMAL0 FVFEX_MASK(2)
#define FVFEX_NORMAL1 FVFEX_MASK(3)
#define FVFEX_BINORMAL FVFEX_MASK(4)
#define FVFEX_TANGENT FVFEX_MASK(5)
//更多的顶点element类型
...
与这些宏定义匹配的是一张表,在这张表里每一个自定义的顶点element类型都有一些对应的信息,这些信息,可以用来填写一个D3DVERTEXELEMENT9结构,像这样
struct FVFExInfo
{
FVFExInfo(FVFEx fvf,int type,DWORD size,int usage,int usageindex)
{
m_fvf=fvf;
m_type=type;
m_size=size;
m_usage=usage;
m_usageindex=usageindex;
}
FVFEx m_fvf;
int m_type;
DWORD m_size;
int m_usage;
int m_usageindex;
};
FVFExInfo g_aFVFList[]=
{
FVFExInfo(FVFEX_XYZ0, D3DDECLTYPEX_FLOAT3 , 12, D3DDECLUSAGEX_POSITION, 0),
FVFExInfo(FVFEX_XYZ1, D3DDECLTYPEX_FLOAT3 , 12, D3DDECLUSAGEX_POSITION, 1),
FVFExInfo(FVFEX_NORMAL0, D3DDECLTYPEX_FLOAT3 , 12, D3DDECLUSAGEX_NORMAL, 0),
FVFExInfo(FVFEX_NORMAL1, D3DDECLTYPEX_FLOAT3 , 12, D3DDECLUSAGEX_NORMAL, 1),
FVFExInfo(FVFEX_BINORMAL, D3DDECLTYPEX_FLOAT3 , 12, D3DDECLUSAGEX_BINORMAL, 0),
FVFExInfo(FVFEX_TANGENT, D3DDECLTYPEX_FLOAT3 , 12, D3DDECLUSAGEX_TANGENT, 0),
//更多的顶点element类型
...
};
这样,使用这张表,我们可以把一个FVFEx格式+一个stream号转换成一个vertex declaration.
根据多年前的测试结果,创建一个vertex declaration是一件很慢的事情,所以我们会维护一张hash表,在绑定一个vb时,使用这张表,我们根据FVFEx和stream号作为key来查找到对应的vertex declaration,再设到device中去.
我们只在将一个vb绑定到device的时候,才会把FVFEx转换为vertex declaration,而在engine的其它地方,我们都只用FVFEx来定义一个verter buffer的顶点格式,为此我写了一些函数,用以支持这套系统,常用的一些有:
extern DWORD fvfSize(FVFEx fvf);//得到某个顶点格式的数据大小,in byte
extern int fvfOffset(FVFEx fvf,FVFEx fvfPart);//得到顶点格式中某个element的偏移量,in byte,return -1 if error
extern void fvfCopy(DWORD nVertice,void *pDest,FVFEx fvfDest,void *pSrc,FVFEx fvfSrc);//copy all the copyable elements from source buffer to dest buffer
extern int fvfToD3DVERTEXELEMENT9(FVFEx fvfTotal,FVFEx fvf,D3DVERTEXELEMENT9 *pElements,int iStream);//转换成D3DVERTEXELEMENT9结构
...
关于FVFEx大致就是这样,下回介绍mesh资源.