Sign in to follow this  
Drats

Retrieving vertex data from D3DXMESH

Recommended Posts

HI I am loading my d3dxmesh from a xfile using the usual D3DXLoadMeshFromX function. I now want to read my vertex and index data from this d3dxmesh. From what I know the way to do this is first to lock the vertex buffer... UCHAR *vertices ; mesh->LockVertexBuffer(0, (void**)&vertices) ; //Then copy the vertex data over using memcpy memcpy( vertices, SOURCE, SIZE ); //And then unlock the buffer My question is what is the source over here?? Where do I get the vertices in the first place from? Any help will be appreciated. Thanks

Share this post


Link to post
Share on other sites
Quote:
Original post by Drats
HI

I am loading my d3dxmesh from a xfile using the usual D3DXLoadMeshFromX function. I now want to read my vertex and index data from this d3dxmesh. From what I know the way to do this is first to lock the vertex buffer...

UCHAR *vertices ;

mesh->LockVertexBuffer(0, (void**)&vertices) ;

//Then copy the vertex data over using memcpy

memcpy( vertices, SOURCE, SIZE );

//And then unlock the buffer

My question is what is the source over here??

Where do I get the vertices in the first place from?

Any help will be appreciated. Thanks
If you want to copy out of your ID3DXMesh vertex buffer, then the source is the vertices pointer, and your flags should be D3DLOCK_READONLY. And your memcpy() first two arguments should be the other way around, and SOURCE should be a buffer you've allocated for the vertices to go in to.

Share this post


Link to post
Share on other sites
In addition to the above, note that if you just want to access the vertices before you Unlock(), you can just access vertices like a normal array.


// assume your vertex structure is called Vertex

Vertex *vertices ;

mesh->LockVertexBuffer(0, (void**)&vertices) ;

Vertex ThirdVertex=vertices[3]; // for example

//And then unlock the buffer


Unless you want to create a seperate copy of the vertices to manipulate after you have Unlock()-ed, there is no need to copy at all.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
If you want to copy out of your ID3DXMesh vertex buffer, then the source is the vertices pointer, and your flags should be D3DLOCK_READONLY. And your memcpy() first two arguments should be the other way around, and SOURCE should be a buffer you've allocated for the vertices to go in to.


Sorry but I am a little confused. How can the vertices pointer be the source?

Does that mean that when I do this

mesh->LockVertexBuffer(0,(void**)&vertices)

the vertices pointer gets filled with the vertex data from the mesh automatically?

Thanks

Share this post


Link to post
Share on other sites
Quote:
Original post by Drats
Sorry but I am a little confused. How can the vertices pointer be the source?

Does that mean that when I do this

mesh->LockVertexBuffer(0,(void**)&vertices)

the vertices pointer gets filled with the vertex data from the mesh automatically?
Yes. Locking the vertex buffer gives you a pointer to the memory inside the buffer (It's a little more complicated than that, but never mind). So you can read / write into that pointer however you wish (Although I'd recommend the readonly flag if you're just reading from it for better performance).

Share this post


Link to post
Share on other sites
[EDIT - This reply was to drats, not EvilSteve [smile]]

Kinda - the vertices pointer points to the data. However, where that data is and how it got there is kind of down to Direct3D and the graphics hardware.

For example, if the vertex data was only in a hardware specific format on the card when you call Lock(), behind the scenes the chances are the entire buffer has been copied into system memory somewhere.

If the buffer is in the managed pool, the system maintains a copy in system memory so the Lock() probably just points the pointer at that.

There are other possibilities but I'm not an expert on graphics memory.

The important thing is that you can't treat vertices[] like normal memory - you don't own it in the normal sense and it is only accessible until you Unlock().

Share this post


Link to post
Share on other sites

Could you also explain how memcpy works? I am getting an unhandled error exception for the following code:

UCHAR * vertices ;
UCHAR * vertbuf ; // The destination buffer

m_pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&vertices) ;

memcpy(vertbuf , vertices , sizeof(vertices)) ;

m_pMesh->UnlockVertexBuffer() ;

What am I doing wrong?

Share this post


Link to post
Share on other sites
Quote:
Original post by Drats

Could you also explain how memcpy works? I am getting an unhandled error exception for the following code:

