Not sure mine is the ideal approach, but I'll post what I have. I'm using one dynamic vertex buffer, at a size equal to a const UINT MaxInstances.
First here are my vertex declarations
//used to clone the mesh
const D3DVERTEXELEMENT9 VBD_GEOMETRYDATA[] =
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
{0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
D3DDECL_END()
};
//this declaration is actually used for rendering
extern LPDIRECT3DVERTEXDECLARATION9 DECL_GEOMETRYPACKET;
const D3DVERTEXELEMENT9 VBD_GEOMETRYPACKET[] =
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
{0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
{1, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1},
{1, 16, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2},
{1, 32, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3},
{1, 48, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 4},
D3DDECL_END()
};
//class describing a single element of the instance buffer
struct OBJECT_INSTANCE
{
//the four rows of the world matrix of an instance
float Row1[4];
float Row2[4];
float Row3[4];
float Row4[4];
};
Here the function to create the instance buffer (should only be called once, but outside of performance wont break anything if its called multiple times)
??
void CreateInstanceBuffer()
{
//checks if the buffer already exists and releases it if it does
ReleaseInstanceBuffer();
BufferSize = MaxInstances*sizeof(OBJECT_INSTANCE);
D3DDEV->CreateVertexBuffer(BufferSize, D3DUSAGE_DYNAMIC, NULL, D3DPOOL_DEFAULT, &InstanceBuffer, NULL);
D3DDEV->CreateVertexDeclaration(VBD_GEOMETRYPACKET, &DECL_GEOMETRYPACKET);
}
Then after loaded the mesh, clone it with your geometry declaration
//when you load the tempmesh is the mesh you wish to instance
D3DXMESH newmesh;
tempmesh->CloneMesh(D3DXMESH_MANAGED, VBD_GEOMETRYDATA, D3DDEV, &_newmesh)
//store the vertex and index buffers once
newmesh->GetVertexBuffer(&MeshVB);
newmesh->GetIndexBuffer(&MeshIB);
//if you no longer need the old mesh release it and replace it with the new once
tempmesh->Release();
tempmesh = newmesh;
Here is the code I use to lock the instance buffer, note that AddIndex keeps track of where to add the next instance (and hence will also keep track of how many instance you have currently added to the buffer) when I'm done rendering one mesh I just set AddIndex to 0, and it overwrites whatever data is already there the next time I start adding instances (also note that the instance buffer should be locked and the data stored in OBJECT_INSTANCE *LockedBuffer)
bool AddInstanceToLockedBuffer(D3DXMATRIX World)
{
//the buffer is not locked or you have reached the max instances
if (!BufferLocked) return false;
if (AddIndex >= MaxInstances) return false;
D3DXMATRIX InverseWorld;
D3DXMatrixInverse(&InverseWorld, 0, World);
/*----------------------------------------------
fill LockedBuffer[AddIndex] with World
----------------------------------------------*/
//increment the instance buffers current index
AddIndex++;
return true;
}
UINT GetNumInstances()
{
return AddIndex;
}
void ResetInstanceBuffer()
{
AddIndex = 0;
}
Lastly is rendering
//set the instancing parameters
D3DDEV->SetVertexDeclaration(DECL_GEOMETRYPACKET);
D3DDEV->SetIndices(MeshIB);
D3DDEV->SetStreamSourceFreq( 0, D3DSTREAMSOURCE_INDEXEDDATA | GetNumInstances());
D3DDEV->SetStreamSource(0, _vb, 0, D3DXGetDeclVertexSize(VBD_GEOMETRYPACKET, 0));
D3DDEV->SetStreamSourceFreq( 1, D3DSTREAMSOURCE_INSTANCEDATA | 1 );
D3DDEV->SetStreamSource(1, InstanceBuffer, 0, D3DXGetDeclVertexSize
(VBD_GEOMETRYPACKET, 1));
//set all the properties of your effect
//render the mesh
UINT numpasses;
Effect->Begin(&numpasses, 0);
for (int i=0; i<numpasses; i++)
{
Effect->BeginPass(i);
D3DDEV->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, _Mesh->GetNumVertices(),
0, Mesh->GetNumFaces());
Effect->EndPass();
}
Effect->End();
//reset instancing parameters
D3DDEV->SetStreamSourceFreq(0, 1);
D3DDEV->SetStreamSourceFreq(1, 1);
Pretty long, but hope that helps someone out. Also if anyone has any suggestions that would be cool too!
Edit: Probably noticed I haven't gotten around to rendering multiple subsets of the mesh yet, I will be doing that next. One of the links posted in one of the above replies has some stuff on that though.