2
$\begingroup$

So, I have an imported UE scene with many duplicated objects. Like. 1000 objects and 100-200 of them duplicated 2 or 3 times.

Now my idea was to use Python to compare coordinates and rotation and then delete the "spare" objects. We tried around a few hours but couldn't find any working resolution.

This is the last code we've got:

import bpy
selected = bpy.context.selected_objects
tmp = []
for ob in selected:
    if ob.location in tmp:
       bpy.data.objects.remove(ob)
    else:
       tmp.append(ob.location)

Blender tells me that the object can't be deleted because there are still users left. Including ob.user_clear() Makes Blender instantly crash.

Any ideas?

$\endgroup$
  • $\begingroup$ How about rather than actually deleting them in your script, add the objects to a group instead. You could then use the normal Blender UI to select that group and delete them that way instead as that might avoid the problem. $\endgroup$ – Rich Sedman Sep 7 '16 at 11:53
  • $\begingroup$ Just select them then use the bpy.ops function to delete them all. $\endgroup$ – JakeD Sep 7 '16 at 12:27
2
$\begingroup$

Enter this into the text editor, select all objects, then run. The duplicates should be removed...I would recommend to leave off the delete command, so that you can delete them manually with the X key (the rest of the script just selects objects in the same location).

import bpy

locations = list()

for obj in bpy.context.selected_objects:  # iterate over selection
    if obj.location not in locations:
        locations.append(obj.location)  # add to locations list
        obj.select = False  # deselect object

# your selection should now only be duplicates, so then delete (this could be done manually)
bpy.ops.object.delete(use_global=False)
$\endgroup$
2
$\begingroup$

Be wary, ob.location is a reference to objects data

Use a copy of an objects location to your tmp list tmp.append(ob.location.copy()), otherwise you are keeping a reference to an object you may* be deleting. (Which crashes blender). Will point out this isn't the case in the logic of question script

A simple script to demonstrate.

import bpy
ob = bpy.context.object
tmp = []
tmp.append(ob.location)
ob.location = (10, 10, 10)
print(tmp)

will always print [Vector((10.0, 10.0, 10.0))]

import bpy
from bpy import context

selected = context.selected_objects
scene = context.scene
tmp = []
for ob in selected:
    if ob.location in tmp:
       scene.objects.unlink(ob)
       # do_unlink to be sure to be sure
       bpy.data.objects.remove(ob, do_unlink=True)
    else:
       tmp.append(ob.location.copy())
  • Might also pay to check that (u-v).length < TOL where u is location of current obj, and v is each vector in the list, rather than trusting location in list. On a few tests with duped cube works ok without...

Script below uses tolerance on each objects matrix_world.translation for global location. It sorts the selected object list by this vector, rather than checking against another list every iteration.

import bpy

context = bpy.context

TOL = 0.00001
obs = sorted((o for o in context.selected_objects), key=lambda o: o.matrix_world.translation)
obs[0].select = False
for i in range(1, len(obs)):
    o = obs[i].matrix_world.translation
    p = obs[i-1].matrix_world.translation
    obs[i].select = (o - p).length < TOL
$\endgroup$
  • $\begingroup$ Your method works good! And I learned a bit :D. Although it can only remove one duplication at a time $\endgroup$ – X_Joshi_X Sep 7 '16 at 17:41

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.