in pymeshlab, how can I load from an array and directly add custom ID for vertices and faces

910 views Asked by At

I'm using pymeshlab to process meshes directly from Blender as per code below.

However I want to be able to link the vertices and faces back to original mesh in Blender so when the mesh has been processed I can display the differences from the original.

Ideally I would just like each vertex and face to store the Blender ID from the original mesh, and this to be included in the export to pymeshlab and the reimport back but I'm not sure how.

This is complicated by the apparent need to triangulate all the faces before pymeshlab will accept them (because the import via numpy array is restricted to three vertices per face). Thus I need to generate a custom Blender ID for faces which are triangulated. Obviously this is a pain, and any advice on importing ngons/etc direcly into pymeshlab would be appreaciated too as if I could just use the same ID as the original Blender mesh it would be vastly simpler.

It is possible to correlate them based on original import location - eg by calculating a custom property with the same formula in both applications, but this seems much less elegant.

ms.apply_filter("define_new_per_vertex_attribute", name="v_ID", expr="x+y+z")

I'm thus wondering if there is a way to just load a numpy array of IDs directly into the mesh as I copy it to pymeshlab [and any way of not having to triangulate first].

import bpy
import pymeshlab
import numpy      



def exportMeshToMeshLab(blenderMesh):
    # NB pymeshlab is fussy
    # verts and faces have to be provided in a numpy array with verts as type float64 and faces as int32
    # faces have to be triangulated - quads and ngons are not allowed
    
    verts = []  #numpyp.empty((0,3), float64)
    for v in blenderMesh.vertices:
            verts.append([v.co[0], v.co[1], v.co[2]])
    verts = numpy.asarray(verts, dtype=numpy.float64)
    if len(verts) == 0:
        print("No vertices were found, so function aborting")
        return
#    print(verts.shape)   # must report (numOfVerts, 3)
#    print(verts.dtype.name)   # must report float64


    faces = []
    tooManyVerts = False
    for poly in blenderMesh.polygons:
        curFace = []
        for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total):
            curFace.append(blenderMesh.loops[loop_index].vertex_index)
        if len(curFace) == 3:
            faces.append(curFace)
        else:
            tooManyVerts = True
            
            
    if tooManyVerts:
        print("WARNING: Meshlab will only accept faces with THREE vertices")
    if len(faces) == 0:
        print("No triangular faces were found, so function aborting")
        return
    faces = numpy.asarray(faces, dtype=numpy.int32)
#    print(faces.shape)   # must report (numOfVerts, 3)
#    print(faces.dtype.name)

    # create a new Mesh with the two arrays
    meshlabMesh = pymeshlab.Mesh(verts, faces)

    # create a new MeshSet (a meshset can have multiple meshes each in a differnt layer - but that's not covered with this function)
    meshlabMeshSet = pymeshlab.MeshSet()

    # add the mesh to the MeshSet with the current name
    meshlabMeshSet.add_mesh(meshlabMesh, blenderMesh.name)

    return meshlabMeshSet


def importMeshFromMeshLab(meshlabMesh):
    # NB from_pydata in Blender is fussy
    # verts and faces have to be provided in a standard Python list (NOT a numpy array)
    
    verts = meshlabMesh.current_mesh().vertex_matrix().tolist()
    #vID= meshlabMesh.current_mesh().vertex_matrix().ID   # TODO: return this
    faces = meshlabMesh.current_mesh().face_matrix().tolist()
    #fID= meshlabMesh.current_mesh().face_matrix().ID   # TODO: return this
    
    return verts, faces  #, vID, fID


print("START")

print("Exporting the selected Blender mesh object to MeshLab")
print("NB - this will fail if all faces are not triangulated first")
me = bpy.context.object.data
mls = exportMeshToMeshLab(me)            # (mls = meshlabMeshSet)


# apply filter to the current selected mesh (last loaded)
mls.compute_geometric_measures()

# compute the geometric measures of the current mesh
# and save the results in the out_dict dictionary
out_dict = mls.compute_geometric_measures()

print("compute_geometric_measures are:")
print("  avg_edge_length = ", out_dict['avg_edge_length'])
print("  total_edge_length = ", out_dict['total_edge_length'))
print("  mesh_volume = ", out_dict['mesh_volume'))
print("  surface_area = ", out_dict['surface_area'))

print("Now we're importing the mesh back into Blender")
print(" and creating a new object from the mesh")


print("Importing the MeshLab mesh back to Blender and creating it as a new object")
verts, faces = importMeshFromMeshLab(mls)
mesh = bpy.data.meshes.new("meshFromMeshLab")  # add the new mesh
mesh.from_pydata(verts, [], faces)   # this could also be done as a bmesh too...
ob = bpy.data.objects.new("meshFromMeshLab", mesh)
bpy.context.collection.objects.link(ob)

print("DONE")





The primary code sources are:

https://github.com/dgm3333/3d-print-toolbox-modified/blob/master/meshlab_integration.py

https://blenderartists.org/t/meshlab-accessible-direct-from-blender-python/1299022

https://pymeshlab.readthedocs.io/en/0.1.9/tutorials/import_mesh_from_arrays.html

https://pymeshlab.readthedocs.io/en/0.1.9/tutorials/user_defined_mesh_attributes.html

0

There are 0 answers