3
$\begingroup$

I have a model with hundreds of mesh objects. Unfortunately I clicked shift+D to duplicate many and did not notice. Later after working a while, I was rendering, and noticed there are double meshes overlapping each other.

How can I find all duplicate meshes and delete them. I could do it manually, just clicking, but it would take a long time and if I miss something i will not notice.

Is there any option, or can anybody suggest to me, how can I find all duplicate mesh and delete them.

Model with all double (duplicate) mesh

Final Result enter image description here

enter image description here

$\endgroup$
  • $\begingroup$ I have not written any Python for Blender for a while, but it should be relatively easy to loop through each object in turn and output the number of vertices stopping when the number matches the mesh you are trying to locate, or just listing them. $\endgroup$ – rob Aug 8 '17 at 7:45
  • $\begingroup$ @rob any solution to find and delete all my duplicate mesh $\endgroup$ – atek Aug 8 '17 at 7:47
  • 1
    $\begingroup$ see blender.stackexchange.com/questions/13757/… $\endgroup$ – rob Aug 8 '17 at 8:14
  • $\begingroup$ @rob i did not get what u mean. Is that about mesh calculation. I need my all double mesh to be search and highlight so that i can delete it. Do to the double mesh my file is heavy. so quick way to find and delete i needed. Thanks for the link $\endgroup$ – atek Aug 8 '17 at 8:19
5
$\begingroup$

Following a BIG CLUE given by @rob I've slapped together a script.

Give each mesh object in selection a key faces_edges_verts, for instance the default cube would have a key "6_12_8", 6 faces, 12 edges, 8 verts. Then check whether the coordinates of global bounding box, of any other objects that have same counts, match.

If they do select those objects to mark them as possible dupes.

To run suggest: Select all in scene, run script from text editor, and see if those objects that remain selected are dupes.... or close enough to... and then if all good hit x

import bpy
from mathutils import Vector
context = bpy.context

scene = context.scene
TOL = 1.0e-4 # reduced tolerance.

# routine to text matches on bound box.
def test(ob1, ob2):
    # test if ob1 and ob2 which have same face/edge/vert count are same
    bbox1 = [ob1.matrix_world * Vector(b) for b in ob1.bound_box]
    bbox2 = [ob2.matrix_world * Vector(b) for b in ob2.bound_box]

    diffs = [v2 - v1 for  v1, v2 in zip(bbox1, bbox2)]    
    return all(d.length < TOL for d in diffs)
    #return sum(diffs, Vector()).length < TOL

obs = {}
keys = []
for ob in context.selected_objects:
    ob.select = False
    if ob.type != 'MESH':
        continue
    # make a key from faces, edges, verts
    mesh = ob.data
    key = "%d_%d_%d" % (len(mesh.polygons), len(mesh.edges), len(mesh.vertices))
    obs[ob.name] = key
    keys.append(key)

possible_dupes = set([x for x in keys if keys.count(x) > 1])
print("-" * 40)
while possible_dupes:
    p = possible_dupes.pop()
    dupes = sorted(name for name, value in obs.items() if value == p)
    while dupes:
        original = dupes.pop(0)
        original_ob = scene.objects.get(original)

        for dupe in dupes:

            dupe_ob = scene.objects.get(dupe)
            if dupe_ob.select:
                continue

            if test(original_ob, dupe_ob):
                dupe_ob.select = True
                scene.objects.active = dupe_ob
                print("possible dupe: ", dupe, " of original: ", original)

Result on your file. Dupe objects moved to layer 3 on left. Reduced file size from 91 to 47 meg on removal of dupes.

enter image description here

Updated for version 2.8

Some minor changes for the new API, notably ob.select_set() and ob.select_get() for setting and getting selection, and `@' for matrix / vector multiplication

import bpy
from mathutils import Vector
context = bpy.context

scene = context.scene
view_layer = context.view_layer
TOL = 1.0e-3 # reduced tolerance.

# routine to text matches on bound box.
def test(ob1, ob2):
    # test if ob1 and ob2 which have same face/edge/vert count are same
    bbox1 = [ob1.matrix_world @ Vector(b) for b in ob1.bound_box]
    bbox2 = [ob2.matrix_world @ Vector(b) for b in ob2.bound_box]

    diffs = [v1 - v2 for  v1, v2 in zip(bbox1, bbox2)]    
    return all(d.length < TOL for d in diffs)
    #return sum(diffs, Vector()).length < TOL

obs = {}
keys = []
for ob in context.selected_objects:
    ob.select_set(False)
    if ob.type != 'MESH':
        continue
    # make a key from faces, edges, verts
    mesh = ob.data
    key = "%d_%d_%d" % (len(mesh.polygons), len(mesh.edges), len(mesh.vertices))
    obs[ob.name] = key
    keys.append(key)

possible_dupes = set([x for x in keys if keys.count(x) > 1])
print("-" * 50)
while possible_dupes:
    p = possible_dupes.pop()
    dupes = sorted(name for name, value in obs.items() if value == p)
    while dupes:
        original_ob = scene.objects.get(dupes.pop(0))
        for dupe in dupes:

            dupe_ob = scene.objects.get(dupe)
            if dupe_ob.select_get():
                continue

            if test(original_ob, dupe_ob):
                dupe_ob.select_set(True)
                view_layer.objects.active = dupe_ob
                print("possible dupe: ", dupe, " of original: ", original_ob.name)
$\endgroup$
  • $\begingroup$ Thanks for the reply and the script. I have paste the script and run some double it remove and some it remove original also the result is in the above in the question section. $\endgroup$ – atek Aug 8 '17 at 10:23
  • $\begingroup$ more than 300Mb $\endgroup$ – atek Aug 8 '17 at 11:11
  • $\begingroup$ new update script still i think not working. It is selected but confuse which to delete. If i delete the left then the door panel is not selected in the other side. pasteall.org/pic/index.php?id=117849 $\endgroup$ – atek Aug 8 '17 at 13:17
  • $\begingroup$ i have attach the file have u done experiment on it $\endgroup$ – atek Aug 8 '17 at 15:26
  • 1
    $\begingroup$ Thanks for your beautiful script. When your script it help me to finish my model above is my final render. $\endgroup$ – atek Aug 9 '17 at 18:08

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.