How can I join multiple objects automatically in groups determined by how far away they are from each other?
Make a Grid of view3d region and join via Border Select

Made an operator that splits the 3d view into an i x j grid. Joins all the meshes in each grid sector using border select.  Note this is a 2D approach, and assumes that looking at your level in top view and assigning a grid will suffice.  If your objects are spread more randomly in all three dimensions each joined selection would look more like a column.

After running script below, find in view3d &gt; object &gt; Grid Join menu.



Did have the View All operator in the operator, but decided it may be better to let user choose view, and zoom into all objects wishes to join.


Divide visible screen area into a grid with 16 x segments, and 9 y segments and join those selected by border select in each.

import bpy
from bpy.props import IntProperty

class SimpleOperator(bpy.types.Operator):
    """Join objects based on View3d Grid"""
    bl_idname = "view3d.grid_join"
    bl_label = "Grid Join"
    bl_options = {'REGISTER', 'UNDO'}
    x_segments = IntProperty(default=16, min=1)
    y_segments = IntProperty(default=9, min=1)
    @classmethod
    def poll(cls, context):
        scene = context.scene
        return len([o for o in scene.objects if o.type == 'MESH'])

    def execute(self, context):
        scene = context.scene
        joined_obs = []
        bpy.ops.object.select_by_type(type='MESH')
        #bpy.ops.view3d.view_all()
        region = context.region
        print(region.width, region.height)
        w = region.width / self.x_segments
        h = region.height / self.y_segments
        for i in range(self.x_segments):
            xmin, xmax  = i * w, (i + 1) * w
            for j in range(self.y_segments):                
                ymin, ymax  = j * h, (j + 1) * h
                #print(xmin, xmax, ymin, ymax)
                bpy.ops.view3d.select_border(
                       xmin=xmin, 
                       xmax=xmax,
                       ymin=ymin,
                       ymax=ymax,
                       deselect=False,
                       extend=False)
                for o in context.selected_objects:
                    o.select = o.type == 'MESH'
                if len(context.selected_objects):
                    scene.objects.active = context.selected_objects[0]
                    bpy.ops.object.join()
                    context.object.hide = True
                    joined_obs.append(context.object)

        for o in joined_obs:
            o.hide = False
        return {'FINISHED'}

def menu_draw(self, context):
    self.layout.operator("view3d.grid_join")

def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.types.VIEW3D_MT_object.prepend(menu_draw)

def unregister():
    bpy.utils.unregister_class(SimpleOperator)
    bpy.types.VIEW3D_MT_object.remove(menu_draw)

if __name__ == "__main__":
    register()

    # test call