I just do not understand DrawIndexedPrimitives

Started by
9 comments, last by legalize 16 years, 6 months ago
I have spent a bunch of time googling this and searching forums. I have tried dozens of things and things just do not work out right and no matter what documentation I am reading, I should be doing it right, I think... Anway, I have a set of quads and indicies, the Index Buffer is initalized like this: nt[] Indices = new int[Size]; // We will need to arrange the indices to match a triangle pattern for our quads. // // V0 ----- V2 // | /| // | / | // | / | // | / | // V1 ----- V3 // // Triangle 1 = V0, V1, V2 // Triangle 2 = V2, V1, V3 int J; // Loop in steps of 6 for (J = 0; J < Size; J += 6) { Indices[J + 0] = J + 0; // V0 Indices[J + 1] = J + 1; // V1 Indices[J + 2] = J + 2; // V2 Indices[J + 3] = J + 2; // V2 Indices[J + 4] = J + 1; // V1 Indices[J + 5] = J + 3; // V3 } Buff.SetData(Indices, 0, LockFlags.None); Then I have a function which makes my quads dynamically that I am certain I have right because I have tested this with DrawPrimitives and they look right. The specific issue with DrawIndexed is that after the first quad, the remaining triangles / quads get messed up and I end up with warped or missingle triangles. This is my normal draw command: device.DrawIndexedPrimitives(PrimitiveType.TriangleList, StartingVertex, 0, SpritesToDrawThisRound * 6, 0, SpritesToDrawThisRound * 2); That produces screwy results, and if I were to hardcode something like this: device.DrawIndexedPrimitives(PrimitiveType.TriangleList, StartingVertex, 0, SpritesToDrawThisRound * 6, 0, 6); I will get the same results, which is expected. However, and this next DOES work but shouldn't work and I have no idea why. device.DrawIndexedPrimitives(PrimitiveType.TriangleList, StartingVertex, 0, SpritesToDrawThisRound * 6, 0, 2); device.DrawIndexedPrimitives(PrimitiveType.TriangleList, StartingVertex + 4, 0, SpritesToDrawThisRound * 6, 0, 2); device.DrawIndexedPrimitives(PrimitiveType.TriangleList, StartingVertex + 8, 0, SpritesToDrawThisRound * 6, 0, 2); Now these last 3 draw calls should be the same thing as the 2nd draw call (the first hardcoded one). They should be giving me the same results, but they do not. They draw each quad perfectly. This makes me think that maybe the index buffer isn't staying aligned with the vert buffer when I tell them to draw more then one triangle pair. If it is or is not, I have no idea what is going on or how to fix it.
Advertisement
Hey, I'm a DirectX newbie too but I'm going to try my hand at helping someone out because I've already gone through this problem (not this specific one, but index buffers).

Your code that *does work* is not equivalent to the code you posted before it that *does not work*. Instead, this would be a 3rd version of the non-working code:

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, StartingVertex + 0, 0, 6, 0, 2);device.DrawIndexedPrimitives(PrimitiveType.TriangleList, StartingVertex + 4, 0, 6, 0, 2);device.DrawIndexedPrimitives(PrimitiveType.TriangleList, StartingVertex + 8, 0, 6, 0, 2);


Let me know if this helps any!

edit: By assumption, it appears that you have 3 sprites to draw each frame, so I was working under that. But you see the difference, between your 3rd version and my 3rd version, right?

edit 2: Ignore everything above this. I was looking it over again b/c it just didn't make sense that your code should work like that, and I think I'm 90% sure I found your problem now. It's your 'J' loop. Note that vertices are grouped in 4s, while indices are grouped in 6s. In your 'J' loop, you're using the same J that increments by 6 on both sides of the equality. See the fix?
Also, you may already know this, but be sure to check the DEBUG output when you're testing new code like this. If you are passing an invalid parameter or something else that causes a non-fatal error (which is extremely easy to do with DrawIndexedPrimitive) then the debug output can point you in the right direction.
My friend, the answers you seek are in this article. Just use the vertex order they use, and all will be well for you.
Debug output? Is it some way to see stuff like vertex buffers and whatnot? If so, I have been looking for a way to do this for a while.

As far as how many sprites I draw each round, it can be a 1000 of them. Just for this test I was using 3.

Polaris, you said they are not equivalent, can you elaborate? What is the 'working' version of my original draw function?

