Dec 23, 2014

Copy Cycles Color to Viewport

Someone recently queried on BlenderArtists about an annoyance in Blender that I had been aware of for a long time but never thought to do anything about it.

Copy Cycles Color to Viewport
When creating a new Cycles material by clicking the New button in the Materials panel, there is a disconnect between the two colour wells that are created, one for the Diffuse shader, the other for Viewport colour. As you adjust the Diffuse colour, the change doesn't transfer over to Viewport. If you want them to match, you have to manually copy the colour.

I decided to finally solve the issue with some Python code. I couldn't find a callback function to fully automate the copying, so I created a button. Because dark objects are difficult to work with in the Viewport, I added a lightness offset (Value in HSV colour space). Very dark colours from the shader are copied a little lighter to the Viewport.

Further to what I posted on BlenderArtists, I improved the script by dispensing with a new panel for the button, appending it to the Settings panel instead. The button will only appear if Cycles is the active render engine.

You can test the script by running it from the Text window in Blender. You can also download it as a full add-on (with tab indentation) from Google Drive: CyclesColorToViewport.py

import bpy
import mathutils

class CopyCyclesColorToViewport(bpy.types.Operator):
  '''Copies Color of first Cycles node with Color input to Viewport Color of the 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):
    return context.active_object is not None

  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 HSV Value of 0.1
              cyColor.v = max(0.1,cyColor.v)
              obMat.diffuse_color = cyColor
              break
    return {'FINISHED'}

def btn_draw(self, context):
  if context.scene.render.engine == "CYCLES":
    layout = self.layout
    row = layout.row()
    row.operator("object.copy_cycles_color_to_viewport")

def register():
  bpy.utils.register_module(__name__)
  bpy.types.CyclesMaterial_PT_settings.append(btn_draw)

def unregister():
  bpy.types.CyclesMaterial_PT_settings.remove(btn_draw)
  bpy.utils.unregister_module(__name__)

if __name__ == '__main__':
  register()

3 comments:

  1. Thanks a lot! This has been a growing thorn in my side, recently. Works like a charm.

    ReplyDelete
  2. Great! Thanks you very much

    ReplyDelete
  3. Muito bom, me ajudou bastante.
    Obrigado!

    ReplyDelete

Note: Only a member of this blog may post a comment.