Help needed: Boolean Union of many objects


(chrissie) #1

Hello, I have to Boolean Union ca. 150 objects. I have code (below) that works very well on lists of 10-20 selected objects. But if the object count is higher, then Blender calculates forever. Therefor I would like to split the 150-object-list into ca. 10 15-object-lists. Only a pretty small modification of the code below is needed - how can I do this best please? Many thanks!

Here is the code I have already:

##START “UNION OF SELECTED OBJECTS”:
sce = bpy.context.scene
myobjectlistlength = len(bpy.context.selected_objects)
bpy.context.selected_objects[0].name
myobjectlistlength
[B]for
index in range(0, myobjectlistlength):
print (index)
bpy.context.selected_objects[index].name

originalunionobject = sce.objects.get(bpy.context.selected_objects[0].name)
originalunionobject_name = bpy.context.selected_objects[0].name
mymodifier = originalunionobject.modifiers.new(‘mymodifier’, ‘BOOLEAN’)
mymodifier.operation = ‘UNION’
nextobject = sce.objects.get(bpy.context.selected_objects[1].name)
mymodifier.object = nextobject
bpy.context.scene.objects.active = originalunionobject
bpy.ops.object.modifier_apply(apply_as=‘DATA’, modifier=“mymodifier”)
sce.objects.unlink(nextobject)

for index in range(0, myobjectlistlength-2):
print (index)
sce = bpy.context.scene
##we always just retrieve the new next element with index 1, because the list gets
##shorter and shorter with each new union:
bpy.context.selected_objects[1].name
myobjectlistlength
index+1
mymodifier = originalunionobject.modifiers.new(‘mymodifier’, ‘BOOLEAN’)
mymodifier.operation = ‘UNION’
nextobject = sce.objects.get(bpy.context.selected_objects[1].name)
mymodifier.object = nextobject
bpy.context.scene.objects.active = originalunionobject
bpy.ops.object.modifier_apply(apply_as=‘DATA’, modifier=“mymodifier”)
sce.objects.unlink(nextobject)
##END “UNION OF SELECTED OBJECTS”[/B]


(batFINGER) #2

Hi chrissie,

to markup code in the forum wrap it in [noparse]


[/noparse] tags.

The object.join operator may be a simpler way to do this.


import bpy


# join them
bpy.ops.object.join()

afterwards you may wish to remove doubles with


bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles()


(Greg Zaal) #3

@batFINGER - he wants boolean union, not just simply joining objects.

@chrissie - without wraping the code in the tags batFINGER mentions, we cant tell where the indents are and thus cant help. Or you could upload to pasteall.org.

I’ve been wanting something like this for a long time, but been to lazy to try code it :slight_smile:


(batFINGER) #4

I’m aware of the difference @gregzaal.

I’m Not sure how much of an improvement would be achieved by splitting the list into smaller parts. If there is no intersection then join would be alot quicker to achieve the same result. Even changing the originally posted code with a simple bounding box intersection test to either join or add and apply modifier would speed it up IMO. Splitting the list into non intersecting sets, joining then using union would be my suggestion. Surely each and all of the 150 objects don’t intersect each other.


(Greg Zaal) #5

Great idea. Best would be to try join all the non-intersecting objects into just a few objects first, only then doing the boolean.


(tcdoe) #6

I too have this need.
What I have is a bunch (appx 200) of spheres and cylinders that I need to boolean together (like a molecule). Unfortunately I cannot join them and boolean them because they are randomly indexed.
here’s a picture (and a closeup) for example:



Sorry for the dreadful lighting. So each cylinder and each sphere are disconnected objects, and the cylinders “penetrate” into the spheres. I have tried some code snippets suggested by others but none comes close.

What I’m thinking is to somehow, starting with any randomly chosen sphere, to sequentially boolean every connecting cylinder, then proceed to iteratively repeat this process until everything’s connected. Unfortunately since i’m new to blender scripting and python i’m crazy stuck.

Any help appreciated.


(CoDEmanX) #7

hm… if there was a way to add vertices on edges at intersection points, one could do that and then select only the outside vertices, invert selection and delete verts :frowning:


(chrissie) #8

Hi! Here is what I created / used (“quick and dirty”, but it works for me):


<b>##START “UNION OF SELECTED OBJECTS”:
sce = bpy.context.scene
myobjectlistlength = len(bpy.context.selected_objects)
bpy.context.selected_objects[0].name
myobjectlistlength
[B]for</b> index <b>in</b> range(0, myobjectlistlength): 
  print (index)
   bpy.context.selected_objects[index].name

originalunionobject = sce.objects.get(bpy.context.selected_objects[0].name)
originalunionobject_name = bpy.context.selected_objects[0].name
mymodifier = originalunionobject.modifiers.new('mymodifier', 'BOOLEAN')
mymodifier.operation = 'UNION'
nextobject = sce.objects.get(bpy.context.selected_objects[1].name)
mymodifier.object = nextobject
bpy.context.scene.objects.active = originalunionobject
bpy.ops.object.modifier_apply(apply_as='DATA', modifier="mymodifier")
sce.objects.unlink(nextobject)

