Skip to main content
gamedev.net
🔒 Locked

Vertex Declarations and SetStreamSource

Started by esunder Aug 21, 2008 at 9:28 AM 7 replies 2.5k views
Original Post
esunder
esunder
Hello all, I am working on getting my Direct3D engine into a more flexible state and am looking at doing so with vertex declarations. My goal is to be able to render multiple types of vertex buffers. One vertex buffer might have just positions and colors. The next vertex buffer will have positions, normals, colors, textures, bones, weights, etc. I have animation and a few basic lighting techniques implemented on the shader that work, but now I need to render other objects that are not as complex as the animated model. Their objects do not need all the extra information in the larger vertex buffer. -------------------------------- Here are some fundamental questions: Do I need a unique vertex declaration for each unique vertex buffer? On top of this, I need to call device->SetVertexDeclaration() on each declaration before I bind and render the buffer. For each component of my vertex buffer, do I need a matching call to SetStreamSource with an offset into the buffer. --------------------------------- Now here are my CreateVertexBuffer and RenderVertexBuffer functions, i know its kinda big, and not as clean as it could be, but i am trying to just get it up and working and then I can do some code clean up. Also, you can see the assumptions i made to the questions above.

//Buffer creation functions.
LPDIRECT3DVERTEXDECLARATION9 D3DEngine::CreateDeclaration(Vertex* vertices)
{
	int x = BitSet(vertices->m_type);
	x+=1;

	////////////////////////////////////////////////////
	/*----------DECLARATIONS----------*/
	D3DVERTEXELEMENT9* elements = new D3DVERTEXELEMENT9[x];
	int i = 0;
	if(vertices->m_type & VERT_POSITION)
	{	
		elements.Stream		= i;
		elements.Offset		= 0;
		elements.Type		= D3DDECLTYPE_FLOAT3;
		elements.Method		= D3DDECLMETHOD_DEFAULT;
		elements.Usage		= D3DDECLUSAGE_POSITION;
		elements.UsageIndex	= 0;
		
		i++;
	}

	if(vertices->m_type & VERT_NORMAL)
	{	
		elements.Stream		= i;
		elements.Offset		= 0;
		elements.Type		= D3DDECLTYPE_FLOAT3;
		elements.Method		= D3DDECLMETHOD_DEFAULT;
		elements.Usage		= D3DDECLUSAGE_NORMAL;
		elements.UsageIndex	= 0;

		i++;
	}

	if(vertices->m_type & VERT_COLOR)
	{	
		elements.Stream		= i;
		elements.Offset		= 0;
		elements.Type		= D3DDECLTYPE_D3DCOLOR;
		elements.Method		= D3DDECLMETHOD_DEFAULT;
		elements.Usage		= D3DDECLUSAGE_COLOR;
		elements.UsageIndex	= 0;

		i++;
	}

	if(vertices->m_type & VERT_TEX0)
	{	
		elements.Stream		= i;
		elements.Offset		= 0;
		elements.Type		= D3DDECLTYPE_FLOAT2;
		elements.Method		= D3DDECLMETHOD_DEFAULT;
		elements.Usage		= D3DDECLUSAGE_TEXCOORD;
		elements.UsageIndex	= 0;

		i++;
	}

	if(vertices->m_type & VERT_TEX1)
	{	
		elements.Stream		= i;
		elements.Offset		= 0;
		elements.Type		= D3DDECLTYPE_FLOAT2;
		elements.Method		= D3DDECLMETHOD_DEFAULT;
		elements.Usage		= D3DDECLUSAGE_TEXCOORD;
		elements.UsageIndex	= 1;

		i++;
	}

	if(vertices->m_type & VERT_TEX2)
	{	
		elements.Stream		= i;
		elements.Offset		= 0;
		elements.Type		= D3DDECLTYPE_FLOAT2;
		elements.Method		= D3DDECLMETHOD_DEFAULT;
		elements.Usage		= D3DDECLUSAGE_TEXCOORD;
		elements.UsageIndex	= 2;

		i++;
	}

	if(vertices->m_type & VERT_BONE)
	{	
		elements.Stream		= i;
		elements.Offset		= 0;
		elements.Type		= D3DDECLTYPE_FLOAT4;
		elements.Method		= D3DDECLMETHOD_DEFAULT;
		elements.Usage		= D3DDECLUSAGE_TEXCOORD;
		elements.UsageIndex	= 3;

		i++;
	}

	if(vertices->m_type & VERT_WEIGHT)
	{	
		elements.Stream		= i;
		elements.Offset		= 0;
		elements.Type		= D3DDECLTYPE_FLOAT4;
		elements.Method		= D3DDECLMETHOD_DEFAULT;
		elements.Usage		= D3DDECLUSAGE_TEXCOORD;
		elements.UsageIndex	= 4;

		i++;
	}

	if(vertices->m_type & VERT_TANGENT)
	{	
		elements.Stream		= i;
		elements.Offset		= 0;
		elements.Type		= D3DDECLTYPE_FLOAT4;
		elements.Method		= D3DDECLMETHOD_DEFAULT;
		elements.Usage		= D3DDECLUSAGE_TANGENT;
		elements.UsageIndex	= 0;

		i++;
	}
	
	D3DVERTEXELEMENT9 endelement = D3DDECL_END();
	elements = endelement;	

	LPDIRECT3DVERTEXDECLARATION9 declaration;
	m_device->CreateVertexDeclaration(elements, &declaration);
	//m_device->SetVertexDeclaration(declaration);

	return declaration;
}

