-1

I wrote an OBJ file parser in c++ and it works. It can load any OBJ file into a model. However it is incredibly slow. Loading one low-poly model can take up to ten seconds. Is there any way to optimize this or is my computer just extremeley low? Any help is greatly appreciated.

RawModel OBJLoader::loadModel(const std::string& filePath, Loader* loader)
{
    std::ifstream file(filePath);

    std::vector<glm::vec3> positions;
    std::vector<glm::vec2> uvs;
    std::vector<glm::vec3> normals;

    std::vector<unsigned int> indices;

    float* positionsArray = nullptr;
    float* uvsArray = nullptr;
    float* normalsArray = nullptr;
    unsigned int* indicesArray = nullptr;

    bool firstFace = true;

    std::string line;
    while (std::getline(file, line))
    {
    std::vector<std::string> currentLine = split(line, ' ');

    if (line[0] == 'v' && line[1] == ' ')
    {
    glm::vec3 pos(parseFloat(currentLine[1]), parseFloat(currentLine[2]), parseFloat(currentLine[3]));
    positions.push_back(pos);
    }
    else if (line[0] == 'v'  && line[1] == 't')
    {
    glm::vec2 uv(parseFloat(currentLine[1]), parseFloat(currentLine[2]));
    uvs.push_back(uv);
    }
    else if (line[0] == 'v'  && line[1] == 'n')
    {
    glm::vec3 norm(parseFloat(currentLine[1]), parseFloat(currentLine[2]), parseFloat(currentLine[3]));
    normals.push_back(norm);
    }
    else if (line[0] == 'f'  && line[1] == ' ')
    {
    if (firstFace)
    {
    uvsArray = new float[positions.size() * 2];
    normalsArray = new float[positions.size() * 3];
    firstFace = false;
    }

    std::vector<std::string> vert1 = split(currentLine[1], '/');
    std::vector<std::string> vert2 = split(currentLine[2], '/');
    std::vector<std::string> vert3 = split(currentLine[3], '/');

    processVertex(vert1, &indices, uvs, normals, uvsArray, normalsArray);
    processVertex(vert2, &indices, uvs, normals, uvsArray, normalsArray);
    processVertex(vert3, &indices, uvs, normals, uvsArray, normalsArray);
    }
    }

    positionsArray = new float[positions.size() * 3];
    indicesArray = new unsigned int[indices.size()];

    for (int i = 0; i < positions.size(); i++)
    {
    positionsArray[i*3] = positions[i].x;
    positionsArray[i*3+1] = positions[i].y;
    positionsArray[i*3+2] = positions[i].z;
    }

    for (int i = 0; i < indices.size(); i++) indicesArray[i] = indices[i];

    return loader->loadToVao(positionsArray, positions.size() * 3, indicesArray, indices.size(), uvsArray,positions.size()*2);
}

float parseFloat(const std::string& str)
{
return std::stof(str);
}

int parseInt(const std::string& str)
{
return std::stoi(str);
}

void  processVertex(std::vector<std::string> vertData, std::vector<unsigned         int>* indices, std::vector<glm::vec2> uvs, std::vector<glm::vec3> normals,
float* uvsArray, float* normalsArray)
{
unsigned int currentVertexPointer = parseInt(vertData[0]) - 1;
indices->push_back(currentVertexPointer);

glm::vec2 currentUv = uvs[parseInt(vertData[1]) - 1];
uvsArray[currentVertexPointer * 2] = currentUv.x;
uvsArray[currentVertexPointer * 2 + 1] = 1 - currentUv.y;

glm::vec3 currentNorm = normals[parseInt(vertData[2]) - 1];
normalsArray[currentVertexPointer * 3] = currentNorm.x;
normalsArray[currentVertexPointer * 3+1] = currentNorm.y;
normalsArray[currentVertexPointer * 3+2] = currentNorm.z;
}

std::vector<std::string> split(const std::string &str, const char &delim)
{
typedef std::string::const_iterator iter;
iter beg = str.begin();
std::vector<std::string> tokens;

while (beg != str.end()) {
//cout << ":" << beg._Myptr << ":" << endl;
iter temp = find(beg, str.end(), delim);
if (beg != str.end())
tokens.push_back(std::string(beg, temp));
beg = temp;
while ((beg != str.end()) && (*beg == delim))
beg++;
}

return tokens;
}
  • There are lots of OBJ readers out there. Check one out and figure that out yourself. This is more of a software reviewing question rather than a programming problem. – Marco A. Jun 03 '15 at 13:48
  • I recommend clarifying OBJ because on windows systems, the intermediate file (after compiling) is usually "*.obj", often called an OBJ file. – Thomas Matthews Jun 03 '15 at 15:04
  • Take a look at "tinyobjloader", I use it a lot and it seems to have a good performance – RedAgito Jun 05 '15 at 01:11

1 Answers1

1

First, obvious improvements:

  • Pass vector vertData to processVertex by (const) reference.
  • Reuse one vector where possible by clearing it
  • Rewrite split to take vector by reference as argument, and write to this vector instead of return value

Second, forget about first, and use profiler. For example, gprof on linux, or sleepy on windows.

  • Additionally, you can try to speed up the std:ifstream. See http://stackoverflow.com/questions/5166263/how-to-get-iostream-to-perform-better – Danny Ruijters Jun 03 '15 at 14:49
  • You will also gain performance when you pre-allocate (vector::resize) the vectors storage. Otherwise your data will be copied around multiple times – BDL Jun 04 '15 at 11:31