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$
  • $\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 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 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 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 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 at 12:33
1
$\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$
  • $\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 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 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 at 17:15

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.