Reputation: 41
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)
Upvotes: 1
Views: 462
Reputation: 211277
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
Upvotes: 2