If you're reading this, we've just migrated servers! If anything looks broken please email dion@thinkmoult.com :)

Blender: selecting all the objects of (2) or more materials


A nice way to select all of the objects that share that material.
Is there way, however, to select (2) objects that have (2) materials and do something like this to select all the objects in the scene that share at least one of these (2) materials?

Comments

  • There is no way to do it with built-in tools AFAIK. Here's a self-contained script that you can run from the text editor and easily install as an addon by simply saving it as a .py and running it with the addon installer.
    It adds a menu entry in the Select Menu. You can add it to your quick favorites or add a shortcut afterwards.

    import bpy
    
    
    def get_materials(self, context):
        return [(mat.name,) * 3 for mat in bpy.data.materials if not mat.is_grease_pencil]
    
    
    class MaterialPropertyGroup(bpy.types.PropertyGroup):
        name: bpy.props.EnumProperty(items=get_materials)
    
    
    
    def update_materials(self, context):
        while len(self.materials) < self.materials_search:
            self.materials.add()
    
    class OBJECT_OT_SelectSharedMaterials(bpy.types.Operator):
        bl_idname = "object.select_shared_materials"
        bl_label = "Select Shared Materials"
    
        materials: bpy.props.CollectionProperty(type=MaterialPropertyGroup)
        materials_search: bpy.props.IntProperty(update=update_materials, default=2, name="Materials", min=1)
    
        def invoke(self, context, event):
            self.materials_search = max(2, self.materials_search)
            return context.window_manager.invoke_props_dialog(self)
    
        def execute(self, context):
            materials_to_search = set()
            for i in range(self.materials_search):
                materials_to_search.add(self.materials[i].name)
            for o in context.visible_objects:
                material_slots_names = {m.name for m in o.material_slots}
                contains_required_materials = len(material_slots_names.intersection(materials_to_search)) == self.materials_search
                o.select_set(contains_required_materials)
    
            return {'FINISHED'}
    
        def draw(self, context):
            self.layout.prop(self, "materials_search")
            for i in range(self.materials_search):
                self.layout.prop(self.materials[i], "name", text=f"Mat {i}")
    
    
    def menu_func(self, context):
        self.layout.operator(OBJECT_OT_SelectSharedMaterials.bl_idname, text=OBJECT_OT_SelectSharedMaterials.bl_label)
    
    # Register and add to the "object" menu (required to also use F3 search "Simple Object Operator" for quick access)
    def register():
        bpy.utils.register_class(MaterialPropertyGroup)
        bpy.utils.register_class(OBJECT_OT_SelectSharedMaterials)
        bpy.types.VIEW3D_MT_select_object.append(menu_func)
    
    
    def unregister():
        bpy.utils.unregister_class(OBJECT_OT_SelectSharedMaterials)
        bpy.utils.unregister_class(MaterialPropertyGroup)
        bpy.types.VIEW3D_MT_select_object.remove(menu_func)
    
    
    if __name__ == "__main__":
        register()
    
    NigeltlangCoentheoryshaw
  • Is there way, however, to select (2) objects that have (2) materials and do something like this to select all the objects in the scene that share at least one of these (2) materials?

    You could also use BlenderBIM spreadsheet writer for this.

    Nigel
  • Very cool. Would it be relatively easy to auto-populate the following input fields with materials from selected objects in the scene?

  • edited February 2022

    Sure ! Here's a different version that selects all objects which have at least the same materials as the active object :

    There's is no confirmation dialog anymore though.

    import bpy
    
    
    class OBJECT_OT_SelectSharedMaterials(bpy.types.Operator):
        bl_idname = "object.select_shared_materials"
        bl_label = "Select Shared Materials"
    
        @classmethod
        def poll(cls, context):
            return context.active_object and hasattr(context.active_object, "material_slots")
    
        def execute(self, context):
            materials_to_search = set(m_s.name for m_s in context.active_object.material_slots if m_s.material is not None)
            for o in context.visible_objects:
                o_materials = {m_s.name for m_s in o.material_slots if m_s.material is not None}
                o.select_set(materials_to_search.issubset(o_materials))
    
            return {'FINISHED'}
    
    
    def menu_func(self, context):
        self.layout.operator(OBJECT_OT_SelectSharedMaterials.bl_idname, text=OBJECT_OT_SelectSharedMaterials.bl_label)
    
    def register():
        bpy.utils.register_class(OBJECT_OT_SelectSharedMaterials)
        bpy.types.VIEW3D_MT_select_object.append(menu_func)
    
    
    def unregister():
        bpy.utils.unregister_class(OBJECT_OT_SelectSharedMaterials)
        bpy.types.VIEW3D_MT_select_object.remove(menu_func)
    
    
    if __name__ == "__main__":
        register()
    
    theoryshaw
  • Hi @Gorgious
    Might be doing something wrong, but the latest script doesn't seem to install...
    video: https://www.dropbox.com/s/wfoe5bxkewu87t5/2022-04-20_09-24-37.mp4?dl=0
    Also, when I run in manually, it seems to only select the materials of the most active object.
    video: https://www.dropbox.com/s/rkg4hd1i0nnvx61/2022-04-20_09-31-23_Blender_blender.mp4?dl=0

  • edited April 2022

    Alright @theoryshaw this script lacks a few lines of code that are mandatory for Blender to consider it as an add-on. See https://docs.blender.org/manual/en/latest/advanced/scripting/addon_tutorial.html#what-is-an-add-on
    Also it was designed to select only the materials of the active object, but with very minor tweaks we can change it to select shared materials of all selected objects, too.
    Lastly I stumbled upon a bug where the wrong subset of material names was tested.

    Here's the updated code :

    import bpy
    
    
    bl_info = {
        "name": "Select Shared Materials",
        "blender": (2, 80, 0),
        "category": "Object",
    }
    
    
    class OBJECT_OT_SelectSharedMaterials(bpy.types.Operator):
        bl_idname = "object.select_shared_materials"
        bl_label = "Select Shared Materials"
    
        @classmethod
        def poll(cls, context):
            return context.active_object and hasattr(context.active_object, "material_slots")
    
        def execute(self, context):
            materials_to_search = set()
            for obj in context.selected_objects:
                materials_to_search.update(m_s.name for m_s in obj.material_slots if m_s.material is not None)
            for o in context.visible_objects:
                if not o.material_slots:
                    continue
                o_materials = {m_s.name for m_s in o.material_slots if m_s.material is not None}
                o.select_set(o_materials.issubset(materials_to_search))
    
            return {'FINISHED'}
    
    
    def menu_func(self, context):
        self.layout.operator(OBJECT_OT_SelectSharedMaterials.bl_idname, text=OBJECT_OT_SelectSharedMaterials.bl_label)
    
    def register():
        bpy.utils.register_class(OBJECT_OT_SelectSharedMaterials)
        bpy.types.VIEW3D_MT_select_object.append(menu_func)
    
    
    def unregister():
        bpy.utils.unregister_class(OBJECT_OT_SelectSharedMaterials)
        bpy.types.VIEW3D_MT_select_object.remove(menu_func)
    
    
    if __name__ == "__main__":
        register()
    
  • edited April 2022

    Bingo! thanks so much. The only last tweak i would suggest is not selecting objects that don't have a material.
    video: https://www.dropbox.com/s/lcckozuxs7s6hd2/2022-04-20_10-36-08_Blender_blender.mp4?dl=0
    BTW, do you have an Ethereum address? I'd like to shoot you payment for your time.

  • @theoryshaw oohh alright I missed that during my testing ! I added a couple line of codes to my previous answer, you can copy/paste it :) Thanks for the kind offer but I have to decline, I already am lucky enough to be able to participate here during my work hours so I'm already compensated for that ;)
    Cheers

  • Wow this script is awesome!

    Out of curiosity, if you want to select by material, could the "Select by material" feature in the IFC Materials panel do what you're after? If there are two materials, semantically you might want to select all elements that use that material set?

Sign In or Register to comment.