1

I'm trying to write a reader for Wavefront .OBJ files so I can draw meshes in OpenGL. To do this, I've tried to accumulate some basic C++ file IO knowledge since I mostly know java. I understand from this questions answer that >> can be used to read in data from the stream.

My strategy for reading my OBJ file is too first read in the char which identifies what the next pieces of data are, then keep trying to read in the numbers until I can't anymore. Then I try to read in the next char. My code is as follows:

std::ifstream objReader;
objReader.open("resources//file.obj");
char m;
int vindex = 0;
int iindex = 0; 
while (objReader >> m) {
    std::cout << "here" << std::endl;
    if (m == 'v') {
        GLfloat cv;
        while (objReader >> cv) {
            objData[vindex] = cv; //obj data is my verticies array to be drawn
            std::cout << objData[vindex] << std::endl;
            vindex++;

        }
    }
    else if (m == 'f') {
        GLuint ci;
        while (objReader >> ci) {
            indicies[iindex] = ci; //indicies is my EBO array 
            std::cout << indicies[iindex] << std::endl;
            iindex++;
        }
    } 
}

The file data I'm using can be found here.

Now when I run this code, it opens up the file fine and it sucesfully reads in the first line. It indetifies the marker as the char v then it stores the following 3 floats into my array. The problem is, it just ends there. The loop breaks and it never even continues to the second line. Somehow, it can no longer find any other chars in the file. Why does this happen and how can I fix it?

Thanks!

Community
  • 1
  • 1
Ashwin Gupta
  • 2,159
  • 9
  • 30
  • 60
  • Is this OBJ file a binary file (contains control characters)? If so Is the stream opened with the `ios::binary` option? – PaulMcKenzie Jul 09 '16 at 01:04
  • Think about what state the stream is in when you exit `while (objReader >> cv)` or `while (objReader >> ci)` Or just read Sam Varshavchik's answer. Crom that guy must type fast. – user4581301 Jul 09 '16 at 01:05
  • @PaulMcKenzie I just do this: `objReader.open("resources//file.obj");`. Edited the reader opening into question body also. – Ashwin Gupta Jul 09 '16 at 01:06
  • @user4581301 well honestly don't know much about states of streams, I'll look it up though. – Ashwin Gupta Jul 09 '16 at 01:07

2 Answers2

2

Your inner loop parses the floats like this:

while (objReader >> cv)

I'm assuming that this GLfloat is a typedef for a float or a double, based on your description.

The only way that this loop ends is when the operator>> fails. And when operator>> fails, it puts the stream into an error state. After the error state is set, all subsequent operations on the stream fail automatically.

So, subsequent to this, when execution returns to the top level loop the second time around:

while (objReader >> m) {

This will now fail immediately, because the stream is now in an error state.

You could manually clear the error state, but this is not really a clean way to handle input.

If you always expect three float values, replace the inner loop with a for loop that iterates three times.

If the number of float variables varies, I guess you can stick with your current approach, and explicitly clear the error condition by invoking the clear() method, after the while loop:

objReader.clear();

For a simple parsing requirement that's probably good enough...

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Ok thanks I'll give this a shot really quick and get back to you. The first way will work for the verticies, problem is that the faces data vary in length so I'll need to clear the stream. – Ashwin Gupta Jul 09 '16 at 01:09
  • Well, this appears to be working. I'm getting an acess violation however when the letter `s` comes up in the file, any idea why that might happen? – Ashwin Gupta Jul 09 '16 at 01:13
  • Who knows. You need to show the code for the `s` letter. And you better make it a [mcve], with sample input. This is going to be undefined behavior, for which a [mcve] is typically required, in order to fully analyze it. – Sam Varshavchik Jul 09 '16 at 01:29
  • Oh nvm, solved the problem. Just didn't allocate enough space in the array for my vertices. XD – Ashwin Gupta Jul 09 '16 at 05:05
2

No point repeating Sam Varshavchik's answer, so here's a solution:

  1. Read each line in the file into into a std::string with std::getline
  2. write the line into a std::stringstream
  3. read the character out of the stringstream as you do now and enter the integer or float case
  4. read integers of floats from the stringstream until end of line just like you do now.

Quick, untested hack example that should work:

std::ifstream objReader;
objReader.open("resources//file.obj");
char m;
int vindex = 0;
int iindex = 0; 
std::string line;
while (std::getline(objReader, line)) {

    std::cout << "here: " << line << std::endl;
    std::stringstream linereader(line);
    if (linereader >> m) // only enter if we read a character on the line
                         // testing here because we use the while to get a line 
    {// and continue as you did, but reading linereader instead of objReader
        if (m == 'v') { // 
            GLfloat cv;
            while (linereader >> cv) {
                objData[vindex] = cv; //obj data is my verticies array to be drawn
                std::cout << objData[vindex] << std::endl;
                vindex++;

            }
        }
        else if (m == 'f') {
            GLuint ci;
            while (linereader >> ci) {
                indicies[iindex] = ci; //indicies is my EBO array 
                std::cout << indicies[iindex] << std::endl;
                iindex++;
            }
        }
    }
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • Ah ok I see thats clever. This way I don't have to do a messy `clear()` call. +1 for answer, however Sam's is broader and so I'm going to give accept to him. Thanks, I hope you don't mind. – Ashwin Gupta Jul 09 '16 at 01:15
  • Definitely do. Sam explained what went wrong in easy-to-read detail. – user4581301 Jul 09 '16 at 01:20