UCHAR * vertices ;
UCHAR * vertbuf ; // The destination buffer

m_pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&vertices) ;

memcpy(vertbuf , vertices , sizeof(vertices)) ;

m_pMesh->UnlockVertexBuffer() ;

What am I doing wrong?


This is not an issue with how memcpy works - this is an issue with the fact that you are copying into memory that has not been allocated.

In the example above, vertbuf is just a pointer - it is not pointing at anything.

First attempt at a solution might be this:


UCHAR * vertices ;
UCHAR * vertbuf=new UCHAR[sizeof(Vertex)*NumberOfVertices];

m_pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&vertices) ;

memcpy(vertbuf , vertices , sizeof(vertices)) ;

m_pMesh->UnlockVertexBuffer() ;

// do stuff with vertbuf

delete [] vertbuf;


However, there is a major problem - sizeof(vertices) as the third argument to memcpy returns the size of the pointer, not the size of the data - think about it - how can the compiler know the size of the data returned by Lock()?

So you need another way to know the size of the data, which you can get from a method of D3DXMesh - GetNumVertices().


DWORD NumberOfVertices=m_pMesh->GetNumVertices();

UCHAR * vertices ;
UCHAR * vertbuf=new UCHAR[sizeof(Vertex)*NumberOfVertices];

m_pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&vertices) ;

memcpy(vertbuf , vertices , sizeof(Vertex)*NumberOfVertices) ;

m_pMesh->UnlockVertexBuffer() ;

// do stuff with vertbuf

delete [] vertbuf;


Now we should be working.

But modern C++ idioms try to avoid manual allocation/deletions if possible since if you forget to delete[] or if an exception gets thrown anywhere after the new and before the delete, you end up with memory leaks.

Thankfully, the standard library std::vector<> provides an exception-safe and automatic solution:


DWORD NumberOfVertices=m_pMesh->GetNumVertices();

UCHAR * vertices ;
std::vector<UCHAR> vertbuf(sizeof(Vertex)*NumberOfVertices);

m_pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&vertices) ;

memcpy(&(vertbuf[0]) , vertices , sizeof(vertices)) ;

m_pMesh->UnlockVertexBuffer() ;

// do stuff with vertbuf


Now vertbuf's memory is automatically released whenever vertbuf goes out of scope, be it through scope exit by function return, exception being thrown etc. For more info on the concept, RAII.

Another point - given that you are almost certainly going to want to access the data above as vertices rather than UCHARs, why not use vertices in the first place? If we assume that your vertex structure is called Vertex:


DWORD NumberOfVertices=m_pMesh->GetNumVertices();

Vertex * vertices ;
std::vector<Vertex> vertbuf(NumberOfVertices);

m_pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&vertices) ;

memcpy(&(vertbuf[0]) , vertices , sizeof(UCHAR)*NumberOfVertices) ;

m_pMesh->UnlockVertexBuffer() ;

Vertex V=vertbuf[23];
float X=vertbuf[8].X;


See what I'm driving at? Obviously the last two lines depend on the particular implementation you've chosen for your vertex structure.

Finally, prefer std::copy over memcpy in C++:


// forgive maybe syntax errors - am at work and can't double check
std::copy(vertices,vertices+sizeof(UCHAR)*NumberOfVertices,&(vertbuf[0]));


Irrelevant in this situation but a good habit to get into since std::copy will respect copy constructors for non-POD structures but should resolve down to a simple memcpy for POD types.

But I still reiterate - are you sure you actually need to do this copy? What are you using this system copy for? Why can't you just access vertices[] directly before you Unlock()?

The performance gain EvilSteve mentions about using D3DLOCK_READONLY is only applicable if you only need read access to the buffer. If you are looking to modify the buffer then there is no advantage to using D3DLOCK_READONLY then creating your own copy of the buffer. The whole point of Lock() methods is that they give you access to your vertex/index/texture buffers in what you can treat as normal system memory.

Generally you only make a copy of a vertex buffer in system memory manually if you need the copy to persist around for access after you have Unlock()-ed the resource but this is unusual. A classic example might be retrieving a mesh to use for collision information, but you would almost always convert this into a collision-specific structure (i.e. no texture coordinates or diffuse colour elements) in which case you might as well do the conversion directly from the Lock()-ed pointer.

