Opengl obj object loader with textures

10,221

Your OBJ loader doesn't even attempt to parse MTL files.

You'll have to add MTL handling yourself.

You'll probably just want to support map_Kd.

Good luck.

Share:
10,221
Greg Chapman
Author by

Greg Chapman

Updated on June 04, 2022

Comments

  • Greg Chapman
    Greg Chapman almost 2 years

    I am using an object loader to load an obj model that I exported from blender. I can load the model but none of the textures are loaded and I don't know why. Should they be loaded when I render the model in the main class or do I need to load the textures as well as rendering the model?

    objloader.cpp

    /************************************************************
    *Loads obj file - limited to vertices, faces, normals, texture maps
    *loads to object structure as defined in .h file
    ************************************************************/
    
    #include <stdio.h>
    #include <iostream>
    #include <io.h>
    #include <stdlib.h>
    #include "glut.h"
    
    #include "objloader.h"
    
    using namespace std;
    
    void object_type::render()
    {
        glBegin(GL_TRIANGLES); // glBegin and glEnd delimit the vertices that define a primitive (in our case triangles)
    
        for (int j=0;j<polygons_qty;j++)
        {
            //----------------- FIRST VERTEX -----------------
            //Normal coordinates of the first vertex
            glNormal3f( normcoord[ polygon[j].n[0] - 1 ].i,
                    normcoord[ polygon[j].n[0] - 1 ].j,
                    normcoord[ polygon[j].n[0] - 1 ].k);
            // Texture coordinates of the first vertex
                glTexCoord2f( mapcoord[ polygon[j].t[0] - 1 ].u,
                      mapcoord[ polygon[j].t[0] - 1 ].v);
            // Coordinates of the first vertex
            glVertex3f( vertex[ polygon[j].v[0] - 1].x,
                    vertex[ polygon[j].v[0] - 1].y,
                    vertex[ polygon[j].v[0] - 1].z);
    
            //----------------- SECOND VERTEX -----------------
            //Normal coordinates of the first vertex
            glNormal3f( normcoord[ polygon[j].n[1] - 1 ].i,
                    normcoord[ polygon[j].n[1] - 1 ].j,
                    normcoord[ polygon[j].n[1] - 1 ].k);
            // Texture coordinates of the first vertex
            glTexCoord2f( mapcoord[ polygon[j].t[1] - 1 ].u,
                      mapcoord[ polygon[j].t[1] - 1 ].v);
            // Coordinates of the first vertex
            glVertex3f( vertex[ polygon[j].v[1] - 1].x,
                    vertex[ polygon[j].v[1] - 1].y,
                    vertex[ polygon[j].v[1] - 1].z);
    
            //----------------- THIRD VERTEX -----------------
            //Normal coordinates of the first vertex
            glNormal3f( normcoord[ polygon[j].n[2] - 1 ].i,
                    normcoord[ polygon[j].n[2] - 1 ].j,
                    normcoord[ polygon[j].n[2] - 1 ].k);
            // Texture coordinates of the first vertex
            glTexCoord2f( mapcoord[ polygon[j].t[2] - 1 ].u,
                      mapcoord[ polygon[j].t[2] - 1 ].v);
            // Coordinates of the first vertex
            glVertex3f( vertex[ polygon[j].v[2] - 1].x,
                    vertex[ polygon[j].v[2] - 1].y,
                    vertex[ polygon[j].v[2] - 1].z);
        }
        glEnd();
    }
    
    
    /*
    vertex_type vertex[MAX_VERTICES]; 
    mapcoord_type mapcoord[MAX_VERTICES];
    normcoord_type normcoord[MAX_NORMALS];
    polygon_type polygon[MAX_POLYGONS];
    int id_texture
    */
    
    
    int object_type::objdatadisplay()
    {
       int i;
       printf("VERTICES: %d\n",vertices_qty); 
    for (i =0;i<vertices_qty;i++)
     {
         printf("%f %f %f\n",vertex[i].x,vertex[i].y,vertex[i].z);
     }
    
       printf("NORMALS: %d\n",normcoord_qty); 
    for (i =0;i<normcoord_qty;i++)
    {
         printf("%f %f %f\n",normcoord[i].i,normcoord[i].j,normcoord[i].k);
    }
       printf("MAP COORDS: %d\n",mapcoord_qty);
    for (i =0;i<mapcoord_qty;i++)
    {
         printf("%f %f\n",mapcoord[i].u,mapcoord[i].v);
    }
       printf("POLYGONS: %d\n",polygons_qty); 
    for (i=0;i<polygons_qty;i++) //for each vertex of polygon (triangle)
    {
        for (int j = 0;j<3;j++)
        {   
                 printf("%d::%d/%d/%d\n",i,polygon[i].v[j],polygon[i].t[j],polygon[i].n[j]);
        }
    }
    return 1;
    }
    
    
    int object_type::objloader(char *p_filename)
    {
        int ivertex=0; //Index variable
        int inormal =0;
        int ipolygon=0;
        int imap=0;
        char string[256];
    FILE *l_file; //File pointer
    
    char l_char; //Char variable
    
    unsigned short l_face_flags; //Flag that stores some face information
    
    if ((l_file=fopen (p_filename, "rt"))== NULL) return 0; //Open the file
    
    
    while (!feof(l_file)) //Loop to scan the whole file 
    {
        fscanf(l_file,"%c",&l_char);
        if(l_char=='\n')//read char if'/n' -skip to next and read   
            fscanf(l_file,"%c",&l_char);
        switch (l_char) //parse
        {   
        default: fgets(string,256,l_file);
            break;
        case 'v':   //a vertex or a normal or a text co-ord
            fscanf(l_file,"%c",&l_char);
            switch (l_char)  
            {
            case ' ':   //a vertex -expect and so read 3 floats next
                  fscanf(l_file,"%f %f %f",&vertex[ivertex].x, &vertex[ivertex].y,&vertex[ivertex].z);
    
    
                  ivertex++;
                  break;
            case 'n': //a normal -expect and so read 3 floats next
                  fscanf(l_file,"%f %f %f",&normcoord[inormal].i, &normcoord[inormal].j,&normcoord[inormal].k);
                  inormal++;
                  break;
            case 't': //a texture map coord-expect and so read 2 floats next
                  fscanf(l_file,"%f %f",&mapcoord[imap].u, &mapcoord[imap].v);
                  imap++;
                  break;
            }  //end switch
            break;
        case 'f': //a face read next assume format is -> f 1/1/1 2/2/2 3/3/3
            for (int i=0;i<3;i++) //for each vertex of polygon (triangle)
            {
                 fscanf(l_file,"%c",&l_char); //read space char - ignore this
                 fscanf(l_file,"%d",&polygon[ipolygon].v[i]); //read vertex.
                 fscanf(l_file,"%c",&l_char); //read space char - ignore this
                 fscanf(l_file,"%d",&polygon[ipolygon].t[i]); //read text coord.
                 fscanf(l_file,"%c",&l_char); //read space char - ignore this
                 fscanf(l_file,"%d",&polygon[ipolygon].n[i]); //read normal.
            }
            ipolygon++;
            break;
    
        } //end switch
    }
    
    fclose (l_file); // Closes the file stream
    vertices_qty = ivertex;
    polygons_qty = ipolygon;
    mapcoord_qty = imap;
    normcoord_qty = inormal;
    
    return 1;  //if successful    
    }
    

    objloader.h

    #ifndef OBJLOAD
    #define OBJLOAD
    
    /************************************************
    *Loads obj file - limited to vertices, faces, normals, texture maps
    *loads to object structure as defined in .h file
    ****************************************************/
    
    
    #define MAX_VERTICES 8000 // Max number of vertices (for each object)
    #define MAX_POLYGONS 8000 // Max number of polygons (for each object)
    #define MAX_NORMALS 8000 // Max number of polygons (for each object)
    
    // Our vertex type
    typedef struct{
    float x,y,z;
    }vertex_type;
    
    // Our normal type
    typedef struct{
    float i,j,k;
    }normcoord_type;
    
    // The polygon (triangle), 3 numbers that aim 3 vertices
    typedef struct{
    int v[3],t[3],n[3];
    }polygon_type;
    
    // The mapcoord type, 2 texture coordinates for each vertex
    typedef struct{
    float u,v;
    }mapcoord_type;
    
    // The object type
    class object_type{
    public:
    int id_texture; 
    object_type(){}
    ~object_type(){}
    int objloader(char *p_filename); 
    int objdatadisplay();
    void render();
    
    private:
    char name[20];
    int vertices_qty;
    int polygons_qty;
    int mapcoord_qty;
    int normcoord_qty;
    
    vertex_type vertex[MAX_VERTICES]; 
    mapcoord_type mapcoord[MAX_VERTICES];
    normcoord_type normcoord[MAX_NORMALS];
    polygon_type polygon[MAX_POLYGONS];
    
    };
    #endif
    

    main.cpp

    #include "objloader.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <GL/glew.h>
    
    #ifdef __WIN32__
    #include <windows.h>
    #endif
    
    #include "glut.h"  //glut has all ogl relevant .h files included 
    
    int screen_width=800;
    int screen_height=600;
    
    //angle of rotation
    float xpos = 0, ypos = 0, zpos = 0, xrot = 0, yrot = 0, angle=0.0;
    
    float cRadius = 10.0f; // our radius distance from our character
    
    float lastx, lasty;
    
    object_type *objarray[2];  //objects container for our world. Used throughout so global
    
    //Lights settings
    GLfloat light_ambient[]= { 0.1f, 0.1f, 0.1f, 0.1f };
    GLfloat light_diffuse[]= { 1.0f, 1.0f, 1.0f, 0.0f };
    GLfloat light_specular[]= { 1.0f, 1.0f, 1.0f, 0.0f };
    GLfloat light_position[]= { 100.0f, 0.0f, -10.0f, 1.0f };
    
    //Materials settings
    GLfloat mat_ambient[]= { 0.5f, 0.5f, 0.0f, 0.0f };
    GLfloat mat_diffuse[]= { 0.5f, 0.5f, 0.0f, 0.0f };
    GLfloat mat_specular[]= { 1.0f, 1.0f, 1.0f, 0.0f };
    GLfloat mat_shininess[]= { 1.0f };
    
    
    /************************************
     *
     * SUBROUTINE init(void)
     *
     * Used to initialize OpenGL and to setup our world
     *
    ************************************/
    
    void init(void)
    {
        glClearColor(0.0, 0.0, 0.0, 0.0); // Clear background color to black
    
        // Viewport transformation
        glViewport(0,0,screen_width,screen_height);  
    
        // Projection transformation
        glMatrixMode(GL_PROJECTION); // Specifies which matrix stack is the target for matrix operations 
        glLoadIdentity(); // We initialize the projection matrix as identity
        gluPerspective(45.0f,(GLfloat)screen_width/(GLfloat)screen_height,5.0f,10000.0f);     
    
    
    //Lights initialization and activation
        glLightfv (GL_LIGHT1, GL_AMBIENT, light_ambient);
        glLightfv (GL_LIGHT1, GL_DIFFUSE, light_diffuse);
        glLightfv (GL_LIGHT1, GL_DIFFUSE, light_specular);
        glLightfv (GL_LIGHT1, GL_POSITION, light_position);    
        glEnable (GL_LIGHT1);
        glEnable (GL_LIGHTING);
    
        //Materials initialization and activation
    glMaterialfv (GL_FRONT, GL_AMBIENT, mat_ambient);
        glMaterialfv (GL_FRONT, GL_DIFFUSE, mat_diffuse);
        glMaterialfv (GL_FRONT, GL_DIFFUSE, mat_specular);
        glMaterialfv (GL_FRONT, GL_POSITION, mat_shininess);    
    
    //Other initializations
        glShadeModel(GL_SMOOTH); // Type of shading for the polygons
    glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Texture mapping perspective correction
        glEnable(GL_TEXTURE_2D); // Texture mapping ON
        glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); // Polygon rasterization mode (polygon filled)
    glEnable(GL_CULL_FACE); // Enable the back face culling
        glEnable(GL_DEPTH_TEST); // Enable the depth test 
    
    glTranslatef(0.0f, 0.0f, -cRadius);
        glRotatef(xrot,1.0,0.0,0.0);
    
    angle++; //increase the angle
    
    for (int i=0;i<2;i++)
    {
    printf("*************\n");
        objarray[i] = new (object_type);
        objarray[i]->objloader("C:/3dModels/Museum.obj");
        objarray[i]->objdatadisplay();      
    }
    
    }
    
    
    /**********************************************************
     *
     * SUBROUTINE resize(int p_width, int p_height)
     *
     * This routine must be called everytime we resize our window.
     *
     * Input parameters: p_width = width in pixels of our viewport
     *                   p_height = height in pixels of our viewport
     * 
     *********************************************************/
    
    void resize (int p_width, int p_height)
    {
    if (screen_width==0 && screen_height==0) exit(0);
        screen_width=p_width; // We obtain the new screen width values and store it
        screen_height=p_height; // Height value
    
        glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // We clear both the color and the depth buffer so to draw the next frame
        glViewport(0,0,screen_width,screen_height); // Viewport transformation
    
        glMatrixMode(GL_PROJECTION); // Projection transformation
        glLoadIdentity(); // We initialize the projection matrix as identity
        gluPerspective(45.0f,(GLfloat)screen_width/(GLfloat)screen_height,5.0f,10000.0f);
    
        glutPostRedisplay (); // This command redraw the scene (it calls the same routine of glutDisplayFunc)
    }
    
    /**********************************************************
     *
     * SUBROUTINE keyboard(void)
     *
     * Subroutine to handle keyboard input
     * 
     *********************************************************/
    
    
    void keyboard (unsigned char key, int x, int y) {
        if (key=='q')
        {
        xrot += 1;
        if (xrot >360) xrot -= 360;
        }
    
        if (key=='z')
        {
        xrot -= 1;
    
    if (xrot < -360) xrot += 360;
        }
    
        if (key=='w')
        {
        float xrotrad, yrotrad;
        yrotrad = (yrot / 180 * 3.141592654f);
        xrotrad = (xrot / 180 * 3.141592654f); 
        xpos += float(sin(yrotrad));
        zpos -= float(cos(yrotrad));
        ypos -= float(sin(xrotrad));
        }
    
        if (key=='s')
        {
        float xrotrad, yrotrad;
        yrotrad = (yrot / 180 * 3.141592654f);
        xrotrad = (xrot / 180 * 3.141592654f); 
        xpos -= float(sin(yrotrad));
        zpos += float(cos(yrotrad));
        ypos += float(sin(xrotrad));
        }
    
        if (key=='d')
        {
        float yrotrad;
        yrotrad = (yrot / 180 * 3.141592654f);
        xpos += float(cos(yrotrad)) * 0.2;
        zpos += float(sin(yrotrad)) * 0.2;
        }
    
        if (key=='a')
        {
        float yrotrad;
        yrotrad = (yrot / 180 * 3.141592654f);
        xpos -= float(cos(yrotrad)) * 0.2;
        zpos -= float(sin(yrotrad)) * 0.2;
        }
    
        if (key==27)
        {
        exit(0);
        }
    }
    
    /**********************************************************
     *
     * SUBROUTINE mouseMovement(void)
     *
     * Subroutine to handle mouse input
     * 
     *********************************************************/
    
    void mouseMovement(int x, int y) {
    int diffx=x-lastx; //check the difference between the current x and the last x position
    int diffy=y-lasty; //check the difference between the current y and the last y position
    lastx=x; //set lastx to the current x position
    lasty=y; //set lasty to the current y position
    xrot += (float) diffy; //set the xrot to xrot with the addition of the difference in the y position
    yrot += (float) diffx;    //set the xrot to yrot with the addition of the difference in the x position
    }
    
    
    /**********************************************************
     *
     * SUBROUTINE display(void)
     *
     * This is our main rendering subroutine, called each frame
     * 
     *********************************************************/
    
    void display(void)
    {
    
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // This clear the background color to dark blue
        glMatrixMode(GL_MODELVIEW); // Modeling transformation
        glPushMatrix();
    glLoadIdentity(); // Initialize the model matrix as identity
    
        glTranslatef(0.0f, 0.0f, -cRadius); // We move the object forward (the model matrix is multiplied by the translation matrix)
        glRotatef(xrot,1.0,0.0,0.0); // Rotations of the object (the model matrix is multiplied by the rotation matrices)
    
        glRotatef(yrot,0.0,1.0,0.0);
    
    glTranslated(-xpos,0.0f,-zpos); //translate the screen to the position of our camera
    
    
    if (objarray[0]->id_texture!=-1) 
    {
        glBindTexture(GL_TEXTURE_2D, objarray[0]->id_texture); // We set the active texture 
        glEnable(GL_TEXTURE_2D); // Texture mapping ON
        printf("Txt map ON");
    }
    else
        glDisable(GL_TEXTURE_2D); // Texture mapping OFF
    
    
    objarray[0]->render();
    glPopMatrix();
    glPushMatrix();
    glTranslatef(5.0,0.0,-20.0);
    glFlush(); // This force the execution of OpenGL commands
    glutSwapBuffers(); // In double buffered mode we invert the positions of the visible buffer and the writing buffer
    }
    
    
    
    /**********************************************************
     *
     * The main routine
     * 
     *********************************************************/
    
    
    int main(int argc, char **argv)
    {
        // We use the GLUT utility to initialize the window, to handle the input and to interact with the windows system
        glutInit(&argc, argv);    
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
        glutInitWindowSize(screen_width,screen_height);
        glutInitWindowPosition(0,0);
        glutCreateWindow("Demo 1: To exit press ESC");    
        glutDisplayFunc(display);
        glutIdleFunc(display);
        glutReshapeFunc (resize);
    
    glutPassiveMotionFunc(mouseMovement); //check for mouse movement
    glutKeyboardFunc (keyboard); 
    
    init();
        glutMainLoop();
    
        return(0);    
    }
    

    Example obj from blender, cube.obj:

    # Blender v2.66 (sub 1) OBJ File: 'cube.blend'
    # www.blender.org
    mtllib cube.mtl
    g Cube
    v 1.000000 0.665869 -1.000000
    v 1.000000 0.665869 1.000000
    v -1.000000 0.665869 1.000000
    v -1.000000 0.665869 -1.000000
    v 1.000000 2.665869 -0.999999
    v 0.999999 2.665869 1.000001
    v -1.000000 2.665869 1.000000
    v -1.000000 2.665869 -1.000000
    vt 0.000000 0.334353
    vt 0.332314 0.333333
    vt 0.333333 0.665647
    vt 1.000000 0.001019
    vt 0.998981 0.333333
    vt 0.666667 0.332314
    vt 1.000000 0.665647
    vt 0.667686 0.666667
    vt 0.334353 0.666667
    vt 0.333333 0.334353
    vt 0.666667 0.665647
    vt 0.333333 0.332314
    vt 0.001020 0.333333
    vt 0.332314 0.000000
    vt 0.333333 0.001019
    vt 0.665647 0.000000
    vt 0.001019 0.666667
    vt 0.667686 0.000000
    vt 0.666667 0.334353
    vt 0.665647 0.333333
    vt 0.000000 0.001020
    vt 0.334353 0.333333
    vn 0.000000 -1.000000 0.000000
    vn -0.000000 1.000000 0.000000
    vn 1.000000 -0.000000 0.000001
    vn -0.000000 -0.000000 1.000000
    vn -1.000000 -0.000000 -0.000000
    vn 0.000000 0.000000 -1.000000
    vn 1.000000 0.000000 -0.000000
    usemtl Material
    s off
    f 1/1/1 2/2/1 3/3/1
    f 5/4/2 8/5/2 7/6/2
    f 1/5/3 5/7/3 6/8/3
    f 2/9/4 6/10/4 3/11/4
    f 3/12/5 7/13/5 4/14/5
    f 5/15/6 1/16/6 4/6/6
    f 4/17/1 1/1/1 3/3/1
    f 6/18/2 5/4/2 7/6/2
    f 2/19/7 1/5/7 6/8/7
    f 6/10/4 7/20/4 3/11/4
    f 7/13/5 8/21/5 4/14/5
    f 8/22/6 5/15/6 4/6/6