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