1
$\begingroup$

What I want

I have two sets of objects and want to intersect every object of one set with every object of the other set by means of the boolean modifier. The output can be reduced to the vertices of intersection for every object pair which I write into an ascii file.

The problem

When executing the script, the execution time for the first iterations is low, however, it takes forever with further iterations. The problem seems to be well known (see here and here; and related to bad programming/use of blender api) when using many bpy.ops. which causes updating the whole scene every time.

But in my understanding this updating should not make things worse when the number of objects is low for every update. That's why I delete every object and mesh except for the two objects to intersect with each other.

I guess the problem is that not everything of the object is deleted even though I delete all objects and meshes after each iteration and the remaining part will then be updated with an increasing number of "something". This may be confirmed by the continously increasing memory Blender needs.

The question

What kind of items does remain to cause the increase of computation time (and memory) although I delete all objects and meshes (len(list(bpy.data.meshes)) and len(list(bpy.data.objects)) is zero) after every iteration?

Theoretically it should be working if restarting blender after each iteration, but this will cause a large overhead for startup. Maybe there is something like reset scene?

For the purpose of deletion I use the following code:

def deleteObj(obj):
    bpy.ops.object.select_all(action='DESELECT')
    obj.select = True
    bpy.ops.object.delete(use_global = True)

and

meshes = list(bpy.data.meshes)
for me in meshes:
    me.user_clear()
    bpy.data.meshes.remove(me)

For the intersection I use:

def getIntersection (obj1, obj2):
    bpy.ops.object.select_all(action='DESELECT')
    obj1.select = True
    bpy.context.scene.objects.active = obj1
    mod = obj1.modifiers.new('Boolean', type='BOOLEAN')
    mod.object = obj2
    mod.operation = 'INTERSECT'
    bpy.ops.object.modifier_apply(apply_as='DATA', modifier = mod.name)

Edit Added minimised code:

# read-in functions
# set parameters for objects creation
# open file for output
for i in R1:
    obj1 = createObj(Parameters1, i)
    for j in R2:
        obj2 = createObj(Parameters2, j)
        mesh = getIntersection(obj1, obj2)
        deleteObj(obj2)
        Verts = list(mesh.vertices)
        if len(Verts) > 0:
            writeVerticesToFiles(file, mesh)
        bpy.data.meshes.remove(mesh)
    deleteObj(obj1)
    meshes = list(bpy.data.meshes)
    for me in meshes:
        me.user_clear()
        bpy.data.meshes.remove(me)
$\endgroup$
1
$\begingroup$

As you note rightly, do not use bpy.ops if you can avoid it because it forces a scene update every single time.

So I recommend doing this:

me = obj.to_mesh(bpy.context.scene, True, "PREVIEW", calc_tessface=True, calc_undeformed=False)

No operator is used here and it will give you a copy of the the mesh data with all modifiers applied.

Deletion of objects from blender or with ops still keeps their data around until you close and reopen the file. Then data blocks that are not used (users = 0) will be discarded. IIRC, you can force-delete objects completely via API, but this is not recommended as it tends to cause crashes.

You can, however, achieve the same result as with the operator using:

bpy.data.objects.remove(obj, True)
$\endgroup$
  • $\begingroup$ Your suggestion impressively speeded up me script by a factor of 2, but still, each iteration needs more time. For deleting objects I still need bpy.ops., don't I, and for the boolean modifier I still need two objects (not only meshes)? $\endgroup$ – Phann Jan 26 '18 at 13:35
  • $\begingroup$ Nice! How about skipping the deletion of objects altogether? Alternatively, get rid of the ops - see: blender.stackexchange.com/questions/27234/… $\endgroup$ – HENDRIX Jan 26 '18 at 13:39
  • $\begingroup$ I managed to get rid of all bpy.ops (except one to deselect all) which gave me another 20% improvement. Unfortunately the time still increases from initial 1s to 10s after 20 iterations, and I need something about 1000 iterations which will still be impossible. My workaround would be to restart blender after a couple of iterations and continue with a reseted instance. $\endgroup$ – Phann Jan 26 '18 at 13:54
  • $\begingroup$ Can you post more / all of your code? There might be some costly loops or something you could avoid. $\endgroup$ – HENDRIX Jan 26 '18 at 14:02
  • 1
    $\begingroup$ Yup, that's what I would suggest. Currently you create all R2 objects again and again for each R1. Create each object only once, then in the nested for loop, just set the links between the existing objects and get a mesh data with applied modifiers. Note that you don't even have to do list(mesh.vertices), just iterate over them directly. After you have finished processing the applied mesh copy, you can delete it with del if you don't want to store it in blender. All this should make it a lot more efficient in the end. $\endgroup$ – HENDRIX Jan 26 '18 at 14:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.