Cycles New Material Viewport Color Question

When using Cycles I normally add a new material via the script: space_view3d_materials_utils.py
Because when I do it this way, the Viewport Color automatically follows the Diffuse Color.
But if I add a new material via the Add a New Material button on Material Tab it does not. I have to copy and paste the Diffuse color to the Viewport Color.

Here is a summary of the commands invoked and what they do:
Command from script: bpy.ops.view3d.assign_material() Viewport Color = Diffuse Color
Add a New Material button on Material Tab: bpy.ops.material.new() Viewport Color NOT= Diffuse Color

It seems like the preferable method would be to have this happen automatically when a new material is assigned. Could this be automated at the script level, or is it a base code change?


I think the menu command is really assigning a BI material, nodes are not activated yet. You get the same if you switch to Blender Render, create new material, switch to Cycles Render. But if you’re already in Cycles, then clicking New for a new material will create the material with nodes already activated.

If you really like the diffuse and viewport colours being linked, just carry on using the menu. I can’t see any other solution, not in Python.

I was intrigued enough by this to create a button in the Material panel. It copies the colour of the first Color input of a Cycles node tree that it finds to the Viewport colour of the material. This will fail on complex node trees, probably giving you the colour from a Mix node or whatever, but it works on a simple node material such as when you press New.

The code includes a Value offset for the colour so that very dark colours are copied over a little brighter. I find this helpful as very dark objects are difficult to work with in the viewport. You can see the effect of this in the screenshot attached.

HTH!


import bpy
import mathutils

class CopyCyclesColorToViewport(bpy.types.Operator):
	'''Copies Color of first Cycles node with Color input to Viewport Color of active material'''
	bl_idname = "object.copy_cycles_color_to_viewport"
	bl_label = "Copy Cycles Color to Viewport"
	bl_options = {"REGISTER", "UNDO", "INTERNAL"}

	@classmethod
	def poll(cls, context):
		if context.object != None: ### check that an object is selected
			return context.object.select
		return False

	def execute(self, context):
		if context.active_object.active_material != None:
			obMat = context.active_object.active_material
			if obMat.cycles.id_data.node_tree != None:
				for node in obMat.cycles.id_data.node_tree.nodes:
					for nodeInput in node.inputs:
						if nodeInput.type == 'RGBA':
							cyColor = mathutils.Color((nodeInput.default_value[0], nodeInput.default_value[1], nodeInput.default_value[2]))
							# prevent the color from being too dark for viewport by using a minimum Value value of 0.1
							cyColor.v = max(0.1,cyColor.v)
							obMat.diffuse_color = cyColor
							break
		return {'FINISHED'}

class CopyCyclesColorToViewportPanel(bpy.types.Panel):
	"""Creates a Panel in the Material window"""
	bl_label = "Viewport Color"
	bl_idname = "MATERIAL_PT_cycles_viewport_color"

	bl_space_type = 'PROPERTIES'
	bl_region_type = 'WINDOW'
	bl_context = 'material'


	def draw(self, context):
		layout = self.layout

		row = layout.row()
		row.operator("object.copy_cycles_color_to_viewport")

def register():
	bpy.utils.register_module(__name__)

def unregister():
	bpy.utils.unregister_module(__name__)

if __name__ == '__main__':
	register()

Attachments


This is great. It works fine as I would normally use it. That is to quickly set the viewport color when first creating a new material. I hope others will try it and it may eventually become a default behavior.

This is awesome! I am dealing with the inverse problem and was hoping you can help. I’m working on a 2 minute piece that has a lengthy asset library built by various artists. All the assets have simple diffuse shader setups with the appropriate color applied in the Diffuse shaders first node input. I’ve written the following script to convert the active material of an asset to the custom node group I’ve built that applies a “paper cut” look to the object. However, I’ve struggled getting this to copy the default value of the existing shader (the objects color) to the new node group after it’s updated. Here’s my script as it stands (this is my first script, so please forgive any glaring issues :D):



import bpy
ob = bpy.context.object
paper = bpy.data.materials['paper_template']
me = ob.data.materials
name = ob.active_material.name
color =  ob.active_material.diffuse_color
for mats in me:
    #
    viewport_colour = ob.active_material.diffuse_color
    ob.active_material = paper.copy()
    ob.active_material.name = name
    ob.active_material.diffuse_color = color
    #

I stumbled across your script in my attempts to find a way to copy the viewport color back into the paper shader group after I run this script, or better yet to tweak my script to do this automatically. Any help would be awesome, either way your script is definitely part of my toolset, thanks!