Creating mesh with python - performance issue

I’ve built a script that I need in my tool chain to load in meshes from a text file. It works nicely but it is significantly slower than loading in a blend file or using other default importers. I’ve tried to figure out what is the bottle neck in my approach without any luck. Except I know it’s not reading the text file, it’s somewhere in the mesh creation (snippet below).
Any hints how to improve the performance are greatly welcome.

def read_mesh(  collection,
    import bmesh, bpy
    empty_object = 'ParentEmpty', None )
    mesh_data =
    mesh_data.from_pydata(vertices, [], faces)
    scene_object =, mesh_data)
    scene_object.parent = empty_object
    for poly in
        poly.use_smooth = True
    bm =
    uv_layer = bm.loops.layers.uv.verify()
    for face in bm.faces:
        for loop in face.loops:
            luv = loop[uv_layer]
            luv.uv = tuple(text_coords)
    return empty_object

To profile different parts of a function use perf_counter from the time module.

from time import perf_counter

def function():
    t = perf_counter()

    # Code to measure between.

    print(round((perf_counter() - t) * 1000, 3), "ms")

Then move the t and print statement around to check different parts of the function.

Hard to tell what exactly is slowing your script down without an example, but as a general rule you should avoid doing loops. Blender can generate meshes incredibly fast with foreach methods since they operate directly on C arrays, or linked lists, which is still faster than through rna/python.

    for poly in
        poly.use_smooth = True

can be done like:"use_smooth", [1] * len(

Not understanding how you’re doing the uv loop. Looking at the code, seems you’re assigning the same text_coords to every uv coord in mesh loop. Not sure why you’d want that, but in either case better to ditch the bmesh and use foreach method on mesh_data to assign uvs.

1 Like

Thanks a lot!
That foreach tip is great. I’ll give it a go.
I was using the bmesh because I was struggling to assign uv coords otherwise. Need to study a bit more. Any further hint how to do that specifically. That bm.loops.layers.uv.verify() does some magic I’m not quite sure I understand how to do otherwise.
And I can see where I lost you with the uv loop. I accidentally deleted the indexing in the luv.uv = tuple(text_coords) while simplifying the snippet for posting. text_coords is a list of lists and it should be something like this luv.uv = tuple(text_coords[loop.vert.index])

I tried replacing the loop with the foreach but it doesn’t seem to work.
Results in this:

Error: Array length mismatch (got -1, expected more)
TypeError: object of type 'bool' has no len()
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\temp\"", line 106, in my_import"use_smooth", True)
TypeError: couldn't access the py sequence```

Edit, my bad. Didn’t know how to use that foreach_set properly. Obviously had to provide a list Trues.
Maybe there’s more sophisticated way to give that “true” argument, but this already did a noticeable improvement. Not a leap but still faster. thanks

That’s completely my bad :stuck_out_tongue:

It should have been:"use_smooth", [1] * len(

The verify method essentially creates a uv layer if it doesn’t exist and returns a layer access object, essentially a dictionary key. Bmesh lightly wraps the underlying C data in nothing more than skeleton bindings (that’s why bmesh.from_edit_mesh() is ridiculously fast, it’s just wrapping the current edit mesh struct), and a custom layer access object is a part of that less-overhead paradigm.

Uv coordinates should be sequenced the same way as mesh loops.
First you need to create a uv map if it doesn’t exist.

uv =
if uv is None:
    uv ="UVMap")

I’m assuming text_coords is a 2d sequence. In which case you’re better off putting it into a numpy array and let it flatten the thing before using foreach.

import numpy as np

arr = np.array(text_coords, "f")"uv", arr.ravel())
1 Like