This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Flexible Vertex Format Generator Submitted by

The description is part of the code listing.

Download Associated File: FVF_Gen.h (11,399 bytes)

    #ifndef UMP_FVF_GEN_H
    #define UMP_FVF_GEN_H

    //  Copyright (C) 2000 by Vesa Karvonen. All rights reserved.
    //
    //  Permission is granted to freely use this software as long as this
    //  copyright message is retained.
    //
    //  September 30. 2000.

    #include <d3d.h>

    namespace D3D_Util
    {

    //  Flexible Vertex Format Generator
    //  ================================
    //
    //  Direct3D Immediate Mode uses flag values to describe vertex formats
    //  used for DrawPrimitive-based rendering. Each combination of these
    //  flexible vertex format (FVF) flags corresponds to a vertex type.
    //  Traditionally the programmer has manually translated FVF codes into C++
    //  structures. For example the FVF code:
    //
    //      const DWORD my_vertex_format =
    //          D3DFVF_XYZ    |
    //          D3DFVF_NORMAL |
    //          D3DFVF_TEX1   ;
    //
    //  corresponds to the following structure:
    //
    //      struct My_Vertex_Type
    //      {
    //          float m_pos[3];
    //          float m_normal[3];
    //          float m_tex_1[2];
    //      };
    //
    //  The C++ language features make it possible to automate this process,
    //  so that the vertex type is generated by the compiler. Using the
    //  FVF_Gen<> metaprogram, a vertex type, (nearly) equivalent to the above
    //  structure, can be obtained simply by giving the FVF code to the
    //  generator:
    //
    //      typedef FVF_Gen<my_vertex_format>::Res My_Vertex_Type;
    //
    //  The most notable difference to a hand written vertex structure is that
    //  the generated vertex type is not a POD structure and therefore can not
    //  be initialized with an aggregate initializer.
    //
    //  It is possible to parametrize the generator so that the user can choose
    //  the names and types of each possible vertex member. Unfortunately at
    //  least the Visual C++ 6 compiler gives an internal compiler error when
    //  that feature is enabled. (You can of course use some workaround. Most
    //  such workarounds are rather cumbersome to use.)
    //
    //  Although the generator was originally implemented mostly for fun it is
    //  not just a toy. Using the generator has a number of benefits:
    //  - it saves you from typing vertex structures explicitly
    //  - it allows you to localize changes to vertex formats better
    //  - it contains the rules for translating an FVF code into a vertex type
    //    and you don't have to learn or remember them anymore
    //
    //  I should point out that because the generator works by concatenating
    //  classes using parametrized multiple inheritance there is no C++ standard
    //  imposed guarantee on the layout of the resulting type. In practise on
    //  Visual C++ 6 and most other compilers the layout will be exactly as
    //  designed.
    //
    //  How does it work?
    //  =================
    //
    //  First the bits of the FVF code are interpreted using the D3DFVF macros.
    //  After the interpretation, we have a set of boolean and integer constants
    //  that describe the vertex format conveniently.
    //
    //      enum
    //      {
    //          has_pos    = /*...*/,
    //          has_rhw    = /*...*/,
    //          has_normal = /*...*/,
    //          // ...
    //      };
    //
    //  Then the generator uses a Config trait class to generate the required
    //  element types. For instance, a vertex may contain 0 to 5 blending
    //  weights. The generator uses the trait class to generate blending weight
    //  element with the appropriate number of weights.
    //
    //  Finally the generator concatenates the generated elements into a vertex
    //  one-by-one using parameterized multiple inheritance. Only the elements
    //  that were specified in the FVF code are concatenated into the resulting
    //  vertex type. The final result is a simple inheritance cascade with the
    //  required elements.
    //
    //  For example, the FVF code (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
    //  yeilds the type Both<Both<Pos,Normal>,Tex_1>, whose inheritance
    //  hierarchy looks like this:
    //
    //      Pos          Normal
    //       A             A
    //       |             |
    //       +------+------+
    //              |
    //      Both<Pos,Normal>   Tex_1
    //              A             A
    //              |             |
    //              +------+------+
    //                     |
    //         Both<Both<Pos,Normal>,Tex_1>
    //
    //  The generator also checks that the FVF code is valid and that the
    //  various elements of the vertex seem appropriate.
    //
    //  Customizing the generated vertex
    //  ================================
    //
    //  You can customize the Config trait class, so that you'll get the desired
    //  kind of elements in your vertices. Suppose you want that your vertices
    //  have float fields x, y, z for the position and a D3DVECTOR normal for
    //  the normal - a weird configuration. You can accomplish this by using the
    //  following definitions in the Config class:
    //
    //      struct Default_FVF_Gen_Config
    //      {
    //          struct Pos    {float x, y, z;};
    //          // ...
    //          struct Normal {D3DVECTOR normal;};
    //          // ...
    //      };
    //
    //  Bibliography
    //  ============
    //
    //  If you are interested in metaprogramming, you can find some information
    //  here:
    //
    //  http://extreme.indiana.edu/~tveldhui/papers/Template-Metaprograms/meta-art.html

    #define UMP_META_ASSERT(condition)\
        typedef bool Meta_Assertion[2 * !!(condition) - 1]

    //  Disables the annoying "Truncated to 255 characters" warning
    #ifdef _MSC_VER
    #pragma warning (disable:4786)
    #endif

    //  Meta-If implementation
    template<bool>
    struct Select
    {
        template<class T0, class T1>
        struct Type
        {
            typedef T1 Res;
        };
    };

    template<>
    struct Select<true>
    {
        template<class T0, class T1>
        struct Type
        {
            typedef T0 Res;
        };
    };

    template<bool i, class T0, class T1>
    struct If
    {
        typedef typename Select<i>::template Type<T0,T1>::Res Res;
    };

    template<class T>
    struct Is_Void
    {
        enum {res = false};
    };

    template<>
    struct Is_Void<void>
    {
        enum {res = true};
    };

    //  Type catenator
    template<class B0, class B1>
    struct Both : B0, B1
    {
    };

    template<class B0, class B1>
    class Cat
    {
        typedef typename If<Is_Void<B0>::res, B1, B0>::Res T0;
        typedef typename If<Is_Void<B0>::res, void, B1>::Res T1;
    public:
        typedef typename If<Is_Void<T1>::res, T0, Both<T0,T1> >::Res Res;

    private:
        typedef typename If<Is_Void<T1>::res, int, T1>::Res Checked_T1;

        UMP_META_ASSERT(
            Is_Void<T1>::res ||
            sizeof(Res) == sizeof(T0)+sizeof(Checked_T1));
    };

    //  Conditional catenator
    template<bool condition, class B0, class B1>
    class Cat_If
    {
        typedef typename Cat<B0, B1>::Res Both;
    public:
        typedef typename If<condition, Both, B0>::Res Res;
    };

    //  This trait class is used for configuring the FVF generator.
    struct Default_FVF_Gen_Config
    {
        struct Pos          {float m_pos[3];};
        struct RHW          {float m_rhw;};
        template<int n>
        struct Weights_Gen  {float m_weights[n]; typedef Weights_Gen<n> Res;};
        struct Normal       {float m_normal[3];};
        struct Diffuse      {DWORD m_diffuse;};
        struct Specular     {DWORD m_specular;};
        template<int n>
        struct Tex_1_Gen    {float m_tex_1[n]; typedef Tex_1_Gen<n> Res;};
        template<int n>
        struct Tex_2_Gen    {float m_tex_2[n]; typedef Tex_2_Gen<n> Res;};
        template<int n>
        struct Tex_3_Gen    {float m_tex_3[n]; typedef Tex_3_Gen<n> Res;};
        template<int n>
        struct Tex_4_Gen    {float m_tex_4[n]; typedef Tex_4_Gen<n> Res;};
    };

    //  This metaprogram generates a vertex structure according to the specified
    //  D3D Flexible Vertex Format code. This implementation handles only up to
    //  4 texture coordinate sets. Furthermore, under Visual Studio 6 or earlier
    //  the configuration feature does not compile.
    template<DWORD fvf, class Config = Default_FVF_Gen_Config>
    class FVF_Gen
    {
    #if defined(_MSC_VER) && _MSC_VER <= 1200   //  Optimistic???
        typedef Default_FVF_Gen_Config Config;
    #endif

        enum
        {
            dummy           = 1,

            has_pos         = (fvf & D3DFVF_POSITION_MASK) != 0,
            has_rhw         = (fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW,
            has_weights     = (fvf & D3DFVF_POSITION_MASK) >= D3DFVF_XYZB1,
            num_weights     = has_weights
                                ? (fvf-D3DFVF_XYZB1 & D3DFVF_POSITION_MASK) >> 1
                                : dummy,
            has_normal      = !!(fvf & D3DFVF_NORMAL),
            has_diffuse     = !!(fvf & D3DFVF_DIFFUSE),
            has_specular    = !!(fvf & D3DFVF_SPECULAR),
            has_tex_coords  = !!(fvf & D3DFVF_TEXCOUNT_MASK),
            num_tex_coords  = (fvf&D3DFVF_TEXCOUNT_MASK)>>D3DFVF_TEXCOUNT_SHIFT,

    #define UMP_DIM_TEX(i) ((0x1432 >> ((fvf >> ((i) * 2 + 14)) & 0xC)) & 0xF)

            dim_tex_1       = UMP_DIM_TEX(1) ? UMP_DIM_TEX(1) : dummy,
            dim_tex_2       = UMP_DIM_TEX(2) ? UMP_DIM_TEX(2) : dummy,
            dim_tex_3       = UMP_DIM_TEX(3) ? UMP_DIM_TEX(3) : dummy,
            dim_tex_4       = UMP_DIM_TEX(4) ? UMP_DIM_TEX(4) : dummy,

    #undef UMP_DIM_TEX

            is_valid        =
                has_pos &&
                (has_normal || has_diffuse || has_specular || has_tex_coords) &&
                !(has_rhw && (has_normal || has_weights))
        };

        UMP_META_ASSERT(num_tex_coords <= 4);   //  Generator limitation!
        UMP_META_ASSERT(is_valid);

        typedef typename Config::Pos                                    Pos;
        typedef typename Config::RHW                                    RHW;
        typedef typename Config::template Weights_Gen<num_weights>::Res Weights;
        typedef typename Config::Normal                                 Normal;
        typedef typename Config::Diffuse                                Diffuse;
        typedef typename Config::Specular                               Specular;
        typedef typename Config::template Tex_1_Gen<dim_tex_1>::Res     Tex_1;
        typedef typename Config::template Tex_2_Gen<dim_tex_2>::Res     Tex_2;
        typedef typename Config::template Tex_3_Gen<dim_tex_3>::Res     Tex_3;
        typedef typename Config::template Tex_4_Gen<dim_tex_4>::Res     Tex_4;

        UMP_META_ASSERT(sizeof(Pos)     == sizeof(float) * 3);
        UMP_META_ASSERT(sizeof(RHW)     == sizeof(float));
        UMP_META_ASSERT(sizeof(Weights) == sizeof(float) * num_weights);
        UMP_META_ASSERT(sizeof(Normal)  == sizeof(float) * 3);
        UMP_META_ASSERT(sizeof(Diffuse) == sizeof(float));
        UMP_META_ASSERT(sizeof(Specular)== sizeof(float));
        UMP_META_ASSERT(sizeof(Tex_1)   == sizeof(float) * dim_tex_1);
        UMP_META_ASSERT(sizeof(Tex_2)   == sizeof(float) * dim_tex_2);
        UMP_META_ASSERT(sizeof(Tex_3)   == sizeof(float) * dim_tex_3);
        UMP_META_ASSERT(sizeof(Tex_4)   == sizeof(float) * dim_tex_4);

        typedef typename Cat_If<has_pos,            void,   Pos     >::Res V_1;
        typedef typename Cat_If<has_rhw,            V_1,    RHW     >::Res V_2;
        typedef typename Cat_If<has_weights,        V_2,    Weights >::Res V_3;
        typedef typename Cat_If<has_normal,         V_3,    Normal  >::Res V_4;
        typedef typename Cat_If<has_diffuse,        V_4,    Diffuse >::Res V_5;
        typedef typename Cat_If<has_specular,       V_5,    Specular>::Res V_6;
        typedef typename Cat_If<(num_tex_coords>0), V_6,    Tex_1   >::Res V_7;
        typedef typename Cat_If<(num_tex_coords>1), V_7,    Tex_2   >::Res V_8;
        typedef typename Cat_If<(num_tex_coords>2), V_8,    Tex_3   >::Res V_9;
        typedef typename Cat_If<(num_tex_coords>3), V_9,    Tex_4   >::Res V_10;

    public:
        typedef V_10 Res;
    };

    #undef UMP_META_ASSERT
    }//namespace D3D_Util

    #endif

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.