发信人: nhyjq(人类·静修)
整理人: nhyjq(2003-01-05 15:33:15), 站内信件
|
by: 方泓
介绍
DX8中的FVF(Flexible Vertex Format Flags)是用来描述在单个数据流里交错存储的顶点内容, 它是用一个整形数(DWORD)来表示,更多关于FVF的说明,可以在DX8 SDK文档[1]中找到。
从FVF的值我们就可以知道此FVF所表示的一些基本属性。如果我们在编译时间就知道了这个值,那么我们甚至在编译时间就可以知道并利用这些属性,由此我们可以做一些有趣的应用。但是如何才能在编译时间就得到这些属性呢 ?
实现
C++中的template机制就能实现上面所说的目标,这种称为C++ Meta Programming的方法是C++ template的一种有趣而且也有一定实用价值的应用,对此有兴趣的人可以读读Todd Veldhuizen的著名文章[2]。
在这里我引进一个名为TFVFProperties template类(或结构),它的template参数就是FVF值,这个类非常的单纯,就是根据此FVF值在编译时间算出一些基本属性,并将这些基本属性信息保存在里面待用,这样我们在编译时就能得到指定FVF中所包含的属性信息,我们可以利用这些属性来做一些有趣的应用。
这个类的实现不算很复杂,下面我就直接将实现列在下面:
template<DWORD t_dwFVF>
struct TFVFProperties
{
BOOST_STATIC_CONSTANT(DWORD, sc_dwFVF = t_dwFVF);
private:
BOOST_STATIC_CONSTANT(int, _sc_nDummy = 1);
public:
BOOST_STATIC_CONSTANT(bool, sc_bHasPosition = ((t_dwFVF & D3DFVF_POSITION_MASK) != 0));
BOOST_STATIC_CONSTANT(bool, sc_bHasRHW = ((t_dwFVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW));
BOOST_STATIC_CONSTANT(bool, sc_bHasWeights = ((t_dwFVF & D3DFVF_POSITION_MASK) >= D3DFVF_XYZB1));
BOOST_STATIC_CONSTANT(bool, sc_bHasMatrixIndices = ((t_dwFVF & D3DFVF_LASTBETA_UBYTE4) != 0));
BOOST_STATIC_CONSTANT(int, sc_nWeights = sc_bHasWeights
? (sc_bHasMatrixIndices
? (t_dwFVF - D3DFVF_XYZB1 & D3DFVF_POSITION_MASK) >> 1
: ((t_dwFVF - D3DFVF_XYZB1 & D3DFVF_POSITION_MASK) >> 1) + 1)
: _sc_nDummy);
BOOST_STATIC_CONSTANT(bool, sc_bHasNormal = ((t_dwFVF & D3DFVF_NORMAL) != 0));
BOOST_STATIC_CONSTANT(bool, sc_bHasPointSize = ((t_dwFVF & D3DFVF_PSIZE) != 0));
BOOST_STATIC_CONSTANT(bool, sc_bHasDiffuse = ((t_dwFVF & D3DFVF_DIFFUSE) != 0));
BOOST_STATIC_CONSTANT(bool, sc_bHasSpecular = ((t_dwFVF & D3DFVF_SPECULAR) != 0));
BOOST_STATIC_CONSTANT(bool, sc_bHasTexCoords = ((t_dwFVF & D3DFVF_TEXCOUNT_MASK) != 0));
BOOST_STATIC_CONSTANT(int, sc_nTexCoords = ((t_dwFVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT));
// D3DFVF_TEXCOORDSIZEx
#define _M_DIM_TEX(mCoordIndex)\
(0x1432 >> (((t_dwFVF >> ((mCoordIndex - 1) * 2 + 16)) & 0x03) * 4)) & 0x0F
BOOST_STATIC_CONSTANT(int, sc_nDimTex1 = _M_DIM_TEX(1) ? _M_DIM_TEX(1) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex2 = _M_DIM_TEX(2) ? _M_DIM_TEX(2) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex3 = _M_DIM_TEX(3) ? _M_DIM_TEX(3) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex4 = _M_DIM_TEX(4) ? _M_DIM_TEX(4) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex5 = _M_DIM_TEX(5) ? _M_DIM_TEX(5) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex6 = _M_DIM_TEX(6) ? _M_DIM_TEX(6) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex7 = _M_DIM_TEX(7) ? _M_DIM_TEX(7) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex8 = _M_DIM_TEX(8) ? _M_DIM_TEX(8) : _sc_nDummy);
#undef _M_DIM_TEX
private:
BOOST_STATIC_CONSTANT(bool, _sc_bIsValid = sc_bHasPosition
&& (sc_bHasNormal || sc_bHasDiffuse || sc_bHasSpecular || sc_bHasTexCoords)
&& ! (sc_bHasRHW && (sc_bHasNormal || sc_bHasWeights)));
M_CAssert(sc_nTexCoords <= 8);
M_CAssert(_sc_bIsValid);
};
由此类的实现可以看出,象sc_bHasPosition这样的bool值属性的获取是非常简单的,只要将FVF值与D3DFVF_POSITION_MASK相与看是否为0就可以得到了。一些属性数值的取得较为复杂一些,如sc_nWeights, sc_nDimTex* 等,不过相信只要仔细看DX8SDK的文档[1]可以得到解答,在此我不再说明。
应用
下面我们来看TFVFProperties的一个简单应用:根据FVF自动生成Vertex shader的declaration token数组,我们可以用下面这个template类来做这项工作:
template<DWORD t_dwFVF, DWORD t_dwStreamNumber>
struct TFVFDeclGen
{
BOOST_STATIC_CONSTANT(DWORD, e_StreamNumber = t_dwStreamNumber);
public:
static const DWORD sm_adwDecl[D3DVSDE_NORMAL2 + 2 + 1];
};
template<DWORD t_dwFVF, DWORD t_dwStreamNumber>
const DWORD TFVFDeclGen<t_dwFVF, t_dwStreamNumber>::sm_adwDecl[] =
{
D3DVSD_STREAM(e_StreamNumber),
TFVFProperties<t_dwFVF>::sc_bHasPosition ? D3DVSD_REG(D3DVSDE_POSITION, D3DVSDT_FLOAT3) : D3DVSD_NOP(),
TFVFProperties<t_dwFVF>::sc_bHasWeights
? D3DVSD_REG(D3DVSDE_BLENDWEIGHT, (TFVFProperties<t_dwFVF>::sc_nWeights == 1)
? D3DVSDT_FLOAT1
: (TFVFProperties<t_dwFVF>::sc_nWeights == 2)
? D3DVSDT_FLOAT2
: (TFVFProperties<t_dwFVF>::sc_nWeights == 3)
? D3DVSDT_FLOAT3
: (TFVFProperties<t_dwFVF>::sc_nWeights == 4)
? D3DVSDT_FLOAT4
: D3DVSDT_FLOAT4)
: D3DVSD_NOP(),
TFVFProperties<t_dwFVF>::sc_bHasMatrixIndices
? D3DVSD_REG(D3DVSDE_BLENDINDICES, D3DVSDT_UBYTE4) : D3DVSD_NOP(),
TFVFProperties<t_dwFVF>::sc_bHasNormal ? D3DVSD_REG(D3DVSDE_NORMAL, D3DVSDT_FLOAT3) : D3DVSD_NOP(),
TFVFProperties<t_dwFVF>::sc_bHasPointSize ? D3DVSD_REG(D3DVSDE_PSIZE, D3DVSDT_FLOAT1) : D3DVSD_NOP(),
TFVFProperties<t_dwFVF>::sc_bHasDiffuse ? D3DVSD_REG(D3DVSDE_DIFFUSE, D3DVSDT_D3DCOLOR) : D3DVSD_NOP(),
TFVFProperties<t_dwFVF>::sc_bHasSpecular ? D3DVSD_REG(D3DVSDE_SPECULAR, D3DVSDT_D3DCOLOR) : D3DVSD_NOP(),
#define _M_GenTexureDecl(mN, mN_1)\
TFVFProperties<t_dwFVF>::sc_nTexCoords >= ##mN\
? D3DVSD_REG(D3DVSDE_TEXCOORD##mN_1,\
(D3DVSDT_FLOAT1 + TFVFProperties<t_dwFVF>::sc_nDimTex##mN - 1))\
: D3DVSD_NOP()
_M_GenTexureDecl(1, 0),
_M_GenTexureDecl(2, 1),
_M_GenTexureDecl(3, 2),
_M_GenTexureDecl(4, 3),
_M_GenTexureDecl(5, 4),
_M_GenTexureDecl(6, 5),
_M_GenTexureDecl(7, 6),
_M_GenTexureDecl(8, 7),
#undef _M_GenTexureDecl
D3DVSD_NOP(), // D3DVSDE_POSITION2
D3DVSD_NOP(), // D3DVSDE_NORMAL2
D3DVSD_END()
};
代码很容易理解,在这我不加解说了。这样的话我们在编译时就能求出数组的值,省了一点点运行开销,这个方法我去年底曾经在cpp3d的论坛上简单说过,也有朋友认为这数组可能是运行时才算出的,所以我还写了一个最简单的试验程序在VC6 SP5下验证了一下:
{
...
const DWORD dwFVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
printf("%X\n%X-%X-%X-%X\n%X-%X-%X-%X\n%X-%X-%X-%X\n%X-%X-%X-%X-%X\n%X\n"
, TFVFDeclGen<dwFVF, 0>::m_adwDecl[0], ...);
...
}
用IDA反汇编出来的代码可以清楚的看到生成的declaration 数组: 可见在这些值并非在运行时才计算出的。
; Segment type: Pure data
_rdata segment para public 'DATA' use32
assume cs:_rdata
;org 4070DCh
db 0 ;
db 0 ;
db 0 ;
db 0 ;
dword_4070E0 dd 20000000h ; DATA XREF: _main+76r
dword_4070E4 dd 40020000h ; DATA XREF: _main+6Fr
dword_4070E8 dd 0 ; DATA XREF: _main+68r
dword_4070EC dd 0 ; DATA XREF: _main+62r
dword_4070F0 dd 40020003h ; DATA XREF: _main+5Br
dword_4070F4 dd 0 ; DATA XREF: _main+54r
dword_4070F8 dd 0 ; DATA XREF: _main+4Er
dword_4070FC dd 0 ; DATA XREF: _main+47r
dword_407100 dd 40010007h ; DATA XREF: _main+40r
dword_407104 dd 0 ; DATA XREF: _main+3Ar
dword_407108 dd 0 ; DATA XREF: _main+33r
dword_40710C dd 0 ; DATA XREF: _main+2Cr
dword_407110 dd 0 ; DATA XREF: _main+26r
dword_407114 dd 0 ; DATA XREF: _main+1Fr
dword_407118 dd 0 ; DATA XREF: _main+18r
dword_40711C dd 0 ; DATA XREF: _main+12r
dword_407120 dd 0 ; DATA XREF: _main+Br
dword_407124 dd 0 ; DATA XREF: _main+5r
dword_407128 dd 0FFFFFFFFh ; DATA XREF: _mainr
下面我们来看看如何使用这个类的代码示例:
class CD3DVertexShader
{
....
public:
#define M_FVF2Decl(mFVF) meta::TType2Type<TFVFDeclGen<mFVF,0> >()
template<typename TWrapDecl>
inline bool Create(TWrapDecl, const DWORD* pVSCode)
{
return Create(TWrapDecl::TOriginalType::sm_adwDecl, pVSCode);
}
inline bool Create(const DWORD* pdwDecl, const DWORD* pVSCode)
{
DWORD dwUsage = 0L;
if (CD3DRenderDevice::Instance()->IsSoftwareVertexProcessing())
dwUsage |= D3DUSAGE_SOFTWAREPROCESSING;
// Create the vertex shader
return SUCCEEDED(CD3DRenderDevice::Instance()->CreateVertexShader(...);
}
...
};
const DWORD c_dwFVF0 = D3DFVF_XYZ | D3DFVF_DIFFUSE;
const DWORD c_dwFVF1 = D3DFVF_XYZ | D3DFVF_DIFFUSE;
const DWORD c_dwFVF2 = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1,
const DWORD c_dwFVF3 = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1;
...
{
...
// Create the vertex shader
m_VS0.Create(M_FVF2Decl(c_dwFVF0), dwSimple0VertexShader);
m_VS1.Create(M_FVF2Decl(c_dwFVF1), dwSimple1VertexShader);
m_VS2.Create(M_FVF2Decl(c_dwFVF2), dwSimple2VertexShader);
m_VS3.Create(M_FVF2Decl(c_dwFVF3), dwSimple3VertexShader);
...
}
这里需要提醒的是,declaration是提供给shader使用的,用来表达输入数值的秩序,所以记得你的shader代码得到输入数据要和它一致。
对比D3DX中的D3DXDeclaratorFromFVF()方法,这种方法在编译时生成数组,没什么运行开销。但是需要FVF是一个编译时知道的常量。而D3DXDeclaratorFromFVF()则是运行期间生成declaration数组的,有一点点运行开销,但是FVF不必是常量。对比实际中更常用的直接定义declaration数组,好处是比较方便,不需要定义,但不能用于一些特殊情况,而且有多个Stream时就用不上了,而直接定义则有很大的灵活性,所以在实用中可以几种方法一起使用。
总结
本文介绍了一个C++ template类TFVFProperties及其实现,并介绍它的一个具体应用,生成静态Vertex Shader的declaration数组,可以在某些情况下简化编程,但要注意在使用上有一些限制。
下一篇我将介绍应用TFVFProperties的一个更为复杂的应用。
注释
[1] Microsoft Corp., Microsoft DirectX 8.1 SDK. Microsoft Visual C++ Documentation Homepage, Microsoft Corp., 2001.
[2] Todd Veldhuizen. "Template Metaprograms.", C++ Report, 7, May, pp 36-43, 1995.
---- ∵我是人类(♂)㊣,天蝎座
∴我冷静、深沉
My OICQ is 726556
欢迎来游戏开发版逛逛,我是斑竹 |
|