How to effectively retrieve vertices from .x meshes?

Started by
9 comments, last by Buckeye 14 years, 8 months ago
The xyz coordinates are all I need. I need them to create an AABB. Or is there another way to do this such that this is unnecessary? I've looking in GetVertexBuffer(), but then vertex buffer gives me void* which I'm quite afraid to tinker with even if I knew the offsets.
Advertisement
If you're doing a GetVertexBuffer for the mesh, nothing to be afraid of, though. Just caste that pointer to (for example) D3DXVECTOR*. You'll also need to now the size of each vertex so you can increment the pointer. You'll also need to know how many vertices there are in the buffer. The following assumes you're using DX9, but it shows the principle.

myPtr is typed as a BYTE* to match numBytes.
BYTE *myPtr;HRESULT hr = mesh->GetVertexBuffer((LPDIRECT3DVERTEXBUFFER9)&myPtr);int numVerts = mesh->GetNumVertices();int numBytes = mesh->GetNumBytesPerVertex();for(int i=0; i<numVerts; i++) {   myPtr += i*numBytes;   D3DXVECTOR3 v = *(D3DXVECTOR3*)myPtr; // use myPtr as a pointer to D3DXVECTOR3   // get your v.x, v.y, and v.z here}

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Have a look into D3DXComputeBoundingBox() and D3DXComputeBoundingSphere() in the API. They need some information about your mesh like a pointer to the beginning of the mesh and how to "stride" through the vertex data for the mesh looking for just the (x, y, z).

That last part can be a bit confusing. For example if the X-file provides position (x, y, z) and texture coordinates (u, v), then you need to provide a stride the size of the texture coordinates (i.e. 2 floats, i.e. 8 bytes) so that the helper function can jump from the first (x, y, z) coordinate to the second one by adding 8 bytes to its internal pointer.

These functions will spit out the min/max of a bounding box or the center and maximal radius of a bounding sphere. Here's a small example (you might have to tinker with it, since VertexPNT has pos(x,y,z), normal(x,y,z) and texcoord(u,v)):

DWORD numVerts = m_meshObject->GetNumVertices();
DWORD vSize = m_meshObject->GetNumBytesPerVertex();

VertexPNT *pVertex = NULL;
m_meshObject->LockVertexBuffer( D3DLOCK_READONLY, (void **) &pVertex );
D3DXComputeBoundingBox( &pVertex[ 0 ].pos, numVerts, vSize, &m_minPt, &m_maxPt);
D3DXComputeBoundingSphere( &pVertex[ 0 ].pos, numVerts, vSize, &m_vCenter, &m_fRadius );
m_meshObject->UnlockVertexBuffer();
Thanks a bunch. I'll try those when I get home.
Quote:Original post by Buckeye
Just caste that pointer to (for example) D3DXVECTOR*.

BYTE *myPtr;HRESULT hr = mesh->GetVertexBuffer((LPDIRECT3DVERTEXBUFFER9)&myPtr);[...]   D3DXVECTOR3 v = *(D3DXVECTOR3*)myPtr; // use myPtr as a pointer to D3DXVECTOR3

Uauauauauauahhhhh ... no, no you are not actually doing that? While this might work the correct way is as Steve_Segreto said. Call the LockVertexBuffer method of the IMesh interface and get the data pointer from there. The reason behind the lock should be obvious. Your way of casting the vb is not only ugly, it also lacks the lock which can bring you trouble.
------------------------------------I always enjoy being rated up by you ...
@Waterwalker: You're correct about locking the vertex buffer being the safe thing to do. And I missed the ComputeBoundingxxx calls which is certainly the better way of doing it.
Quote:Your way of casting the vb is .. ugly

Ugly? Nah. You have to cast buffer pointers somehow in any case. Why not make it convenient? You cast buffer pointers everytime you load (just about) any buffers in DirectX. What's the difference here? [SMILE]

The call to D3DXComputeBoundingxxx itself asks for a cast to D3DXVECTOR3* so it's not ugly, it's consistent!

Besides, it's safe because vertex coords always come first and you don't have to "tinker" as suggested above. Tinker with every mesh you load and hardcode a vertex structure? Pfui. Decoding the FVF (or, even better, the declarations) is the correct way, not guessing. If you load a mesh that's not native to your code, how would you otherwise reliably access the vertex data, say for tex coords or normals??

EDIT:
Using Steve_Segreto's suggestion and bypassing the "tinkering":
DWORD numVerts = m_meshObject->GetNumVertices();DWORD vSize = m_meshObject->GetNumBytesPerVertex();D3DXVECTOR3 *pVertex = NULL;m_meshObject->LockVertexBuffer( D3DLOCK_READONLY, (void **) &pVertex );D3DXComputeBoundingBox( pVertex, numVerts, vSize, &m_minPt, &m_maxPt);D3DXComputeBoundingSphere( pVertex, numVerts, vSize, &m_vCenter, &m_fRadius );m_meshObject->UnlockVertexBuffer();


[Edited by - Buckeye on August 14, 2009 9:17:04 AM]

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Quote:Original post by Buckeye
@Waterwalker: You're correct about locking the vertex buffer being the safe thing to do. And I missed the ComputeBoundingxxx calls which is certainly the better way of doing it.
Quote:Your way of casting the vb is .. ugly

Ugly? Nah. You have to cast buffer pointers somehow in any case.

Sure you have to cast somewhen. I am not arguing this, no matter how much I dislike this C style cast [smile]. The only thing I was complaining about was your casting the pointer to the vb and not using the IVertexBuffer interface to access its internal data pointer.

Thanks for providing the updated code sample featuring the official way to do it.

------------------------------------I always enjoy being rated up by you ...
Thanks for the replies. It's working now. I've used LockVertexBuffer() then iterated through the vertices.

AABBs are much larger when the object is rotated (computed AABB of rotated base AABB). Got to fix that.
FYI: This may be what you're already doing ("computed AABB of rotated base AABB"). It depends on how you've coded everything, but you only need to compute the AABB from the mesh once. When you rotate the object, rotate the AABB and get the mins and maxes from the rotated AABB rather than the mesh.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

If all of an object's vertices are rotated after loading via a transformation matrix, and you compute a bounding box for it, it really isn't an axis-aligned bounding box (AABB) anymore. It's just an OBB (oriented bounding box). Perhaps the algorithm is making the bounding box so much larger because it's trying to keep it axis-aligned? Not sure how those D3DXCompute functions are implemented internally.

I would suggest computing the bounding box without transforming each vertex by a rotation and then storing the world transform for the mesh and the inverse world transform (D3DXMatrixInverse() with a NULL determinant). Then you can move calculations "into" the bounding box's object space by vector-matrix multiplying them with the inverse world transform.

For example, let's say you are curious if a world-space velocity vector will interesect the AABB of a mesh. But the mesh is rotated 45 degrees around y-axis in your world. The meshes' world transform and inverse world transform reflect this rotation, but the AABB was computed from the object space vertices of the mesh and doesn't reflect the rotation. What you can do is vector-matrix multiply the velocity vector against the inverse world transform and then check if it intersects the AABB and it will "re-orient" the velocity vector so it's coming at your bounding box in object space from the same angle as it comes at your mesh in your game-world.

This topic is closed to new replies.

Advertisement