Creating a more dynamic vertex buffer struct

Started by
2 comments, last by Maverick_24 16 years, 1 month ago
I have a mesh class with these structs right now:

struct VertexFormat_XYZ_NORM_UV
{
	float x, y, z;
	float nx, ny, nz;
	float u, v;
};
struct VertexFormat_XYZ_RGBA
{
	float x, y, z;
	char r, g, b, a;
};

then i fill one (not both) of these std::vectors with the mesh data

std::vector<VertexFormat_XYZ_NORM_UV> VertexBuffer1;
std::vector<VertexFormat_XYZ_RGBA> VertexBuffer2;

This seems kind of a cheesy and non-dynamic approach since i only have support for 2 vertex buffer types, and if i wanted to add more types i would have to hardcode a new struct for each one. I would like a better solution. What is a common approach, if there is one? Is there a common programming strategy to tackling this exact problem? Thanks in advance
Advertisement
Take a look at Flexible Vertex Format Generator. It doesn't completely solve the problem, but at least it minimizes code redundancy.
deathkrushPS3/Xbox360 Graphics Programmer, Mass Media.Completed Projects: Stuntman Ignition (PS3), Saints Row 2 (PS3), Darksiders(PS3, 360)
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 :)
I started coding up something more like this.....a dynamic array of floats....if any elements are not floats i use reinterpret_cast<> to get the whatever the bits are into a 32 bit "float"

Any comments/criticisms would be appreciated, thanks

	void CreateVertexBuffer( eVertexFormat vertFormat )	{		int iNumFloatsPerStride = 0;		if ( vertFormat == XYZ_NORM_UV )		{			vbType = XYZ_NORM_UV;			iStride = sizeof(float) * 8;			iNumFloatsPerStride = 8;		}		else if ( vertFormat == XYZ_ARGB )		{			vbType = XYZ_ARGB;			iStride = sizeof(float) * 4;			iNumFloatsPerStride = 4;		}				iTotalVerts = vertList.size();		int iVBSize = iTotalVerts * iNumFloatsPerStride;		vertexBuffer = new float[iVBSize];		for ( int i = 0; i < iTotalVerts; ++i )		{			int iIndex = i * iNumFloatsPerStride;			//This Vertex Format Contains XYZ Positional Data			if ( vertFormat == XYZ_NORM_UV || vertFormat == XYZ_ARGB )			{				vertexBuffer[iIndex++] = vertList.at(i).x;				vertexBuffer[iIndex++] = vertList.at(i).y;				vertexBuffer[iIndex++] = vertList.at(i).z;			}			//This Vertex Format Contains XYZ Normal Data			if ( vertFormat == XYZ_NORM_UV )			{				vertexBuffer[iIndex++] = normalList.at(i).x;				vertexBuffer[iIndex++] = normalList.at(i).y;				vertexBuffer[iIndex++] = normalList.at(i).z;			}			//This Vertex Format Contains UV Coordinate Data			if ( vertFormat == XYZ_NORM_UV )			{				vertexBuffer[iIndex++] = uvcoordList.at(i).u;				vertexBuffer[iIndex++] = uvcoordList.at(i).v;			}			//This Vertex Format Contains Per-Vertex Color Data			if ( vertFormat == XYZ_ARGB )			{				char ch[4] = {	colorList.at(i).a,								colorList.at(i).r,								colorList.at(i).g,								colorList.at(i).b,	};								vertexBuffer[iIndex++] = *reinterpret_cast<float*>(&ch);			}					}	}

This topic is closed to new replies.

Advertisement