# Update all materials, but keep their color and textures

I am working with a very large scene file that was imported as .obj. This gave me basic blender internal materials with colors and textures. I then used an addon to convert all of the BI materials to Cycles ones (https://blenderartists.org/forum/showthread.php?394600-Materials-Specials-amp-conversion)

What I have now is every material is a Diffuse Node with a color, or with an image texture node hooked up to it with the appropriate texture.

What I would like to do now is make a more complex node setup (likely in a group), and then copy it to all the objects in the file, but have them keep their own diffuse color and texture.

Are there any existing addons or scripts that can do this?

Sorry late to the party but I had a related question yesterday and I think this solves your issue.

This approach is based on this great answer as well as this great answer (thanks to @zeffii and @p2or). It's a simple operator, you can either install it like an Add-on or paste and run it in blenders text editor per session.

1. Select all desired objects in the '3d View' (allows to limit the selection)
2. In the 'Node Editor' select that Material Node, you'd like to replace
3. Strike Spacebar and type "Switch Shader N..."
4. Select your desired Node Type from the list

# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

bl_info = {
"author": "brockmann",
"version": (0, 0, 1),
"blender": (2, 80, 0),
"category": "Node"
}

import bpy
import itertools

from bpy.props import (EnumProperty,
PointerProperty,
)
from bpy.types import (Operator,
PropertyGroup,
)

# ------------------------------------------------------------------------
# replace the node (by zeffii and p2or)
# based on https://blender.stackexchange.com/q/42306/3710
# ------------------------------------------------------------------------

def replace_node(node, new_node_type):
node_tree = node.id_data
props_to_copy = 'name location height width'.split(' ')

reconnections = []
mappings = itertools.chain.from_iterable([node.inputs, node.outputs])
for i in (i for i in mappings if i.is_linked):
reconnections.append([L.from_socket.path_from_id(), L.to_socket.path_from_id()])
props = {j: getattr(node, j) for j in props_to_copy}

# get first color attribute
if hasattr(node, "inputs"):
for socket_input in node.inputs:
if socket_input.type == 'RGBA':
clr = socket_input.default_value
break

ids = ([node.bl_rna.identifier for node in bpy.types.ShaderNode.__subclasses__()])
if new_node_type not in ids:
new_node.node_tree = bpy.data.node_groups[new_node_type]
else:
new_node = node_tree.nodes.new(type=new_node_type)

for prop in props_to_copy:
setattr(new_node, prop, props[prop])

# set first color attribute
if hasattr(new_node, "inputs"):
for new_socket_input in new_node.inputs:
if new_socket_input.type == 'RGBA':
#setattr(new_socket_input, "default_value", clr)
new_socket_input.default_value = clr
break

nodes = node_tree.nodes
temp_name = node.name
nodes.remove(node)
new_node.name = new_node_type

reconnections_new = []
for i in reconnections:
reconnections_new.append([s.replace(temp_name, new_node.name) for s in i])

for str_from, str_to in reconnections_new:
try:
except:
pass

node_tree.nodes.active = new_node

# ------------------------------------------------------------------------
#    store properties in the active scene
# ------------------------------------------------------------------------

names = ([node.bl_rna.name for node in bpy.types.ShaderNode.__subclasses__()])
ids = ([node.bl_rna.identifier for node in bpy.types.ShaderNode.__subclasses__()])
d = dict(zip(ids,names))
for i in bpy.data.node_groups: d.update({i.name:i.name})
return [(key, val, "") for key, val in d.items()]

description="",
)

# ------------------------------------------------------------------------
# Search Popup Operator
# based on https://blender.stackexchange.com/a/45069/3710
# ------------------------------------------------------------------------

bl_label = "Switch Shader Node (Selected Objects)"
bl_options = {'REGISTER', 'UNDO'}

@classmethod
def poll(cls, context):
space = context.space_data
return space.type == 'NODE_EDITOR'

def execute(self, context):
#self.report({'INFO'}, "You've selected: %s" % self.shader_node_selection)
space = context.space_data
node_active = context.active_node.type
node_selected = context.selected_nodes

if not (len(node_selected) == 1) and node_active:
self.report({'ERROR'}, "1 node must be selected")
return {'FINISHED'}

rel_mats = []
for o in bpy.context.selected_objects:
if o.type not in ["EMPTY", "LAMP", "SPEAKER", "CAMERA"]:
for m in o.material_slots:
rel_mats.append(m.material)

replaced_nodes = []
for i in rel_mats:
nodes = i.node_tree.nodes
for n in nodes:
if n.type == node_active:
replaced_nodes.append(i.name)

out_str = "Replaced {0} by {1} for {2} Materials".format(out[0], out[1], out[2])
self.report({'INFO'}, (out_str))
return {'FINISHED'}

def invoke(self, context, event):
wm = context.window_manager
wm.invoke_search_popup(self)

return {'FINISHED'}

# ------------------------------------------------------------------------
# register and unregister functions
# ------------------------------------------------------------------------

def register():

def unregister():

if __name__ == "__main__":
register()

# test call

• This is great! I just need a way to handle the texture and color preservation and hookup now. It seems the full answer is split across a couple of the answers here. – Ascalon May 18 '17 at 20:10
• Ooops, forgot to copy the color... Done. What do you mean by texture preservation? No experience with BI, can you share a test file to play with? Thanks @Drudge – brockmann May 19 '17 at 14:21
• I just mean keeping the texture node (if there is one) and reconnecting it to the color input. – Ascalon May 19 '17 at 16:31
• returning to this after a long time, I can confirm that this script works great for switching the selected node type to my Group without breaking connections to Texture nodes. However, it does not carry over the color of the Diffuse node I'm replacing. I need a way to create an RGB node in each material that has a diffuse, have it copy the Diffuse's color, and then hook the RGB to the diffuse. Any ideas? – Ascalon Sep 6 '17 at 2:57
import bpy
for material in bpy.data.materials:
if (material.use_nodes):
nodes = material.node_tree.nodes
for node in nodes:
if (node.name=='Material Output'):
out = node
elif  (node.name=='Image Texture'):
img = node
elif: (node.name=='Diffuse BSDF'):
diff = node
# if diffuse node is not linked,then save it's colorvalue
color = node.inputs[0].default_value
else:
# else we should set color to None, and be aware
color = None
else:
nodes.remove(node)

if (color==None):
if (img==None):
#No image and diffuse was not empty, set color to grey
RGB.outputs[0].default_value = (0.7,0.7,0.7)
img = RGB
else:
RGB.outputs[0].default_value = color
img = RGB



This code does almost what you want. It saves one image texture node, one material output node, and deletes everything else, and then adds emission and holdout node and connects them via backfacing. As for part of your own shader, i think you can save it as a node group and append it into node setup via python too

• Do you know how to make it take the color in the diffuse node and make an RGB node that matches it, then hook that to a node group input? – Ascalon May 18 '17 at 7:45
• updated code in my answer a bit – Vii May 19 '17 at 9:25

I don't know if that's exactly what do you want, if you have your material already made just:

1. select all the objects you want to have that material, and select for last the one that has the material.
2. It should have an orange border, while the other ones should have a red border.
3. Now press Ctrl + l and click on "materials", and that should do it.

You would need to create a script that looped through each material in the scene. Here's generally what the script should do.

First create your group node with an RGB input.

Then have your script loop through each material in the scene and have it: