0

I'm having a problem reading an OBJ file... Problem 1 is that the vectors are declared as floats (and have been declared as doubles), but print out in the terminal gives me all integers. Terminal output has been:

    1-1-1
    1-11
    -1-11
    -1-1-1

when it ought to be:

     1.000000 -1.000000 -1.000000
     1.000000 -1.000000 1.000000
     -1.000000 -1.000000 1.000000
     -1.000000 -1.000000 -1.000000

Problem 2 is that I have been unable to read the faces in the file correctly. The output has been:

    1
    5
    1
    2

when it should be:

    1 2 3 4
    5 8 7 6
    1 5 6 2
    2 6 7 3

I'm trying to get the results to print out correctly now, since I will next be putting these results in nodes using dynamic memory allocation due to their varying in size. Here's the code:

/* This function opens the OBJ file */
void openFile(string nf){   
std::ifstream file;
file.open (nf, std::ifstream::out);

string line;
string o_name;
string faces;
char o;
char v;
char f;
int i = 0;
float x;
float y;
float z;

if(file.is_open()){
std::cout<< "file is open!\n";
//include actions for OBJ file...Just read. You work from memory
    while(std::getline(file, line))
    {
        if(line.empty())        //line is empty, then file is empty!
            std::cout << "file is empty! \n";
        /* finds the shape name*/
        if (line[i] == 'o')
        {
            std::istringstream iss (line);
            iss >> o >> o_name;
            std::cout << o_name << endl;
            line.clear();
            //createNodeO(string o_name);
        }
        /* finds the vertices*/             
        if (line[i] == 'v')
        {
            std::istringstream iss (line);
            iss >> v >> x >> y >> z;
            std::cout << x << y << z << endl;
            line.clear();
            //createNodeO(float x, float y, float z);
        }

        /* finds the faces*/                        
        if (line[i] == 'f')
        {
            std::istringstream iss (line);
            iss >> f >> faces;
            std::cout << faces << endl;
            //createNodeO(string faces);
        }
    }
}
else{
    std::cout<< "could not open the file!\n";
}
file.close();
std::cout<< "file is closed!\n";    

}

According to the g++ compiler, I'm crazy when it comes to the faces part. Please help!

Joe Diaz
  • 23
  • 4
  • If you want formatted output you'll need to read through the documentation for iomanip. The reason you only get the first face is you only read 2 values from that line, f and the first number. Formatted input stops on whitespace by default. – Retired Ninja Sep 20 '18 at 02:23
  • Thanks, @Retired Ninja! – Joe Diaz Sep 20 '18 at 18:47
  • My problem remains that I cant get the faces completely. I have tried several ways to get them, but I've been unable to solve that problem. – Joe Diaz Sep 25 '18 at 13:22

1 Answers1

1

This is sorta how I would do it. You could also use operator>> for each type if you felt fancy.

Differences from your example:

I ignore everything but verts and faces and assume there's no w in the verts and no vt, vn, vp, etc. in the file. There are possibly names, groups, materials, etc. that you might run into too. If you find yourself getting in over your head or your desire to write parsers wanes you might check out a library to do it. http://www.assimp.org/ is a good one, and there's a good viewer here http://www.open3mod.com/ which can definitely help if what you are parsing doesn't quite look right.

Your code is tokenizing and writing straight to your destination variables, I chose to tokenize first and then convert the tokens to the right type after some basic validation of the line. Either works. I'd usually do it the way you did, maybe with operator>> or a helper function.

I tested with a few of the files from here: https://people.sc.fsu.edu/~jburkardt/data/obj/obj.html

Hope this helps.

#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

struct vertex
{
    double x, y, z;
};

struct face
{
    std::vector<int> v;
};

struct object
{
    std::vector<vertex> verts;
    std::vector<face> faces;
};

std::ostream& operator<<(std::ostream& out, const vertex& v)
{
    out << "v " 
        << std::fixed << std::setprecision(6) << v.x << " "
        << std::fixed << std::setprecision(6) << v.y << " "
        << std::fixed << std::setprecision(6) << v.z;
    return out;
}

std::ostream& operator<<(std::ostream& out, const face& f)
{
    out << "f";
    for (size_t i = 0; i < f.v.size(); ++i)
    {
        out << " " << f.v[i];
    }
    return out;
}

std::ostream& operator<<(std::ostream& out, const object& o)
{
    for (size_t i = 0; i < o.verts.size(); ++i)
    {
        out << o.verts[i] << "\n";
    }
    for (size_t i = 0; i < o.faces.size(); ++i)
    {
        out << o.faces[i] << "\n";
    }
    return out;
}

std::vector<std::string> split(const std::string& s)
{
    std::vector<std::string> ret;
    std::string token;
    std::stringstream ss(s);
    while (ss >> token)
    {
        ret.push_back(token);
    }
    return ret;
}

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        std::cerr << "Usage: " << argv[0] << " <obj filename>\n";
        return -1;
    }

    std::fstream in(argv[1]);
    if (!in)
    {
        std::cerr << "File: " << argv[1] << " could not be opened\n";
        return -1;
    }

    object o;
    std::string line;
    int lineNumber = 0;
    while (std::getline(in, line))
    {
        ++lineNumber;

        if (line.empty() || line[0] == '#')
        {
            continue;
        }

        std::vector<std::string> tokens = split(line);
        if (line[0] == 'v')
        {
            if (tokens.size() < 4)
            {
                std::cerr << "Invalid vert: '" << line << "' on line: " << lineNumber << "\n";
                continue;
            }

            vertex v;
            v.x = std::stod(tokens[1]);
            v.y = std::stod(tokens[2]);
            v.z = std::stod(tokens[3]);
            o.verts.push_back(v);
        }
        else if (line[0] == 'f')
        {
            if (tokens.size() < 4)
            {
                std::cerr << "Invalid face: '" << line << "' on line: " << lineNumber << "\n";
                continue;
            }

            face f;
            for (size_t i = 1; i < tokens.size(); ++i)
            {
                f.v.push_back(std::stoi(tokens[i]));
            }
            o.faces.push_back(f);
        }
    }

    std::cout << o;
    return 0;
}
Retired Ninja
  • 4,785
  • 3
  • 25
  • 35