int D3DEngine::CreateVertexBuffer(Vertex* &vertices, int numVertices, int mode)
{
	//Create new vertex buffer id.
	int vertexBufferId = 0;
	vertexBufferId = (int)m_vertexBuffers.size();

	//Create new Direct3D vertex buffer struct.
	D3DVertexBuffer newBuffer;

	if(vertices->v0)
	{
		for(int i=0; i<numVertices; i++)
		{
			vertices->v0 = -vertices->v0;
		}
	}
	
	newBuffer.declaration = CreateDeclaration(vertices);

	LPDIRECT3DVERTEXBUFFER9 pvb;
	if( FAILED( m_device->CreateVertexBuffer(numVertices*vertices->Size(), D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &pvb, NULL) ))
		MessageBox(NULL, "Failed to create vertex buffer.", "Failed to create vertex buffer.", 0);
	else
	{
		void* pVertices;
		//Lock the vertex buffer.
		pvb->Lock( 0, numVertices*vertices->Size(), (void**)&pVertices, 0 );
	    
		//Get the data from my vertex format.
		float* data;
		vertices->Data(data);
		//Copy the data over.
		memcpy(pVertices, data, numVertices*vertices->Size());
		delete [] data;
		//Unlock the buffer.
		pvb->Unlock();
	}
	
	//Populate the struct.
	newBuffer.vb = pvb;
	newBuffer.type = vertices->m_type;
	newBuffer.stride = vertices->Size();
	newBuffer.numVertices = numVertices;
	newBuffer.data = vertices;
	//Set the mode of the buffer and calc the number of triangles.
	switch(mode)
	{
		case TRI_STRIP:
		{
			newBuffer.numTriangles = numVertices - 2;
			newBuffer.mode = D3DPT_TRIANGLESTRIP;
		}break;
		case TRI_LIST:
		{
			newBuffer.numTriangles = numVertices/3;
			newBuffer.mode = D3DPT_TRIANGLELIST;
			
		}break;
		case TRI_FAN:
		{
			newBuffer.numTriangles = numVertices - 2;
			newBuffer.mode = D3DPT_TRIANGLEFAN;
		}break;
		case LINE_LIST:
		{
			newBuffer.numTriangles = numVertices/2;
			newBuffer.mode = D3DPT_LINELIST;
		}
	}

	//Add the vertex buffer to the vector.
	m_vertexBuffers.push_back(newBuffer);

	//Return the ID.
	return vertexBufferId;
}

