0
$\begingroup$

I am doing a bisection with the blender API (i.e. python, not the GUI), but it seems that the region of the mesh that's cut by the plane isn't filled with a polygon (and there's also no N-gon there: I tried to select the face for triangulation, but no luck. And it also renders as a hole).

for ob in new_objects:
    Objcounter+=1
    obj = bpy.data.objects[ob]
    if obj.type == 'EMPTY':
      print("At "+str(Objcounter)+"/"+str(len(new_objects))+" Object is empty, skipping")
    if obj.type != 'EMPTY':
      bpy.context.view_layer.objects.active = None
      bpy.ops.object.select_all(action='DESELECT')
      print("At "+str(Objcounter)+"/"+str(len(new_objects))+" Now bisecting: "+ob)
      #from help
      #bpy.ops.mesh.bisect(plane_co=(0.0, 0.0, 0.0), plane_no=(0.0, 0.0, 0.0), use_fill=False, clear_inner=False, clear_outer=False, threshold=0.0001, xstart=0, xend=0, ystart=0, yend=0, flip=False, cursor=5)
      bpy.context.view_layer.objects.active = obj
      bpy.data.objects[obj.name].select_set(True)
      bpy.ops.object.editmode_toggle()
      bpy.ops.mesh.bisect(plane_co=(0.0, 0.0, 0.0), plane_no=(1.0, 0.0, 0.0), use_fill=True, clear_inner=False, clear_outer=True, threshold=0.0001, xstart=0, xend=0, ystart=0, yend=0, flip=False, cursor=5)
      bpy.ops.object.editmode_toggle()
      #close faces
      triangulate_object(obj)

Noting that there was already an attempt at triangulation (hoping this was an N-gon -> triangle issue), but no luck. The full python script is here and the gltf being imported is here, and no matter what I tried, I cannot close the geometries... I would really be very grateful for a helping hand! Many thanks in advance!

$\endgroup$
2
  • $\begingroup$ Playing around with the geometry that I am using, the 'fill' option doesn't operate well with it even in the GUI, trying to do the bisection there - but it works fine with impromptu primitives. I can only surmise that there's something wrong with the geometry I am trying to bisect, but I cannot figure out exactly what... $\endgroup$ Sep 7, 2022 at 12:17
  • $\begingroup$ So, it seems that using a much costlier boolean operation works fine, but it seems a bit excessive and it's very computationally expensive (as I'll still have to take care of further 5M volumes in this geometry). Booleans sometimes lead to more complicated consequences, so I'd like to avoid those if possible :-P $\endgroup$ Sep 7, 2022 at 12:56

1 Answer 1

1
$\begingroup$

I've recently just had the same problem, the trick for me was to use bmesh.ops.triangle_fill

Collect the edges after bpy.ops.mesh.bisect and use them as the edges= in bmesh.ops.triangle_fill

EDIT

Added code for this to your code.
Also, you should learn to work in the bmesh ops as often as you can, it's much faster and more flexible, although more dificult to learn it has many benefits

EDIT 2

The following code is your full script with the added clean up using mesh.ops.remove_doubles (being that the imported file scale is SO huge, you may need to increase the dist=0.5 value) and face filling using bmesh.ops.triangle_fill

import bpy
import csv
import bmesh
from mathutils import Vector

def triangulate_object(obj):
    me = obj.data
    # Get a BMesh representation
    bm = bmesh.new()
    bm.from_mesh(me)

    bmesh.ops.triangulate(bm, faces=bm.faces[:])
    # V2.79 : bmesh.ops.triangulate(bm, faces=bm.faces[:], quad_method=0, ngon_method=0)

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


### Function to do cutouts
def SubtractObjects(name_a, name_b, new_one_name):
    # Create a boolean modifier named 'my_bool_mod' for the cube.
    mod_bool =  name_a.modifiers.new('my_bool_mod', 'BOOLEAN')
    # Set the mode of the modifier to DIFFERENCE.
    mod_bool.operation = 'DIFFERENCE' #'DIFFERENCE'
    # Set the object to be used by the modifier.
    mod_bool.object = name_b
    bpy.context.view_layer.objects.active = name_a
    # Apply the modifier.
    res = bpy.ops.object.modifier_apply(modifier = 'my_bool_mod')

    #name_b.select_set(True) # 2.8+
    #bpy.ops.object.delete()
    name_a.name = new_one_name
    return res

###Make cutout object
def MakeCutoutObject(objname):
    bpy.ops.mesh.primitive_cube_add(size=20.0, calc_uvs=True, enter_editmode=False, align='WORLD', location=(+10.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0))
    obj = bpy.context.active_object
    obj.name = objname
    return obj

#print("Scale change in blender")
#oldscale = bpy.context.scene.unit_settings.scale_length
#bpy.context.scene.unit_settings.scale_length = 100

print("Starting import")
prior_objects = [object.name for object in bpy.context.scene.objects]
prior_objects_master = [object.name for object in bpy.context.scene.objects if not object.parent]

#import objects
bpy.ops.import_scene.gltf(filepath='0_L3MO_converted.gltf')

new_current_objects_master = [object.name for object in bpy.context.scene.objects if not object.parent]
new_objects_master = set(new_current_objects_master)-set(prior_objects_master)

