Meshlab accessible direct from Blender python

For the last few months it’s been possible to use Meshlab filters directly from Blender Python, so I thought I’d post a “how to”

To use meshlab filters on Blender meshes
Details are at:

You first need to install pymeshlab
You can do this from the Blender scripting workspace in Blender 2.92
BUT you have to have admininistor priviledges so for Windows users
JUST THIS ONCE open Blender by R-clicking on it in the start menu and select"Run as Administrator"

Open up the System Console (under the Window menu)
Then paste the following into the scripting workspace and run it

import subprocess
import sys
import os
 
# path to python.exe
python_exe = os.path.join(sys.prefix, 'bin', 'python.exe')
 
# upgrade pip
subprocess.call([python_exe, "-m", "ensurepip"])
subprocess.call([python_exe, "-m", "pip", "install", "--upgrade", "pip"])
 
# install required packages
subprocess.call([python_exe, "-m", "pip", "install", "pymeshlab"])

print("DONE")

Now exit Blender and restart it normally

After restart you can import meshlab using Blender Python and pass it meshes and run filters on them without having to save and load it back and forth as a file
The following is a simple script to check it’s working.
Output will appear in the System Console window (not the main Blender Python window)

Also for more information see:
#https://pymeshlab.readthedocs.io/en/latest/tutorials.html
#https://pymeshlab.readthedocs.io/en/latest/filter_list.html

import pymeshlab
print("\n\n")
pymeshlab.print_pymeshlab_version()
print("\n\n")

filters = pymeshlab.filter_list()
print("AVAILABLE FILTERS ARE")
for f in filters:
    print(f)
print("\n\n")

print("SPECIFIC FILTER PARAMETERS can be listed for each filter, eg:-")
print(">>> pymeshlab.print_filter_parameter_list('discrete_curvatures')")
pymeshlab.print_filter_parameter_list('discrete_curvatures')

For instruction on the process of strategy to load a blender mesh in to meshlab see the tutorial
https://pymeshlab.readthedocs.io/en/latest/tutorials/import_mesh_from_arrays.html

Incidentally when I import pymeshlab I get the following error in the Blender python console but not in the System console, and it seems to work fine (so I just ignore it)
Warning: Unable to load the following plugins:

  • filter_sketchfab.dll: filter_sketchfab.dll does not seem to be a Qt Plugin.*
    Cannot load library C:…\filter_sketchfab.dll: The specified module could not be found.
2 Likes

Oh heres a minimal example of the actual process of exporting and importing to/from MeshLab. This example will take the selected object mesh and send it to MeshLab, then bring it back as a new mesh linked to the scene.
NB (!!) MeshLab will only import triangular meshes so you have to convert the mesh first.
Also MeshLab requires the mesh data to be in Numpy arrays, and Blender from_pydata requires it as standard lists (but this conversion is manaaged within the export/import functions).

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()
    faces = meshlabMesh.current_mesh().face_matrix().tolist()
    #name = meshlabMesh.current_mesh().mesh_name()   # TODO: return this
    
    return verts, faces  #, name


print("START")

# Export a Blender mesh to MeshLab
me = bpy.context.object.data
meshlabMeshSet = exportMeshToMeshLab(me)

# Import a MeshLab mesh to Blender
verts, faces = importMeshFromMeshLab(meshlabMeshSet)
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(verts)
print(faces)

print("DONE")
1 Like

Great Job!
I was really hoping that someone would start this kind of integration.

Interesting enough the filter architecture of meshlab (and therefore of pymeshlab) offers a lot of reflection info that can help the direct exposition of filters (and their parameters) to make the use of meshlab stuff inside blender much easier…

Let us know if you need any help or any kind of helper function inside pymeshlab to ease the process (I am thinking for example to better quad/poly mesh integration or smarter managment of textures)

One thing which would be really helpful is to be able to pass an array with a UID to identify each blender mesh component into meshlab which was then passed back with the mesh after processing. That way any modifications could be easily linked back to the original mesh - I’m currently using a hit-and-miss approach of comparing spacial coordinates (which fails if anything gets moved).

In pymeshlab there is an unique id associated to each mesh into the MeshSet.
For example you can get the id of the current mesh running ms.current_mesh_id().
I don’t know if it could help, but should be more accountable than comparing coordinates :slight_smile: