Render multiple meshes with one VBO

Hello guys,
my engine currently supports only loading single 3D-Models. I am using ASSIMP and now i want to load files with several meshes in it.
I have tried to put the data from the following meshes into one list (and therefore into one VBO / IBO) but that doesnt work out for me (the mesh is deformed):

        for (unsigned int m = 0; m < scene->mNumMeshes; m++)
        {
            aiMesh* aMesh = scene->mMeshes[m];

            // Fill vertices
            for (unsigned int j = 0; j < aMesh->mNumVertices; j++)
            {
                aiVector3D* pPos        = &(aMesh->mVertices[j]);
                aiVector3D* pTexCoord   = &(aMesh->mTextureCoords[0][j]);
                aiVector3D* pNormal     = &(aMesh->mNormals[j]);
                aiVector3D* pTangent    = &(aMesh->mTangents[j]);
                aiVector3D* pBiTangent  = &(aMesh->mBitangents[j]) ;
                // Create vertex and push it onto the list
                Vertex vertex{
                    Vec3f(pPos->x, pPos->y, pPos->z),
                    Vec2f(pTexCoord->x, pTexCoord->y),
                    Vec3f(pNormal->x, pNormal->y, pNormal->z),
                    Vec3f(pTangent->x, pTangent->y, pTangent->z),
                    Vec3f(pBiTangent->x, pBiTangent->y, pBiTangent->z)
                };
                vertices.push_back(vertex);
            }

            // Fill indices
            for (unsigned int k = 0; k < aMesh->mNumFaces; k++)
            {
                const aiFace& Face = aMesh->mFaces[k];
                if (Face.mNumIndices != 3)
                    continue;

                indices.push_back(Face.mIndices[0]);
                indices.push_back(Face.mIndices[1]);
                indices.push_back(Face.mIndices[2]);
            }

        }

// Then upload them via staging buffers
// and draw the mesh via vkCmdDrawIndexed();

I feel like im missing only a little peace of information to get this working. What i doing wrong?

Thanks in Advance :slight_smile:

You need three pieces of information for each mesh.

First, you need to know the value of vertices.size() before you start adding vertices to the array. Second, you need to know the value of indices.size() before you start adding indices. Third, you need the number of vertices in that mesh to be rendered; that is, you need the number of indices.push_back() calls you made for that particular mesh.

As an example:


struct MeshData
{
	size_t startVertIndex;
	size_t startIndex;
	size_t numIndices;
};

vector<MeshData> perMeshData;
perMeshData.reserve(scene->mNumMeshes);
for (unsigned int m = 0; m < scene->mNumMeshes; m++)
{
	MeshData currMesh;
	currMesh.startVertIndex = vertices.size();
	currMesh.startIndex = indices.size();

	aiMesh* aMesh = scene->mMeshes[m];

	// Fill vertices
	for (unsigned int j = 0; j < aMesh->mNumVertices; j++)
	{
		aiVector3D* pPos        = &(aMesh->mVertices[j]);
		aiVector3D* pTexCoord   = &(aMesh->mTextureCoords[0][j]);
		aiVector3D* pNormal     = &(aMesh->mNormals[j]);
		aiVector3D* pTangent    = &(aMesh->mTangents[j]);
		aiVector3D* pBiTangent  = &(aMesh->mBitangents[j]) ;
		// Create vertex and push it onto the list
		Vertex vertex{
			Vec3f(pPos->x, pPos->y, pPos->z),
			Vec2f(pTexCoord->x, pTexCoord->y),
			Vec3f(pNormal->x, pNormal->y, pNormal->z),
			Vec3f(pTangent->x, pTangent->y, pTangent->z),
			Vec3f(pBiTangent->x, pBiTangent->y, pBiTangent->z)
		};
		vertices.push_back(vertex);
	}

	// Fill indices
	for (unsigned int k = 0; k < aMesh->mNumFaces; k++)
	{
		const aiFace& Face = aMesh->mFaces[k];
		if (Face.mNumIndices != 3)
			continue;

		indices.push_back(Face.mIndices[0]);
		indices.push_back(Face.mIndices[1]);
		indices.push_back(Face.mIndices[2]);
	}

	currMesh.numIndices = indices.size() - currMesh.startIndex;
	perMeshData.push_back(currMesh);
}

When it comes time to render with one of the MeshData objects, you do this (after setting up any appropriate state):


void RenderMesh(VkCommandBuffer *cmd, const MeshData &meshData)
{
	vkCmdDrawIndexed(cmd,
		meshData.numIndices,
		1,
		meshData.startIndex,
		meshData.startVertIndex,
		0);
}

You don’t need to change vertex buffer state between rendering calls. You would only need to change descriptor or push constant state, so that your shader can get the data it needs for that mesh.

The startIndex value tells Vulkan how many indices forward in the index buffer it needs to skip before reading the first index. numIndices tells it how many indices to read. And startVertIndex provides an offset to the index read from the buffer, so that vertex attribute buffers don’t have to be adjusted.

1 Like

Thanks you so much! I knew it was that easy :slight_smile: works perfect!