Jayanam
Jayanam

Reputation: 128

How can I load and render an OBJ file that may include Triangles, Quads, or N-Gons in OpenGL?

I have the following c++ code to load an object file, at least the vertices and the vertex indices.

 bool ObjMeshImporter::from_file(const std::string& filepath, nelems::Mesh* pMesh)
  {
    std::ifstream in(filepath, std::ios::in);
    if (!in)
    {
      return false;
    }

    std::vector<glm::vec3> t_vert;

    std::string line;
    while (std::getline(in, line))
    {
      if (line.substr(0, 2) == "v ")
      {
        // read vertices
        std::istringstream s(line.substr(2));
        glm::vec3 v; s >> v.x; s >> v.y; s >> v.z;

        // Add to temporary vertices before indexing
        t_vert.push_back(v);
      }
      else if (line.substr(0, 2) == "f ")
      {
        // TODO: Store UVs and Normals
        unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
        int count_found = sscanf_s(line.substr(2).c_str(), 
          "%d/%d/%d %d/%d/%d %d/%d/%d\n", 
          &vertexIndex[0], &uvIndex[0], &normalIndex[0], 
          &vertexIndex[1], &uvIndex[1], &normalIndex[1], 
          &vertexIndex[2], &uvIndex[2], &normalIndex[2]);

        if (count_found != 9) {
          return false;
        }

        pMesh->add_vertex_index(vertexIndex[0]-1);
        pMesh->add_vertex_index(vertexIndex[1]-1);
        pMesh->add_vertex_index(vertexIndex[2]-1);

      }
    }

    // Now use the indices to create the concrete vertices for the mesh
    for (auto v_idx : pMesh->GetVertexIndices())
    {
      glm::vec3 vertex = t_vert[v_idx];
      pMesh->add_vertex(vertex);
    }
    return true;
  }

It works pretty good for an object like this (Blender icosphere):

enter image description here

This model is made of triangles.

But when I load one which has quads, it doesnt work:

enter image description here

of course, because this is my face render method:

glDrawArrays(GL_TRIANGLES, 0, (GLsizei)mVertexIndices.size());

So I know, I have to extend the parser and for every line in "f" check how many indices I have got and best would be to store a VFace-class that knows how many vertices a face is made of - ok.

So I would render faces with tris as GL_TRIANGLE and faces with quads as GL_QUADS... but what about the NGONS? And also how to render the lines?

EDIT: I saw there is GL_POLYGON. But do I have to create separate vertexbuffers for these?

Upvotes: 1

Views: 3043

Answers (1)

gkv311
gkv311

Reputation: 2982

No, I want to render faces with triangles, quads or ngons.

I believe that this question has no reasonable solution. Modern graphics hardware renders only 3 types of primitives: points, lines segments and triangles. So that direct answer is that you have to generate a triangulation from any soup of non-triangle geometry (quads, n-polygons, B-Spline surfaces, analytical surfaces, etc.). This can be done by your own code or using existing library (the latter is preferred, as splitting polygons is not that trivial - I've seen several implementation working in general, but failing in one or another use case on some OBJ files).

There are good reasons why modern hardware operates only on triangles - they can be used for rendering any other geometry, they are perfectly parallelized (compared to geometry having variable number of nodes in element) and this makes hardware simpler.

Here are some approaches for rendering non-triangles in OpenGL with comments:

  • GL_QUADS. Deprecated since OpenGL 3.0. It should be noted, though, that according to various publications and discussions, at least NVIDIA and probably AMD hardware still actually have hardware-accelerated support for these primitives. You will need two different draw passes for rendering triangles mixed with quads (e.g. split indices for each primitive type, although you may still share common vertex buffer). If you ever used GL_QUADS in application, you may also notice that non-flat quad passed to graphics hardware is split into triangles, and different GPU vendors do this in different order (e.g. produce different triangle pair).

  • GL_POLYGON. Also Deprecated since OpenGL 3.0. This primitive existed in OpenGL at days, when vertex processing has been done not by graphics hardware but by CPU. So that this one probably never had any real hardware acceleration - OpenGL driver just split polygons into triangles in a slow and inefficient way. There is another reason why GL_POLYGON is practically useless for OBJ files - GL_POLYGON is only for convex polygons (trivially splittable into triangles), while OBJ files often contain concave polygons. You may find some OBJ specifications on internet requiring polygons being convex, while others not specifying polygon details at all - but practically you cannot ignore this, as real-world OBJ file do have complex polygons (as long as you care about reading arbitrary files).

  • GL_PATCHES for Tessellation Shaders. These are designed for hardware-accelerated triangulation of patches of fixed size, which is a powerful mechanism for displaying smoothable surfaces. These, however, have nothing to do with n-polygons in OBJ file, as defining geometry for tessellation shader requires following very different patterns.

  • Adjacency primitives for Geometry Shaders. Primitives like GL_TRIANGLES_ADJACENCY provide additional information about triangle surroundings, but they also has no direct relationship to n-polygons in OBJ file. Unlike Tesselation Shaders, Geometry Shader allows provoking variable number of new triangles with arbitrary specified node positions. This technically allows generating arbitrary n-polygon split into triangles within Geometry Shader. Although to achieve that, geometry data have to be provided not within main vertex data, but with additional structures (like TBO or similar). Practically, this approach makes no sense to implement (as long as you are not doing some research), as Geometry Shaders are proved to be very inefficient for tessellation-alike tasks, leading to very poor performance.

  • Compute Shaders. These can be used for arbitrary tasks, including splitting polygons into triangles using custom data structures and/or rendering them, but this is unlikely a reliable way for rendering polygons - also a subject for research rather than practical solution.

Upvotes: 1

Related Questions