MetaProgramming Just Might Save My Life

Started by
12 comments, last by pauls_1979 18 years, 4 months ago
I'm working on a multi-platform graphics engine and have recently implemented a flexible vertex format similar to that used in DirectX. I use a set of flags that tell the render routines what info is contained within a vertex. At the moment the code to setup a vertex buffer looks something like this: graphicsDevice.SetVertexBuffer(pVertexBuffer, numberOfVertices, VERT_POS | VERT_NORMAL | VERT_TEX1) This works fine but when it comes to manipulating the data it's a bit of a pain 'cause you can't just access a simple data structure like you would with a fixed vertex format. I was thinking it would be really useful if I could create some kind of vertex template class (or is that class template) to control access, making everything a bit easier and a bit safer. I'd imagine a simplified version working something like this: template <int FORMAT> class FlexVertex { public: inline void SetPos(float x, float y, float z) { } inline void SetNormal(float x, float y, float z) { } inline void SetTex1(float u, float v) { } static inline int Size() { int size; if(FORMAT & VERTEX_FORMAT_POS) { size += sizeof(float) * 3; } if(FORMAT & VERTEX_FORMAT_NORMAL) { size += sizeof(float) * 3; } and so on... return(size); } char m_data[???]; }; void RenderTriangle() { FlexVertex<VERTEX_FORMAT_POS> verts[3]; verts[0].SetPos(0, 0, 0); verts[1].SetPos(0, 0, 0); and so on... } Only trouble is I've not had that much experience with templates and meta-programming so I'm not even sure this is possible. I would need to be able to calculate the size of the vertex from the format at compile-time but obviously this isn't all that straightforward. Am I getting too ambitious or is this something I could I could implement relatively easily? Anyone got any ideas? Any help would be greatly appreciated, thanks in advance.
Advertisement
Wow, I've just spent the last hour learning all about meta-programming and believe it or not I've actually managed to get my template vertex class working (and it even works with the microsoft compiler). If anyone's interested I can post the code, it's a bit of a mess at the moment tho I'm afraid.
congrats, well done!
i've only dealt a little with template meta programming. but i'm really interested in what you've come up with. would be very nice to post your code.

you might also take a look into game developer magazine, issue october 2003, p.34, article "Taming Vertex Data Using C++ Templates" by Iain Cantlay. really covers a lot ...

you could check out the accompanying source code too:

see http://www.gdmag.com/code.htm, oct03.zip (817KB)
October 2003 archive. Contains Jon Blow's adaptive compression code and Iain Cantlay's template library for manipulating vertex data. (Updated 10/2/03.)

hth,
ghostd0g
Is there any particular reason why you're using this flexible vertex class? Because yeah, it sounds like a pain.

Is there any specific reason why you wouldn't just use a vertex with a fixed format? Size constraints?
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
If you're really interested in templates then C++ Templates by Vandevoorde and Josuttis is an excellant source. It is extremely detailed in how you accomplish differant tasks. Not in the application such as making a container class, but rather in technique such as how specializations are resolved.
Keys to success: Ability, ambition and opportunity.
Thanks to ghostd0g and LilBudyWizer for the links, I'll definately be checking them out. Template meta-programming is something I've wanted to play around with for a while now but I've never really had the need until now, it takes a bit to get your head round but it's very useful. Oh and to answer Endar's question, the reason I'm using a flexible format is that if I was to use a single, fixed format for all vertices it would be extremely wasteful and I'm pretty sure it would affect rendering performance. The only other alternative would be to create lots of fixed formats but that would be a bit of a future-proofing nightmare. Hope that makes sense. Anyway here's a copy of the source, it's not perfect but I think it's a pretty good start.

#ifndef VERTEX_HEADER#define VERTEX_HEADER/***************************** Macros and definitions *****************************/#define VERTEX_FORMAT_POS    0x0001#define VERTEX_FORMAT_COLOUR 0x0002#define VERTEX_FORMAT_NORMAL 0x0004#define VERTEX_FORMAT_TEX0   0x0008#define VERTEX_FORMAT_TEX1   0x0010#define VERTEX_FORMAT_TEX2   0x0020#define VERTEX_FORMAT_TEX3   0x0040#define VERTEX_FORMAT_TEX4   0x0080#define VERTEX_FORMAT_TEX5   0x0100#define VERTEX_FORMAT_TEX6   0x0200#define VERTEX_FORMAT_TEX7   0x0400/************************************ Template meta-program classes ************************************/template <unsigned flag> struct VertexDataSize{	static const unsigned ms_size = 0;};template <> struct VertexDataSize<VERTEX_FORMAT_POS> {	static const unsigned ms_size = sizeof(float) * 3;};template <> struct VertexDataSize<VERTEX_FORMAT_COLOUR> {	static const unsigned ms_size = sizeof(Colour32);};template <> struct VertexDataSize<VERTEX_FORMAT_NORMAL> {	static const unsigned ms_size = sizeof(float) * 3;};template <> struct VertexDataSize<VERTEX_FORMAT_TEX0> {	static const unsigned ms_size = sizeof(float) * 2;};template <> struct VertexDataSize<VERTEX_FORMAT_TEX1> {	static const unsigned ms_size = sizeof(float) * 2;};template <> struct VertexDataSize<VERTEX_FORMAT_TEX2> {	static const unsigned ms_size = sizeof(float) * 2;};template <> struct VertexDataSize<VERTEX_FORMAT_TEX3> {	static const unsigned ms_size = sizeof(float) * 2;};template <> struct VertexDataSize<VERTEX_FORMAT_TEX4> {	static const unsigned ms_size = sizeof(float) * 2;};template <> struct VertexDataSize<VERTEX_FORMAT_TEX5> {	static const unsigned ms_size = sizeof(float) * 2;};template <> struct VertexDataSize<VERTEX_FORMAT_TEX6> {	static const unsigned ms_size = sizeof(float) * 2;};template <> struct VertexDataSize<VERTEX_FORMAT_TEX7> {	static const unsigned ms_size = sizeof(float) * 2;};template <unsigned shift, unsigned format> struct VertexSize{	static const unsigned ms_size = VertexDataSize<format & (1 << shift)>::ms_size + VertexSize<shift + 1, format>::ms_size;};template <unsigned format> struct VertexSize <31, format>{	static const unsigned ms_size = 0;};template <unsigned shift, unsigned format, unsigned flag> struct VertexOffset{	static const unsigned ms_offset = shift < (flag >> 1) ? VertexDataSize<format & (1 << shift)>::ms_size + VertexOffset<shift + 1, format, flag>::ms_offset : 0;};template <unsigned format, unsigned flag> struct VertexOffset <31, format, flag>{	static const unsigned ms_offset = 0;};/******************* Vertex class *******************/template <int FORMAT> class Vertex{	public:		static inline UInt32 GetFormat()		{			return(FORMAT);		}		inline void SetPos(Vector3 &pos)		{			CompileAssert(FORMAT & VERTEX_FORMAT_POS);			*(Vector3 *)(m_data + VertexOffset<0, FORMAT, VERTEX_FORMAT_POS>::ms_offset) = pos;		}		inline void SetColour(Colour32 colour)		{			CompileAssert(FORMAT & VERTEX_FORMAT_COLOUR);			*(Colour32 *)(m_data + VertexOffset<0, FORMAT, VERTEX_FORMAT_COLOUR>::ms_offset) = colour;		}		inline void SetTextureCoordinate0(TextureCoordinate &textureCoordinate)		{			CompileAssert(FORMAT & VERTEX_FORMAT_TEX0);			*(TextureCoordinate *)(m_data + VertexOffset<0, FORMAT, VERTEX_FORMAT_TEX0>::ms_offset) = textureCoordinate;		}		inline void SetTextureCoordinate1(TextureCoordinate &textureCoordinate)		{			CompileAssert(FORMAT & VERTEX_FORMAT_TEX1);			*(TextureCoordinate *)(m_data + VertexOffset<0, FORMAT, VERTEX_FORMAT_TEX1>::ms_offset) = textureCoordinate;		}		inline void SetTextureCoordinate2(TextureCoordinate &textureCoordinate)		{			CompileAssert(FORMAT & VERTEX_FORMAT_TEX2);			*(TextureCoordinate *)(m_data + VertexOffset<0, FORMAT, VERTEX_FORMAT_TEX2>::ms_offset) = textureCoordinate;		}		inline void SetTextureCoordinate3(TextureCoordinate &textureCoordinate)		{			CompileAssert(FORMAT & VERTEX_FORMAT_TEX3);			*(TextureCoordinate *)(m_data + VertexOffset<0, FORMAT, VERTEX_FORMAT_TEX3>::ms_offset) = textureCoordinate;		}		inline void SetTextureCoordinate4(TextureCoordinate &textureCoordinate)		{			CompileAssert(FORMAT & VERTEX_FORMAT_TEX4);			*(TextureCoordinate *)(m_data + VertexOffset<0, FORMAT, VERTEX_FORMAT_TEX4>::ms_offset) = textureCoordinate;		}		inline void SetTextureCoordinate5(TextureCoordinate &textureCoordinate)		{			CompileAssert(FORMAT & VERTEX_FORMAT_TEX5);			*(TextureCoordinate *)(m_data + VertexOffset<0, FORMAT, VERTEX_FORMAT_TEX5>::ms_offset) = textureCoordinate;		}		inline void SetTextureCoordinate6(TextureCoordinate &textureCoordinate)		{			CompileAssert(FORMAT & VERTEX_FORMAT_TEX6);			*(TextureCoordinate *)(m_data + VertexOffset<0, FORMAT, VERTEX_FORMAT_TEX6>::ms_offset) = textureCoordinate;		}		inline void SetTextureCoordinate7(TextureCoordinate &textureCoordinate)		{			CompileAssert(FORMAT & VERTEX_FORMAT_TEX7);			*(TextureCoordinate *)(m_data + VertexOffset<0, FORMAT, VERTEX_FORMAT_TEX7>::ms_offset) = textureCoordinate;		}	private:		char	m_data[VertexSize<0, FORMAT>::ms_size];};#endif //VERTEX_HEADER


