2
$\begingroup$

I'd like to add a mirror modifier to each object of a certain collection.

The problem is that the modifier will be only added to the last object which even gets the same modifier several times and I don't know why:

 def add_empty_collection(self):
    for obj in bpy.data.collections['my_collection'].all_objects:
        print("obj_: ", obj)
        bpy.ops.object.select_all(action='DESELECT') # Deselecting all
        obj.select_set(True)
        bpy.ops.object.modifier_add(type='MIRROR')
        bpy.context.object.modifiers["Mirror"].use_axis[0] = False
        bpy.context.object.modifiers["Mirror"].use_axis[1] = True
        bpy.context.object.modifiers["Mirror"].use_axis[2] = False
        bpy.context.object.modifiers["Mirror"].mirror_object = bpy.data.objects["target"]

Q: How to add a mirror modifier to all objects of a certain collection?

$\endgroup$
3
$\begingroup$

You're better off using bpy.data.objects to manipulate them. This way you don't need to rely on what is selected, all you need is the objects name.

import bpy

mod_name = 'My-Mirror-Modifier'

for obj in bpy.data.collections["My-Collection"].all_objects:
    if obj.type == 'MESH':
        mod = obj.modifiers.new(mod_name, 'MIRROR')
        mod.use_axis[0] = False
        mod.use_axis[1] = True
        mod.use_axis[2] = False
        mod.mirror_object = bpy.data.objects["Light"]
$\endgroup$
2
  • 3
    $\begingroup$ As you have obj from the loop, you don't need to go to bpy.data.objects, use obj directly. Also you'd better get the modifier in a variable mat = obj.modifiers.new(...) and use mat after that (name given as parameter could result in name.001 for instance). $\endgroup$
    – lemon
    Nov 29 '20 at 7:15
  • 1
    $\begingroup$ @lemon is absolutely right (as usual :) thanks lemon) I've updated my answer with his recommendations. (assigning the result of the modifier creation to a variable is new to me, it's good to know!) $\endgroup$
    – Psyonic
    Nov 29 '20 at 7:38
2
$\begingroup$

For operators use context.

Given the self in the question code, making the assumption you are writing an operator

Further to the answer of @RoufirHassan by way of explanation, looping over the collection objects de-selecting and selecting single, was not changing the active object. Adding context.object to print statement would confirm this.

Is the active object and zeroth selected object always the same?

to fix set the active object to the loop object before calling operator.

context.view_layer.objects.active = obj

Blender 2.8 API, python, set active object

However an issue here would be scripts have a tendency to slow down horribly if looping a lot of operators. The operator run count using amended question code would be 2 x number of objects in collection.

Modifier add operator adds to the end of modifier stack

if using the modifiers add operator the newly added modifier will be

context.object.modifiers[-1]

ie the last one. Once again in blender using name can be dodgy, just like with objects adding another with same name will not have the same name.

Prime candidate for KeyError

Never use bpy.data.whatevers["Foo"] unless you are certain "Foo" exists. Where it can have value None Also recommend using scene objects, as if the object in bpy.data.objects is not linked to scene it likely wont work as expected.

target = context.scene.objects.get("Foo")

or use the context. In this case for example, the collection can be context collection (last selected in outliner) and the modifier target can be context.object

Overriding context

Can pass a context dictionary to operators. Below is method explained by @RoufirHassan using override. Note: this will add all modifiers of first mesh object in collection (including newly added mirror) to all other mesh objects in collection, except to the mirror target if it is a mesh object in same collection

This will run only 2 operators no matter how many objects are in the collection.

import bpy

context = bpy.context

coll = context.collection
ob = context.object

mesh_obs = [o for o in coll.all_objects 
        if o.type == 'MESH'
        and o is not ob]


if mesh_obs:
    ao = mesh_obs.pop(0)
    bpy.ops.object.modifier_add(
            {"object" : ao},
            type='MIRROR'
            )
    mod = ao.modifiers[-1]
    mod.use_axis = (True, False, True)
    mod.mirror_object = ob
    bpy.ops.object.make_links_data(
            {"object" : ao,
             "selected_editable_objects": mesh_obs
             },
             type='MODIFIERS',
             )

API methods.

Finally would recommend using the API method as suggested in answer of @Pysonic, except use the context to set collection and modifier target.

$\endgroup$
1
$\begingroup$

Select all the meshes in your collection and add a mirror modifier.

But as you said it will only mirror the active object ( active object = last object selected with different colored outline). So in order to mirror every single object just type (Ctrl+L) > modifiers. It will link all objects with the same modifier.

You can use it for any other modifiers.

$\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.