C ++ .obj解析器,解析问题或使用OpenGL绘图

时间:2015-04-18 14:15:58

标签: c++ parsing opengl

我在绘制一些3d对象时遇到了一些麻烦。我写了一个非常简单的.obj解析器,我知道它并不支持所有类型的.obj文件,但这对我来说并不合适。我只需要在屏幕上正确渲染一些东西。这是我的解析器

Mesh * Loader::loadObj(char * path) {
    Mesh * objectMesh;

    vector<Vertex *> indexedVertices;
    vector<Vector3 * > normals;
    vector<TextureCoordinate *> textureCoords;
    vector<vector<GLushort>> meshIndices;

    vector<GLushort> verticesIndexed;
    vector<GLushort> texturesIndexed;
    vector<GLushort> normalsIndexed;

    ifstream inFile;

    inFile.open(path);

    if (!inFile.good())
    {
        cerr << "Can't open texture file " << path << endl;
        return false;
    }

    std::cout << "Loading .obj file" << endl;

    while (!inFile.eof()) {
        string line;
        std::getline(inFile, line);

        if (line.substr(0, 2) == "v ") {//vertices
            std::vector<string> elements = Operations::split(Operations::trim(line.substr(3)), *" ");
            if (elements.size() > 1) {
                std::cout << "Loading vertex data: " << line << endl;
                Vertex * v = new Vertex(std::atof(elements.at(0).c_str()), std::atof(elements.at(1).c_str()), std::atof(elements.at(2).c_str()));
                indexedVertices.push_back(v);
            }
        }
        else if (line.substr(0, 2) == "vn") {//normals
            std::vector<string> elements = Operations::split(Operations::trim(line.substr(3)), *" ");
            std::cout << "Loading normal data: " << line << endl;
            if (elements.size() > 1) {
                Vector3 * v = new Vector3(std::atof(elements.at(0).c_str()), std::atof(elements.at(1).c_str()), std::atof(elements.at(2).c_str()));
                normals.push_back(v);
            }
        }
        else if (line.substr(0, 2) == "vt") {//tex coords
            std::cout << "Loading texture data: " << line << endl;
            std::vector<string> elements = Operations::split(Operations::trim(line.substr(3)), *" ");
            if (elements.size() > 1) {
                TextureCoordinate * t = new TextureCoordinate(std::atof(elements.at(0).c_str()), std::atof(elements.at(1).c_str()));
                textureCoords.push_back(t);
            }
        }
        else if (line.substr(0, 2) == "f ") {//faces / indices
            std::cout << "Loading face data: " << line << endl;
            std::vector<string> elements = Operations::split(Operations::trim(line.substr(2)), *" ");
            if (elements.size() > 1) {
                for (int i = 0; i < elements.size(); i++) {
                    std::vector<string> e = Operations::split(Operations::trim(elements.at(i)), *"/");
                    if (e.size() > 1) {
                        verticesIndexed.push_back(std::atof(e.at(0).c_str()) - 1);
                        texturesIndexed.push_back(std::atof(e.at(1).c_str()) - 1);
                        normalsIndexed.push_back(std::atof(e.at(2).c_str()) - 1);
                    }
                }
            }
        }
    }

    meshIndices.push_back(verticesIndexed);
    meshIndices.push_back(texturesIndexed);
    meshIndices.push_back(normalsIndexed);


    std::cout << "Face count: " << verticesIndexed.size() << " " << texturesIndexed.size() << " " << normalsIndexed.size() << endl;

    objectMesh = new Mesh(indexedVertices, normals, textureCoords, meshIndices);

    std::cout << "Vertices count: " << indexedVertices.size() << endl;
    std::cout << "Normals count: " << normals.size() << endl;
    std::cout << "Textures count: " << textureCoords.size() << endl;


    inFile.close();

    return objectMesh;

}

我相信这会正确解析数据,因为矢量的结束大小与文件中的数据匹配,例如它会打印4700个顶点,文件中会有4700个顶点。

我使用以下

绘制对象
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);


for (int i = 0; i < mesh->meshIndices.size(); i++) {
    glVertexPointer(3, GL_FLOAT, 0, mesh->indexedVertices[0]);
    glNormalPointer(GL_FLOAT, 0, mesh->normals[0]);
    glTexCoordPointer(2, GL_FLOAT, 0, mesh->textureCoords[0]);

    glDrawElements(GL_TRIANGLES, mesh->meshIndices[i].size(), GL_UNSIGNED_SHORT, &mesh->meshIndices[i][0]);
}

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);

enter image description here

enter image description here

为什么对象没有正确绘制?

编辑:这有效,但速度非常慢

glBegin(GL_TRIANGLES);
for (int i = 0; i < mesh->meshIndices[0].size(); i++)//36
{

        glTexCoord2fv(&mesh->textureCoords[mesh->meshIndices[1][i]]->u);
        glNormal3fv(&mesh->normals[mesh->meshIndices[2][i]]->x);
        glVertex3fv(&mesh->indexedVertices[mesh->meshIndices[0][i]]->x);

}

1 个答案:

答案 0 :(得分:0)

问题在于您使用索引的方式。代码的相关关键部分是在网格中存储顶点,纹理坐标和法线索引的位置:

meshIndices.push_back(verticesIndexed);
meshIndices.push_back(texturesIndexed);
meshIndices.push_back(normalsIndexed);

然后使用这些索引绘制:

for (int i = 0; i < mesh->meshIndices.size(); i++) {
    glVertexPointer(3, GL_FLOAT, 0, mesh->indexedVertices[0]);
    glNormalPointer(GL_FLOAT, 0, mesh->normals[0]);
    glTexCoordPointer(2, GL_FLOAT, 0, mesh->textureCoords[0]);

    glDrawElements(GL_TRIANGLES, mesh->meshIndices[i].size(),
                   GL_UNSIGNED_SHORT, &mesh->meshIndices[i][0]);
}

这样做是将网格绘制3次。一旦使用顶点索引,一次使用纹理坐标索引,一次使用正常索引。这可能不会很好。您需要绘制一次,使用顶点,纹理坐标和位置的正确组合。

难点在于OBJ格式对每个顶点属性(位置,纹理坐标,法线)使用单独的索引,而OpenGL仅支持对所有属性使用单组索引。

有两种主要方法可以实现这一目标:

  1. 不要使用索引。这是通过在解析时应用索引查找以及构建新数组来完成的,然后可以使用它们进行渲染而无需编制索引。使用以下方法勾画出代码的关键部分:

    if (line.substr(0, 2) == "v ") {
        ...
        indexedVertices.push_back(v);
    } else if (line.substr(0, 2) == "vn") {
        ...
        indexedNormals.push_back(v);
    } else if (line.substr(0, 2) == "vt") {
        ...
        indexedTextureCoords.push_back(t);
    } else if (line.substr(0, 2) == "f ") {
        ...
        unindexedVertices.push_back(
            indexedVertices[std::atof(e.at(0).c_str()) - 1]);
        unindexedTextureCoords.push_back(
            indexedTextureCoords[std::atof(e.at(1).c_str()) - 1]);
        unindexedNormals.push_back(
            indexedNormals[std::atof(e.at(2).c_str()) - 1]);
    }
    

    然后,您可以使用未编入索引的属性,并使用glDrawArrays()绘制没有索引的属性。

    缺点是这会复制共享顶点,因此会消耗更多内存,并且速度会变慢。

  2. 为顶点,纹理坐标和法线的每个唯一索引组合创建一个OpenGL顶点。我在这里的旧答案包含一些伪代码,说明如何做到这一点:OpenGL - Index buffers difficulties