MJP: I read that link, but I do not see what I am doing wrong. Let me go over the variables in my draw function a bit and maybe someone can point out my error.

I didn't see the problem with my call, but maybe now that I am looking at my Index Buffer setup algorithm I think it is hosed.

It just occurred to me, that if the Vertex Index is based of J, which is done in sets of 6 (6 indexes per quad) that the index values will be in sets of 6, when they need to be in sets of 4. Grrr. Such an obvious error, and I only looked at that thing like 5 times and never saw it till now.
Here in case this helps...this is the code I use for doing pretty much the exact thing you're trying to do. Sorry that its in C++, but you should be able to get the gist of it.


//setup the vertex bufferParticleVertex* vbPointer = NULL;vertexBuffer->Lock(0, 0, (void**)&vbPointer, 0);for (UINT i = 0; i < PARTICLE_BATCH_SIZE; i++){	UINT index = i * 4;	D3DXVECTOR3 position; // (translatedPosition.x, translatedPosition.y,   translatedPosition.z);	//first vertex of the quad, bottom left corner	vbPointer[index].position = D3DXVECTOR3(-1.0f,-1.0f, 0);	vbPointer[index].tex1 = D3DXVECTOR2(0.0f, 1.0f);	vbPointer[index].instanceIndex = (FLOAT) i;	//top left corner	vbPointer[index + 1].position = D3DXVECTOR3(-1.0f,1.0f, 0); 	vbPointer[index + 1].tex1 = D3DXVECTOR2(0.0f, 0.0f);	vbPointer[index + 1].instanceIndex = (FLOAT) i;	//top right corner	vbPointer[index + 2].position =  D3DXVECTOR3(1.0f,1.0f, 0);	vbPointer[index + 2].tex1 = D3DXVECTOR2(1.0f, 0.0f);	vbPointer[index + 2].instanceIndex = (FLOAT) i;	//bottom right corner 	vbPointer[index + 3].position = D3DXVECTOR3(1.0f,-1.0f, 0); 	vbPointer[index + 3].tex1 = D3DXVECTOR2(1.0f, 1.0f);	vbPointer[index + 3].instanceIndex = (FLOAT) i;}vertexBuffer->Unlock();//setup the index bufferunsigned short* indexBufferPointer = NULL;if (FAILED(indexBuffer->Lock(0, 0, (VOID**)&indexBufferPointer, 0)))        return (FALSE);for (UINT i = 0; i < PARTICLE_BATCH_SIZE; i++){	unsigned short offset = i * 4;	UINT index = i * 6;	indexBufferPointer[index] = offset + 0;	indexBufferPointer[index + 1] = offset + 1;	indexBufferPointer[index + 2] = offset + 2;	indexBufferPointer[index + 3] = offset + 3;	indexBufferPointer[index + 4] = offset + 0;	indexBufferPointer[index + 5] = offset + 2;}indexBuffer->Unlock();

This does remind me of a question I have had about how to do a very basic thing.

Over and over I see examples of people assigning verts values or setting up matrixes, and they do something like "new Vertex3(x, y, z)". The question I have about this is the "new" part. Isn't this memory allocation and isn't this a bad thing to do so trivially? Or are value types very efficent to 'new' opposed to normal objects?
Quote:Original post by TheSilverHammer
This does remind me of a question I have had about how to do a very basic thing.

Over and over I see examples of people assigning verts values or setting up matrixes, and they do something like "new Vertex3(x, y, z)". The question I have about this is the "new" part. Isn't this memory allocation and isn't this a bad thing to do so trivially? Or are value types very efficent to 'new' opposed to normal objects?


If its C++ then yes, they're doing dynamic memory allocation. And yes, its generally something that you want to be very careful about when you do it. Something like a vector with 3 floats is a small amount of memory (12 bytes), but its still not something you would want to do inside the render loop. Outside the loop and during initialization is generally okay, though.

What about in C#? I see these "new" stuff in all sorts of examples of render loops.
In C#, structs and other value types are allocated on the stack when new'ed, so there is no heap allocation. Also, C# has garbage collection, so any memory allocated on the heap is reclaimed automatically sometime after the reference goes out of scope. It's still something to be careful with though as inopportune garbage collections can lead to stuttering and other undesirable behavior.

This topic is closed to new replies.

Advertisement