new_current_objects = [object.name for object in bpy.context.scene.objects]
new_objects = set(new_current_objects)-set(prior_objects)

##set scale
#print("change scale back to normal")
#bpy.context.scene.unit_settings.scale_length = oldscale
#setState0()

bpy.ops.object.select_all(action='DESELECT')
Objcounter = 0
for ob in new_objects:
    Objcounter+=1
    obj = bpy.data.objects[ob]
    if obj.type == 'EMPTY':
      print("At "+str(Objcounter)+"/"+str(len(new_objects))+" Object is empty, skipping")
    if obj.type != 'EMPTY':
        bpy.context.view_layer.objects.active = None
        bpy.ops.object.select_all(action='DESELECT')
        print("At "+str(Objcounter)+"/"+str(len(new_objects))+" Now bisecting: "+ob)
        #from help
        #bpy.ops.mesh.bisect(plane_co=(0.0, 0.0, 0.0), plane_no=(0.0, 0.0, 0.0), use_fill=False, clear_inner=False, clear_outer=False, threshold=0.0001, xstart=0, xend=0, ystart=0, yend=0, flip=False, cursor=5)
        bpy.context.view_layer.objects.active = obj
        bpy.data.objects[obj.name].select_set(True)
#        bpy.ops.mesh.remove_doubles(threshold=0.5, use_unselected=True, use_sharp_edge_from_normals=False)
        bpy.ops.object.editmode_toggle()

        ### NEW CODE START ###

        # crerate new bmesh object
        bm_bisect = bmesh.from_edit_mesh(obj.data)
        bmesh.ops.remove_doubles(bm_bisect, verts=bm_bisect.verts, dist=0.5)
        # Concatenate the mesh's vertices, edges and faces into a single list
        geom_0 = list(bm_bisect.verts) + list(bm_bisect.edges) + list(bm_bisect.faces)
        # Bisect the mesh
        ret = bmesh.ops.bisect_plane(bm_bisect, geom=geom_0, dist=0.1, plane_co=(0.0, 0.0, 0.0), plane_no=(1.0, 0.0, 0.0), use_snap_center=False, clear_inner=False, clear_outer=True)
        # Retrieve only the edges from the returned geometry
        edge_list = [edges for edges in ret["geom_cut"] if isinstance(edges, bmesh.types.BMEdge)]
        del ret
        # fill the faces
        bmesh.ops.triangle_fill(bm_bisect, edges=edge_list)
        # update the original objects mesh data
        bmesh.update_edit_mesh(obj.data)
        # remove the bmesh object
        bm_bisect.free()

        ### NEW CODE END ###
         
        bpy.ops.object.editmode_toggle()
          #close faces
        #triangulate_object(obj)
      
    #rename master
for ob in new_objects_master:
    print("Now dealing with object: "+ob)
    obj = bpy.data.objects[ob]
    obj.name = "0_L3MO"
    
##remove cutout afterwards
#bpy.ops.object.select_all(action='DESELECT')
#cutout.select_set(True) # 2.8+
#bpy.ops.object.delete()
    
print("Will now save import")
bpy.ops.wm.save_mainfile()

This has the added benefit of leaving any internal edges (holes) in the cut plane unfilled

$\endgroup$
6
  • $\begingroup$ Thanks!! Can you give me a full example? I am trying but I am getting an error of: Error: Python: Traceback (most recent call last): File "/Users/daviddc/Dropbox/archive/aliceproject/magnet/ImportAndSave.py", line 83, in <module> edge_list = [edges for edges in ret[obj.name] if isinstance(edges, bmesh.types.BMEdge)] TypeError: 'set' object is not subscriptable ... I must have misinterpreted something here! :-( $\endgroup$ Sep 7, 2022 at 17:05
  • $\begingroup$ So, I noticed you use bisect_plane instead of my bpy.ops.mesh.bisect. I adapted to that, but now I am getting a weird TypeError: bmesh operators expect a single BMesh positional argument, all other args must be keywords error. The arguments I use are: ret = bmesh.ops.bisect_plane(bm, geom=geom0, dist=0.0000, plane_co=(0, 0, 0), plane_no=(1, 0, 0), use_snap_center=False, clear_inner=True, clear_outer=False) and bm and geom0 are: bm = obj.data and geom0 = bm.edges. I must still be missing something stupid... $\endgroup$ Sep 7, 2022 at 17:27
  • $\begingroup$ Oh, my bad... I didn't notice you are using bpy.ops Sorry.... My example is for using bmesh. I'll update my answer using your code above to make a bmesh, do the bisect then return it to a real mesh $\endgroup$
    – Psyonic
    Sep 8, 2022 at 7:14
  • $\begingroup$ Thanks, @psyonic! However, my mesh isn't closing with this solution (it runs and everything, but no luck with getting the cut plane caps)... I am also trying via the blender interface itself and not managing. I will keep trying ... $\endgroup$ Sep 8, 2022 at 8:39
  • 1
    $\begingroup$ Super thanks!!! That worked!!! Sadly, I am now facing some other issues. I will try to solve those myself but since they're unrelated (even if it might be the fault of the rather bad gltf generation sofware I use, which I did not write), I will write another question if I don't sort it out myself. Thanks again!! $\endgroup$ Sep 9, 2022 at 12:24

You must log in to answer this question.

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