Are you just experimenting or is there some reason you need the vertex buffer in system memory after you Unlock() the buffer?

HTH

[Edited by - EasilyConfused on September 17, 2008 1:24:54 AM]

Share this post


Link to post
Share on other sites
Wow thanks for the super explanation. And you guessed correctly, I do want a copy of the vertex and index data to pass to my collision detection routines. That is also why I was declaring the vertices as unsigned chars. Because I want to be able to manipulate them one at a time. Basically multiply them with the WORLD MATRIX to get coordinates in world space.

Also how do I find out the vertex structure that my vertices are using? Since I will be loading the mesh from an xfile and not allocating the vertex structure myself. Could I not do this:

UCHAR * vertices ;
UCHAR * vertbuf=new UCHAR[sizeof(vertices)*NumberOfVertices];

Thanks once again. Much appreciated.

Share this post


Link to post
Share on other sites
Quote:
Original post by Drats
Wow thanks for the super explanation. And you guessed correctly, I do want a copy of the vertex and index data to pass to my collision detection routines. That is also why I was declaring the vertices as unsigned chars. Because I want to be able to manipulate them one at a time. Basically multiply them with the WORLD MATRIX to get coordinates in world space.

Also how do I find out the vertex structure that my vertices are using? Since I will be loading the mesh from an xfile and not allocating the vertex structure myself. Could I not do this:

UCHAR * vertices ;
UCHAR * vertbuf=new UCHAR[sizeof(vertices)*NumberOfVertices];

Thanks once again. Much appreciated.


Nope.

sizeof(vertices) returns the size of the pointer. Previously you were trying to use it as the size of the data, now you are trying to use it as the size of a vertex (i.e. the stride between one vertex and another in the vertex buffer).

I'm not sure offhand (at work, can't look it up) how you query the size of the vertex format it uses. I know there is a GetFVF() method that will return a DWORD containing the flexible vertex structure.

Perhaps someone else can advise how to get the vertex size.

Note though that you can't just multiply the vertex data as a string of UCHARs by a world matrix - only some parts of the vertex contain coordinates, and they will most likely be floats, so mulitplying those as UCHARs will be meaningless.

Other elements will be things like texture coordinates, diffuse colours etc. You can't treat vertex buffer data as an array of UCHARs for anything much except copying.

Share this post


Link to post
Share on other sites
Quote:
Original post by EasilyConfused
I'm not sure offhand (at work, can't look it up) how you query the size of the vertex format it uses. I know there is a GetFVF() method that will return a DWORD containing the flexible vertex structure.

Perhaps someone else can advise how to get the vertex size.
ID3DXBaseMesh::GetNumBytesPerVertex.

If you just need the vertex positions, you can usually assume that the position is the first member of the vertex structure. In that case, you copy out the first 12 bytes of the vertex struct, and then skip forwards by vertex_stride. Example:

// Using a const BYTE* because it's const (Since you lock with READONLY, and
// because UCHAR could be 2 bytes or more under unicode I think.
const BYTE* pVertices;

// ALWAYS check return values of functiosn that return pointers!
HRESULT hResult = m_pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&pVertices);
if(FAILED(hResult))
{
// Error, bail
return false;
}

// Get num verts and vertex size
DWORD nVerts = m_pMesh->GetNumVertices();
DWORD nStride = m_pMesh->GetNumBytesPerVertex();

// Declare a dynamic sized array of 3D vectors (Vertex positions), and reserve it
std::vector<D3DXVECTOR3> vertices;
vertices.reserve(nVerts);

// Process each vertex...
for(DWORD i=0; i<nVerts; ++i)
{
// Get the position (at the start of the vertex struct)
const D3DXVECTOR3* pPosition = (const D3DXVECTOR3*)pVertices;
vertices.push_back(*pPosition);

// Move on to the next vertex
pVertices += nStride;
}

// Unlock the VB
m_pMesh->UnlockVertexBuffer() ;

// Do stuff with vertices, it's automatically cleaned up when it goes out of scope,
// no need to delete[] it.


Share this post


Link to post
Share on other sites

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