home > programming > notes >

FVF頂点テンプレート

解説

Direct3DでFVFフラグを使ってカスタム頂点を作るときに楽ができるテンプレートです。

テンプレート引数にFVFフラグを入れて構造体を生成します。

FVFVertex<D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1> v[4];

「fvf」で構造体生成に使ったFVFフラグを取り出すこともできます。

メンバは以下の通りです。フラグによって作られたり作られなかったりしますので、DirectX SDKのヘルプを参照のこと。

頂点構造体メンバ表
名前 説明
pos D3DXVECTOR3またはD3DXVECTOR4 位置
b float[] ベータ
index DWORDまたはD3DCOLOR ブレンド用インデックス
normal D3DXVECTOR3 法線
psize float ポイントサイズ
diffuse DWORD ディフューズ色
specular DWORD スペキュラ色
t float[] テクスチャ座標

テクスチャ座標はセットが複数あっても一次元配列になりますが、これはD3DFVF_TEXCOORDSIZEnでセット毎に異なる次元数を指定可能なため二次元配列化ができないからです。テクスチャセットn(0から)の開始インデックスは、テンプレートTexIndex<n>::resultで求めることができます。

ソースは型選択にLoki::Selectを使用しています。これはテンプレートライブラリLokiの中の一つですが、Loki::Selectのソース自体は以下のような短いものです(テンプレート引数の部分特殊化が出来ないVC6などではもう少し長くなります)。

template <bool flag, typename T, typename U>
struct Select
{
    typedef T Result;
};
template <typename T, typename U>
struct Select<false, T, U>
{
    typedef U Result;
};

FVFTraitsはFVFの値のチェック処理をまとめたテンプレートクラスです。その後に定義されているのが頂点クラスの部品です。そしてGenVertexTypeテンプレートクラスでFVFTraitsを使いながら継承を使って頂点クラスを組み立てます。

構造体生成に多重継承を使っていないのは、VisualC++6.0でのテストの結果、多重継承を用いると構造体サイズが余分に膨らんでしまったからです。そういった理由で一重継承となっていますが、それでもコンパイラやそのオプションによっては正しいサイズの構造体が生成されないかもしれません。不審を感じたら構造体サイズのチェックを推奨します。


コード

#include <d3d9.h>
#include <d3dx9.h>
#include "TypeManip.h" //Loki

template <DWORD f>
struct FVFTraits
{
    enum { Pos = (f & D3DFVF_POSITION_MASK) };

    enum { isXYZ = (Pos == D3DFVF_XYZ) };
    enum { isXYZRHW = (Pos == D3DFVF_XYZRHW) };
    enum { isXYZB1 = (Pos == D3DFVF_XYZB1) };
    enum { isXYZB2 = (Pos == D3DFVF_XYZB2) };
    enum { isXYZB3 = (Pos == D3DFVF_XYZB3) };
    enum { isXYZB4 = (Pos == D3DFVF_XYZB4) };
    enum { isXYZB5 = (Pos == D3DFVF_XYZB5) };
    enum { isXYZW = (Pos == D3DFVF_XYZW) };
    enum { numB =
            isXYZB1 ? 1 :
            isXYZB2 ? 2 :
            isXYZB3 ? 3 :
            isXYZB4 ? 4 :
            isXYZB5 ? 5 : 0 };
    enum { isLASTBETA_UBYTE4 = !!(f & D3DFVF_LASTBETA_UBYTE4) };
    enum { isLASTBETA_D3DCOLOR = !!(f & D3DFVF_LASTBETA_D3DCOLOR) };
    enum { hasNORMAL = !!(f & D3DFVF_NORMAL) };
    enum { hasPSIZE = !!(f & D3DFVF_PSIZE) };
    enum { hasDIFFUSE = !!(f & D3DFVF_DIFFUSE) };
    enum { hasSPECULAR = !!(f & D3DFVF_SPECULAR) };

    enum { numTEX = (f & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT };

    template <unsigned int index>
    class CoordSize
    {
    private:
        enum { shift = index * 2 + 16 };
        enum { flag = (f >> shift) & 3 };
    public:
        enum { result = 
            (flag == D3DFVF_TEXTUREFORMAT1) ? 1 :
            (flag == D3DFVF_TEXTUREFORMAT2) ? 2 :
            (flag == D3DFVF_TEXTUREFORMAT3) ? 3 : 4 };
    };

    template <unsigned int n>
    struct TexIndex
    {
        enum { result = CoordSize<n - 1>::result + TexIndex<n - 1>::result };
    };

    template <>
    struct TexIndex<0>
    {
        enum { result = 0 };
    };

    enum { maxTEXINDEX = TexIndex<numTEX>::result };
};

namespace Private
{
    struct EMPTY
    {};

    struct XYZ
    {
        D3DXVECTOR3 pos;
    };

    struct XYZRHW
    {
        D3DXVECTOR4 pos;
    };

    template <unsigned int n>
    struct XYZB
    {
        D3DXVECTOR3 pos;
        float b[n];
    };

    template <unsigned int n>
    struct XYZB_LB_UBYTE4
    {
        D3DXVECTOR3 pos;
        float b[n - 1];
        DWORD index;
    };

    template <unsigned int n>
    struct XYZB_LB_D3DCOLOR
    {
        D3DXVECTOR3 pos;
        float b[n - 1];
        D3DCOLOR index;
    };

    struct XYZW
    {
        D3DXVECTOR4 pos;
    };

    template <class Base>
    struct NORMAL : public Base
    {
        D3DXVECTOR3 normal;
    };

    template <class Base>
    struct PSIZE : public Base
    {
        float psize;
    };

    template <class Base>
    struct DIFFUSE : public Base
    {
        DWORD diffuse;
    };

    template <class Base>
    struct SPECULAR : public Base
    {
        DWORD specular;
    };

    template <class Base, unsigned int n>
    struct TEX : public Base
    {
        float t[n];
    };

    template <DWORD f>
    class GenVertexType
    {
    private:
        typedef FVFTraits<f> t;
        typedef Loki::Select<t::isXYZ, XYZ,
            Loki::Select<t::isXYZRHW, XYZRHW,
            Loki::Select<t::isXYZW, XYZW,
            Loki::Select<!(t::numB), EMPTY,
            Loki::Select<t::isLASTBETA_UBYTE4, XYZB_LB_UBYTE4<t::numB>,
            Loki::Select<t::isLASTBETA_D3DCOLOR, XYZB_LB_D3DCOLOR<t::numB>, XYZB<t::numB>
            >::Result
            >::Result
            >::Result
            >::Result
            >::Result
            >::Result Pos;
        typedef Loki::Select<t::hasNORMAL, NORMAL<Pos>, Pos>::Result Normal;
        typedef Loki::Select<t::hasPSIZE, PSIZE<Normal>, Normal>::Result PSize;
        typedef Loki::Select<t::hasDIFFUSE, DIFFUSE<PSize>, PSize>::Result Diffuse;
        typedef Loki::Select<t::hasSPECULAR, SPECULAR<Diffuse>, Diffuse>::Result Specular;
        typedef Loki::Select<!!(t::numTEX), TEX<Specular, t::maxTEXINDEX>, Specular>::Result Tex;
    public:
        typedef Tex result;
    };
} //namespace Private

template <DWORD f>
struct FVFVertex : public Private::GenVertexType<f>::result
{
    enum { fvf = f };
    template <unsigned int n>
    struct TexIndex
    {
        enum { result = FVFTraits<f>::TexIndex<n>::result };
    };
};