<b># for</b> index <b>in</b> range(0, 10):
<b>for</b> index <b>in</b> range(0, myobjectlistlength-2):  
   print (index)  
   sce = bpy.context.scene
##we always just retrieve the new next element with index 1, because the list gets
##shorter and shorter with each new union:
   bpy.context.selected_objects[1].name
   myobjectlistlength
   index+1
   mymodifier = originalunionobject.modifiers.new('mymodifier', 'BOOLEAN')
   mymodifier.operation = 'UNION'
   nextobject = sce.objects.get(bpy.context.selected_objects[1].name)
   mymodifier.object = nextobject
   bpy.context.scene.objects.active = originalunionobject
   bpy.ops.object.modifier_apply(apply_as='DATA', modifier="mymodifier")
   sce.objects.unlink(nextobject)
##END  “UNION OF SELECTED OBJECTS[/B]

You first select all objects to boolean union.
Then execute the code in the python console.

I once had a memory problem when I wanted to boolean union too many objects in one go.
I created a variant of the code which 1st unioned in chunks of 15, then unioned those.
Let me know if you need this.

Let me know if you have questions.


(milkyandy) #9

Ahoi,

got it triggered out how to Union boolean many objects:

#!BPY
“”"
Name: ‘Muli Boolean Union’
Blender: 242
Group: ‘Misc’
Tooltip: ‘trying’
“”"
from Blender import *
from Blender import Draw, Scene, Object
from Blender.Mathutils import Rand

runs through all objects and boolean:union them together

def supb(scn, sel):
i = 0
for objss in sel:
if i == 0:
objFirst = objss
i = 1
else: # i == 1
if i == 1:
objSecond = objss
i = 2

      # adding modifier  
      mods = objFirst.modifiers            # adding a modifier
      mod = mods.append(Modifier.Types.BOOLEAN) # with boolean
      mod[Modifier.Settings.OPERATION] = 1 # and union
     
      # doing the modifier
      mod[Modifier.Settings.OBJECT] = objSecond # adding the element to the modifier
      objFirst.makeDisplayList()  # doing the modifier
      
      # deleting the old two

scn.unlink( objFirst )

      scn.unlink( objSecond )
     else:
      i = 3

Window.RedrawAll() #??

def main():
scn = Scene.GetCurrent()
if not scn.objects.context:
return

sel = scn.objects.context
objects = [ob for ob in scn.objects.context]
Count = len(objects)

i = 0

while i < 10:

i = i + 1

f = Count
for i in range(0, f):
supb( scn, sel )
sel = scn.objects.context

Window.RedrawAll()

if name == ‘main’:
main()


also here is a script, which Looks good…was not working with me:

have fun


(raymondnijssen) #10

This question is old but maybe some people still want this and end up here.

A quite easy, non scripting solution is:

  1. join all meshes you want to union
  2. draw a large cube around it, that contains all your joined meshes.
  3. apply a boolean operator “intersect” on this cube and your joined mesh

Seems to work! :slight_smile:


(Ramon S) #11

raymondnijssen…you’re the best!!!

I haven’t tried it in complex figures but it seems to work perfectly!!! AWESOME


(a120471) #12

The idea is really perfect. And I just tried on some complex meshes… it seems that part of the object is missing.




I’ll try the script way soon, maybe it won’t work either.
:eek:


(a120471) #13

The script way is not working either. :frowning: Here’s my code, it maybe helpful to someone sometime.


loopCategoryNum = [50, 776]
loopCategory = ["ball", "cylinder"]
basePath = "E:/Coding/skeleton_code/skeleton_code/bin/Mesh/"


for categoryIndex in range(len(loopCategory)):
    loopIndex = int((loopCategoryNum[categoryIndex]-1)/50)
    for i in range(loopIndex+1):
        bpy.ops.import_scene.obj(filepath = basePath + loopCategory[categoryIndex] + str(i*50) + ".obj")
        para1 = bpy.data.objects[loopCategory[categoryIndex] + str(i*50)]
        bpy.context.scene.objects.active = para1
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.tris_convert_to_quads()
        bpy.ops.object.mode_set(mode='OBJECT')
        for j in range(i*50+1,i*50+50):
            if j&gt;=loopCategoryNum[categoryIndex]:
                break
            bpy.ops.import_scene.obj(filepath = basePath + loopCategory[categoryIndex] + str(j) + ".obj")
            para2 = bpy.data.objects[loopCategory[categoryIndex] + str(j)]
            bpy.context.scene.objects.active = para2
            bpy.ops.object.mode_set(mode='EDIT')
            bpy.ops.mesh.tris_convert_to_quads()
            bpy.ops.object.mode_set(mode='OBJECT')
            bpy.context.scene.objects.active = para1
            boolean_modifier = para1.modifiers.new('Name', 'BOOLEAN')
            boolean_modifier.operation = 'UNION'
            boolean_modifier.object = para2
            bpy.ops.object.modifier_apply(apply_as='DATA', modifier = 'Name')
            bpy.context.scene.objects.active = para2
            bpy.ops.object.delete()
        #if i!=0:
            #para2 = bpy.data.objects[loopCategory[categoryIndex] + str(i*50-50)]
            #bpy.context.scene.objects.active = para1
            #boolean_modifier = para1.modifiers.new('Name', 'BOOLEAN')
            #boolean_modifier.operation = 'UNION'
            #boolean_modifier.object = para2
            #bpy.ops.object.modifier_apply(apply_as='DATA', modifier = 'Name')
            #bpy.context.scene.objects.unlink(para2)

