Jump to content
  • Advertisement
Sign in to follow this  
tre

Problems with OBJ-loader

This topic is 2774 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi.
I'm working on migrating to a newer version of OpenGL, because of this I'm reworking quite a lot of code.
The thing that's bugging out is my OBJ-loader.
The following function throws the "String subscript out of range" error message at the "while"-loop.
[source lang="cpp"]void Models::parseAndLoad(const char* filename){
std::ifstream myfile;
myfile.open(filename);

if(!myfile){
alert(L"No file!");
return;
}

std::string c;
std::vector<std::string> expline;
std::vector<std::string> expline_f;
float currentVertex;
int inFace;

while(getline(myfile, c)){
if(c[0] == 'v' && c[1] == ' '){
expline = explode(" ", c);

currentVertex = convertFromString(expline.at(2)); // first value
vertices.push_back(currentVertex);
currentVertex = convertFromString(expline.at(3)); // second value
vertices.push_back(currentVertex);
currentVertex = convertFromString(expline.at(4)); // third value
vertices.push_back(currentVertex);
}else if(c[0] == 'v' && c[1] == 't'){
expline = explode(" ", c);

currentVertex = convertFromString(expline.at(2)); // first value
texcoords.push_back(currentVertex);
currentVertex = convertFromString(expline.at(3)); // second value
texcoords.push_back(currentVertex);
currentVertex = convertFromString(expline.at(4)); // third value
texcoords.push_back(currentVertex);
}else if(c[0] == 'v' && c[1] == 'n'){
expline = explode(" ", c);

currentVertex = convertFromString(expline.at(2)); // first value
normals.push_back(currentVertex);
currentVertex = convertFromString(expline.at(3)); // second value
normals.push_back(currentVertex);
currentVertex = convertFromString(expline.at(4)); // third value
normals.push_back(currentVertex);
}else if(c[0] == 'f'){
expline = explode(" ", c); // splits the row into three parts that contain the face information

expline_f = explode("/", expline.at(1)); // splits the first row into vertex, texture and normal information
inFace = convertFromString(expline_f.at(0), 2);
vFaces.push_back(inFace-1);
inFace = convertFromString(expline_f.at(1), 2);
tFaces.push_back(inFace-1);
inFace = convertFromString(expline_f.at(2), 2);
nFaces.push_back(inFace-1);

expline_f = explode("/", expline.at(2)); // splits the second row as above
inFace = convertFromString(expline_f.at(0), 2);
vFaces.push_back(inFace-1);
inFace = convertFromString(expline_f.at(1), 2);
tFaces.push_back(inFace-1);
inFace = convertFromString(expline_f.at(2), 2);
nFaces.push_back(inFace-1);

expline_f = explode("/", expline.at(3)); // splits the third row
inFace = convertFromString(expline_f.at(0), 2);
vFaces.push_back(inFace-1);
inFace = convertFromString(expline_f.at(1), 2);
tFaces.push_back(inFace-1);
inFace = convertFromString(expline_f.at(2), 2);
nFaces.push_back(inFace-1);
}else{
// failsafe
continue; // do nothing
}
}

myfile.close();

initVBO();
}[/source]

In the above the first "subscript" error occurs at the declaration of the while loop (while(getline(myfile, c))).
I really don't know what the error message means. As I understand it, some people get it when trying to load an empty file. The file I'm using is not empty. I've checked :)
Also, the code above used to work in Visual C++ 2008 and a lower OpenGL version.

Thanks in advance,
Marcus

Share this post


Link to post
Share on other sites
Advertisement
Instead of testing the value of myfile, use myfile.isopen(). The variable myfile should never be null.

Also, instead of using while( getline(...) ), try using while( !myfile.eof() && !myfile.bad() ) or something similar. Then use getline(...) inside the while loop.

Share this post


Link to post
Share on other sites

Instead of testing the value of myfile, use myfile.isopen(). The variable myfile should never be null.

Also, instead of using while( getline(...) ), try using while( !myfile.eof() && !myfile.bad() ) or something similar. Then use getline(...) inside the while loop.

Thanks for the tips. I'm now using this instead of my old code. I'll read more on the ifstream functions as well.

However, I'm still getting my debug assertion error: "string subscript out of range".

Edit
The following change to my while loop gives me a message box with the message "Space!" once and then throws the error. Still can't figure this out.
[source lang="cpp"]while(!myfile.eof() && myfile.good()){
std::getline(myfile, c);

if(c[1] == ' '){
alert(L"Space!");
}
}[/source]

Edit 2
And removing the while loop removes the error. So I guess I've at least found where it goes south...

Edit 3
So, I've allocated a console so that I can get some output while parsing the file.
I think I've located the error. In my OBJ file, while reading everything is outputted until line 2865. The strange thing is that row 2866 is looking just the same as row 2865, save one more character (9 turns to 10 in the first value).
v -9.99746 -10.42154 300.79782
v -10.16290 -10.42154 300.99942

