6
$\begingroup$

How can I link a dupligroup of a group from File A in to File B and override the material of an object within the dupligroup with a material from File B?

The end goal would be to have multiple dupligroups of linked objects all linked from one file, but with different materials. Having the code in one script with the dupligroups and the materials to be applied would be ideal; like this:

The line of code below, only with box 1, 2, and 3, and sphere 1, 2, and 3, with blue, green, pink, and orange, yellow, purple materials on six seperate lines for changing the materials of all the boxes and spheres to all the different materials. Example only, does not work.

I got the idea from this video on Python overrides in Blender: https://youtu.be/bOLMkw_wDGE

The video just shows how to override the attributes of materials and modifiers, but it seems like it should be possible to override a whole material of a dupligroup's child objects. For example:

  • source.blend has a group calling 'thing' with an object called 'box' in it with a material applied called 'red'.
  • dest.blend has a material called 'blue' and a dupligroup of 'thing' from source.blend.
  • In dest.blend I want to override the 'red' material and put 'blue' on the 'box' that is within the linked dupligroup of 'thing.'
  • The final goal is being able to import multiple dupligroups of 'thing' and have different materials (from dest.blend) on all of the different dupligroups' 'box' objects.

The closest I've come is this Python line within dest.blend in the python console:

bpy.data.objects['box'].material_slots['red'].material = bpy.data.materials['blue']

This does NOT give me an error message (and it does autocomplete to that point) but it just doesn't do anything.

This is the closest I've come because all my other attempts have given me error messages. It also looked like I was on the right track because just putting in:

bpy.data.objects['box'].material_slots['red'].material

returned:

bpy.data.materials['red']

which matches the syntax of:

bpy.data.materials['blue']

But as I said it didn't seem to do anything.

What am I doing wrong here? Is it even possible to do this with a dupligroup? Any ideas would be very much appreciated. Thank you.

A few notes: I do know about the Edit Linked Libraries add-on, but that doesn't work for what I want to use this method for. As far as I can tell the add-on just lets you more easily edit the linked object's source file thereby editing the linked object, whereas with this method I'd like to be able to have multiple instances of the linked object all linked from one file, but with different materials.

I also know about this thread: Multiple Linked Objects with unique Materials / Group Materials override but not only can I not get any of those examples to work, but also they're just dealing with linked objects and not dupligroups. Dupligroups are a lot more useful in my opinion.

I am new to Blender Python scripting so specific code examples with explanations would be very much appreciated. Thank you.

(This question was asked about Blender versions 2.7*)

This is a (vastly improved) repeat of a question I asked on BlenderArtists.org: http://blenderartists.org/forum/showthread.php?385851-Override-dupligroup-s-object-s-material&highlight= I hope that's okay. The thread died months ago without any solutions.

$\endgroup$
1
$\begingroup$

I came across this question because I have a similar problem. Since material_slots doesn't work, I tried the same method on the object data (in this case, the mesh). And voilá, that works! So instead of doing this:

bpy.data.objects['linked_object'].material_slots[0] = bpy.data.materials['new_material']

Try this:

bpy.data.objects['linked_object'].data.materials[0] = bpy.data.materials['new_material']

(You need to force a viewport update by clicking or so, but apart from that it seems to work fine)

$\endgroup$
  • $\begingroup$ Oh my god, after all these years the legend himself comes in and gives the answer we've needed for so long! Thank you so much Sebastian! The only problem left is that it seems to override the material of every instance of the object within every dupligroup instance within the scene. Still incredibly useful! But ideally there would be a way to link 2 dupligroups from the same source and override dupligroup1 with material1 and dupligroup2 with material2, in the same scene. I'm wracking my brain over Python API docs for dupligroups nows. I'll report back if I find something. $\endgroup$ – InverseTelecine Aug 29 '17 at 20:17
  • $\begingroup$ Awesome, good to hear it's usefull :) As for your question: I think for that you would need to do something similar to the code samples from the answers below, meaning you would have to somehow duplicate the objects in the dupligroup and assign the new material there. Because the dupligroup system is simply instancing a group which consists of certain objects, which use certain materials. As soon as you change anything inside the group, it will affect every instance of it. $\endgroup$ – sebastian_k Aug 31 '17 at 8:04
  • $\begingroup$ Okay, that makes sense. So these are all useful methods! Great! Although I do have some hope that this sort of task will become even easier in Blender 2.8. Especially with this quote from the 2.8 outline "At the heart of Blender, the dependency graph is getting a complete overhaul. Not only delivering better performance but also allowing overrides on data in a way not possible before." $\endgroup$ – InverseTelecine Sep 9 '17 at 20:37
5
+50
$\begingroup$

Here is the basic code how to do it with Python: http://www.pasteall.org/66951

import bpy

#1 Link like you do now. Dupligroup object is selected
#-----------------------------------------------
#2 take copies (.copy()) of our group objects
#-----------------------------------------------
o = bpy.context.object
objs = [i.copy() for i in o.dupli_group.objects]

#3 link them to the scene
#-----------------------------------------------
bpy.ops.object.select_all(action='DESELECT')
for i in objs:
    bpy.context.scene.objects.link(i)
    i.select = True

#4 make single users (mat+tex)
#-----------------------------------------------
bpy.ops.object.make_single_user(object=False, obdata=False, material=True, texture=True, animation=False)

