Sign in to follow this  

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

This topic is 3042 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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 << "Loading model '" << filename << "'..." << std::endl;
	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 this post


Link to post
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 this post


Link to post
Share on other sites
Thanks for the quick reply.

Quote:
Original post by Buckeye
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?


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 Buckeye
Also, 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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by Namethatnobodyelsetook
Sounds 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 Buckeye
EDIT: 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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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!

Share this post


Link to post
Share on other sites

This topic is 3042 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this