Parsing a Wavefront .obj file using C++
Asked Answered
A

5

7

While trying to a parse a wavefront .obj file, I thought of two approaches:

  1. Create an 2D array the size of the number of vertices. When a face uses a vertex, get it's coordinates from the array.
  2. Get the starting position of the vertex list and then when a face uses a vertex, scan the lines until you reach the vertex.

IMO, option 1 will be very memory intensive, but much faster. Since option 2 involves extensive file reading, (and because the number of vertices in most objects becomes very large) this will be much slower, but less memmory intensive.

The question is: Comparing the tradeoff between memory and speed, which option would be better suited to an average computer? And, is there an alternative method?

I plan to use OpenGL along with GLFW to render the object.

Anorthic answered 17/10, 2011 at 15:28 Comment(1)
Though it only handles quite a restricted subset, you might want to take a look at some code I posted in a previous answer for inspiration.Maravedi
I
6

IMO, Option 1 will be very memory intensive, but much faster.

You must get those vertices into memory anyway. But there's no need for a 2D array, which BTW would cause two pointer indirections, thus a major performance hit. Just use a simple std::vector<Vertex> for your data, the vector index is the index for the accompanying face list.

EDIT due to comment

class Vertex
{
    union { struct { float x, y, z }; float pos[3] };
    union { struct { float nx, ny, nz }; float normal[3] };
    union { struct { float s, t }; float pos[2] };
    Vertex &operator=();
}

std::vector<Vertex>;
Incite answered 17/10, 2011 at 15:57 Comment(1)
But how would you store the x,y,and z coordinates, ?Anorthic
F
4

Generally you read the list of vertices into an array. Parsing ASCII text is extremely slow; do it only once when loading the file and then store everything in arrays in memory.

Same goes with the triangles / faces. Each triangle generally is composed of a list of three vertex indexes. That should also be stored in an array.

You may find the OBJ reader in the VTK open source library to be useful: http://www.vtk.org/doc/nightly/html/classvtkOBJReader.html. We use it and have had no reason to write our own... Use VTK directly, or you may find studying the source code to be good for further inspiration of your own reader.

In my opinion, one of the major shortcomings with OBJ files is the use of ASCII. 3D ASCII files (be it STL, PLY, OBJ, etc.) are very slow to load if they are ASCII due to the string parsing. Binary format files are much faster and should always be used if performance is an issue: the load time for a good binary format is instantaneous.

Flooded answered 17/10, 2011 at 15:41 Comment(4)
"the load time for a good binary format is instantaneous." Where are you buying those zero-seek, infinite-bandwidth disk drives? I want some too! :)Culdesac
Perhaps I should have phrased as "orders of magnitude faster". Binary files can be read as fast as your disk driver can copy the file contents into RAM. We routinely work with matrices of floating point numbers that can be stored as either ASCII or binary files. With identical values / matrix sizes, the binary ones load/save in less than a second, whereas the ASCII ones take half a minute. (We use the C++ file stream class to convert to/from ASCII). The ASCII format is only used for debugging....Flooded
(And it's not just us; try opening/saving 3D files in MeshLab and compare the performance of ASCII vs. binary. The difference is noticeably different.)Flooded
+1 for the VTK reference. Their OBJ loading method is 10 times simpler than what i was usingAnorthic
X
2

Just load them into arrays. Memory should not be an issue. Your system (usually) has way more memory than your GPU. If you are running into memory problems, you are probably loading a model that is too detailed. (I am semi-assuming that you are going to make a game in OpenGL. If you have a specific need for such large model files, you will still have to work out a way to load the appropriate chunks.)

Xmas answered 17/10, 2011 at 16:42 Comment(0)
D
0

You shouldn't need a 2 dimensional array. Your models should be triangulated and then you can simply load the obj file using gluts obj loader. Simply store points, faces and normals in 3 seperate arrays/buffers. There is an example how you can do it here, but if you want to do it fast you should go for a binary format.

Dianemarie answered 17/10, 2011 at 15:44 Comment(4)
The .obj has not been generated by me, and it contains quads as well. Also, the 2d array is because one dimension refers to the vertex number, and the other stores x,y,z coordinates.Anorthic
@viraj: The vertex number should be used as the index into the array. That's what it's meant to be used for.Incite
also, while reading float values from an ascii file, cant i use in >> number where in is an ifstream object and number is a float ?Anorthic
When reading a quad, simply convert it into 2 triangles (i.e add 6 indices instead of 4; beware of face winding). You'll have to do this sooner or later anyway.Brinkley
P
0

This is a pretty decent solution for prototyping, running a script that generates the arrays for use in OpenGL or your preferred rendering API. obj2opengl.pl is a perl script, you'll need perl installed that you can find here. GitHub link is here.

While running the perl script you may get a runtime error on line 154 concerning if(defined(@center)). Replace it with if(@center).

From the example, once the header file is generated with the data, you can use the it as shown:

/*
created with obj2opengl.pl

source file    : ./banana.obj
vertices       : 4032
faces          : 8056
normals        : 4032
texture coords : 4420


// include generated arrays
#import "./banana.h"

// set input data to arrays
glVertexPointer(3, GL_FLOAT, 0, bananaVerts);
glNormalPointer(GL_FLOAT, 0, bananaNormals);
glTexCoordPointer(2, GL_FLOAT, 0, bananaTexCoords);

// draw data
glDrawArrays(GL_TRIANGLES, 0, bananaNumVerts);
*/
Patti answered 25/8, 2015 at 22:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.