#5 change matslot 0 material 
#-----------------------------------------------
for i in objs:
    i.material_slots[0].material = bpy.data.materials[0]    

#6 Use original group name
#-----------------------------------------------
groupname = o.dupli_group.name

for i in objs:
    bpy.data.groups[groupname].objects.link(i)

#7 set dupligroup for empty
#-----------------------------------------------
o.dupli_group = bpy.data.groups[groupname]

#Now you have exact mesh clones with different materials and they are still linked on mesh level and you control them with empty object like before.
$\endgroup$
  • $\begingroup$ This is specifically thought the script, not manually. Like you can group your new objects and set dupligroup (empty) to that group. Ps: I dont care any points, for me its the same who will get the point(s), I just want to help people when I see I can. If I know the answer and I dont get any points, I dont care, just give points to people who cares. $\endgroup$ – JuhaW Mar 24 '16 at 18:21
  • $\begingroup$ Man! It makes my head hurt thinking about how it works, but it does work! The material can be changed on the dupligroup, the source file's group edited, and the dupligroup in the destination will update based on the source edits. There are some issues caused by this method, like any new geometry added into the source group not getting the new material in the destination, but it seems likely that this will be the best answer I get! Thank you so much JuhaW! $\endgroup$ – InverseTelecine Mar 29 '16 at 3:43
3
$\begingroup$

As for what you are asking, per the manual this is not possible (please see the first three paragraphs here), as it states the following:

Append and Link

These functions help you reuse materials, objects and other data-blocks loaded from an external source blend-file. You can build libraries of common content and share them across multiple referencing files.

Link creates a reference to the data in the source file such that changes made there will be reflected in the referencing file the next time it is reloaded.

Whereas Append makes a full copy of the data into your blend. You can make further edits to your local copy of the data, but changes in the external source file will not be reflected in the referencing file.

Meaning - Using Linked anything is a one-way street, to get the definition to reside in the source file (including active materials)

Even running the following code confirms that materials are not modifiable, consistent with this definition of the Manual.

Read Only Confirmation of the Manual

So the remainder of this answer, is showing how to accomplish what you are intending to do, without keeping to the strict rule of linking, because the structure/design of the software does not allow this kind of usage.


import bpy

##I have three materials defined in my current file
###'a1', 'a2', 'a3'

##When I manually Append the Group in, I run this code:
newSet = bpy.context.selected_objects

count = 0
for i in newSet:
    i.active_material = bpy.data.materials['a' + str(count + 1)]
    if count == 2:
        count = 0
    else:
        count = count + 1
##Then I'm Done

##Now I can make instances of this group

##If I need another variation, you can do the Append again, and run the code again.
###When you do, you will just get another group name: 
####eg. the first one will be 'InstanceGroup', and the second will be 'InstanceGroup.001' with its own objects

##You can easily put some random logic or whatever to select the material in the desired method, but this works.

Here is a demonstration of the comments below.

As you will see, you can keep appending in the same group.

The reason for telling you that you should modify the code logic to fit your purposes, is because as demonstrated, you don't get a variation in color assignment every time you do these steps. So my suggestion is that you add some random assignment logic for this purpose.

enter image description here

Use this code instead if you want to see the material assignment randomized:

import bpy
import random

newSet = bpy.context.selected_objects

count = 0
for i in newSet:
    myint = str(random.choice([1, 2, 3]))
    i.active_material = bpy.data.materials['a' + myint]
    if count == 2:
        count = 0
    else:
        count = count + 1
$\endgroup$
  • $\begingroup$ Your code post isn't too far off from working, just pay attention to active material and slots together. Here's a post that describes this a little more $\endgroup$ – Rick Riggs Mar 24 '16 at 18:01
  • $\begingroup$ bpy.data.objects['box'].active_material = bpy.data.materials['blue'] gave me the error AttributeError: bpy_struct: attribute "active_material" from "Object" is read-only. Is that indicating that this code won't work with a dupligroup? I'm going to try your suggested code now, but I was also wondering how I might get my code to work. Thank you Rick! $\endgroup$ – InverseTelecine Mar 25 '16 at 14:25
  • $\begingroup$ The first thing I tried was just the simplification of your code: newSet = bpy.context.selected_objects for i in newSet: i.active_material = bpy.data.materials['blue'] but that didn't work. (Read only error again) But your answer involved more steps than just the code. Could you explain the steps in the last three commented lines in your code a little more? What do you mean by "now I can make instances of this group"? Do you mean that the code has to be run in the source file and then instances of the code-altered group linked to other files from there? Thank you Rick! $\endgroup$ – InverseTelecine Mar 25 '16 at 14:47
  • $\begingroup$ Oh, I should mention that running that code on a dupligroup (from another file) verbatim with the "blue" material's name changed to "a1" also did not work. As I said, I think what I'm doing wrong must be related to the steps in your commented lines, which I don't understand. Sorry. $\endgroup$ – InverseTelecine Mar 25 '16 at 14:50
  • $\begingroup$ The 1st step is to link in your group from the source file. As soon as you do this, these items exist independently & are selected. If you run the code at this point (on the selected objects), then the code should work just fine. After you are done running the code you can instance this group however you want to. As for the last lines of my answer, there are two things that I was saying here. 1. You could modify the logic in terms of material assignment to fit your desires. 2. If you Link your group again, you will get a different set with the .001, 002, 003, etc... @InverseTelecine $\endgroup$ – Rick Riggs Mar 25 '16 at 20:21

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.