1
$\begingroup$

I'm working on a script that will generate a mesh based on handles (empties) in the 3D interface. So when the user drags a handle it replaces the mesh data, but leaves the object intact. Here's part of the script:

try:
    obj = bpy.data.objects[name]
    old_mesh = obj.data
    
    # add  new mesh
    mesh = bpy.data.meshes.new(name)
    obj.data = mesh
    
    # remove old mesh
    removeMeshFromMemory(old_mesh)
    
except KeyError:
    # Create Object and link to scene
    mesh = bpy.data.meshes.new(name)
    obj = bpy.data.objects.new(name, mesh)
    scene = bpy.context.scene
    scene.collection.objects.link(obj)
    
mesh.from_pydata(verts, [], faces)

And the contents of the remove mesh function is here:

def removeMeshFromMemory(mesh):
    passedName = mesh.name
    print("removeMeshFromMemory:[%s]." % passedName)
    # Extra test because this can crash Blender if not done correctly.
    result = False
    #mesh = bpy.data.meshes.get(passedName)
    if mesh != None:
        if mesh.users == 0:
            try:
                mesh.user_clear()
                can_continue = True
            except:
                can_continue = False
            
            if can_continue == True:
                try:
                    bpy.data.meshes.remove(mesh)
                    result = True
                    print("removeMeshFromMemory: MESH [" + passedName + "] removed from memory.")
                except:
                    result = False
                    print("removeMeshFromMemory: FAILED to remove [" + passedName + "] from memory.")
            else:
                # Unable to clear users, something is holding a reference to it.
                # Can't risk removing. Favor leaving it in memory instead of risking a crash.
                print("removeMeshFromMemory: Unable to clear users for MESH, something is holding a reference to it.")
                result = False
        else:
            print ("removeMeshFromMemory: Unable to remove MESH because it still has [" + str(mesh.users) + "] users.")
    else:
        # We could not fetch it, it does not exist in memory, essentially removed.
        print("We could not fetch MESH [%s], it does not exist in memory, essentially removed." % passedName)
        result = True
    return result

When dragging the handle slowly for several seconds (replacing mesh 50+ times), Blender eventually crashes. It gives the following error:

Blender(61663,0x11de3cdc0) malloc: Incorrect checksum for freed object 0x7fc42fd82600: probably modified after being freed.
Corrupt value: 0xa80000026e
Blender(61663,0x11de3cdc0) malloc: *** set a breakpoint in malloc_error_break to debug

Is there a better way to replace the mesh data that's more efficient and won't cause crashes like this?

$\endgroup$

1 Answer 1

1
$\begingroup$

You can just use the operator orphans_purge to make sure orphan meshes get freed. I don't think the impact on performance would be noticeable unless you have a huge scene.

obj = bpy.data.objects.get(name)
mesh = bpy.data.meshes.new(name)
if obj:        
    obj.data = mesh        
    bpy.ops.outliner.orphans_purge()
else:
    obj = bpy.data.objects.new(name, mesh)
    bpy.context.scene.collection.objects.link(obj)

mesh.from_pydata(verts, [], faces)

Also, a few tips :

  • Avoid Try/Except when using dictionaries, prefer using get(key, default)
  • Prefer foo is not None to foo != None
  • Prefer if bar: to if bar == True:

In simple blender scripts you shouldn't have to use Try/Except unless you specifically know which kind of exception your code is going to throw, and even then you should be able to not use it in most cases.

$\endgroup$
2
  • $\begingroup$ Very helpful tips! But now I'm running into the old context problem with the orphans_purge call. The error was "Operator bpy.ops.outliner.orphans_purge.poll() failed, context is incorrect". Is there a way to override the context on that function? $\endgroup$
    – Joel L
    Commented Sep 1, 2020 at 11:00
  • $\begingroup$ Ok, I believe it's working now without crashes. The orphans_purge call needed the OUTLINER context, so I passed that into it, and then it errored because that function can't be called inside a driver because it updates the dependency graph in the middle of a depsgraph update. So to fix that I called it inside a depsgraph_update_post callback instead. $\endgroup$
    – Joel L
    Commented Sep 2, 2020 at 10:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .