0
votes

I want to load Crisis Nanosuit model using Assimp in OpenGL/GLSL. The model has several meshes as described by the node tree in assimp. Each mesh is associated with one or more textures (diffuse or specular etc). How do I render textures over the model and still do it with a single draw call?

I have been able to load models without textures so far. And here is how I did it: I used the node tree to find out how many meshes are present in the model and stacked them using an Array-of-structures. This contained float values of Positions, Normals, Texcoords, Color_Ambient, Color_diffuse, Color_specular and Shininess into a buffer, VBO. Since the meshes are stacked, the index array was offset for each mesh accordingly. Finally with a single draw call, the model was rendered successfully. Here is the entire code and the relavent parts are as follows

struct Vertex
{
    glm::vec3 position;
    glm::vec3 normal;
    glm::vec2 texcoord;
    glm::vec3 colorambient;
    glm::vec3 colordiffuse;
    glm::vec3 colorspecular;
    float shininess;
};

// Creating a nodestack of all the meshes
void modelloader::NodeTreeTraversal(aiNode *node)
{
    if(node->mNumChildren==0)
        nodestack.push_back(node);
    else
        for(unsigned int i=0; i<node->mNumChildren; i++)
            this->NodeTreeTraversal(node->mChildren[i]);
}

// Look into assimp data structures for data and populate them into opengl's vbo's and ebo.
void modelloader::ProcessMeshes()
{
    // currently this method loads vertex positions, normals, textures;
    // also loads material info such as ambient, diffuse and specular colors with shininess as 16.0f
    Vertex vertex;
    unsigned int offset_faces=0;

    for(unsigned int i=0; i<this->nodestack.size(); i++)
    {
        aiNode *node = nodestack[i];
        for(unsigned int j=0; j<node->mNumMeshes; j++)
        {
            aiMesh *mesh = this->scene->mMeshes[node->mMeshes[j]];

            aiColor4D ambient;
            aiColor4D diffuse;
            aiColor4D specular;

            if(this->scene->HasMaterials()) {
                aiMaterial *mtl = scene->mMaterials[mesh->mMaterialIndex];
                aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient);
                aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse);
                aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular);
            }

            // load all mesh data
            for(unsigned int k=0; k<mesh->mNumVertices; k++)
            {
                // positions and normals
                vertex.position = glm::vec3(mesh->mVertices[k].x, mesh->mVertices[k].y, mesh->mVertices[k].z); // load positions
                vertex.normal = glm::vec3(mesh->mNormals[k].x, mesh->mNormals[k].y, mesh->mNormals[k].z); // load normals

                // load textures
                if(this->scene->HasTextures())
                    vertex.texcoord = glm::vec2(mesh->mTextureCoords[0][k].x, mesh->mTextureCoords[0][k].y);
                else vertex.texcoord = glm::vec2(0.0f, 0.0f);

                // load materials
                vertex.colorambient = glm::vec3(ambient.r, ambient.g, ambient.b);
                vertex.colordiffuse = glm::vec3(diffuse.r, diffuse.g, diffuse.b);
                vertex.colorspecular = glm::vec3(specular.r, specular.g, specular.b);
                vertex.shininess = 16.0f;

                // push back all the data for each vertex
                meshdata.push_back(vertex);
            }

            // create index data
            for(unsigned int l=0; l<mesh->mNumFaces; l++) {
                this->indices.push_back(mesh->mFaces[l].mIndices[0]+offset_faces);
                this->indices.push_back(mesh->mFaces[l].mIndices[1]+offset_faces);
                this->indices.push_back(mesh->mFaces[l].mIndices[2]+offset_faces);
            }
            offset_faces = offset_faces+mesh->mNumVertices;
        }
    }
    this->MeshData = &meshdata[0].position.x;
    this->MeshDataSize = meshdata.size() * 18 * sizeof(float);

    this->Indices = indices.data();
    this->IndicesSize = indices.size()*sizeof(unsigned int);
}
// draw call
void modelloader::RenderModel()
{
    glBindVertexArray(this->VAO);
    glDrawElements(GL_TRIANGLES, this->IndicesSize/sizeof(unsigned int), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

Here is the output image.

Now, when loading textures (separate image files for each part of the body), when I activated all the textures atonce, it stretched each texture file over the entire body. How do I do it properly?

My preliminary thoughts are: Activate all the texture files. Add an attribute called "mesh_number" in the VBO and in the fragment shader, use the appropriate texture corresponding to the "mesh_number". I dont know if this will work. How is it usually done? Do you have any code samples?

This problem gets solved when the draw call is applied to each mesh in the model as done here. 1) But aren't draw calls expensive? Shouldn't I be drawing the entire mesh at one go? 2) Should I create a single image file of all the body parts collaged into one; much like a sprite sheet?

1

1 Answers

0
votes

You need to activate each texture via:

glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, <cour texture id>);
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, <cour texture id>);

So all the textures can be rendered for your draw-call.