3
$\begingroup$

I have a bmesh holding some geometry. I store pointers to some of the vertices because I need them later. Then I add a sphere using bmesh.ops.create_icosphere. Afterward, when accessing the previously stored vertices, I get:

ReferenceError: BMesh data of type BMVert has been removed

Since I am adding new geometry, I expected the old geometry to remain valid. Does the method switch to object mode and back internally?

Here's a minimal working example showing the issue. To test, select the default cube, then run the script. It will show that all original verts, stored in orig_verts, are dead.

import bpy
import bmesh

# Get the active mesh
me = bpy.context.object.data

# Get a BMesh representation
bm = bmesh.new()   # create an empty BMesh
bm.from_mesh(me)   # fill it in from a Mesh

# Keep a list of the original verts:
orig_verts = [v for v in bm.verts]

bmesh.ops.create_icosphere(
    bm,
    diameter=3,
    subdivisions=2 )
    
for v in orig_verts:
    print("orig_vert", v, v.is_valid)

for v in bm.verts:
    print("new bm verts:", v, v.is_valid)

# Finish up, write the bmesh back to the mesh
bm.to_mesh(me)
bm.free()

Output for the standard cube:

orig_vert <BMVert dead at 0x7f27d6bf4db0> False
orig_vert <BMVert dead at 0x7f27d6bf4f60> False
orig_vert <BMVert dead at 0x7f27d6bf4d20> False
orig_vert <BMVert dead at 0x7f27d6bf4d50> False
orig_vert <BMVert dead at 0x7f27d6bf4cc0> False
orig_vert <BMVert dead at 0x7f27d6bf4ea0> False
orig_vert <BMVert dead at 0x7f27d6bf4de0> False
orig_vert <BMVert dead at 0x7f27d6bf4810> False
new bm verts: <BMVert(0x7f27d41f8650), index=0> True
new bm verts: <BMVert(0x7f27d41f8688), index=1> True
new bm verts: <BMVert(0x7f27d41f86c0), index=2> True
...

Using version 2.93.4.

Update: When I set subdivisons to a number lower than 2, it works! However, I'd like to have more subdivisions...

Update2: Note that my goal is to manipulate certain original vertices (let's say vertex 2) after adding a sphere. Note also that my real mesh is far more complex than what I have mentioned here, so storing vertices is really the best way to go AFAIK, however as mentioned above, the pointers point to invalid dead vertices after adding more geometry. I've also noticed similar behavior with other bmesh.ops (for example bmesh.ops.subdivide_edges).

A possible workaround might be to find the new vertex at the position of the original vertex. Ugly, though...

$\endgroup$
1
  • $\begingroup$ So your original vertices change their indices as well? Otherwise a better workaround would be to store their indices rather than positions. $\endgroup$ Apr 8, 2022 at 20:33

1 Answer 1

2
$\begingroup$

To answer your exact question: bmesh doesn't switch between object and edit mode, but its operations tend to copy the original data to a new data structure and then delete the original data.

However, if you really do want the original verts, you can make a copy of the bmesh before the operation. Here is your original code, modified to show the copy and to contain the screen update:

import bpy
import bmesh

# Get the active mesh
me = bpy.context.object.data

# Get a BMesh representation
bm = bmesh.new()   # create an empty BMesh
bm.from_mesh(me)   # fill it in from a Mesh
bm2 = bm.copy()

# Keep a list of the original verts:
bm.verts.ensure_lookup_table()
orig_verts = [v for v in bm.verts]
copy_verts = [v for v in bm2.verts]

bmesh.ops.create_icosphere(
    bm,
    diameter=3,
    subdivisions=2 )
    
for v in orig_verts:
    print("orig_vert", v, v.is_valid)

for v in copy_verts:
    print("copy_vert", v, v.is_valid)

for v in bm.verts:
    print("new bm verts:", v, v.is_valid)

# Finish up, write the bmesh back to the mesh
bm.to_mesh(me)
bm.free()

bpy.context.view_layer.update()
$\endgroup$
9
  • $\begingroup$ Thank you, but I was in object mode, so object.mode == "OBJECT" would return True and the exact same code would be executed as in your example (the parts in the 'if', not the 'else')...? I've also tried enforcing EDIT mode instead, same result. Interestingly, when I reduce the number of subdivisions to less than 2, it works (original vertices remain alive), but I need 2 subdivisions at least... $\endgroup$ Apr 7, 2022 at 20:18
  • $\begingroup$ @Germanunkol I've updated my answer. Please comment if it's not working for you. $\endgroup$ Apr 8, 2022 at 15:36
  • 1
    $\begingroup$ @Germanunkol I covered your specific problem in the first paragraph. You can't store the vertices because Blender changes the data structures out from under you. The best you an do is create a copy bmesh and refer to it. $\endgroup$ Apr 8, 2022 at 19:01
  • 1
    $\begingroup$ @Germanunkol Yeah, I'm sorry there's no way to preserve the original data; but the workaround gives you the starting point for doing the search by coordinates. $\endgroup$ Apr 8, 2022 at 19:09
  • 1
    $\begingroup$ True, but I think I'll just store the original coordinates instead. Should be enough for my use-case and saves me having to create and pass around a copy of the full mesh. $\endgroup$ Apr 8, 2022 at 19:12

You must log in to answer this question.

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