[Edited by - pauls_1979 on December 2, 2005 4:08:26 AM]
Quote:Original post by Endar
Is there any particular reason why you're using this flexible vertex class? Because yeah, it sounds like a pain.

Is there any specific reason why you wouldn't just use a vertex with a fixed format? Size constraints?



since you don't have to go down the fixed function pipeline anymore you may need special vertexformats on different occasions.

"C++ Template Metaprogramming" by David Abrahams and Aleksey Gurtovoy (ISBN: 0321227255) seems to be valuable too.
I [once programmed a version of what you're talking about] using void pointer arithmetic. It is not nice but it is readable and there are no templates :)

Well I was actually manipulatng vertices of D3D FVF formats.
You would first take a pointer to the vertex (void pointer) and its FVF constant.
You would check FVF against D3DFVF_XYZRHW for example. If the check returned true you would advance by the size of the element: 4 * sizeof int. If you finally reach the element that you want, you may recast the pointer into whatever the type of the element is. Of course the order of testing is vital (D3D dictates an order and it is ofcourse found in the SDK)...
Am I talking about the same thing that you are talking about?
[ my blog ]
Quote:
I [once programmed a version of what you're talking about] using void pointer arithmetic. It is not nice but it is readable and there are no templates :)

Well I was actually manipulatng vertices of D3D FVF formats.
You would first take a pointer to the vertex (void pointer) and its FVF constant.
You would check FVF against D3DFVF_XYZRHW for example. If the check returned true you would advance by the size of the element: 4 * sizeof int. If you finally reach the element that you want, you may recast the pointer into whatever the type of the element is. Of course the order of testing is vital (D3D dictates an order and it is ofcourse found in the SDK)...
Am I talking about the same thing that you are talking about?


That's exactly what I'm talking about and pretty much exactly how I implemented it before creating this template class (although I also have to worry about supporting OpenGL and software rendering). It's fine doing everything with simple pointers within core render functions 'cause they're not likely to change but I was worried about ease of use for other programmers. Just creating simple routines for manipulating data would become more of a chore and therefore more of a risk. It would be all too easy for someone new to the engine to get the data in the wrong order (which as you mentioned, would not be good) or something like that.

This topic is closed to new replies.

Advertisement