#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
|