It is however at the end of the vertex position list. At row 2872 there's a whitespace. I've tried looking for this and telling the program to "continue". But it doesn't help.
So I guess it's the size of the line at row 2866 that's messing with my code.
Could this be true?
And how do I fix this? It's really a show stopper, if I can't change it.

Thanks!

Edit 4
New code
[source lang="cpp"]while(getline(myfile, currentString)){
if(currentString[0] == 'v' && currentString[1] == ' '){
std::cout << i << ": " << currentString << std::endl;
}else if(isspace(currentString.at(0))){
continue;
}else{
continue;
}

i++;
}[/source]

Edit 5
Allright, the amount of edits is getting extreme, now :)
However, now I've noticed that the problem can't be with line 2866. I'm getting correct output. Totally missed that.
I've figured out that the problem is with lines that contain no characters at all. I don't know how to check for that. I've tried "currentString.empty()", "currentString[0] == '\n'", "isspace(currentString[0])" and of course the "else" statement which I thought would handle everything that wasn't covered in the if/else-if statements.

Hope you guys have any insight. I'm out of ideas for the moment.

Share this post


Link to post
Share on other sites
Phew!
I found my error as I posted in my previous (last) edit.
The error was that a few rows in my OBJ-file was empty.
I solved this by checking for the length of the current string. If it is larger than X (in my case I chose 4) then we can parse the string, if not we should continue to the next one.

[source lang="cpp"]void Models::parseAndLoad(const char* filename){
std::ifstream myfile;
myfile.open(filename);

if(!myfile.is_open()){
alert(L"No file!");
return;
}

std::string currentString;
std::vector<std::string> expline;
std::vector<std::string> expline_f;
float currentVertex;
int inFace;
int i = 1;

while(!myfile.eof()){
getline(myfile, currentString);

if(currentString.length() >= 4){ // check the length of the current string, so that it actually contains something
if(currentString[0] == 'v' && currentString[1] == ' '){ // vertex positions
expline = explode(" ", currentString);

from_string<float>(currentVertex, expline.at(2), std::dec);
vertices.push_back(currentVertex);
from_string<float>(currentVertex, expline.at(3), std::dec);
vertices.push_back(currentVertex);
from_string<float>(currentVertex, expline.at(4), std::dec);
vertices.push_back(currentVertex);
}else if(currentString[0] == 'v' && currentString[1] == 't'){ // vertex texturecoordinates
expline = explode(" ", currentString);

from_string<float>(currentVertex, expline.at(2), std::dec);
texcoords.push_back(currentVertex);
from_string<float>(currentVertex, expline.at(3), std::dec);
texcoords.push_back(currentVertex);
from_string<float>(currentVertex, expline.at(4), std::dec);
texcoords.push_back(currentVertex);
}else if(currentString[0] == 'v' && currentString[1] == 'n'){ // vertex normals
expline = explode(" ", currentString);

from_string<float>(currentVertex, expline.at(2), std::dec);
normals.push_back(currentVertex);
from_string<float>(currentVertex, expline.at(3), std::dec);
normals.push_back(currentVertex);
from_string<float>(currentVertex, expline.at(4), std::dec);
normals.push_back(currentVertex);
}else if(currentString[0] == 'f'){ // faces
expline = explode(" ", currentString); // splits the row into the three parts that contain the polygon face information

expline_f = explode("/", expline.at(1));
from_string<int>(inFace, expline_f.at(0), std::dec);
vFaces.push_back(inFace-1);
from_string<int>(inFace, expline_f.at(1), std::dec);
tFaces.push_back(inFace-1);
from_string<int>(inFace, expline_f.at(2), std::dec);
nFaces.push_back(inFace-1);

expline_f = explode("/", expline.at(2));
from_string<int>(inFace, expline_f.at(0), std::dec);
vFaces.push_back(inFace-1);
from_string<int>(inFace, expline_f.at(1), std::dec);
tFaces.push_back(inFace-1);
from_string<int>(inFace, expline_f.at(2), std::dec);
nFaces.push_back(inFace-1);

expline_f = explode("/", expline.at(3));
from_string<int>(inFace, expline_f.at(0), std::dec);
vFaces.push_back(inFace-1);
from_string<int>(inFace, expline_f.at(1), std::dec);
tFaces.push_back(inFace-1);
from_string<int>(inFace, expline_f.at(2), std::dec);
nFaces.push_back(inFace-1);
}else{ // contains none of the above characters, skip the rest
continue;
}
}else{ // the string contains nothing, just continue to the next string
continue;
}
}

myfile.close();

initVBO();
}[/source]

If you have any tips or pointers on the above code, you're welcome to comment.

Thanks!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!