import bpy import bmesh import operator import time from mathutils import Vector from collections import defaultdict from math import pi from mathutils import Color from . import settings material_prefix = "TT_color_" gamma = 2.2 def assign_slot(obj, index): if index < len(obj.material_slots): obj.material_slots[index].material = get_material(index) # Verify color assign_color(index) def safe_color(color): if len(color) == 3: if bpy.app.version > (2, 80, 0): # Newer blender versions use RGBA return (color[0], color[1], color[2], 1) else: return color elif len(color) == 4: if bpy.app.version > (2, 80, 0): # Newer blender versions use RGBA return color else: return (color[0], color[1], color[2]) return color def assign_color(index): material = get_material(index) if material: # material.use_nodes = False rgb = get_color(index) rgba = (rgb[0], rgb[1], rgb[2], 1) if material.use_nodes and bpy.context.scene.render.engine == 'CYCLES' or material.use_nodes and bpy.context.scene.render.engine == 'BLENDER_EEVEE': # Cycles material (Preferred for baking) material.node_tree.nodes["Principled BSDF"].inputs[0].default_value = rgba material.diffuse_color = rgba elif not material.use_nodes and bpy.context.scene.render.engine == 'BLENDER_EEVEE': # Legacy render engine, not suited for baking material.diffuse_color = rgba def get_material(index): name = get_name(index) # Material already exists? if name in bpy.data.materials: material = bpy.data.materials[name] # Check for incorrect matreials for current render engine if not material: replace_material(index) if not material.use_nodes and bpy.context.scene.render.engine == 'CYCLES': replace_material(index) elif material.use_nodes and bpy.context.scene.render.engine == 'BLENDER_EEVEE': replace_material(index) else: return material print("Could nt find {} , create a new one??".format(name)) material = create_material(index) assign_color(index) return material # Replaace an existing material with a new one # This is sometimes necessary after switching the render engine def replace_material(index): name = get_name(index) print("Replace material and create new") # Check if material exists if name in bpy.data.materials: material = bpy.data.materials[name] # Collect material slots we have to re-assign slots = [] for obj in bpy.context.view_layer.objects: for slot in obj.material_slots: if slot.material == material: slots.append(slot) # Get new material material.user_clear() bpy.data.materials.remove(material) # Re-assign new material to all previous slots material = create_material(index) for slot in slots: slot.material = material def create_material(index): name = get_name(index) # Create new image instead material = bpy.data.materials.new(name) material.preview_render_type = 'FLAT' if bpy.context.scene.render.engine == 'CYCLES': # Cycles: prefer nodes as it simplifies baking material.use_nodes = True return material def get_name(index): return (material_prefix+"{:02d}").format(index) def get_color(index): if index < bpy.context.scene.texToolsSettings.color_ID_count: return getattr(bpy.context.scene.texToolsSettings, "color_ID_color_{}".format(index)) # Default return (Black) return (0, 0, 0) def set_color(index, color): if index < bpy.context.scene.texToolsSettings.color_ID_count: setattr(bpy.context.scene.texToolsSettings, "color_ID_color_{}".format(index), color) def validate_face_colors(obj): # Validate face colors and material slots previous_mode = bpy.context.object.mode count = bpy.context.scene.texToolsSettings.color_ID_count # Verify enough material slots if len(obj.material_slots) < count: for i in range(count): if len(obj.material_slots) < count: bpy.ops.object.material_slot_add() assign_slot(obj, len(obj.material_slots)-1) else: break # TODO: Check face.material_index bpy.ops.object.mode_set(mode='EDIT') bm = bmesh.from_edit_mesh(obj.data) for face in bm.faces: face.material_index %= count obj.data.update() # Remove material slots that are not used if len(obj.material_slots) > count: bpy.ops.object.mode_set(mode='OBJECT') for i in range(len(obj.material_slots) - count): if len(obj.material_slots) > count: # Remove last bpy.context.object.active_material_index = len( obj.material_slots)-1 bpy.ops.object.material_slot_remove() # Restore previous mode bpy.ops.object.mode_set(mode=previous_mode) def hex_to_color(hex): hex = hex.strip('#') lv = len(hex) fin = list(int(hex[i:i + lv // 3], 16) for i in range(0, lv, lv // 3)) r = pow(fin[0] / 255, gamma) g = pow(fin[1] / 255, gamma) b = pow(fin[2] / 255, gamma) fin.clear() fin.append(r) fin.append(g) fin.append(b) return tuple(fin) def color_to_hex(color): rgb = [] for i in range(3): rgb.append(pow(color[i], 1.0/gamma)) r = int(rgb[0]*255) g = int(rgb[1]*255) b = int(rgb[2]*255) return "#{:02X}{:02X}{:02X}".format(r, g, b) def get_color_id(index, count): # Get unique color color = Color() color.hsv = (index / (count)), 0.9, 1.0 return color