3

I'd like to import obj models into my opengl program. I have a class / data format that I use to pass attribute data into shaders:

class CustomVertex : public IVtxFmt
{
public:
  float m_Position[3];      // x, y, z      offset 0, size = 3*sizeof(float)    
  float m_Normal[3];        // nx, ny, nz;  offset 3
  float m_TexCoords[2];     // u, v         offset 6
  float m_Colour[4];        // r, g, b, a   offset 8
  float m_Tangent[3];       // r, g, b      offset 12
  float m_Bitangent[3];     // r, g, b      offset 15
};

So I'm working with a model of a log cabin I downloaded from the Internet.

The log cabin has several vertices, normals, and texture coord definitions, followed by a list of face definitions.

So my first instinct was to parse the obj file and end up with

vector<vertex>
vector<Normal>
vector<TexCoord>

That's not straightforward to translate into my CustomVertex format, since there might be 210 vertices, 100 tex coords and 80 normals defined in the file.

After a list of ~390 faces in this format:

f 83/42/1 67/46/1 210/42/1 

I encounter the following in the file:

#
# object tile00
#

followed by more vertex definitions.

So from this, I have inferred that a model might consist of several sub objects, each defined by a number of faces; each face defined by 3 x vertex / normal / texcoord index values.

So in order to arrive with a vector of CustomVertex, I'm thinking that I need to do the following:

create and populate:

vector <vertex>
vector <normal>
vector <texcoord>

vector <indices>

I need to create a CustomVertex for each unique v/vn/vt triple in the face definitions.

So I thought about creating a map:

std::vector<CustomVertex> and
std::map< nHashId, CustomVertex_index >

So my idea is that for each v/vn/vt I encounter, I create a hash of this string e.g. nHashId = hash("80/50/1")* and search the map for the hash. If none exists, I create a CustomVertex and add it to the vector, then I add the newly created hash and the CustomVertex_index into the map.

*: By creating a hash of the v/vn/vt string, I'm creating a unique numeric value that corresponds to that string, which I'm hoping is faster to search/compare in the map than the equivalent text.

If I come across a match to the hash, I consider that the customvertex already exists and instead of creating a new CustomVertex, I just add the CustomVertex_index entry to the indices vector and move on.

Since this seems like a computationally expensive exercise, I guess I'll be dumping my CustomVertex arrays (and corresponding indices arrays) to disk for later retrieval, rather than parse the obj file every time.

Before I ask my questions, may I point out that due to time constraints and not wanting to have to redesign my Vbo class (a non-trivial task), I'm stuck with the CustomVertex format - I know its possible to supply attributes in separate arrays to my shaders, but I had read that interleaving the data like I have with CustomVertex can enhance performance.

So to my questions: 1. Does my method seem sound or crazy? If crazy, please point out where I'm going wrong.

  1. Can you spot any potential issues?

  2. Has anyone done this before and can recommend a simpler way to achieve what I'm trying to?

fishfood
  • 4,092
  • 4
  • 28
  • 35
  • 2
    If you can hash something, why not just use an `unordered_map`? Otherwise your method seems fine and not overly complicated. – pmr Jan 04 '13 at 01:07
  • By creating a hash of the v/vn/vt string, I'm creating a unique numeric value that corresponds to that string, which I'm hoping is faster to search/compare in the map than the equivalent text. – fishfood Jan 04 '13 at 01:11
  • @lapin: That's called "premature optimization". Just compare the indices (as *numbers*, not text). If your profiling shows it to be slow, resolve it then. – Nicol Bolas Jan 04 '13 at 01:56
  • 1
    Given that the data is in string format to begin with, and all 3 indices need to be compared, I'm not actually sure that using the string as the map key is all that bad an idea. Though if you really do want to create an integer hash, I'd create *that* from the indidices and not the string. I agree with the above comment though, that you should start with whatever naive version works, and optimise where necessary. – JasonD Jan 04 '13 at 13:25

2 Answers2

0

Can you spot any potential issues?

You mean besides hash collisions? Because I don't see the part of your algorithm that handles that.

Has anyone done this before and can recommend a simpler way to achieve what I'm trying to?

There's a much simpler way: just compare the indices and not use hashes.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
0

Instead of creating a string hash of "v/vn/vt", the idea is to only hash v as an integer. After that you get a bucket that contains all the "v/vn/vt" combinations that share the same v index.

If a hash collision happens(same v encountered), you would compare the collided combination with those in the bucket to see if it is really duplicated. If not, remember to add the collided combination to the bucket.