6
$\begingroup$

I have use case where I need split a large mesh into small pieces. When I select and separate each of the desired pieces separately, the consolidation of the indices of the large mesh (i.e. building new packed vertex and face lists) takes alot of time. The consolidation is not really necessary in my use case, as I will be splitting off more pieces right away. If I mark all the different split regions using different materials and separate by material, the runtime suggest to me that this approach is also splitting of the regions piece by piece and consolidating the larger mesh in each iteration. So what is the most efficient way to tackle this problem?

$\endgroup$
2
$\begingroup$

This can be done by separating the big mesh into small meshes using the below script:

import bpy
ob = bpy.data.objects['MyMesh'] #### replace MyMesh with the name of your Mesh object
bpy.ops.object.select_all(action='DESELECT')
ob.select = True
bpy.context.scene.objects.active = ob
bpy.ops.mesh.separate(type='LOOSE')

UPDATE

If the object has no loose parts and you know the list of faces to separate then you can loop on them then separate using selection using the script below which I tested and it is working:

import bpy
ob = bpy.data.objects['MyMesh'] #### replace MyMesh with the name of your Mesh object
bpy.ops.object.select_all(action='DESELECT')
ob.select = True
bpy.context.scene.objects.active = ob
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
faces_to_separate = [0,2,4,6,8,10] #### add the list of faces you want to separate
mesh = ob.data
bpy.ops.object.mode_set(mode='OBJECT')
for face in mesh.polygons:
    if face.index in faces_to_separate:
        face.select = True
bpy.ops.object.mode_set(mode='EDIT')        
bpy.ops.mesh.separate(type='SELECTED')
bpy.ops.object.mode_set(mode='OBJECT')
$\endgroup$
  • $\begingroup$ @ideasman42 sorry, I updated my answer. $\endgroup$ – Tak Feb 6 '17 at 2:29
  • 1
    $\begingroup$ This wont work if ob is hidden, or its layer isn't visible - better to pass in a custom context. $\endgroup$ – ideasman42 Feb 6 '17 at 2:37
  • $\begingroup$ I don't understand your suggestion. Just to clarify: The input mesh has no loose elements, it's a connected mesh that is to be split in specific parts. I know the indices of the faces that go into the split meshes and right now assign different materials to those faces and separate using the 'MATERIAL' type. $\endgroup$ – DrPepperJo Feb 8 '17 at 12:36
  • $\begingroup$ @DrPepperJo answer updated to separate using a list of faces $\endgroup$ – Tak Feb 8 '17 at 23:32
  • $\begingroup$ Thanks, this is exactly what I do, but as I mention in the question, this is highly inefficient, as the source mesh structure is consolidated in each iteration. As mentioned above, this also seems to happen if I asign materials instead of selecting and separating all parts in one run using the 'MATERIAL' type in the separate function. $\endgroup$ – DrPepperJo Feb 13 '17 at 23:58
2
$\begingroup$

The general case can get pretty messy, but if you just want to copy geometry and materials you can start from the code at http://web.purplefrog.com/~thoth/blender/python-cookbook/split-copy-mesh.html . It splits the mesh according to the material. It avoids bpy.ops and instead uses mesh.from_python to build the geometry of each fragment from a subset of the original.

Here is a copy of the relevant section:

import bpy
from math import *
from mathutils import *


def split_copy_mesh(obj, discriminant):

    accumulator = {}

    for polygon in obj.data.polygons:
        tag = discriminant(polygon)

        acc = accumulator.get(tag)
        if acc is None:
            acc = {
                "tag":tag,
                "verts":[],
                "faces":[],
                "materials":[],
                "vertMap": {}
            }
            accumulator[tag] = acc

        face = []
        for vi in polygon.vertices:
            vi2 = acc["vertMap"].get(vi)
            if vi2 is None:
                verts = acc["verts"]
                vi2 = len( verts )
                acc["vertMap"][vi] = vi2
                verts.append(obj.data.vertices[vi].co)
            face.append(vi2)
        acc["faces"].append(face)
        acc["materials"].append(polygon.material_index)

    rval = []
    for tag,acc in accumulator.items():
        print(tag)

        mesh = bpy.data.meshes.new(tag)
        #print(acc)
        mesh.from_pydata(acc["verts"], [], acc["faces"])
        mesh.validate(True)

        for mat in obj.data.materials:
            mesh.materials.append(mat)

        for i in range(len(mesh.polygons)):
            mesh.polygons[i].material_index = acc["materials"][i]

        obj = bpy.data.objects.new(tag, mesh)
        bpy.context.scene.objects.link(obj)
        rval.append(obj)

    return rval

#fabricate_test_mesh(bpy.context.scene, 3, 8)
copies = split_copy_mesh(bpy.data.objects['multisphere'], lambda p: "m%d"%p.material_index)

for obj in copies:
    obj.location = (5,0,0)

If you want to preserve modifiers or uv maps, or shape keys or other information, you will have to add more code to copy this information from the original mesh.

$\endgroup$

Your Answer

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.