Skip to main content
1 of 5

Scripting - fast separation by vertex count or area

I make a terrain (ground + vegetation mesh) for an old game engine which needs all the geometry to be separated into chunks consisting of ~ 25k vertices max.

So far I use my "separate by radius" script which enters Edit mode, select faces in a given radius, separate using bpy.ops.mesh.separate(type='SELECTED') and continues until it is separated completely.

This is the original script:

import bpy
import bmesh
import time

class SeparateByRadius(bpy.types.Operator):
    bl_idname = "object.separate_by_radius"
    bl_label = "Separate by Radius"
    bl_options = {'REGISTER', 'UNDO'}
    
    max_diameter = bpy.props.IntProperty(name="Radius", default=0)

    def execute(self, context):
        
        if self.max_diameter == 0:
             return {'FINISHED'}
             
        context = bpy.context
        scene = context.scene

        bpy.ops.object.mode_set(mode="EDIT")

        obj = bpy.context.edit_object
        me = obj.data

        bm = bmesh.from_edit_mesh(me)
        
        time_start_updating = time.time()
        
        def separateNextChunk():
            
            bm.faces.ensure_lookup_table()
            
            if len(bm.faces) == 0:
                bpy.ops.object.mode_set(mode="OBJECT")
                bpy.ops.object.select_all(action='DESELECT')
                context.active_object.select = True
                bpy.ops.object.delete()
            else:
                active_median = bm.faces[-1].calc_center_median()

                continue_next = False

                for f in bm.faces:
                    f.select = False
                    if (f.calc_center_median()-active_median).length <= self.max_diameter / 2:
                        f.select = True
                        continue_next = True


                bpy.ops.mesh.separate(type='SELECTED')
                # Show the updates in the viewport
                # and recalculate n-gon tessellation.
                bmesh.update_edit_mesh(me, True)
                
                if continue_next == True:
                    separateNextChunk()

        separateNextChunk()
        
        print("Separated in " + str(time.time() - time_start_updating))

        return {'FINISHED'}

def register():
    bpy.utils.register_class(SeparateByRadius)


def unregister():
    bpy.utils.unregister_class(SeparateByRadius)

if __name__ == "__main__":
    register()

While it does the job, it is EXTREMELY slow - the splitting often results into ~2000 objects and this take hours to separate using this method. I suspect the reason is that we are doing modifications of the original (large) mesh with each single separation.

I have tried to find some alternative methods that would create new objects from the original one, but as I am not a Python programmer, it started to be too complex for me.

I would appreciate so much if anyone can share some specific ideas how to enhance the script to get it work noticeably faster with the same output, the requirements are:

  • separating into chunks of a given max vertex count or given area
  • separation must be based on the existing mesh, no cutting
  • UVs, materials and vertex colors must be preserved perfectly

enter image description here