0
$\begingroup$

I have a collection of over 2000 small meshes. I want to automatically have them joined in case meshes touch each other. So, after running python code, I want to end up with fewer meshes.

I tried the following script (chatGPT):

import bpy

# Get a list of all the meshes in the scene
meshes = bpy.data.meshes

# Loop through each mesh
for mesh in meshes:

    # Create a list of all the other meshes that are touching this mesh
    touching_meshes = []
    for other_mesh in meshes:
        if mesh != other_mesh:
            for edge in mesh.edges:
                if edge.vertices[0] in other_mesh.vertices or edge.vertices[1] in other_mesh.vertices:
                    touching_meshes.append(other_mesh)
                    break

    # Join this mesh with all the other touching meshes
    for other_mesh in touching_meshes:
        bool_modifier = mesh.modifiers.new('bool', 'BOOLEAN')
        bool_modifier.object = bpy.data.objects[str(other_mesh.name)]
        bool_modifier.operation = 'UNION'
        bpy.ops.object.modifier_apply(modifier=bool_modifier.name)

but I get this error :

Python: Traceback (most recent call last): File "C:\Users\bartdh\AppData\Local\Temp\7zOC3093EA1\TsD0209-2.blend\Text.001", line 14, in TypeError: bpy_prop_collection.contains: expected a string or a tuple of strings

Thank you for any help.

New contributor
Bart De Hantsetters is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
$\endgroup$
4
  • 4
    $\begingroup$ GPT chat is barely reliable yet so shouldn't be used. And I can't see the bpy specified in the message in the code you provided. $\endgroup$ Mar 19 at 9:16
  • $\begingroup$ Do you just want to join them, or do you really want a boolean operation between the ones that overlap? $\endgroup$
    – lemon
    Mar 19 at 11:38
  • 1
    $\begingroup$ chatgpt wont be useful if you have no programming knowledge as you need to guide the AI to your solution which will take several follow up questions and understanding of what solution it was trying to propose. $\endgroup$ Mar 19 at 14:26
  • $\begingroup$ is the title of your question what you asked to gpt? this can be done but not (or too complex) for close non convex meshes. $\endgroup$
    – lemon
    Mar 19 at 18:59

1 Answer 1

1
$\begingroup$

This will operate on all selected objects (see the commented line if you want to operate on everything in a collection instead).

import bpy
import bmesh
from mathutils.bvhtree import BVHTree

if bpy.context.mode != 'OBJECT':
    bpy.ops.object.mode_set(mode='OBJECT')

# Use all selected objects
obs = list(bpy.context.selected_objects)
# Uncomment this to use all objects from a collection instead
#obs = list(bpy.data.collections["Collection"].objects)

obs = [ob for ob in obs if ob.type == 'MESH']

# Calculate BVHTrees for each object
# Adjust epsilon to change how strict "is touching" is
trees = []
for ob in obs:
    # Can't use BVHTree.FromObject, it doesn't use world space, see
    # devtalk.blender.org/t/bvhtree-fromobject-blender-2-8-wonkiness
    # Instead convert to BMesh and use that.
    bm = bmesh.new()
    bm.from_mesh(ob.data)  # Note: this is the unevaluated mesh
    bm.transform(ob.matrix_world)
    trees.append(BVHTree.FromBMesh(bm, epsilon=0.0))
    bm.free()

# Compute pairs (i,j) where obs[i] touches obs[j]
intersections = []
for i in range(0, len(trees)):
    for j in range(i + 1, len(trees)):
        if trees[i].overlap(trees[j]):
            intersections.append((i, j))
    trees[i] = None  # can GC this now

# Group into connected pieces
pieces = []
for i, j in intersections:
    pi, pj = None, None
    for index, piece in enumerate(pieces):
        if i in piece: pi = index
        if j in piece: pj = index
    if pi is None and pj is None:
        pieces.append(set([i, j]))
    elif pi is None:
        pieces[pj].add(i)
    elif pj is None:
        pieces[pi].add(j)
    elif pi != pj:
        pieces[pi].update(pieces[pj])
        del pieces[pj]

# Join all objects in each piece
for piece in pieces:
    bpy.ops.object.select_all(action='DESELECT')
    for i in piece:
        obs[i].select_set(True)
    for i in piece:
        bpy.context.view_layer.objects.active = obs[i]
        break
    bpy.ops.object.join()

Example showing touching cubes being joined into one object

$\endgroup$
3
  • $\begingroup$ This is super. Your code is performing exactly what I need. Many thanks $\endgroup$ Mar 20 at 10:24
  • $\begingroup$ @BartDeHantsetters, you should accept this answer and upvote it as you said "Your code is performing exactly what I need. Many thanks". Thanks $\endgroup$
    – lemon
    Mar 21 at 19:23
  • $\begingroup$ scurest, hi, your solution does not take evaluated meshes into account. Though, if it does, how do you imagine the "join part" to do it correctly (I mean, if objects have modifers, the join part as it is is hazardous). $\endgroup$
    – lemon
    Mar 21 at 19:26

Your Answer

Bart De Hantsetters is a new contributor. Be nice, and check out our Code of Conduct.

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.