Tokenyet
Tokenyet

Reputation: 4291

Mesh Simplification with Assimp and OpenMesh

For days ago, I ask a question on how to use the edge collapse with Assimp. Smooth the obj and remove duplicated vertices in software are sloved the basic problem that could make edge collapse work, I mean it work because it could be simplicated by MeshLab like this: simplified mesh

It looks good in MeshLab, but I then do it in my engine which used Assimp and OpenMesh. The problem is Assimp imported the specified vertices and Indices, that could let the halfedge miss the opposite pair (Is this called non-manifold?). The result snapshot use OpenMesh's Quadric Decimation:

Decimation by OpenMesh

To clear to find the problem, I do it without decimation and parse the OpenMesh data structure back directly. Everything is work fine as expect (I mean the result without decimation).

Without Decimation

The code that I used to decimate the mesh:

Loader::BasicData Loader::TestEdgeCollapse(float vertices[], int vertexLength, int indices[], int indexLength, float texCoords[], int texCoordLength, float normals[], int normalLength)
{
    // Mesh type
    typedef OpenMesh::TriMesh_ArrayKernelT<>   OPMesh;
    // Decimater type
    typedef OpenMesh::Decimater::DecimaterT< OPMesh > OPDecimater;
    // Decimation Module Handle type
    typedef OpenMesh::Decimater::ModQuadricT< OPMesh >::Handle HModQuadric;

    OPMesh mesh;
    std::vector<OPMesh::VertexHandle> vhandles;
    int iteration = 0;
    for (int i = 0; i < vertexLength; i += 3)
    {
        vhandles.push_back(mesh.add_vertex(OpenMesh::Vec3f(vertices[i], vertices[i + 1], vertices[i + 2])));
        if (texCoords != nullptr)
            mesh.set_texcoord2D(vhandles.back(),OpenMesh::Vec2f(texCoords[iteration * 2], texCoords[iteration * 2 + 1]));
        if (normals != nullptr)
            mesh.set_normal(vhandles.back(), OpenMesh::Vec3f(normals[i], normals[i + 1], normals[i + 2]));
        iteration++;
    }

    for (int i = 0; i < indexLength; i += 3)
        mesh.add_face(vhandles[indices[i]], vhandles[indices[i + 1]], vhandles[indices[i + 2]]);

    OPDecimater decimater(mesh);
    HModQuadric hModQuadric;
    decimater.add(hModQuadric);
    decimater.module(hModQuadric).unset_max_err();
    decimater.initialize();
    //decimater.decimate(); // without this, everything is fine as expect.
    mesh.garbage_collection();

    int verticesSize = mesh.n_vertices() * 3;
    float* newVertices = new float[verticesSize];
    int indicesSize = mesh.n_faces() * 3;
    int* newIndices = new int[indicesSize];
    float* newTexCoords = nullptr;
    int texCoordSize = mesh.n_vertices() * 2;
    if(mesh.has_vertex_texcoords2D())
        newTexCoords = new float[texCoordSize];
    float* newNormals = nullptr;
    int normalSize = mesh.n_vertices() * 3;
    if(mesh.has_vertex_normals())
        newNormals = new float[normalSize];

    Loader::BasicData data;

    int index = 0;
    for (v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it)
    {
        OpenMesh::Vec3f &point = mesh.point(*v_it);
        newVertices[index * 3] = point[0];
        newVertices[index * 3 + 1] = point[1];
        newVertices[index * 3 + 2] = point[2];
        if (mesh.has_vertex_texcoords2D())
        {
            auto &tex = mesh.texcoord2D(*v_it);
            newTexCoords[index * 2] = tex[0];
            newTexCoords[index * 2 + 1] = tex[1];
        }
        if (mesh.has_vertex_normals())
        {
            auto &normal = mesh.normal(*v_it);
            newNormals[index * 3] = normal[0];
            newNormals[index * 3 + 1] = normal[1];
            newNormals[index * 3 + 2] = normal[2];
        }
        index++;
    }
    index = 0;

    for (f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it)
        for (fv_it = mesh.fv_ccwiter(*f_it); fv_it.is_valid(); ++fv_it)
        {
            int id = fv_it->idx();
            newIndices[index] = id;
            index++;
        }

    data.Indices = newIndices;
    data.IndicesLength = indicesSize;
    data.Vertices = newVertices;
    data.VerticesLength = verticesSize;
    data.TexCoords = nullptr;
    data.TexCoordLength = -1;
    data.Normals = nullptr;
    data.NormalLength = -1;
    if (mesh.has_vertex_texcoords2D())
    {
        data.TexCoords = newTexCoords;
        data.TexCoordLength = texCoordSize;
    }
    if (mesh.has_vertex_normals())
    {
        data.Normals = newNormals;
        data.NormalLength = normalSize;
    }
    return data;
}

Also provide the tree obj I tested, and the face data that generated by Assimp, I fetch out from visual studio debugger, that shows the problem that some of the indices could not find the index pair.

Upvotes: 2

Views: 2201

Answers (1)

Tokenyet
Tokenyet

Reputation: 4291

Few weeks thinking about this and fails, I thought I want some Academic/Mathematical solution for automatically generating these decimated mesh, but now I'm trying to find the simple way to implement this, the way I am able to do is changing the structure for loading multi-object (file.obj) in single custom object (class obj), and switch the object when needed it. The benefit of this is I could manage what should present and ignore any algorithm problem.

By the way, I list some obstacles that push me back to simple way.

  1. Assimp Unique Indices and Vertices, this is nothing wrong, but for the algorithm, no way to make the adjacency half-edge structure for this.
  2. OpenMesh for reading only object file(*.obj), this could be done when using read_mesh function, but the disadvantage is the lack example of document and hard to using in my engine.
  3. Write a custom 3d model importer for any format is hard.

In conclusion, there are two ways to make level of details work in engine, one is using the mesh simplication algorithm and more test to ensure quality, the other is just switch the 3dmodel that made by 3d software, It is not automatic but stable. I use the second method, and I show the result here :)

this work!

However, this is not a real solution with my question, so I won't assign me an answer.

Upvotes: 1

Related Questions