Saturday, July 27, 2013

Modern OpenGL tutorial 3D model .obj loader/parser with C++ and GLM

When you need to draw a more advanced object like character, house, terrain, vehicles we can't pass the vertices by ourselves for these object so we have to use the 3D model and model are simply the meshes made of one or more number of vertices. There are many 3D modeling software one of them is blender which is open source also. Blender help to create the model and export it into different file format like .obj, 3ds, md2 etc among them I choose the .obj (wave front obj) file which is very simple. OBJ only contain vertex, normal, texture and other material information. It doesn't support animation for animation we can use  3ds, md2  3Dmodel. There are many library to load 3D model.

Download
All the code in this series of articles is available from github: https://github.com/smokindinesh/Modern-OpenGL-Series  You can download a zip of all the files from that page, or you can clone the repository if you are familiar with git.

 In this articles I will show you how to write your own .obj model parser.



For a quick and simplicity rather than making our own 3D model we use existing one. At first if you don't have blender then download it form here.
We will export the blender default cube in .obj format. We don't use any other material effect, texture and normal just only vertices are exported.
Open the blender you will see the default cube. You don't have to do anythings just export it in obj format.
For exporting goto file->export->wavefront(.obj) option. Give name "cube" and choose  the directory where you want to export it.While exporting don't forgot to check the triangular faces which is shown in figure.

You will see two file "cube.obj" and "cube.mtl".
The .obj file contains the mesh : vertices and  faces.
The .mtl file contains information about material.
Lets inspects the .obj file how it looks like. You will see pretty simple format.
# Blender v2.67 (sub 0) OBJ File: ''
# www.blender.org
mtllib cude.mtl
o Cube
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
usemtl Material
s off
f 1 2 3
f 5 8 7
f 1 5 6
f 2 6 3
f 3 7 4
f 5 1 4
f 4 1 3
f 6 5 7
f 2 1 6
f 6 7 3
f 7 8 4
f 8 5 4

  • lines starting with # are comments
  • o introduces a new object
  • v introduces a vertex
  • vt introduces texture coordinates
  • vn introduces a normal
  • f introduces a face, using vertex indices, starting at 1
  • usemtl and mtlib describe the look of the model
But in our model we doesn't see vn and vt because we haven't use it. One thing you may surprise that for making complete cube we need 12 triangles that is 12 * 3 = 36 vertex but you see there is only 8 vertex. This is because cube has only 8 corner so we only 8 vertex is enough. For representing all 12 triangles by 8 vertex we use f (face index) we can see
f 1 2 3
f 5 8 7
f 1 5 6
These are the vertex index. f 1 2 3 means vertex first, second and third makes one triangular face among the 12 triangular face of cube. Other f  5 8 7 is also similar 5th , 8th, 7th vertex makes another triangular face and so on. In this way all the 12 triangle is represented.
Our obj parser is limited it doesn't support  multiple object , texture and normal. This is only for learning purpose so that we can understand how to write our own complet obj parser.For that I have created the mesh class.
mesh.h
#ifndef MESH_H
#define MESH_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

class Mesh
{
    public:
        Mesh();//constructor
        void LoadObjModel(const char *filename);//function to load obj model
        std::vector returnMesh();//return the vertices of mesh data
    private:
        std::vector vertices;//to store vertex information of 3D model started with v
        std::vector meshVertices;//to store all 3D model face vertices
        std::vector faceIndex;//to store the number of face index started with f

};

#endif
 // MESH_H

mesh.cpp
#include "../include/Mesh.h"

Mesh::Mesh()
{
    //ctor
}
void Mesh::LoadObjModel(const char *filename)
{
    std::ifstream in(filename, std::ios::in);
    if (!in)
        {
            std::cerr << "Cannot open " << filename << std::endl;
            exit(1);

        }
    std::string line;
    while (std::getline(in, line))
    {

      if (line.substr(0,2)=="v "){
            std::istringstream v(line.substr(2));
            glm::vec3 vert;
            double x,y,z;
            v>>x;v>>y;v>>z;
            vert=glm::vec3(x,y,z);
            vertices.push_back(vert);
      }
      else if(line.substr(0,2)=="f "){
        std::istringstream v(line.substr(2));
        GLuint a,b,c;
        v>>a;v>>b;v>>c;
        a--;b--;c--;
        faceIndex.push_back(a);
        faceIndex.push_back(b);
        faceIndex.push_back(c);
      }

    }

 //calculate all mesh vertices using face index
for(unsigned int i=0; i < faceIndex.size(); i++)
{
    glm::vec3 meshData;
    meshData=glm::vec3(vertices[faceIndex[i]].x,vertices[faceIndex[i]].y,vertices[faceIndex[i]].z);
    meshVertices.push_back(meshData);

}

}

//function for returing mesh vertex data
std::vector Mesh::returnMesh()
{
    return meshVertices;
}

This is end of chapter for my Modern OpenGL tutorial series for beginner. If you haven't follow all previous chapter then you can get here http://www.codeincodeblock.com/2013/07/modern-opengl-32-tutorial-series-with.html

1 comment: