5

I created a small OBJ file loader for use with OpenGL, but faces aren't being read at all. Before adding the face reader the program read everything just fine, so I'm a little stumped here. Here is my code:

void OBJLoader::LoadObjFile(std::string f,std::vector<float>& v,std::vector<float>& n,std::vector<float>& u)
{
std::ifstream file;
file.open(f);

OutputDebugStringA("OPENING FILE-\n");
OutputDebugStringA(f.c_str());
std::string vertex,normal,uv,face;

std::string data;
std::string data2;
std::string data3 = "";

uv = "vt";
normal = "vn";
vertex = "v";
face = "f";

std::size_t var1;
int i = 1;
int j = 1;
int k = 1;
bool loop = true;
int loopcount = 1;

std::vector<float> tV,tU,tN;

if(file.is_open())
{
    while(std::getline(file,data) && file.good())
    {

        var1 = data.find(vertex);
        if(var1 != std::string::npos && var1 < data.size() && data[1] == 0x20)
        {
            data3.append(data,2,std::string::npos);

            double v1,v2,v3;
            std::istringstream ss(data3);
            if(ss >> v1 >> v2 >> v3){ v.push_back((float)v1); v.push_back((float)v2); v.push_back((float)v3); i++;}
            else
            {
                OutputDebugStringA("Error reading vertex data.\n");
                std::stringstream ssc;
                ssc << loopcount;
                std::string str = ssc.str();
                OutputDebugStringA((char*)str.c_str());
                OutputDebugStringA("\n");

            }
            data3 = "";
            data2 = "";
        }

        var1 = data.find(normal);
        if(var1 != std::string::npos && var1 < data.size())
        {
            data3.append(data,3,std::string::npos);

            float v1,v2,v3;
            std::istringstream ss(data3);
            if(ss >> v1 >> v2 >> v3){ n.push_back((float)v1); n.push_back((float)v2); n.push_back((float)v3); j++;}
            else
            {
                OutputDebugStringA("Error reading normal data.\n");
                std::stringstream ssc;
                ssc << loopcount;
                std::string str = ssc.str();
                OutputDebugStringA((char*)str.c_str());
                OutputDebugStringA("\n");
            }
            data3 = "";
            data2 = "";
        }

        var1 = data.find(uv);
        if(var1 != std::string::npos && var1 < data.size())
        {
            data3.append(data,3,std::string::npos);

            float v1,v2,v3;
            std::istringstream ss(data3);
            if(ss >> v1 >> v2){ u.push_back((float)v1); u.push_back((float)v2); u.push_back((float)v3); k++;}
            else
            {
                OutputDebugStringA("Error reading UV data.\n");
                std::stringstream ssc;
                ssc << loopcount;
                std::string str = ssc.str();
                OutputDebugStringA((char*)str.c_str());
                OutputDebugStringA("\n");

            }
            data3 = "";
            data2 = "";

        }

        var1 = data.find(face);
        if(var1 != std::string::npos && var1 < data.size())
        {   
            data3.append(data,2,std::string::npos);
            std::istringstream ss(data3);

                for(int l = 0;l < data3.size();l++)
                {
                    if(data3[l] == '/') data3[l] = ' ';
                }

            std::istringstream ss(data3);
            unsigned int verts[9];
            char slashes[6];

                //if(ss >> verts[0] >> slashes[0] >> verts[1] >> slashes[1] >> verts[2] >> verts[3] >> slashes[2] >> verts[4] >> slashes[3] >> verts[5] >> verts[6] >> slashes[4] >> verts[7] >> slashes[5] >> verts[8])
            if (ss >> verts[0] >> verts[1] >> verts[2] >> verts[3] >> verts[4] >> verts[5] >> verts[6] >> verts[7] >> verts[8])
                else
                {
                    try
                    {
                    tV.push_back(v.at(verts[0]));
                    tV.push_back(v.at(verts[0]+1));
                    tV.push_back(v.at(verts[0]+2));

                    tU.push_back(u.at(verts[1]));
                    tU.push_back(u.at(verts[1]+1));
                    tU.push_back(u.at(verts[1]+2));

                    tN.push_back(n.at(verts[2]));
                    tN.push_back(n.at(verts[2]+1));
                    tN.push_back(n.at(verts[2]+2));
                    //
                    tV.push_back(v.at(verts[3]));
                    tV.push_back(v.at(verts[3]+1));
                    tV.push_back(v.at(verts[3]+2));

                    tU.push_back(u.at(verts[4]));
                    tU.push_back(u.at(verts[4]+1));
                    tU.push_back(u.at(verts[4]+2));

                    tN.push_back(n.at(verts[5]));
                    tN.push_back(n.at(verts[5]+1));
                    tN.push_back(n.at(verts[5]+2));
                    //
                    tV.push_back(v.at(verts[6]));
                    tV.push_back(v.at(verts[6]+1));
                    tV.push_back(v.at(verts[6]+2));

                    tU.push_back(u.at(verts[7]));
                    tU.push_back(u.at(verts[7]+1));
                    tU.push_back(u.at(verts[7]+2));

                    tN.push_back(n.at(verts[8]));
                    tN.push_back(n.at(verts[8]+1));
                    tN.push_back(n.at(verts[8]+2));
                    }
                    catch(std::out_of_range e)
                    {
                        OutputDebugStringA("Out of Range!\n");
                    }
                }
            }
            else
            {
                OutputDebugStringA("Invalid face data! Generic     Error!\n");
                std::stringstream ssc;
                std::stringstream ssc2;

                ssc << loopcount;
                std::string str = ssc.str();
                OutputDebugStringA((char*)str.c_str());
                OutputDebugStringA("\n");
            }

            data3 = "";
            data2 = "";
        }
        loopcount++;

    }

}
else
{

}
}

