Creating Mesh from Python performances issues - Ops vs data?

Hi all!

I am new to Blender and after taking a look at modeling, physics, and particles, I experienced quickly with geometry nodes and quickly decided to go for python scripting.

My initial goal was to generate buildings to make a small city skyline.
I understand that there are probably a lot of existing plugins for that, but thought that it was one simple way to learn.

I built such a script and this kind of work, however, I have a few (probably stupid) questions.

If this helps, my current script is available at https://gist.github.com/tiry/dc76dd16f2549d92ad20030e70df0ac0

Speed:

The execution of the script is painfully slow.
Basically, running my script to generate a few city blocks would take hours whereas the code is fairly simple.

My current guess is that my code is not optimized and especially, I am using operators that probably include a lot of overhead (like Undo, Refresh, Messaging …).

Is there something I can do to speed up the process?

Creating primitives:

I am currently using operators to create cubes and cylinders.

bpy.ops.mesh.primitive_cube_add(size=1, enter_editmode=False, align='WORLD', location=(x, y, h/2+dh), scale=(w, l, h))

My understanding is that I could use the “data” API and directly create mesh objects without using the operators.

However, from what I was able to see, this implies that I need to pre-initialize a data structure with the vertex, edges, and faces.

Am I missing something?

If my python script must generate all the low-level vertex/edge/faces: should I just work on having an external python script generate the mesh in Collada format?

Operators:

My current understanding is that I should avoid using operators for generating mesh because it introduces some overhead and a dependency on the UI (like what collection is selected when running the script).

Is this correct? Should I avoid using operators?

I will appreciate any advice pointing me in the right direction.
Thanks in advance.

Tiry

Yes, never use operators (except for bmesh.ops) unless absolutely necessary. Using them is very rarely necessary.
Use bmesh to create your mesh datablocks and create the objects to host them in the scene through bpy.data.

Thank you !

Moving from operator to data API: 5 buildings generation that took 9 minutes now takes 13s

Old Code

def mkCube(x,y,z,w,l,h):   
    bpy.ops.mesh.primitive_cube_add(size=1, enter_editmode=False, align='WORLD', location=(x, y,z), scale=(w, l, h))
    return bpy.context.selected_objects[0]

New Code

def mkCube(x,y,z,w,l,h,rx=1,ry=1):   

    verts = [
    (x-w/2,y-l/2,z-h/2), (x-w/2,y+l/2,z-h/2), (x+w/2,y+l/2,z-h/2), (x+w/2,y-l/2,z-h/2),
    (x-rx*w/2,y-ry*l/2,z+h/2), (x-rx*w/2,y+ry*l/2,z+h/2), (x+rx*w/2,y+ry*l/2,z+h/2), (x+rx*w/2,y-ry*l/2,z+h/2)]
    faces = [(0,1,2,3), (4,5,6,7), (0,4,5,1), (1,5,6,2), (2,6,7,3), (3,7,4,0)]
     
    mesh = bpy.data.meshes.new("Cube_mesh")
    mesh.from_pydata(verts,[],faces)
    mesh.update(calc_edges=True)

    obj = bpy.data.objects.new("Cube", mesh)   
    bpy.context.scene.collection.objects.link(obj)
    return obj

As long as I am working with Cube this is simple enough, if I want to play with torus, I may find the work of defining vertices and faces a little boring.
Is there a help lib I missed for that ?

There are some addons that ship with Blender that add extra primitive types to the “add” menu, if you peek at those python files you’ll probably have just about all the code you could want to create anything from springs to gears and everything in between.

for bmesh, there are a variety of primitive_add functions for bmesh.ops. There are built-ins for grids/planes, cubes, spheres, circles (and with 4 sides becomes a square), cones, and bizarrely enough- suzanne. no torus, however.