Wednesday, September 19, 2007

Parsing model data

This week we had to parse OBJ files (which is a geometry definition file). The main idea is to parse values from a text file and transfer them into OpenEngine's data structures.

We had to parse 5 kind of information:
  • vertex: e.g. v 15.424347 17.551886 -10.248134
  • normals: e.g. vn -1.000000 0.000000 0.000000
  • textures: e.g. vt 0.503197 0.007948
  • faces: e.g. f 6/340/1 2/337/2 7/339/3
The thing to note is that faces are defined following way:
  • f v/vt/vn v/vt/vn v/vt/vn
Our idea was then to first parse v, vn and vt into following C++ structures:

vector<> > vertexes;
vector<> > normals;
vector<> > textures;

In a scripting language this would be rather trivial, but in C++ it's rather complex. For example, to split after " " you would do this in Python:
  • str.split(" ")
In C++ you must use split from boost library:
  • vector<> split_vec;
    split( split_vec, line, is_any_of(" ") );
Which is a bit more complex and needs a googling :) Anyway, we also choose to make abstractions (to make the code cleaner). For example, we had following helper function:
  • vector OBJResource::parseLine(string line, int dims)
parseLine takes a line and a dimensions and returns a vector of values, an example:
  • parseLine("v 15.424347 17.551886 -10.248134", 3) -> [15.424347, 17.551886, -10.248134]
We could have made more beautiful abstractions, but we weren't that comfortable in C++, for example, we could not figure out how to use Vector<3,> instead of vector, so we had to make following hack:
  • vector vec = parseLine(string(buf), 3);
    vertexes.push_back( Vector<3,>( vec[0], vec[1], vec[2] ) );
When we had parsed v, vn and vt, we parsed after faces. For every face definition, we looked into our v, vn, or vt vectors. An example for vt:
  • sscanf(split_inner[1].c_str(), "%d", &inner_in);
    face->texc[i-1] = textures[inner_in-1];
In other languages, rewriting to other datatypes is more beautiful, e.g. for Java you would write:
  • int a = Integer.parseInt("5");
Parsing the OBJ file wasn't that hard, but it required some C++ knowledge.

Other than that we also had to dig a little into Open Engine, some of the things that we didn't understand at first was:
  • Face class didn't have a "smart" constructor, so we had to modify public field variables directly
  • We had to call face->CalcHardNorm() and face->CalcTangentSpace() explicitly
  • We didn't really understand why FacePtr was smart, but that's probably because we don't have that much experience with C++ programming
Anyway, now we have a little more experience with C++ programming :-D

1 comment:

amix said...

Why boost pointers are smart can be found on Open Engine documentation: Smart Pointers