The method I use is to associate a small descriptor class with each vertex buffer that contains the number of fields in the vertex buffer (position, colour, uv, etc), the vertex buffer's stride (size of each vertex) and the format and component count for each field in the vertex buffer. I've only worked in OpenGL, but I presume that a similar method works for DirectX too.
The VertexField class describes 1 field in a vertex stream:
class VertexField{public: enum Target { kPosition, kNormal, kColour, kTexCoord0, kTexCoord1, kTexCoord2, kTexCoord3, kTexCoord4, kTexCoord5, kTexCoord6, kTexCoord7, }; enum Type { kFloat = GL_FLOAT, kInt = GL_INT, kShort = GL_SHORT, kByte = GL_BYTE, kUnsignedByte = GL_UNSIGNED_BYTE, }; VertexField(); VertexField( Target target, Type type, u16 count, u16 offset ); Target m_target; Type m_type; u16 m_count; u16 m_offset;};
Here's how it's used by the client code to describe a vertex format and to initialise a vertex buffer instance:
struct Vertex{ Point3 m_position; Vector3 m_tangent; Vector3 m_bitangent; float m_u; float m_v; u8 m_colour[ 4 ];};VertexField fields[] ={ VertexField( VertexField::kPosition, VertexField::kFloat, 3, offsetof( Vertex, m_position ) ), VertexField( VertexField::kTexCoord0, VertexField::kFloat, 3, offsetof( Vertex, m_tangent ) ), VertexField( VertexField::kTexCoord1, VertexField::kFloat, 3, offsetof( Vertex, m_bitangent ) ), VertexField( VertexField::kTexCoord2, VertexField::kFloat, 2, offsetof( Vertex, m_u ) ), VertexField( VertexField::kColour, VertexField::kUnsignedByte, 4, offsetof( Vertex, m_colour ) ),};u32 numFields = sizeof( fields ) / sizeof( VertexField );m_pVertexBuffer = new VertexBuffer( fields, numFields, sizeof( Vertex ), vertexCount, VertexBuffer::kStatic );
And here's how it's used when a vertex buffer is bound (I'm assuming the use of vertex array buffers):
void VertexBuffer::Bind() const{ for ( u16 i = 0; i < m_fieldCount; ++i ) { const VertexField& rField = m_pFields;<br> <span class="cpp-keyword">void</span>* pFieldData = ( <span class="cpp-keyword">void</span>* )( u32 )rField.m_offset;<br> <br> <span class="cpp-keyword">switch</span> ( rField.m_target )<br> {<br> <span class="cpp-keyword">case</span> VertexField::kPosition:<br> glVertexPointer( rField.m_count, rField.m_type, m_stride, pFieldData );<br> <span class="cpp-keyword">break</span>;<br> <br> <span class="cpp-keyword">case</span> VertexField::kNormal:<br> SALT_ASSERT( rField.m_count == <span class="cpp-number">3</span> );<br> glNormalPointer( rField.m_type, m_stride, pFieldData );<br> <span class="cpp-keyword">break</span>;<br> <br> <span class="cpp-keyword">case</span> VertexField::kColour:<br> glColorPointer( rField.m_count, rField.m_type, m_stride, pFieldData );<br> <span class="cpp-keyword">break</span>;<br> <br><span class="cpp-comment">// case VertexField::kTexCoord0:</span><br><span class="cpp-comment">// case VertexField::kTexCoord1:</span><br><span class="cpp-comment">// case VertexField::kTexCoord2:</span><br><span class="cpp-comment">// case VertexField::kTexCoord3:</span><br><span class="cpp-comment">// case VertexField::kTexCoord4:</span><br><span class="cpp-comment">// case VertexField::kTexCoord5:</span><br><span class="cpp-comment">// case VertexField::kTexCoord6:</span><br><span class="cpp-comment">// case VertexField::kTexCoord7:</span><br> <span class="cpp-keyword">default</span>:<br> glClientActiveTextureARB( GL_TEXTURE0_ARB + rField.m_target - VertexField::kTexCoord0 );<br> glTexCoordPointer( rField.m_count, rField.m_type, m_stride, pFieldData );<br> <span class="cpp-keyword">break</span>;<br> }<br> }<br>}<br><br><br></pre></div><!–ENDSCRIPT–><br><br>This obviously isn't the whole story but hopefully it should be enough to point you in the right direction.<br><br>I'm not saying this is the best way, but it works very well for me :)