c++openglglm-mathfbxassimp

Assimp can't load other textures except diffuse texture of FBX


I try to load the FBX model in the last contains of this learnopengl tutorial, but I only can load diffuse texture of the FBX. I had tried the solution of adjusting the order of layout in vertex shader, but it didn't work. The result of material->GetTextureCount(type) is 0 when type isn't aiTextureType_DIFFUSE. Here is my code:

mesh.h

#ifndef Mesh_h
#define Mesh_h
#include <iostream>
#include <string>
#include <vector>
using namespace std;

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "ReviewShader.h"
struct Vertex{
    glm::vec3 Position;
    glm::vec3 Normal;
    glm::vec2 TexCoord;
};

struct Texture{
    unsigned int id;
    string type;
    string path;
};

class Mesh{
public:
    vector<Texture> textures;
    vector<unsigned int> indices;
    unsigned int VAO;
    Mesh(vector<Vertex> vertices, vector<Texture> textures, vector<unsigned int> indices){
        this->vertices = vertices;
        this->textures = textures;
        this->indices = indices;

        setupMesh();
    }

    void Draw(ReviewShader shader){
        int diffuseCount = 1;
        int specularCount = 1;
        int reflectionCount = 1;

        shader.use();
        for(int i = 0; i < textures.size(); i++){
            string name = textures[i].type;
            string num;
            if(name == "texture_diffuse")
                num = to_string(diffuseCount++);
            else if(name == "texture_specular")
                num = to_string(specularCount++);
            else if(name == "texture_reflection")
                num = to_string(reflectionCount++);

            shader.setInt((name+num).c_str(), i);
            glActiveTexture(GL_TEXTURE0+i);
            glBindTexture(GL_TEXTURE_2D, textures[i].id);
        }
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
        glActiveTexture(GL_TEXTURE0);
        glBindVertexArray(0);
    }
private:
    vector<Vertex> vertices;

    unsigned int VBO, EBO;

    void setupMesh(){
        glGenVertexArrays(1, &VAO);
        glBindVertexArray(VAO);

        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), &vertices[0], GL_STATIC_DRAW);

        glGenBuffers(1, &EBO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);

        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoord));
        glEnableVertexAttribArray(2);

        glBindVertexArray(0);
    }

};
#endif /* Mesh_h */

model.h

#ifndef Model_h
#define Model_h

#include <iostream>
using namespace std;

#include <string>

#include <vector>
#include "Mesh.h"
#include "ReviewShader.h"

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

//#define STB_IMAGE_IMPLEMENTATION
#include <stb-master/stb_image.h>
vector<Texture> loaded_textures;

class Model{
public:
    vector<Mesh> meshes;
    string directory;

    Model(char *path){
        loadModel(path);
    }
    void Draw(ReviewShader shader){
        for(int i = 0; i < meshes.size(); i++){
            meshes[i].Draw(shader);
        }
    }
private:
    void loadModel(string path){
        Assimp::Importer importer;
//        const aiScene *scene = importer.ReadFile(path, aiProcess_FlipUVs | aiProcess_Triangulate);
        const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
        if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode){
            cout << "ERROR::ASSIMP::" << importer.GetErrorString() << endl;
            return;
        }
        directory = path.substr(0, path.find_last_of("/"));
        processNode(scene->mRootNode, scene);

    }

    void processNode(aiNode *node, const aiScene *scene){
        for(int i = 0; i < node->mNumMeshes; i++){
            aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
            meshes.push_back(processMesh(mesh, scene));
        }
        for(int i = 0; i < node->mNumChildren; i++){
            processNode(node->mChildren[i], scene);
        }
    }

    Mesh processMesh(aiMesh *mesh, const aiScene *scene){
        vector<Vertex> vertices;
        vector<Texture> textures;
        vector<unsigned int> indices;

        for(int i = 0; i < mesh->mNumVertices; i++){
            Vertex vertex;
            glm::vec3 vector;
            vector.x = mesh->mVertices[i].x;
            vector.y = mesh->mVertices[i].y;
            vector.z = mesh->mVertices[i].z;
            vertex.Position = vector;

            vector.x = mesh->mNormals[i].x;
            vector.y = mesh->mNormals[i].y;
            vector.z = mesh->mNormals[i].z;
            vertex.Normal = vector;

            if(mesh->mTextureCoords[0]){
                glm::vec2 tex;
                tex.x = mesh->mTextureCoords[0][i].x;
                tex.y = mesh->mTextureCoords[0][i].y;
                vertex.TexCoord = tex;
            }
            else{
                vertex.TexCoord = glm::vec2(0.0, 0.0);
            }
            vertices.push_back(vertex);
        }

        for(int i = 0; i < mesh->mNumFaces; i++){
            aiFace face = mesh->mFaces[i];
            for(int j = 0; j < face.mNumIndices; j++){
                indices.push_back(face.mIndices[j]);
            }
        }

        aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
        vector<Texture> texture_diffuse = loadMaterialTexture(material, aiTextureType_DIFFUSE, "texture_diffuse");
        textures.insert(textures.end(), texture_diffuse.begin(), texture_diffuse.end());
        vector<Texture> texture_specular = loadMaterialTexture(material, aiTextureType_SPECULAR, "texture_specular");
        textures.insert(textures.end(), texture_specular.begin(), texture_specular.end());
        vector<Texture> texture_ambient = loadMaterialTexture(material, aiTextureType_SHININESS, "texture_reflection");
        textures.insert(textures.end(), texture_ambient.begin(), texture_ambient.end());
        return Mesh(vertices, textures, indices);

    }

    vector<Texture> loadMaterialTexture(aiMaterial *material, aiTextureType type, string Typename){
        vector<Texture> textures;
        cout << material->GetTextureCount(type) << endl;
        for(int i = 0; i < material->GetTextureCount(type); i++){
            bool skip = false;
            aiString str;
            material->GetTexture(type, i, &str);
            string tempStr = str.C_Str();
            tempStr.replace(tempStr.find("\\"), 1, "/");
            cout << tempStr << endl;

            for(int j = 0; j < loaded_textures.size(); j++){
                if(strcmp(loaded_textures[j].path.c_str(), tempStr.c_str()) == 0){
                    skip = true;
                    textures.push_back(loaded_textures[i]);
                    break;
                }
            }

            if(!skip){
                Texture tex;
                tex.id = loadTextureFromFile(tempStr, directory);
                tex.type = Typename;
                tex.path = str.C_Str();
                textures.push_back(tex);
                loaded_textures.push_back(tex);
            }
        }
        return textures;
    }

    unsigned int loadTextureFromFile(string path, string &Directory){
        unsigned int textureID;
        glGenTextures(1, &textureID);
        glBindTexture(GL_TEXTURE_2D, textureID);

        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);

        string filepath = Directory + "/" + path;
        int width, height, urChannel;
        unsigned char *data = stbi_load(filepath.c_str(), &width, &height, &urChannel, 0);
        if(data){
            GLenum format;
            if(urChannel == 1)
                format = GL_RED;
            if(urChannel == 3)
                format = GL_RGB;
            if(urChannel == 4)
                format = GL_RGBA;
            glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
            glGenerateMipmap(GL_TEXTURE_2D);
            stbi_image_free(data);
        }
        else{
            cout << "Read texture Data failed\n";
            stbi_image_free(data);
        }
        return textureID;
    }
};
#endif /* Model_h */

