#include "model.hpp" #include "dbc.hpp" unsigned int TextureFromFile(const std::string& path, const std::string& directory, bool gamma) { std::string filename = path + "/" + filename; unsigned int textureID; glGenTextures(1, &textureID); int width = 0; int height = 0; int nrComponents = 0; unsigned char *data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0); dbc::check(data != nullptr, $F("Failed to load texture {}", filename)); GLenum format = GL_RGB; if (nrComponents == 1) { format = GL_RED; } else if (nrComponents == 3) { format = GL_RGB; } else if (nrComponents == 4) { format = GL_RGBA; } glBindTexture(GL_TEXTURE_2D, textureID); 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); stbi_image_free(data); return textureID; } void Model::Draw(Shader &shader) { for(unsigned int i = 0; i < meshes.size(); i++) { meshes[i].Draw(shader); } } void Model::loadModel(std::string path) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs | aiProcess_CalcTangentSpace); dbc::check(scene != nullptr, "Assimp ReadFile return null"); dbc::check(!(scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE), "Assimp says incomplete."); dbc::check(scene->mRootNode != nullptr, "Assimp loaded scene doesn't have a root node"); directory = path.substr(0, path.find_last_of('/')); processNode(scene->mRootNode, scene); } void Model::processNode(aiNode *node, const aiScene *scene) { for(unsigned int i = 0; i < node->mNumMeshes; i++) { aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; meshes.push_back(processMesh(mesh, scene)); } for(unsigned int i = 0; i < node->mNumChildren; i++) { processNode(node->mChildren[i], scene); } } Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) { std::vector vertices; std::vector indices; std::vector textures; for(unsigned int i = 0; i < mesh->mNumVertices; i++) { Vertex vertex; vertex.Position = glm::vec3( mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z); if(mesh->HasNormals()) { vertex.Normal = glm::vec3( mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z); } if(mesh->mTextureCoords[0]) { vertex.TexCoords = glm::vec2( mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y ); } else { vertex.TexCoords = glm::vec2(0.0f, 0.0f); } vertices.push_back(vertex); } for(unsigned int i = 0; i < mesh->mNumFaces; i++) { aiFace face = mesh->mFaces[i]; for(unsigned int j = 0; j < face.mNumIndices; j++) { indices.push_back(face.mIndices[j]); } } aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; // we assume a convention for sampler names in the shaders. Each diffuse texture should be named // as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER. // Same applies to other texture as the following list summarizes: // diffuse: texture_diffuseN // specular: texture_specularN // normal: texture_normalN // 1. diffuse maps std::vector diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"); textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); // 2. specular maps std::vector specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"); textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); // 3. normal maps std::vector normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal"); textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); // 4. height maps std::vector heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height"); textures.insert(textures.end(), heightMaps.begin(), heightMaps.end()); return Mesh(vertices, indices, textures); } std::vector Model::loadMaterialTextures(aiMaterial *mat, aiTextureType type, std::string typeName) { std::vector textures; for(unsigned int i = 0; i < mat->GetTextureCount(type); i++) { aiString str; mat->GetTexture(type, i, &str); std::string tx_str{str.C_Str()}; bool skip = false; for(unsigned int j = 0; j < textures_loaded.size(); j++) { if(textures_loaded[j].path == tx_str) { textures.push_back(textures_loaded[j]); skip = true; break; } } if(!skip) { Texture texture{ .id = TextureFromFile(tx_str, directory), .type = typeName, .path = tx_str, }; textures.push_back(texture); textures_loaded.push_back(texture); } } return textures; }