I have model created in Blender that have 3 UV Maps, 3 materials and each material has texture. Below you can see it.
Also I have OpenGL application that draw this model. Application is working perfectly with one material, but with two or more materials it draw only first.
Model was imported with Assimp.
I saw that Assimp splits the mesh in three meshes for each material, maybe I handle wrong way these 3 meshes.
Below is illustrated results(left in Blender and right in OpenGL):
Code. Initializing model
void AnimatedModel::initModel(string filePath) {
path = filePath;
loaded = true;
Importer importer;
unsigned int flags = aiProcess_Triangulate |
aiProcess_FlipUVs |
aiProcess_CalcTangentSpace | aiProcess_GenNormals | aiProcess_LimitBoneWeights;
const aiScene* scene = importer.ReadFile(filePath, flags);
if (!scene || scene->mFlags&& AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
cout << "Model::'" << filePath << "': " << importer.GetErrorString() << endl;
loaded = false;
return;
}
processNode(scene->mRootNode, scene);
}
void AnimatedModel::processNode(aiNode* node, const aiScene* scene) {
//process all meshes
unsigned int nrMeshes = node->mNumMeshes;
for (int a = 0; a < nrMeshes; ++a) {
aiMesh* mesh = scene->mMeshes[a];
BoneMesh processedMesh = processBoneMesh(mesh, scene);
meshes.push_back(processedMesh);
}
//do the same with children
for (int a = 0; a < node->mNumChildren; ++a) {
processNode(node->mChildren[a], scene);
}
}
map<string, int> tmp;
BoneMesh AnimatedModel::processBoneMesh(aiMesh* mesh, const aiScene* scene) {
vector<BoneVertex> vertices;
vector<unsigned int> indices;
vector<Texture> textures;
//calculate texture index
string meshName(mesh->mName.C_Str());
int textureIndex = 0;
if (tmp.find(meshName) == tmp.end()) {
tmp.insert(make_pair(meshName, 1));
}
else {
textureIndex = tmp.at(meshName);
tmp.at(meshName) += 1;
}
//read vertices
unsigned int nrVertices = mesh->mNumVertices;
for (int a = 0; a < nrVertices; ++a) {
BoneVertex vertex;
vec3 tmpVector;
//position
tmpVector.x = mesh->mVertices[a].x;
tmpVector.y = mesh->mVertices[a].y;
tmpVector.z = mesh->mVertices[a].z;
vertex.position = tmpVector;
//textures
if (mesh->mTextureCoords[textureIndex]) {
vec2 texCoord;
texCoord.x = mesh->mTextureCoords[textureIndex][a].x;
texCoord.y = mesh->mTextureCoords[textureIndex][a].y;
vertex.texCoords = texCoord;
}
vertices.push_back(vertex);
}
//read faces
unsigned int nrFaces = mesh->mNumFaces;
for (int a = 0; a < nrFaces; ++a) {
aiFace face = mesh->mFaces[a];
for (int b = 0; b < face.mNumIndices; ++b) {
indices.push_back(face.mIndices[b]);
}
}
//read material
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
if (material) {
vector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
}
BoneMesh boneMesh(vertices, indices, textures);
return boneMesh;
}
vector<Texture> AnimatedModel::loadMaterialTextures(aiMaterial* material, aiTextureType texType, string texName) {
vector<Texture> textures;
unsigned int nrTextures = material->GetTextureCount(texType);
for (int a = 0; a < nrTextures; ++a) {
aiString texPath;
aiReturn ret = material->GetTexture(texType, a, &texPath);
if (ret != aiReturn_SUCCESS) {
cout << "Error: Can't get '" << texPath.C_Str() << "' texture" << endl;
continue;
}
unsigned int nrLoadedTextures = this->textures.size();
bool alreadyLoaded = false;
for (int b = 0; b < nrLoadedTextures; ++b) {
if (strcmp(texPath.C_Str(), this->textures[b].path.C_Str()) == 0) {
alreadyLoaded = true;
textures.push_back(this->textures[b]);
break;
}
}
if (!alreadyLoaded) {
Texture texture;
string directory = path.substr(0, path.find('/', 0));
texture.id = loadTextureFromFile(texPath.C_Str(), directory);
texture.path = texPath;
texture.type = texName;
textures.push_back(texture);
this->textures.push_back(texture);
}
}
return textures;
}
unsigned int AnimatedModel::loadTextureFromFile(const string& path, const string& directory, bool gamma) {
unsigned int id;
glGenTextures(1, &id);
int width, height, nrComponents;
string matPath = string(directory + "/" + path);
unsigned char* data = stbi_load(matPath.c_str(), &width, &height, &nrComponents, 0);
if (data) {
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, id);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else
{
cout << "Error: Can't load texture '" << matPath.c_str() << "'" << endl;
}
stbi_image_free(data);
return id;
}
Drawing model
void AnimatedModel::draw(Shader shader) {
for (BoneMesh m : meshes) {
m.draw(shader);
}
}
Initializing Mesh
void BoneMesh::initMesh(){
// create buffers/arrays
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
// load data into vertex buffers
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(BoneVertex), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
// set the vertex attribute pointers
// vertex Positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(BoneVertex), (void*)0);
// vertex texture coords
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(BoneVertex), (void*)offsetof(BoneVertex, texCoords));
//reset
glBindVertexArray(0);
}
Draw Mesh
void BoneMesh::draw(Shader shader)
{
//bind textures
unsigned int diffuseNr = 1;
unsigned int specularNr = 1;
for (unsigned int nrTexture = 0; nrTexture < textures.size(); ++nrTexture)
{
glActiveTexture(GL_TEXTURE0 + nrTexture);
stringstream ss;
string number;
string name = textures[nrTexture].type;
if (name == "texture_diffuse")
ss << diffuseNr++;
else if (name == "texture_specular")
ss << specularNr++;
number = ss.str();
glUniform1i(glGetUniformLocation(shader.getId(), (name + number).c_str()), nrTexture);
glBindTexture(GL_TEXTURE_2D, textures[nrTexture].id);
}
// draw mesh
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, (GLsizei)indices.size(), GL_UNSIGNED_INT, (void*)0);
//set to default
glBindVertexArray(0);
glActiveTexture(GL_TEXTURE0);
}