Creating local override of Material of linked Library

Hello,

Im currently trying to write a small script that allows me to replace specific materials of objects, which where imported using the linked library method. Basically, i have 1 master file which contains links to a handful of other .bpy files.
Now im trying to automate the process of making a local override of a material used in one of the linked libraries, in order to be able to manually change the color of the overwritten material right inside the master file.

Here is the manual process on how i currently would to that task

Now my problem is, that i cant find any bpy functions,that do exactly that. The only things i can find are the outliner functions, of which i cant define which objects are exactly affected, and the second thing is bpy.ops.object.make_override_library(). But that function isnt really doing the same things as the process i showed aboth.

Does anyone know which functions i could call in order to automate the steps i showed aboth?

Thanks in advance :slight_smile:

I did the following steps to make object make_override_library and then make local.
But I don’t know how to select a material on the Outliner.

  1. the user selects the target object.
  2. execute modal operator (state transition with timer)
    a. execute bpy.ops.object.make_override_library()
    b. show target objects
      obj.hide_select = False
      obj.hide_viewport = False
      obj.hide_set(False)
      context.view_layer.objects.active = obj
      bpy.ops.outliner.show_active()
    
    c. select target objects
      obj.select_set(True)
    
    d. execute bpy.ops.outliner.id_operation(outliner_context, type='LOCAL')

The following document explains why you need the modal operator:
https://docs.blender.org/api/current/info_gotcha.html#can-i-redraw-during-script-execution

Here is the code for reference:

1 Like

Thanks for the reply.

Thanks to your input and the github link you send, Ive got something to work for my usecase :slight_smile:

The main things that did make it work was this function and a very precise way of choosing which objects of an imported collection should be selected in order to allow for the material override to be effective.

	bpy.ops.object.make_local(type='SELECT_OBDATA_MATERIAL')

This function makes a local override of the material of a selected object. The selected object MUST be a mesh that has a material object. If it doesnt, it will not work.

Here is my new algorithm that does the job well now. it itterates over each view layer, and creates a local material overrite for every library object that has a material defined:

for viewLayer in bpy.context.scene.view_layers:
    print("Processing ViewLayer: "+ viewLayer.name)
    for layer in viewLayer.layer_collection.children:
        print("\tProcessing Collection: "+ layer.name)

        # Make sure layer is not excluded from the current viewlayer
        layer.exclude = False

        objectsToOverride = []
        for obj in layer.collection.all_objects:
            # only select library object that have materials. 
            # Objects that already have a local override dont have a library defined anymore and therefore will be ignored as well
            if obj.library and len(obj.material_slots) > 0:
                obj.hide_select = False
                obj.hide_viewport = False
                obj.hide_set(False)
                objectsToOverride.append(obj)
        
        for obj in objectsToOverride:
            print("\t\tOverriding object: "+ obj.name)
            # set object to be the current active object
            obj.select_set(True, view_layer=viewLayer)
            viewLayer.objects.active = obj
            # create local override of active object
            bpy.ops.object.make_local(type='SELECT_OBDATA_MATERIAL')

bpy.ops.object.select_all(action='DESELECT')
1 Like

Nice work :smiley:

After reading your code, I came up with the following way to improve my code.

bpy.ops.outliner.id_operation(type='LOCAL')

Rewrite the above as below.

bpy.ops.object.make_local(type='SELECT_OBDATA')

The bpy.ops.outliner.id_operation operator has a lot of limitations, so I had to create a modal operator, but I can eliminate the constraints by using the bpy.ops.object.make_local operator.

1 Like