//Buffer render functions.
void D3DEngine::BindVertexBuffer(int vertexBufferId)
{
	//Make sure the IDs are valid.
	if(vertexBufferId < 0 || vertexBufferId > (int)m_vertexBuffers.size())
		return;

	//Get the vertex struct.
	D3DVertexBuffer vbuffer = m_vertexBuffers[vertexBufferId];

	m_device->SetVertexDeclaration(vbuffer.declaration);

	int d = 0;
	
	/*----------DECLARATIONS----------*/
	if(vbuffer.type & VERT_POSITION)
	{	
		//OutputDebugString("Position\n");
		m_device->SetStreamSource(d++, vbuffer.vb, vbuffer.data->offset_position, vbuffer.stride);
	}
	if(vbuffer.type & VERT_NORMAL)
	{	
		//OutputDebugString("Normal\n");
		m_device->SetStreamSource(d++, vbuffer.vb, vbuffer.data->offset_normal, vbuffer.stride);
	}
	if(vbuffer.type & VERT_COLOR)
	{	
		//OutputDebugString("Color\n");
		m_device->SetStreamSource(d++, vbuffer.vb, vbuffer.data->offset_color, vbuffer.stride);
	}
	if(vbuffer.type & VERT_TEX0)
	{	
		//OutputDebugString("Tex0\n");
		m_device->SetStreamSource(d++, vbuffer.vb, vbuffer.data->offset_tex0, vbuffer.stride);
	}
	if(vbuffer.type & VERT_TEX1)
	{	
		//OutputDebugString("Tex1\n");
		m_device->SetStreamSource(d++, vbuffer.vb, vbuffer.data->offset_tex1, vbuffer.stride);
	}
	if(vbuffer.type & VERT_TEX2)
	{	
		//OutputDebugString("Tex2\n");
		m_device->SetStreamSource(d++, vbuffer.vb, vbuffer.data->offset_tex2, vbuffer.stride);
	}
	if(vbuffer.type & VERT_BONE)
	{
		//OutputDebugString("Bone\n");
		m_device->SetStreamSource(d++, vbuffer.vb, vbuffer.data->offset_bone, vbuffer.stride);
	}
	if(vbuffer.type & VERT_WEIGHT)
	{	
		//OutputDebugString("Weight\n");	
		m_device->SetStreamSource(d++, vbuffer.vb, vbuffer.data->offset_weight, vbuffer.stride);
	}

	if(vbuffer.type & VERT_TANGENT)
	{	
		//OutputDebugString("Tangent\n");
		m_device->SetStreamSource(d++, vbuffer.vb, vbuffer.data->offset_tangent, vbuffer.stride);
	}
}

void D3DEngine::RenderVertexBuffer(int vertexBufferId)
{
	//Make sure the ID is valid.
	if(vertexBufferId < 0 || vertexBufferId > (int)m_vertexBuffers.size())
		return;

	//Get the struct from the vector.
	D3DVertexBuffer vbuffer = m_vertexBuffers[vertexBufferId];

	//Bind the vertex buffer.
	BindVertexBuffer(vertexBufferId);

	//Draw the buffer.
	m_device->DrawPrimitive(vbuffer.mode, 0, vbuffer.numTriangles);

	//Increment the number of triangles drawn.
	if(vbuffer.mode == D3DPT_TRIANGLEFAN || vbuffer.mode == D3DPT_TRIANGLESTRIP || vbuffer.mode == D3DPT_TRIANGLELIST)
	{
		numRenderedTriangles += vbuffer.numTriangles;
	}
}

[Edited by - esunder on August 21, 2008 11:51:11 PM]
~esunderStudent
Evil Steve
Evil Steve
Quote:
Original post by esunder
Do I need a unique vertex declaration for each unique vertex buffer?
Each of your vertex declarations should be unique, there's no point having two identical declarations. So you want one declaration for each vertex buffer "type".

Quote:
Original post by esunder
On top of this, I need to call device->SetVertexDeclaration() on each declaration before I bind and render the buffer.
Whenever the declaration changes, yes.

Quote:
Original post by esunder
For each component of my vertex buffer, do I need a matching call to SetStreamSource with an offset into the buffer.
No, the vertex declaration has that information in it. The elements.Offset member is supposed to be the offset from the start of a vertex to this component type.

EDIT: Your UsageIndex looks a little funky to me. If your vertex has VERT_TEX0, then your declaration has 1 set of texture coordinates (index 0). If your vertex has VERT_TEX2, then your declaration is invalid - it has 1 set of texture coordinates (index 2), but indices 0 and 1 are missing.

