# [D3D9] Only 1st submesh renders w/ indexed vertexbuffer... [SOLVED]

## Recommended Posts

Hey guys, I'm rewriting my model code for the third or fourth time now, this time using the lib3ds which is great so far and offers more than I need, BUT... I've created a few test box models in 3d Studio Max with a varying number of submeshes and my current code only ever seems to render the very first submesh and just discard the rest. Of course, bugs like this always smell like a typo somewhere in the memcpy parts or the CreateVertexBuffer/CreateIndexBuffer function parameters or in the loops etc. but I triple-checked the code and I'm growing more and more certain that it's not a simple typo (although I have to admit, there's no "typo-safe" with me :D), so that's primarily why I decided to post this. It also keeps up any progress on a project which depends on taking little time pretty much. Anyways, check out this code, I tried to make it readable and I think it's reasonable short considering the task it's responsible for. These are the important functions for the Model class:
void Model::CreateVertexBuffer ( IDirect3DDevice9 *device )
{
void *void_pointer;

// create a temporary buffer for the vertices
Vertex *vertices = new Vertex[total_vertices];

int counter = 0;
for ( int i = 0; i < file->nmeshes; i++ )
// loop through all meshes+
{
std::stringstream ss;
ss << "Mesh " << i << " includes " << file->meshes[i]->nvertices << " vertices..." << std::endl << std::endl;

for ( int j = 0; j < file->meshes[i]->nvertices; j++ )
// loop through all vertices of this mesh
{
Vertex temp ( file->meshes[i]->vertices[j][0], file->meshes[i]->vertices[j][1], file->meshes[i]->vertices[j][2] );

ss << counter << ": " << temp.x << ", " << temp.y << ", " << temp.z << std::endl;

// copy data from the model to the temporary buffer
vertices[counter] = temp;

counter++;
}

MessageBox ( 0, ss.str ( ).c_str ( ), NULL, NULL );
}

// create the vertex buffer
device->CreateVertexBuffer ( sizeof ( Vertex ) * total_vertices, 0, D3DFVF_XYZ, D3DPOOL_MANAGED, &vertex_buffer, NULL );

// copy the contents of the temporary buffer into the vertex buffer
vertex_buffer->Lock ( 0, 0, ( void **) &void_pointer, 0 );
memcpy ( void_pointer, vertices, sizeof ( Vertex ) * total_vertices );
vertex_buffer->Unlock ( );

delete [] vertices;
}

void Model::CreateIndexBuffer ( IDirect3DDevice9 *device )
{
void *void_pointer;

// create a temporary buffer for the indices
Index *indices = new Index[total_faces];

int counter = 0;
for ( int i = 0; i < file->nmeshes; i++ )
// loop through all meshes
{
std::stringstream ss;
ss << "Mesh " << i << " includes " << file->meshes[i]->nfaces << " faces..." << std::endl << std::endl;

for ( int j = 0; j < file->meshes[i]->nfaces; j++ )
// loop through all faces of this mesh
{
Index temp ( file->meshes[i]->faces[j].index[0], file->meshes[i]->faces[j].index[1], file->meshes[i]->faces[j].index[2] );

ss << counter << ": " << temp.a << ", " << temp.b << ", " << temp.c << std::endl;

// copy data from the model to the temporary buffer
indices[counter] = temp;

counter++;
}

MessageBox ( 0, ss.str ( ).c_str ( ), NULL, NULL );
}

// create the index buffer
device->CreateIndexBuffer ( sizeof ( Index ) * total_faces, 0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &index_buffer, NULL );

// copy the contents of the temporary buffer into the index buffer
index_buffer->Lock ( 0, 0, ( void **) &void_pointer, 0 );
memcpy ( void_pointer, indices, sizeof ( Index ) * total_faces );
index_buffer->Unlock ( );

delete [] indices;
}

bool Model::Load ( std::string filename, IDirect3DDevice9 *device )
{
file = lib3ds_file_open ( filename.c_str ( ) );

if ( !file )
return false;

// reset vertex and face counter
total_vertices = 0;
total_faces = 0;

for ( int i = 0; i < file->nmeshes; i++ )
// loop through all meshes
{
// add the vertices of this mesh to our count
total_vertices += file->meshes[i]->nvertices;

// add the faces of this mesh to our count
total_faces += file->meshes[i]->nfaces;
}

std::stringstream ss;
ss << "Number of meshes: " << file->nmeshes << "..." << std::endl;
ss << "Number of total vertices: " << total_vertices << "..." << std::endl;
ss << "Number of total faces: " << total_faces << "...";
MessageBox ( 0, ss.str ( ).c_str ( ), NULL, NULL );

// create the vertex- and index buffer
CreateVertexBuffer ( device );
CreateIndexBuffer ( device );

return true;
}

void Model::Render ( IDirect3DDevice9 *device )
{
// select the index buffer
device->SetIndices( index_buffer );

device->SetStreamSource ( 0, vertex_buffer, 0, sizeof ( Vertex ) );

// draw each face as a triangle
device->DrawIndexedPrimitive ( D3DPT_TRIANGLELIST, 0, 0, total_vertices, 0, total_vertices );
}


Vertex is a simple struct consisting of 3 floats and an Index struct consists of 3 unsigned shorts. total_vertices and total_faces are unsigned ints in the Model class. As you can see, I create a lot of debug output and checking that, it all looks perfectly fine. It tells me that file->nmeshes is correct (ie. 3 for 3 submeshes), shows me that it loops through these correctly (submeshes 0, 1 and 2 are iterated through for a 3-meshed model for example) etc. Vertices and indices are fine as well. One thing I can't do at the moment is use PIX to check what actually is in the vertex and index buffer as the thing I'm writing is a plugin and for some reason the host application seems to crash when I run it with PIX. Anyways, can anybody spot any problems in the code? If you need to see more etc. just let me know and I'll be happy to post more. Thanks. [Edited by - d h k on August 18, 2009 11:53:44 AM]

##### Share on other sites
Quick-look:
device->DrawIndexedPrimitive ( D3DPT_TRIANGLELIST, 0, 0, total_vertices, 0, total_vertices );

The last parameter should be number of primitives, not number of verts? total_faces, perhaps?

Also, don't you need a SetDeclaration or SetFVF in your render loop?

##### Share on other sites

Quote:
 Original post by BuckeyeQuick-look:device->DrawIndexedPrimitive ( D3DPT_TRIANGLELIST, 0, 0, total_vertices, 0, total_vertices );The last parameter should be number of primitives, not number of verts? total_faces, perhaps?

I tried total_faces for the primitive count parameter, didn't change a thing though. I'll leave it at total_faces as that is that makes more sense.

Quote:
 Original post by BuckeyeAlso, don't you need a SetDeclaration or SetFVF in your render loop?

Sure, I forgot to post that, I call device->SetFVF ( D3DFVF_XYZ ) before rendering my std::vector of pointers to Model instances.

I do use a vertex and pixel shader instead of the fixed function pipeline, does that make a difference when it comes to (indexed) vertex buffers?

##### Share on other sites
Quote:
 I do use a vertex and pixel shader instead of the fixed function pipeline, does that make a difference when it comes to (indexed) vertex buffers?

Nope.

EDIT: if you're using shaders, then I assume you're setting the passes outside the mesh Render function.

##### Share on other sites
Sounds like you're doing this:

Your submeshes have their own vertex and index data. Each submesh's index list starts at 0.

And you're forgetting to do one of:
Set the 'BaseVertexIndex' parameter of DrawIndexedPrimitive to indicate how far into the VB the second submesh is. ie: index 0 of of submesh 1 is at vertex 100.

Altering the indices put into the IB to refer to the later vertices. Following the above example, you'd add 100 to the indices.

The first method is preferable, as you can still use 16 bit indices as long as your largest submesh is < 64K vertices.

##### Share on other sites
Quote:
 Original post by NamethatnobodyelsetookSounds like you're doing this:Your submeshes have their own vertex and index data. Each submesh's index list starts at 0.Set the 'BaseVertexIndex' parameter of DrawIndexedPrimitive to indicate how far into the VB the second submesh is. ie: index 0 of of submesh 1 is at vertex 100.

I *think* I'm actually doing it a little different, take a look at this excerpt from when I copy the data to the temporary buffer:

int counter = 0;for ( int i = 0; i < file->nmeshes; i++ )// loop through all meshes+{	for ( int j = 0; j < file->meshes[i]->nvertices; j++ )	// loop through all vertices of this mesh	{		Vertex temp ( file->meshes[i]->vertices[j][0], file->meshes[i]->vertices[j][1], file->meshes[i]->vertices[j][2] );					// copy data from the model to the temporary buffer		vertices[counter] = temp;		counter++;	}}

I loop through all the submeshes of a model and then put all the vertices of that submesh and attach them (using the counter variable) to the end of the "big list" that is the temporary buffer called "vertices". So it is my understanding that I end up with one list of all data without any submeshes, then I copy that list to the vertex buffer. The same is going on with the index buffer.

Do I still have to use the BaseVertexIndex parameter?

Quote:
 Original post by BuckeyeEDIT: if you're using shaders, then I assume you're setting the passes outside the mesh Render function.

Yep:

void Render ( ){	unsigned int num_passes;	ResetTextureSamplerStates ( );	BuildViewProjectionMatrices ( );	// clear the back- and z-buffer	device->Clear ( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB ( clear_color.r, clear_color.g, clear_color.b ), 1.0f, 0 );	effect->Begin ( &num_passes, 0 );	for ( unsigned int pass = 0; pass < num_passes; pass++ )	// loop through all passes	{		effect->BeginPass ( pass );		// set format for our models		device->SetFVF ( D3DFVF_XYZ );		std::vector<Model*>::iterator i;		for ( i = model.begin ( ); i < model.end ( ); i++ )		// loop through all models		{			// apply the matrix transformations for the model			(*i)->ApplyMatrixTransformations ( device );							// now get combined matrix			D3DXMATRIX combined_matrix = GetCombinedMatrix ( );			effect->SetMatrix ( "world_view_projection_matrix", &combined_matrix );			// make sure all values send to the shader are updated			effect->CommitChanges ( );			// render model			(*i)->Render ( device );		}		// reset to old format		device->SetFVF ( old_fvf );		effect->EndPass ( );	}	effect->End ( );}

##### Share on other sites
Yes. Lets make 2 submeshes, with a few triangle each. :)

Submesh 1:
Vert A, B, C, D
Index 0, 1, 2, 1, 2, 3

Submesh 2:
Vert E, F, G
Index 0, 1, 2

You append these together:

vert: A, B, C, D, E, F, G
index: 0, 1, 2, 1, 2, 3, 0, 1, 2

Now, when drawing the second submesh, you need to pass in a start index of 6 to start reading the second mesh's indices, and you need a BaseVertexIndex of 4 so that index 0 means use vertex 4.

##### Share on other sites
By George, I think Namethatnobodyelsetook's got it, d h k[ATTENTION]

As an alternative to using base vertex indices, you can add the index offset for each subset when you load it, which is what DrawIndexedPrimitive would do with it anyway.
int counter = 0;int idxOffset = 0;for ( int i = 0; i < file->nmeshes; i++ )// loop through all meshes+{        idxOffset = counter*3;	for ( int j = 0; j < file->meshes[i]->nvertices; j++ )	// loop through all vertices of this mesh	{		Vertex temp ( file->meshes[i]->vertices[j][0]+idxOffset, file->meshes[i]->vertices[j][1]+idxOffset, file->meshes[i]->vertices[j][2]+idxOffset );					// copy data from the model to the temporary buffer		vertices[counter] = temp;		counter++;	}}

##### Share on other sites
Ah, okay, you were both right although I did have to fiddle with the implementation of the solution, in the end, this did the trick:

// in Model::CreateIndexBuffer	int counter = 0;	for ( int i = 0; i < file->nmeshes; i++ )	// loop through all meshes	{		int offset = 0;		std::stringstream ss;		ss << "Mesh " << i << " includes " << file->meshes[i]->nfaces << " faces..." << std::endl << std::endl;		if ( i > 0 )		{			for ( int k = 0; k < i; k++ )				offset += file->meshes[k]->nvertices;		}		for ( int j = 0; j < file->meshes[i]->nfaces; j++ )		// loop through all faces of this mesh		{			Index temp ( file->meshes[i]->faces[j].index[0] + offset, file->meshes[i]->faces[j].index[1] + offset, file->meshes[i]->faces[j].index[2] + offset );			ss << counter << ": " << temp.a << ", " << temp.b << ", " << temp.c << std::endl;			// copy data from the model to the temporary buffer			indices[counter] = temp;			counter++;		}		MessageBox ( 0, ss.str ( ).c_str ( ), NULL, NULL );	}

I'm counting through all the meshes that came before this mesh, add their number of vertices together and offset the indices by that total. Works fine.

Huge thanks to both of you!

## Create an account

Register a new account

• ## Partner Spotlight

• ### Forum Statistics

• Total Topics
627682
• Total Posts
2978617

• 9
• 13
• 12
• 10
• 12