0
$\begingroup$

I've asked this question but couldn't get any answer, so I decided I'll post it again, trying to be more specific.

Let’s assume I have an active object in the scene. The object itself is just a couple of vertical faces but it has a solidify modifier, a hook modifier attached to some of its vertices and a boolean modifier (subtracting part of it with another object). It looks like this: enter image description here The script is supposed to:
- Make a copy of selected object with its modifiers
- In that copy apply some modifiers and remove others
- Remove most of the object leaving only some edges (namely the lower ones) and extrude them
- Name the modified copy
The script looks like this:

import bpy
import bmesh
from mathutils import Vector

context = bpy.context
ob = context.object 

src_verts =[]
bottom_verts =[]
ext_edges =[]

for obj in [o for o in bpy.context.scene.objects if o.type == 'MESH' and o.name == ob.name + ' kondygnacje_podziemne']:
    bpy.data.objects.remove(obj, do_unlink=True)

count_ob = float(len(ob.data.vertices))
center_ob = (1.0 / count_ob) * sum([v.co for v in ob.data.vertices], Vector())

#getting lower vertices of object:
for v in [v for v in ob.data.vertices if v.co[2] < center_ob[2]]:

    src_verts.append(v.index)


#make a copy of object to alter it after applying and removing certain modifiers:      
podz = ob.copy()
podz.data = ob.data.copy()
bpy.context.scene.collection.objects.link(podz)
ob.select_set(False)

context.view_layer.objects.active = podz

#before creating bmesh, apply existing hook and solidify modifiers and remove boolean modifiers:
for mod in [m for m in podz.modifiers if m.type == 'HOOK']:
    #print(podz.name)
    bpy.ops.object.modifier_apply( modifier = mod.name )
for mod in [m for m in podz.modifiers if m.type == 'BOOLEAN']:
    bpy.ops.object.modifier_remove( modifier = mod.name )
for mod in [m for m in podz.modifiers if m.type == 'SOLIDIFY']:
    bpy.ops.object.modifier_apply( modifier = mod.name )
#create bmesh from 'podz' after removing/applying modifiers:
bm_podz = bmesh.new() 
dg = context.evaluated_depsgraph_get()

bm_podz.from_object(podz, dg)

#make a list of verts to delete
wyk=[]
for v in [v for v in bm_podz.verts if v.index not in src_verts]:

    wyk.append(v)

bmesh.ops.delete(bm_podz, geom = wyk, context = 'VERTS')

# get the remaining edges to extrude:
bm_podz.edges.ensure_lookup_table() 
for e in bm_podz.edges:
    ext_edges.append(e)

#extrude edges:

added_e = bmesh.ops.extrude_edge_only(bm_podz, edges = ext_edges)

print(len([e for e in bm_podz.edges if isinstance(e, bmesh.types.BMEdge)]))
extruded = [v for v in added_e['geom'] if isinstance(v, bmesh.types.BMVert)]
# move extruded vertices to desired position:
bmesh.ops.translate(bm_podz, verts=extruded, vec=(0.0, 0.0,-3.0))

#update mesh:
bm_podz.to_mesh(podz.data)

name = ob.name + 'spam'
podz.name = name

bm_podz.free()

del src_verts
del ext_edges
del bottom_verts
del wyk

when it is run from blender text editor it works as intended, creating this (a "belt" along lower edges of object): enter image description here however when I use exactly the same script as a function in addon (to simplify here I paste a simple "addon" with just that one function triggered with boolean switch in object properties panel):

import bpy
import bmesh
from mathutils import Vector



def cz_podz(self, context):
    context = bpy.context
    ob = context.object 

    src_verts =[]
    bottom_verts =[]
    ext_edges =[]

    for obj in [o for o in bpy.context.scene.objects if o.type == 'MESH' and o.name == ob.name + ' kondygnacje_podziemne']:
        bpy.data.objects.remove(obj, do_unlink=True)

    count_ob = float(len(ob.data.vertices))
    center_ob = (1.0 / count_ob) * sum([v.co for v in ob.data.vertices], Vector())

    #getting lower vertices of object:
    for v in [v for v in ob.data.vertices if v.co[2] < center_ob[2]]:

        src_verts.append(v.index)


    #make a copy of object to alter it after applying and removing certain modifiers:      
    podz = ob.copy()
    podz.data = ob.data.copy()
    bpy.context.scene.collection.objects.link(podz)
    ob.select_set(False)

    context.view_layer.objects.active = podz

    #before creating bmesh, apply existing hook and solidify modifiers and remove boolean   modifiers:
    for mod in [m for m in podz.modifiers if m.type == 'HOOK']:
        #print(podz.name)
        bpy.ops.object.modifier_apply( modifier = mod.name )
    for mod in [m for m in podz.modifiers if m.type == 'BOOLEAN']:
        bpy.ops.object.modifier_remove( modifier = mod.name )
    for mod in [m for m in podz.modifiers if m.type == 'SOLIDIFY']:
        bpy.ops.object.modifier_apply( modifier = mod.name )
    #create bmesh from 'podz' after removing/applying modifiers:
    bm_podz = bmesh.new() 
    dg = context.evaluated_depsgraph_get()

    bm_podz.from_object(podz, dg)

    #make a list of verts to delete
    wyk=[]
    for v in [v for v in bm_podz.verts if v.index not in src_verts]:

        wyk.append(v)

    bmesh.ops.delete(bm_podz, geom = wyk, context = 'VERTS')

    # get the remaining edges to extrude:
    bm_podz.edges.ensure_lookup_table() 
    for e in bm_podz.edges:
        ext_edges.append(e)

    #extrude edges:

    added_e = bmesh.ops.extrude_edge_only(bm_podz, edges = ext_edges)

    print(len([e for e in bm_podz.edges if isinstance(e, bmesh.types.BMEdge)]))
    extruded = [v for v in added_e['geom'] if isinstance(v, bmesh.types.BMVert)]
    # move extruded vertices to desired position:
    bmesh.ops.translate(bm_podz, verts=extruded, vec=(0.0, 0.0,-3.0))

    #update mesh:
    bm_podz.to_mesh(podz.data)

    name = ob.name + 'spam'
    podz.name = name

    bm_podz.free()

    del src_verts
    del ext_edges
    del bottom_verts
    del wyk