The commented code is not working, but I think the problems come from blender’s boolean operation itself.
(I try to manually combine those segments, blender(2.76) tells me it can not excute bool operation)

update: I come up with a new idea!!! We can first do the “difference boolean operation” between each pair (as the pre processing). To ensure every two segments are not collided.


(shocksofmighty) #14

I realize this is old, but I came across the thread when I need to do the same thing (for a ball and stick molecular model). Taking pieces from other scripts I came up with something that does exactly this. It uses the selected objects so if you start with something that has many objects (I’ve used it on structures with ~1200 objects imported as VRML2) it can be faster to select it in chunks instead of trying to do it all in one pass. Also, make sure there are no internal objects. PyMol stick representations always put out extra spheres at the end.


import bpy
import bmesh
from mathutils.bvhtree import BVHTree


def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False):
    """
    Returns a transformed, triangulated copy of the mesh
    """


    assert(obj.type == 'MESH')


    if apply_modifiers and obj.modifiers:
        me = obj.to_mesh(bpy.context.scene, True, 'PREVIEW', calc_tessface=False)
        bm = bmesh.new()
        bm.from_mesh(me)
        bpy.data.meshes.remove(me)
    else:
        me = obj.data
        if obj.mode == 'EDIT':
            bm_orig = bmesh.from_edit_mesh(me)
            bm = bm_orig.copy()
        else:
            bm = bmesh.new()
            bm.from_mesh(me)


    # Remove custom data layers to save memory
    for elem in (bm.faces, bm.edges, bm.verts, bm.loops):
        for layers_name in dir(elem.layers):
            if not layers_name.startswith("_"):
                layers = getattr(elem.layers, layers_name)
                for layer_name, layer in layers.items():
                    layers.remove(layer)


    if transform:
        bm.transform(obj.matrix_world)


    if triangulate:
        bmesh.ops.triangulate(bm, faces=bm.faces)


    return bm


def bmesh_check_intersect_objects(obj, obj2):
    """
    Check if any faces intersect with the other object


    returns a boolean
    """
    assert(obj != obj2)


    # Triangulate
    bm = bmesh_copy_from_object(obj, transform=True, triangulate=True)
    bm2 = bmesh_copy_from_object(obj2, transform=True, triangulate=True)
    intersect = False
    BMT1 = BVHTree.FromBMesh(bm)
    BMT2 = BVHTree.FromBMesh(bm2)
    overlap_pairs = BMT1.overlap(BMT2)
    #print (len(overlap_pairs))
    
    if len(overlap_pairs) &gt; 0:
       intersect = True
    
    return intersect


   


sce = bpy.context.scene


myobjectlistlength = len(bpy.context.selected_objects)
startlist = len(bpy.context.selected_objects)
i = 0




while i &lt; myobjectlistlength or myobjectlistlength &gt; 1:
  sce.update()
  obj1 = bpy.context.selected_objects[0]


  for obj2 in bpy.context.selected_objects:
        if obj1.name == obj2.name:
            continue
        #print(obj1.name)
        #print(obj2.name)
        intersect = bmesh_check_intersect_objects(obj1, obj2)


        if intersect:
           mymodifier = obj1.modifiers.new('mymodifier', 'BOOLEAN')
           mymodifier.operation = 'UNION'
           mymodifier.object = obj2
           bpy.context.scene.objects.active = obj1
           bpy.ops.object.convert(target='MESH')
           sce.objects.unlink(obj2) 
           myobjectlistlength = len(bpy.context.selected_objects)
           print("Objects left to merge: %s" % myobjectlistlength)
              
           
  i = i+1




(shocksofmighty) #15

This seemed like a great idea, but it didn’t work for me. The places where the once individual objects meet don’t result in joined vertices after the intersection. This had implications down the road for 3D printing.


(Greg Zaal) #16

There is now an add-on included in Blender by default (2.78): https://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.78/Add-ons#Object_Boolean_Tools

Or download it here: https://git.blender.org/gitweb/gitweb.cgi/blender-addons.git/blob_plain/HEAD:/object_boolean_tools.py


(shocksofmighty) #17

I actually tried this again, realizing that I hadn’t attempted it after removing the fully internal spheres that were in my object. After removing the internal spheres it worked great! Now I’m feeling silly or spending so much time writing that script.