model.vs

#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aUV;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 Normal;
out vec2 UV;
out vec3 Position;

void main(){
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    UV = aUV;
    Normal = mat3(transpose(inverse(model))) * aNormal;
    Position = vec3(model * vec4(aPos, 1.0));    
}

model.fs

#version 330 core
out vec4 FragColor;

uniform sampler2D texture_diffuse;
uniform sampler2D texture_specular;
uniform sampler2D texture_roughness;

in vec3 Normal;
in vec2 UV;
in vec3 Position;

void main(){
    FragColor = texture(texture_diffuse, UV);
}

main.cpp

#include <...>
...
using namespace std;


void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
unsigned int loadTexture(const char *path);
unsigned int loadHDRTexture(char const * path);
unsigned int loadCubemap(vector<string> faces);
void renderQuad();
void renderCube();
void renderPlane();
void renderSphere();
float lerp(float a, float b, float scale);
// size settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

glm::vec3 lightPos = glm::vec3(2.0, 4.0, -2.0);
glm::vec3 lightColor = glm::vec3(0.2, 0.2, 0.7);

// calculation parameter
const float Pi = 3.14159265359;
const float doublePi = Pi * 2.0;


// camera
Camera camera(glm::vec3(0.0f, 0.0f, 10.0f));
float lastX = (float)SCR_WIDTH  / 2.0;
float lastY = (float)SCR_HEIGHT / 2.0;
bool firstMouse = true;

// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;

// texture normal
glm::vec3 normal(0.0, 0.0, 1.0);

// plane normal
glm::vec3 planeNormal = glm::vec3(0.0, 1.0, 0.0);


int main()
{
    // glfw: initialize and configure
    // ------------------------------
    ...   

    // Create shaders
    ...
    ReviewShader modelShader("shader/model.vs", "shader/model.fs");

    // pbr configure
    ...

    Model gunModel = Model("Gun_Model/Cerberus_LP.FBX");

    while(!glfwWindowShouldClose(window))
    {
        // avoid delay
        // -----------
        float currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // process input
        // -------------
        processInput(window);
        glViewport(0, 0, SCR_WIDTH * 2, SCR_HEIGHT * 2);
        glClearColor(1.0, 1.0, 1.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glm::mat4 model = glm::mat4(1.0);
        glm::mat4 view = camera.getViewMatrix();
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);

        modelShader.use();
        model = glm::mat4(1.0);
        model = glm::scale(model, glm::vec3(0.1));
        model = glm::rotate(model, glm::radians(-90.0f), glm::vec3(1.0, 0.0, 0.0));
        modelShader.setMat4("model", model);
        modelShader.setMat4("view", view);
        modelShader.setMat4("projection", projection);
        gunModel.Draw(modelShader);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

Solution

  • At first: which version of the Asset-Importer-Lib do you use? We made a lot of improvements in the FBX-Importer, so switching to the latest 5.0-release is strongly recommended.

    Normally when the number of textures shows zero it is a sign that there is only one texture used in our model. To verify that yo can just use Blender, The interal Asset-Importer-Viewer or the Editor of the Godot-Engine to check that your model is right.

    If there is more than just a diffuse-texture stored in your model please create a new issue report here: Assimp-Bugtracker . And please add a model which we can use to reproduce the issue. Thanks in advance!