Any help is appreciated!

Also here is the OBJ file I am using:

 v 0.000 0.000 0.000
 v 1.000 0.000 0.000
 v 1.000 1.000 0.000
 v 0.000 0.000 0.000
 v 0.000 1.000 0.000
 v 1.000 1.000 0.000
 vt 0.000 0.000
 vt 1.000 0.000
 vt 1.000 1.000
 vn 0.000 0.000 1.000

 f 1/7/10 2/8/10 3/9/10
 f 4/7/10 5/8/10 6/9/10

OK, I fixed the slash issues, but now this block of code is causing std::out_of_range excpetions:

                tV.push_back(v.at(verts[0]));
                tV.push_back(v.at(verts[0]+1));
                tV.push_back(v.at(verts[0]+2));

                tU.push_back(u.at(verts[1]));
                tU.push_back(u.at(verts[1]+1));
                tU.push_back(u.at(verts[1]+2));

                tN.push_back(n.at(verts[2]));
                tN.push_back(n.at(verts[2]+1));
                tN.push_back(n.at(verts[2]+2));
                //
                tV.push_back(v.at(verts[3]));
                tV.push_back(v.at(verts[3]+1));
                tV.push_back(v.at(verts[3]+2));

                tU.push_back(u.at(verts[4]));
                tU.push_back(u.at(verts[4]+1));
                tU.push_back(u.at(verts[4]+2));

                tN.push_back(n.at(verts[5]));
                tN.push_back(n.at(verts[5]+1));
                tN.push_back(n.at(verts[5]+2));
                //
                tV.push_back(v.at(verts[6]));
                tV.push_back(v.at(verts[6]+1));
                tV.push_back(v.at(verts[6]+2));

                tU.push_back(u.at(verts[7]));
                tU.push_back(u.at(verts[7]+1));
                tU.push_back(u.at(verts[7]+2));

                tN.push_back(n.at(verts[8]));
                tN.push_back(n.at(verts[8]+1));
                tN.push_back(n.at(verts[8]+2));
Nick Ellas
  • 97
  • 7
  • Would mind telling us WHERE is the problem? What do you mean by "faces aren't being read at all."? – manuell Dec 26 '13 at 18:12
  • Sorry if I was vague, the if statment containing ss >> verts[0] >> slash[0] fails. The else statement associated with it prints out "Invalid face data!". – Nick Ellas Dec 26 '13 at 18:15
  • Do you have to use write your own parser? There's this really nice parser I've been using recently, and I'd recommend you to take a look at it. https://github.com/syoyo/tinyobjloader – TheAmateurProgrammer Dec 27 '13 at 00:53
  • @TheAmateurProgrammer Thanks for the link! I just thought it would be a nice exercise just to write myself and maybe learn a thing or two. – Nick Ellas Dec 27 '13 at 02:10

1 Answers1

10

It seems that parsing from istream using >> does not work as you are expecting. Did you notice it automatically skips whitespace when parsing vertices, normals and uvs? Yet for faces you try to parse every character. You also want non-whitespace slashes as delimiters.

How about this approach https://stackoverflow.com/a/1895584/642532 ? One answer suggest telling the stream to treat commas (in your case slashes) as whitespace so you can parse faces just as easily as vertices. Another answer suggest using a string tokenizer like StrTk http://www.partow.net/programming/strtk/index.html or Boost.

Community
  • 1
  • 1
Jonas Bötel
  • 4,452
  • 1
  • 19
  • 28
  • OK, I took your advice however I didn't use any of those links yet. I searched through the string containing the current line and turned the slashes into whitespaces, **'/'** to **' '**. Now i'm getting *std::out_of_range exceptions. – Nick Ellas Dec 26 '13 at 21:18
  • 1
    @NickEllas that is because the u/v and normal refences of your faces in the OBJ file *are* out of range. For example normal 10 but there is only one normal defined. I guess those indexes refer to line number or nth element instead. The one normal is the tenth element in your OBJ file. – Jonas Bötel Dec 27 '13 at 04:13