1

I spent hours trying to draw my .obj models in OpenGL. I figured that probably there is something wrong with my Python script (that converts obj models to C++ files). Or there is something wrong with .obj file. I don't know how to make it working properly. OpenGL draws an object, but it is completely different that the object I created in SketchUp.

In short: my script converts .obj correctly (unless I don't know other rules). At first I gather lines with vertices in one list of lines, the same with vn and vt. I gather also data about faces. Later I match appropriate v, vn and vt to the faces. Finally it generates .h and .cpp files with the data. What can be wrong?

Here's the .obj file: https://pastebin.com/g0HwpRqB

    # Alias OBJ Model File
# Exported from SketchUp, (c) 2000-2012 Trimble Navigation Limited
# File units = meters

mtllib sciany1.mtl

g Mesh1 Model

usemtl Brick_Tumbled
v 2.06118 0 0.0146645
vt -2.25413 -0.0320745
vn 0 -1 -0
v -3.02882 0 0.0146645
vt 3.31236 -0.0320745
v -3.02882 0 -9.68534
vt 3.31236 21.184
v 2.06118 0 -9.68534
vt -2.25413 21.184
f 1/1/1 2/2/1 3/3/1 4/4/1 

usemtl FrontColor
vt -119.245 0
vn 0 0 1
vt 81.1487 0
v 2.06118 2.76 0.0146645
vt 81.1487 108.661
v -3.02882 2.76 0.0146645
vt -119.245 108.661
f 2/5/2 1/6/2 5/7/2 6/8/2 

usemtl Brick_Tumbled
vt -0.0160372 0
vn 1 0 -0
vt 10.592 0
v 2.06118 2.76 -9.68534
vt 10.592 6.03675
vt -0.0160372 6.03675
f 1/9/3 4/10/3 7/11/3 5/12/3 

usemtl FrontColor
vt -81.1487 0
vn 0 0 -1
vt 119.245 0
v -3.02882 2.76 -9.68534
vt 119.245 108.661
vt -81.1487 108.661
f 4/13/4 3/14/4 8/15/4 7/16/4 

usemtl Brick_Tumbled
vt -10.592 0
vn -1 0 -0
vt 0.0160372 0
vt 0.0160372 6.03675
vt -10.592 6.03675
f 3/17/5 2/18/5 6/19/5 8/20/5 

vt -3.31236 -0.0320745
vn 0 1 -0
vt 2.25413 -0.0320745
vt 2.25413 21.184
vt -3.31236 21.184
f 6/21/6 5/22/6 7/23/6 8/24/6

Here's the generated .cpp file: https://pastebin.com/YPXnuuzP

            #include "sciany1.h"

            namespace Models {

                Sciany1 sciany1;

                Sciany1::Sciany1() {
                    vertices=Sciany1Internal::vertices;
                    normals=Sciany1Internal::normals;
                    vertexNormals=Sciany1Internal::vertexNormals;
                    texCoords=Sciany1Internal::texCoords;
                    colors=Sciany1Internal::colors;
                    vertexCount=Sciany1Internal::vertexCount;
                }

                Sciany1::~Sciany1() {
                }

                void Sciany1::drawSolid() {
                    glEnable(GL_NORMALIZE);

                    glEnableClientState(GL_VERTEX_ARRAY);
                    //glEnableClientState(GL_COLOR_ARRAY);
                    //glEnableClientState(GL_NORMAL_ARRAY);
                    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

                    glVertexPointer(4,GL_FLOAT,0,vertices);
                    //glColorPointer(4,GL_FLOAT,0,colors);
                    glNormalPointer(GL_FLOAT,sizeof(float)*4,vertexNormals);
                    glTexCoordPointer(2,GL_FLOAT,0,texCoords);

                    glDrawArrays(GL_TRIANGLES,0,vertexCount);

                    glDisableClientState(GL_VERTEX_ARRAY);
                    //glDisableClientState(GL_COLOR_ARRAY);
                    //glDisableClientState(GL_NORMAL_ARRAY);
                    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
                }

                namespace Sciany1Internal {
                        unsigned int vertexCount=18;

                        float vertices[]={
                            2.06,0,0.01,
-3.02,0,0.01,
-3.02,0,-9.68,
-3.02,0,0.01,
2.06,0,0.01,
2.06,2.76,0.01,
2.06,0,0.01,
2.06,0,-9.68,
2.06,2.76,-9.68,
2.06,0,-9.68,
-3.02,0,-9.68,
-3.02,2.76,-9.68,
-3.02,0,-9.68,
-3.02,0,0.01,
-3.02,2.76,0.01,
-3.02,2.76,0.01,
2.06,2.76,0.01,
2.06,2.76,-9.68,


                        };

                        float colors[]={
                        };

                        float normals[]={
                            0,-1,-0,
0,-1,-0,
0,-1,-0,
0,0,1,
0,0,1,
0,0,1,
1,0,-0,
1,0,-0,
1,0,-0,
0,0,-1,
0,0,-1,
0,0,-1,
-1,0,-0,
-1,0,-0,
-1,0,-0,
0,1,-0,
0,1,-0,
0,1,-0,

                        };

                        float vertexNormals[]={

                        };

                        float texCoords[]={
                            -2.25,-0.03,
3.31,-0.03,
3.31,21.18,
-119.24,0,
81.14,0,
81.14,108.66,
-0.01,0,
10.59,0,
10.59,6.03,
-81.14,0,
119.24,0,
119.24,108.66,
-10.59,0,
0.01,0,
0.01,6.03,
-3.31,-0.03,
2.25,-0.03,
2.25,21.18,

                        };
                }
            }

Here's the generated .h file: https://pastebin.com/jaf73yf2

#ifndef SCIANY1_H
#define SCIANY1_H

//Sciany1 model made out of triangles
//Contains arrays:
//vertices - vertex positions in homogenous coordinates
//normals - vertex normals in homogenous coordinates
//texCoords - texturing coordinates
//colors - vertex colors (rgba)
//Culling GL_CW
//TBN friendly

#include "model.h"

namespace Models {
    namespace Sciany1Internal {
        extern float vertices[];
        extern float normals[];
        extern float vertexNormals[];
        extern float texCoords[];
        extern float colors[];
        extern unsigned int vertexCount;
    }

    class Sciany1: public Model {
        public:
            Sciany1();
            virtual ~Sciany1();
            virtual void drawSolid();
    };

    extern Sciany1 sciany1;
}




#endif

Here's my Python script: https://pastebin.com/LivefwgY

def round_number(string_number):
    if "\n" in string_number:
        string_number = string_number.replace("\n", "")
    if "." in string_number:
        parts = string_number.split(".")
        if len(parts[1]) > 2:
            return parts[0] + "." + parts[1][:2]
    return string_number


def line_to_good_line(line):
    processed_numbers=[]
    numbers=line.split(" ")
    for number in numbers:
        if number and number != "\n":
            processed_numbers.append(round_number(number))
    output = str.join(",", processed_numbers) + ",\n"
    return output

def add_numbers_in_the_end(lines, additional_number):
    new_lines=[]
    for line in lines:
        new_lines.append(line[:-1]+","+additional_number+",\n")
    return new_lines


def prepare_output_from_faces(data, faces):
    ready_list = []
    for face in faces:
        for number in face:
            ready_list.append(data[int(number)-1])
    return str.join("", ready_list)


def convert_to_cpp(filename):
    model_name = filename[0].upper() + filename[1:-4]
    upper_model_name = model_name.upper()
    lower_model_name = model_name.lower()

    vertices_lines = []
    normals_lines = []
    tex_coords_lines = []

    vertices_faces = []
    normals_faces = []
    tex_coords_faces = []

    vertices="" # final string output
    normals="" # final string output
    tex_coords="" # final string output

    with open(filename, "r") as file:
        lines = file.readlines()
    for line in lines:
        if line[0:2] == "v ":
            line = line_to_good_line(line[2:])
            vertices_lines.append(line)
        elif line[0:3] == "vn ":
            line = line_to_good_line(line[3:])
            normals_lines.append(line);
        elif line[0:3] == "vt ":
            line = line_to_good_line(line[3:])
            tex_coords_lines.append(line)
        elif line[0:2] == "f ":
            face = line[2:].replace("\n", "").split(" ")
            face[0] = face[0].split("/")
            face[1] = face[1].split("/")
            face[2] = face[2].split("/")
            vertex_face = [face[0][0], face[1][0], face[2][0]]
            tex_coord_face = [face[0][1], face[1][1], face[2][1]]
            normals_face = [face[0][2], face[1][2], face[2][2]]
            vertices_faces.append(vertex_face)
            tex_coords_faces.append(tex_coord_face)
            normals_faces.append(normals_face)

    vertices = prepare_output_from_faces(vertices_lines, vertices_faces)
    normals = prepare_output_from_faces(normals_lines, normals_faces)
    tex_coords = prepare_output_from_faces(tex_coords_lines, tex_coords_faces)
    vertices_number = vertices.count("\n")

    with open(lower_model_name + ".h", "w") as header_file:
        data = """
        /*
        Niniejszy program jest wolnym oprogramowaniem; możesz go
        rozprowadzać dalej i / lub modyfikować na warunkach Powszechnej
        Licencji Publicznej GNU, wydanej przez Fundację Wolnego
        Oprogramowania - według wersji 2 tej Licencji lub(według twojego
        wyboru) którejś z późniejszych wersji.

        Niniejszy program rozpowszechniany jest z nadzieją, iż będzie on
        użyteczny - jednak BEZ JAKIEJKOLWIEK GWARANCJI, nawet domyślnej
        gwarancji PRZYDATNOŚCI HANDLOWEJ albo PRZYDATNOŚCI DO OKREŚLONYCH
        ZASTOSOWAŃ.W celu uzyskania bliższych informacji sięgnij do
        Powszechnej Licencji Publicznej GNU.

        Z pewnością wraz z niniejszym programem otrzymałeś też egzemplarz
        Powszechnej Licencji Publicznej GNU(GNU General Public License);
        jeśli nie - napisz do Free Software Foundation, Inc., 59 Temple
        Place, Fifth face, Boston, MA  02110 - 1301  USA
        */

        #ifndef {}_H
        #define {}_H

        //{} model made out of triangles
        //Contains arrays:
        //vertices - vertex positions in homogenous coordinates
        //normals - vertex normals in homogenous coordinates
        //texCoords - texturing coordinates
        //colors - vertex colors (rgba)
        //Culling GL_CW
        //TBN friendly

        #include "model.h"

        namespace Models {{
            namespace {}Internal {{
                extern float vertices[];
                extern float normals[];
                extern float vertexNormals[];
                extern float texCoords[];
                extern float colors[];
                extern unsigned int vertexCount;
            }}

            class {}: public Model {{
                public:
                    {}();
                    virtual ~{}();
                    virtual void drawSolid();
            }};

            extern {} {};
        }}




        #endif
        """.format(upper_model_name, upper_model_name,
         model_name, model_name, model_name, model_name,
         model_name, model_name, lower_model_name)
        header_file.write(data)

        with open(model_name + ".cpp", "w") as cpp_file:
            data = """
            /*
            Niniejszy program jest wolnym oprogramowaniem; możesz go
            rozprowadzać dalej i / lub modyfikować na warunkach Powszechnej
            Licencji Publicznej GNU, wydanej przez Fundację Wolnego
            Oprogramowania - według wersji 2 tej Licencji lub(według twojego
            wyboru) którejś z późniejszych wersji.

            Niniejszy program rozpowszechniany jest z nadzieją, iż będzie on
            użyteczny - jednak BEZ JAKIEJKOLWIEK GWARANCJI, nawet domyślnej
            gwarancji PRZYDATNOŚCI HANDLOWEJ albo PRZYDATNOŚCI DO OKREŚLONYCH
            ZASTOSOWAŃ.W celu uzyskania bliższych informacji sięgnij do
            Powszechnej Licencji Publicznej GNU.

            Z pewnością wraz z niniejszym programem otrzymałeś też egzemplarz
            Powszechnej Licencji Publicznej GNU(GNU General Public License);
            jeśli nie - napisz do Free Software Foundation, Inc., 59 Temple
            Place, Fifth face, Boston, MA  02110 - 1301  USA
            */

            #include "{}.h"

            namespace Models {{

                {} {};

                {}::{}() {{
                    vertices={}Internal::vertices;
                    normals={}Internal::normals;
                    vertexNormals={}Internal::vertexNormals;
                    texCoords={}Internal::texCoords;
                    colors={}Internal::colors;
                    vertexCount={}Internal::vertexCount;
                }}

                {}::~{}() {{
                }}

                void {}::drawSolid() {{
                    glEnable(GL_NORMALIZE);

                    glEnableClientState(GL_VERTEX_ARRAY);
                    //glEnableClientState(GL_COLOR_ARRAY);
                    //glEnableClientState(GL_NORMAL_ARRAY);
                    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

                    glVertexPointer(4,GL_FLOAT,0,vertices);
                    //glColorPointer(4,GL_FLOAT,0,colors);
                    glNormalPointer(GL_FLOAT,sizeof(float)*4,vertexNormals);
                    glTexCoordPointer(2,GL_FLOAT,0,texCoords);

                    glDrawArrays(GL_TRIANGLES,0,vertexCount);

                    glDisableClientState(GL_VERTEX_ARRAY);
                    //glDisableClientState(GL_COLOR_ARRAY);
                    //glDisableClientState(GL_NORMAL_ARRAY);
                    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
                }}

                namespace {}Internal {{
                        unsigned int vertexCount={};

                        float vertices[]={{
                            {}

                        }};

                        float colors[]={{
                        }};

                        float normals[]={{
                            {}
                        }};

                        float vertexNormals[]={{

                        }};

                        float texCoords[]={{
                            {}
                        }};
                }}
            }}

            """.format(lower_model_name, model_name, lower_model_name, model_name,
            model_name, model_name, model_name, model_name, model_name, model_name,
            model_name, model_name, model_name, model_name, model_name, vertices_number,
            vertices, normals, tex_coords)
            cpp_file.write(data)


filename = input("Input the name of the obj file: ")
convert_to_cpp(filename)
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Deloryn
  • 41
  • 1
  • 7
  • 1
    It's already processed by the script. So vertices are in faces order. For example f 1/2/1 4/2/3 5/3/1 I processed that in vertices it begins with vertex no. 1, vertex no. 4, vertex no.5. I also noticed that there's a mistake (in the above example there are 4 parts of a face). But even if manage with it, it still doesn't solve my problem, because I have another model that has 3 parts of faces and still not working properly – Deloryn May 29 '18 at 19:37

1 Answers1

2

The obj file consists of vertex coordinates with 3 components (x, y, z), normal vectors with 3 components (x, y, z) and texture coordinates with 2 components (u, v).

The first paramter of glVertexPointer specifies the number of coordinates (components) per vertex and the first parameter of glTexCoordPointer specifies the number of coordinates (components) per texture attribute.

You did the the specification of the texture coordinates well, but in the definition of the vertex coordinates you specified 4 components instead of 3.

Change the definition of the array of vertex coordinates:

glVertexPointer(3, GL_FLOAT, 0, vertices);

Note, since the vertex array was defined with 4 components instead of 3, the access to the vertex coordinates is misaligned, and out of bounds at the end. This causes that the vertex coordinates of the rendered mesh seems to be arbitrary.


The 2nd parameter of glNormalPointer specifies the byte offset between consecutive normals. If stride is 0, the normals are understood to be tightly packed in the array.

The normals vectors consists of 3 components (as expected by glNormalPointer) and they are tightly packed.

Either the stride parameter has to be 0:

glNormalPointer(GL_FLOAT, 0, vertexNormals);

or it has to be 3*sizeof(float):

glNormalPointer(GL_FLOAT, sizeof(float)*3, vertexNormals);

In the function round number the attribute coordinates are rounded to 2 decimal places, but in the obj file the vertex coordinates have been submitted with an accuracy up to 5 decimal places.

Change the rounding of the numbers from 2 to 5 decimal places:

def round_number(string_number):
    if "\n" in string_number:
        string_number = string_number.replace("\n", "")
    if "." in string_number:
        parts = string_number.split(".")
        if len(parts[1]) > 5: # <---------------- 5 instead of 2
            return parts[0] + "." + parts[1][:2]
    return string_number 
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thank you for the answer, but it still doesn't solve my problem. Earlier I had a different version of the script, that I was adding 1.0 in the end of each vertex line and 0.0 in normals. I edited the script according to your suggestions and later OpenGL draws the same. – Deloryn May 30 '18 at 08:23
  • @Deloryn I extended the answer – Rabbid76 May 31 '18 at 06:13
  • Thank you. I'm sorry but I had other issues recently, but now I can return to the problem. I removed rounding and still nothing – Deloryn Jun 06 '18 at 18:57