bpy.types.Object.myBool4 = bpy.props.BoolProperty(name="boolean_switch", default=False, update=cz_podz) 


class ParametryZab_Panel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Spam"
    bl_idname = "OBJECT_PT_"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        layout = self.layout
        scene = context.scene
        obj = context.object

        row = layout.row()
        row.prop(obj, "myBool4", toggle = True)


def register():
    bpy.utils.register_class(ParametryZab_Panel)

def unregister():
    bpy.utils.unregister_class(ParametryZab_Panel)

if __name__ == "__main__":
    register()

the result is entirely different: enter image description here It seems like when the script is run from the "addon", it applies/removes modifiers of the original object, not its copy, while when running standalone from text editor it works ok. It's possibly something embarrassingly obvious but I have no idea why that happens.
I'd appreciate if someone could explain that.
Thank You.

$\endgroup$
16
  • $\begingroup$ maybe remove the line context=bpy.context and use the given context. Whats in that context can be controlled somehow, you would need to google though $\endgroup$
    – HenrikD
    Aug 29, 2019 at 22:02
  • $\begingroup$ hmmm, thank you for taking the time to answer. I'll look into that although TBH it's not really clear why would that be a reason considering the context is exactly the same when script is run as a standalone script, and works fine ? $\endgroup$
    – MatH
    Aug 30, 2019 at 5:16
  • $\begingroup$ Possible issue: using vert indices from a mesh and compare them with the indices of another one. Also, I don't get why your are applying the modifiers as finally bm_podz.from_object(podz, dg) does it automatically (using dg)? And also, the order you apply the modifiers does not match the ones shown in the picture. $\endgroup$
    – lemon
    Aug 30, 2019 at 9:12
  • $\begingroup$ Hi, lemon, thank you for taking a look. As for Indices - i'll investigate but that issue would appear also when the script is run directly from text editor, wouldn't it? I didn't go with bm.from_object method, because I wanted to feed bmesh with a copy having some modifiers applied and some removed. As for the order - frankly I didn't realise that matters but again, shouldn't it work wrong way also in the script itself when it is not embedded in add-on? What happens is - when the script is standalone, the modifiers are applied/removed in podz object (as intended), in "addon" to source object. $\endgroup$
    – MatH
    Aug 30, 2019 at 10:52
  • $\begingroup$ Just to clarify - I did use from_object as one can tell, I just didn't use it at the begining, for reasons mentioned above. I guess the order of dealing with modifiers, although important, doesn't matter here, since the first script works as expected. I think the issue is that when the script is in "addon" it applies/rmoves modifiers in ob.. (the source object) despite context.view_layer.objects.active = podz. But I don't understand why. $\endgroup$
    – MatH
    Aug 30, 2019 at 12:33

1 Answer 1

2
$\begingroup$

The issue seems to be link to the context which does not allow:

context.view_layer.objects.active = podz

But you can:

override = { "object": podz, "active_object": podz }

for mod in [m for m in podz.modifiers if m.type == 'HOOK']:
    bpy.ops.object.modifier_apply( override, modifier = mod.name )
for mod in [m for m in podz.modifiers if m.type == 'BOOLEAN']:
    bpy.ops.object.modifier_remove( override, modifier = mod.name )
for mod in [m for m in podz.modifiers if m.type == 'SOLIDIFY']:
    bpy.ops.object.modifier_apply( override, modifier = mod.name )

Don't know if that can be considered as a 'general case'...

$\endgroup$
3
  • $\begingroup$ that did the trick. Didn't know you can do that. Still don't know why the context does not allow context.view_layer.objects.active = podz - I checked and the line does change podz status to active correctly. But still bpy.ops.object.modifier_remove/apply is applied to source object. You think this might be a bug? Anyway, I learned a lot, thank you very much. $\endgroup$
    – MatH
    Aug 31, 2019 at 17:04
  • $\begingroup$ The hint was effectively the original object looses its modifiers. So I conclude context.view_layer.objects.active = podz did not do the job. And context issues "happen sometimes"... $\endgroup$
    – lemon
    Aug 31, 2019 at 17:08
  • $\begingroup$ Yes, but it seemed to do the job - I printed out active object after that line and it was the new object. So what fails IMHO is bpy.ops.object.modifier_remove/apply which is somehow not reading context.view_layer.objects.active = podz or indeed the line itself is not changing dependencies in the scene correctly. $\endgroup$
    – MatH
    Aug 31, 2019 at 17:15

You must log in to answer this question.

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