Search code examples
c++algorithmopengl3dadjacency-matrix

OpenGL triangle adjacency calculation


I am trying to write a program that uses OpenGL's triangle adjacencies feature (GL_TRIANGLES_ADJACENCY) to determine the silhouette of a mesh from a local light source. I'm using ASSIMP to load my mesh, and everything seems to be working correctly as far as loading and displaying the mesh is concerned. Unfortunately, the code I've written to store the indices of the adjacent triangles does not seem to be working correctly.

index[0] = mesh.mFaces[i].mIndices[0];
index[2] = mesh.mFaces[i].mIndices[1];
index[4] = mesh.mFaces[i].mIndices[2];
index[1] = findAdjacentIndex( mesh, index[0], index[2], index[4] );
index[3] = findAdjacentIndex( mesh, index[0], index[2], index[4] );
index[5] = findAdjacentIndex( mesh, index[0], index[2], index[4] );

The basic idea behind my algorithm is that, given a mesh and three indices from that mesh, find all faces (should be 1 or 2, depending on whether there is actually an adjacent face or not) of the mesh that share the edge between the first and second vertices. Then, return the third index of the triangle that does NOT use the third index of our original passed triangle. This way the same algorithm can be used for all indices of the triangle in sequence.

unsigned int Mesh::findAdjacentIndex(const aiMesh& mesh, const unsigned int index1, const unsigned int index2, const unsigned int index3) {
    std::vector<unsigned int> indexMap[2];

    // first pass: find all faces that use the first index
    for( unsigned int i=0; i<mesh.mNumFaces; ++i ) {
        unsigned int*& indices = mesh.mFaces[i].mIndices;
        if( indices[0] == index1 || indices[1] == index1 || indices[2] == index1 ) {
            indexMap[0].push_back(i);
        }
    }

    // second pass: find the two faces that share the second index
    for( unsigned int i=0; i<indexMap[0].size(); ++i ) {
        unsigned int*& indices = mesh.mFaces[indexMap[0][i]].mIndices;
        if( indices[0] == index2 || indices[1] == index2 || indices[2] == index2 ) {
            indexMap[1].push_back(i);
        }
    }

    // third pass: find the face that does NOT use the third index and return its third index
    for( unsigned int i=0; i<indexMap[1].size(); ++i ) {
        unsigned int*& indices = mesh.mFaces[indexMap[1][i]].mIndices;
        if( indices[0] != index3 && indices[1] != index3 && indices[2] != index3 ) {
            if( indices[0] != index1 && indices[0] != index2 ) {
                return indices[0];
            }
            if( indices[1] != index1 && indices[1] != index2 ) {
                return indices[1];
            }
            if( indices[2] != index1 && indices[2] != index2 ) {
                return indices[2];
            }
        }
    }

    // no third index was found, this means there is no face adjacent to this one.
    // return primitive restart index
    return restartIndex;
}

Based on my understanding of what I've written, the above function should work perfectly on this example image taken from the OpenGL spec:

Triangle Adjacency Example

Unfortunately, my function does NOT work on any of my real world meshes and I have no idea why. Passing a simple box mesh through the function for example seems to usually return 0 as the adjacent index for each vertex, which makes little sense to me. The result is that the adjacencies are not uploaded correctly and I get an incorrect silhouette from my object...

If anyone here could thus shed any light on what's going wrong and what I can do to fix it, I'd be very grateful. I'd also be happy to provide more info if any is needed.


Solution

  • You are making it way more complicated than it needed to be. You want to search for triangles that share a specific edge and return the third vertex. Then just do so.

    for(unsigned int i=0; i<mesh.mNumFaces; ++i ) {
        unsigned int*& indices = mesh.mFaces[i].mIndices;
        for(int edge = 0; edge < 3; ++edge) { //iterate all edges of the face
            unsigned int v1 = indices[edge]; //first edge index
            unsigned int v2 = indices[(edge + 1) % 3]; //second edge index
            unsigned int vOpp = indices[(edge + 2) % 3]; //index of opposite vertex
            //if the edge matches the search edge and the opposite vertex does not match
            if(((v1 == index1 && v2 == index2) || (v2 == index1 && v1 == index2)) && vOpp != index3)
                return vOpp; //we have found the adjacent vertex
        }
    }
    return -1;
    

    Furthermore, you need to change your calls. If you call the function three times with the same arguments, you will get the same results, of course:

    index[1] = findAdjacentIndex( mesh, index[0], index[2], index[4] );
    index[3] = findAdjacentIndex( mesh, index[2], index[4], index[0] );
    index[5] = findAdjacentIndex( mesh, index[4], index[0], index[2] );