Here's the relevant function from my vertex declaration class:
IDirect3DVertexDeclaration9* PVertexDecl::GetDecl(){	// If declaration is already valid, no need to regenerate it	if(m_pDecl)		return m_pDecl;	DWORD dwOffset = 0;	// Offset into vertex	DWORD dwIndex[16];	// Index of this type (E.g. 1 for 2nd set of tex coords)	for(int i=0; i<16; ++i)		dwIndex = 0;	for(size_t i=0; i<m_vDecl.size(); ++i)	{		Assert(m_vDecl.Usage < 16, "Unexpected usage");		m_vDecl.Offset = (WORD)dwOffset;		m_vDecl.UsageIndex = (BYTE)dwIndex[m_vDecl.Usage];		switch(m_vDecl.Type)		{		case D3DDECLTYPE_FLOAT1: dwOffset += sizeof(float); break;		case D3DDECLTYPE_FLOAT2: dwOffset += sizeof(float)*2; break;		case D3DDECLTYPE_FLOAT3: dwOffset += sizeof(float)*3; break;		case D3DDECLTYPE_FLOAT4: dwOffset += sizeof(float)*4; break;		case D3DDECLTYPE_D3DCOLOR: dwOffset += sizeof(DWORD); break;		case D3DDECLTYPE_UBYTE4: dwOffset += sizeof(BYTE)*4; break;		case D3DDECLTYPE_SHORT2: dwOffset += sizeof(SHORT)*2; break;		case D3DDECLTYPE_SHORT4: dwOffset += sizeof(SHORT)*4; break;		default: Assert(false, "Unhandled size");		}		++dwIndex[m_vDecl.Usage];	}	// Add the terminator	m_vDecl.push_back(s_streamEnd);	// Now create the declaration	HRESULT hResult = PGraphics::Get().GetDevice()->CreateVertexDeclaration(&m_vDecl[0], &m_pDecl);	if(FAILED(hResult))	{		#ifndef DRUINK_NO_GRAPHICS // Don't link to DXGetErrorStringA			std::stringstream str;			str << "Failed to create vertex declaration. Error: ";			str << DXGetErrorStringA(hResult);			PApp::Get().SetError(str.str());			ELog::Get().ErrorFormat("RENDER  : %s\n", PApp::Get().GetError().c_str());		#endif		return NULL;	}	// Done	return m_pDecl;}
m_vDecl is a std::vector. The code goes through each element in the vertex declaration and accumulates the offset (dwOffset variable) and usage index (dwIndex).
esunder
esunder
Ok I see that the usage may be off for the texture coordinates.

The current problem I am having is that I am trying to render an animated model that uses all of the vertex components, and then I am trying to also render a flat grid of lines that are just position and color.

The model animates and renders fine, however the grid does not. However, in debug mode, the grid renders fine, only the release version is incorrect. Also, If I remove the model and just try to render the lines, they do not show up.
I currently only have 2 declarations, one has all of the vertex components and works fine, the other only has pos and color and does not work.
~esunderStudent
Evil Steve
Evil Steve
Quote:
Original post by esunder
Ok I see that the usage may be off for the texture coordinates.

The current problem I am having is that I am trying to render an animated model that uses all of the vertex components, and then I am trying to also render a flat grid of lines that are just position and color.

The model animates and renders fine, however the grid does not. However, in debug mode, the grid renders fine, only the release version is incorrect. Also, If I remove the model and just try to render the lines, they do not show up.
I currently only have 2 declarations, one has all of the vertex components and works fine, the other only has pos and color and does not work.
That's the correct way to do it, the vertices are different formats so you should have different declarations.
Anything from the Debug runtimes?
esunder
esunder
Is it correct to SetStreamSource for each component of the vertex? Or if i set the offset in the vertex declaration, do i only need to do it once?
~esunderStudent
legalize
legalize
Quote:
Original post by esunder
Is it correct to SetStreamSource for each component of the vertex? Or if i set the offset in the vertex declaration, do i only need to do it once?


A stream source can contain one or more components of a vertex. So just because your vertex has 3 components doesn't necessarily imply you'll have 3 streams. You can have 1, 2, or 3 streams for 3 components.

I discuss this more in Chapter 5. Modeling of my book "The Direct3D Graphics Pipeline". Perhaps taking a look at that chapter will help clear things up for you.
esunder
esunder
All of the vertex declarations that I am seeing online are created differently than mine. Is this not a valid way to do it?

Also, nothing from the Debug runtimes :\. That was the first thing i checked.
BTW, how do you get your code into a box to use less room?

//Buffer creation functions.LPDIRECT3DVERTEXDECLARATION9 D3DEngine::CreateDeclaration(Vertex* vertices){	int x = BitSet(vertices->m_type);	x+=1;	////////////////////////////////////////////////////	/*----------DECLARATIONS----------*/	D3DVERTEXELEMENT9* elements = new D3DVERTEXELEMENT9[x];	int i = 0;	if(vertices->m_type & VERT_POSITION)	{			elements.Stream		= 0;		elements.Offset		= vertices->offset_position;		elements.Type		= D3DDECLTYPE_FLOAT3;		elements.Method		= D3DDECLMETHOD_DEFAULT;		elements.Usage		= D3DDECLUSAGE_POSITION;		elements.UsageIndex	= 0;		i++;	}	if(vertices->m_type & VERT_NORMAL)	{			elements.Stream		= 0;		elements.Offset		= vertices->offset_normal;		elements.Type		= D3DDECLTYPE_FLOAT3;		elements.Method		= D3DDECLMETHOD_DEFAULT;		elements.Usage		= D3DDECLUSAGE_NORMAL;		elements.UsageIndex	= 0;		i++;	}	if(vertices->m_type & VERT_COLOR)	{			elements.Stream		= 0;		elements.Offset		= vertices->offset_color;		elements.Type		= D3DDECLTYPE_D3DCOLOR;		elements.Method		= D3DDECLMETHOD_DEFAULT;		elements.Usage		= D3DDECLUSAGE_COLOR;		elements.UsageIndex	= 0;		i++;	}	if(vertices->m_type & VERT_TEX0)	{			elements.Stream		= 0;		elements.Offset		= vertices->offset_tex0;		elements.Type		= D3DDECLTYPE_FLOAT2;		elements.Method		= D3DDECLMETHOD_DEFAULT;		elements.Usage		= D3DDECLUSAGE_TEXCOORD;		elements.UsageIndex	= 0;		i++;	}	if(vertices->m_type & VERT_TEX1)	{			elements.Stream		= 0;		elements.Offset		= vertices->offset_tex1;		elements.Type		= D3DDECLTYPE_FLOAT2;		elements.Method		= D3DDECLMETHOD_DEFAULT;		elements.Usage		= D3DDECLUSAGE_TEXCOORD;		elements.UsageIndex	= 1;		i++;	}	if(vertices->m_type & VERT_TEX2)	{			elements.Stream		= 0;		elements.Offset		= vertices->offset_tex2;		elements.Type		= D3DDECLTYPE_FLOAT2;		elements.Method		= D3DDECLMETHOD_DEFAULT;		elements.Usage		= D3DDECLUSAGE_TEXCOORD;		elements.UsageIndex	= 2;		i++;	}	if(vertices->m_type & VERT_BONE)	{			elements.Stream		= 0;		elements.Offset		= vertices->offset_bone;		elements.Type		= D3DDECLTYPE_FLOAT4;		elements.Method		= D3DDECLMETHOD_DEFAULT;		elements.Usage		= D3DDECLUSAGE_TEXCOORD;		elements.UsageIndex	= 3;		i++;	}	if(vertices->m_type & VERT_WEIGHT)	{			elements.Stream		= 0;		elements.Offset		= vertices->offset_weight;		elements.Type		= D3DDECLTYPE_FLOAT4;		elements.Method		= D3DDECLMETHOD_DEFAULT;		elements.Usage		= D3DDECLUSAGE_TEXCOORD;		elements.UsageIndex	= 4;		i++;	}	if(vertices->m_type & VERT_TANGENT)	{			elements.Stream		= 0;		elements.Offset		= vertices->offset_tangent;		elements.Type		= D3DDECLTYPE_FLOAT4;		elements.Method		= D3DDECLMETHOD_DEFAULT;		elements.Usage		= D3DDECLUSAGE_TANGENT;		elements.UsageIndex	= 0;		i++;	}	D3DVERTEXELEMENT9 endelement = D3DDECL_END();	elements = endelement;	LPDIRECT3DVERTEXDECLARATION9 declaration;	m_device->CreateVertexDeclaration(elements, &declaration);	//m_device->SetVertexDeclaration(declaration);	return declaration;}


[Edited by - esunder on August 21, 2008 11:43:41 PM]
~esunderStudent
legalize
legalize
It looks like you're trying to make one "uber vertex class" that can handle any possible layout of vertices.

In practice, unless you are doing a very general purpose modeling or mesh transformation tool, you rarely need this level of generality.

Since a vertex declaration is intimately tied to its layout, most people make that association in their code. For instance, something like:

struct Vertex1{    D3DXVECTOR3 _position;    D3DXVECTOR3 _normal;    static IDirect3DVertexDeclaration9 *GetDeclaration(IDirect3DDevice9 *device)    {        D3DVERTEXELEMENT9 elements[] =        {            { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },            { 0, 3*sizeof(float), D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },            D3DDECL_END        };        IDirect3DVertexDeclaration9 *declaration = 0;        THR(device->CreateVertexDeclaration(&elements[0], &declaration));        return declaration;    }};


BTW, use the source bbcode markup to mark a block of text as source code.
esunder
esunder
I dont have to declare my declarations like you did in your example do I? Assuming I need a general vertex format, does my declaration function look like it would produce the right result?
~esunderStudent

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.