# SPDX-License-Identifier: GPL-2.0-or-later # # # (c) Alessandro Zomparelli # # (2020) # # # # http://www.co-de-it.com/ # # # ################################################################################ import bpy import numpy as np import colorsys from numpy import * from bpy.types import ( Operator, Panel ) from bpy.props import ( BoolProperty, EnumProperty, FloatProperty, IntProperty, StringProperty, FloatVectorProperty, IntVectorProperty ) from .utils import * class random_materials(Operator): bl_idname = "object.random_materials" bl_label = "Random Materials" bl_description = "Assign random materials to the faces of the mesh" bl_options = {'REGISTER', 'UNDO'} prefix : StringProperty( name="Prefix", default="Random.", description="Name prefix") color_A : FloatVectorProperty(name="Color A", subtype='COLOR_GAMMA', min=0, max=1, default=[0,0,0]) color_B : FloatVectorProperty(name="Color B", subtype='COLOR_GAMMA', min=0, max=1, default=[1,1,1]) hue : FloatProperty(name="Hue", min=0, max=1, default=0.5) hue_variation : FloatProperty(name="Hue Variation", min=0, max=1, default=0.6) seed : IntProperty( name="Seed", default=0, description="Random seed") count : IntProperty( name="Count", default=3, min=2, description="Count of random materials") generate_materials : BoolProperty( name="Generate Materials", default=False, description="Automatically generates new materials") random_colors : BoolProperty( name="Random Colors", default=True, description="Colors are automatically generated") executed = False @classmethod def poll(cls, context): try: return context.object.type == 'MESH' except: return False def draw(self, context): layout = self.layout col = layout.column(align=True) col.prop(self, "seed") col.prop(self, "generate_materials") if self.generate_materials: col.prop(self, "prefix") col.separator() col.prop(self, "count") #row = col.row(align=True) col.separator() col.label(text='Colors:') col.prop(self, "hue") col.prop(self, "hue_variation") #col.prop(self, "random_colors") if not self.random_colors: col.prop(self, "color_A") col.prop(self, "color_B") def execute(self, context): bpy.ops.object.mode_set(mode='OBJECT') ob = context.active_object if len(ob.material_slots) == 0 and not self.executed: self.generate_materials = True if self.generate_materials: colA = self.color_A colB = self.color_B h1 = (self.hue - self.hue_variation/2) h2 = (self.hue + self.hue_variation/2) count = self.count ob.data.materials.clear() materials = [] for i in range(count): mat_name = '{}{:03d}'.format(self.prefix,i) mat = bpy.data.materials.new(mat_name) if self.random_colors: mat.diffuse_color = colorsys.hsv_to_rgb((h1 + (h2-h1)/(count)*i)%1, 1, 1)[:] + (1,) else: mat.diffuse_color = list(colA + (colB - colA)/(count-1)*i) + [1] ob.data.materials.append(mat) else: count = len(ob.material_slots) np.random.seed(seed=self.seed) n_faces = len(ob.data.polygons) if count > 0: rand = list(np.random.randint(count, size=n_faces)) ob.data.polygons.foreach_set('material_index',rand) ob.data.update() self.executed = True return {'FINISHED'} class weight_to_materials(Operator): bl_idname = "object.weight_to_materials" bl_label = "Weight to Materials" bl_description = "Assign materials to the faces of the mesh according to the active Vertex Group" bl_options = {'REGISTER', 'UNDO'} prefix : StringProperty( name="Prefix", default="Weight.", description="Name prefix") hue : FloatProperty(name="Hue", min=0, max=1, default=0.5) hue_variation : FloatProperty(name="Hue Variation", min=0, max=1, default=0.3) count : IntProperty( name="Count", default=3, min=2, description="Count of random materials") generate_materials : BoolProperty( name="Generate Materials", default=False, description="Automatically generates new materials") mode : EnumProperty( items=( ('MIN', "Min", "Use the min weight value"), ('MAX', "Max", "Use the max weight value"), ('MEAN', "Mean", "Use the mean weight value") ), default='MEAN', name="Mode" ) vg = None @classmethod def poll(cls, context): try: return context.object.type == 'MESH' except: return False def draw(self, context): layout = self.layout col = layout.column(align=True) col.prop(self, "mode") col.prop(self, "generate_materials") if self.generate_materials: col.prop(self, "prefix") col.separator() col.prop(self, "count") #row = col.row(align=True) col.separator() col.label(text='Colors:') col.prop(self, "hue") col.prop(self, "hue_variation") def execute(self, context): ob = context.active_object if self.vg == None: self.vg = ob.vertex_groups.active_index vg = ob.vertex_groups[self.vg] if vg == None: self.report({'ERROR'}, "The selected object doesn't have any Vertex Group") return {'CANCELLED'} weight = get_weight_numpy(vg, len(ob.data.vertices)) if self.generate_materials: h1 = (self.hue - self.hue_variation/2) h2 = (self.hue + self.hue_variation/2) count = self.count ob.data.materials.clear() materials = [] for i in range(count): mat_name = '{}{:03d}'.format(self.prefix,i) mat = bpy.data.materials.new(mat_name) mat.diffuse_color = colorsys.hsv_to_rgb((h1 + (h2-h1)/(count)*i)%1, 1, 1)[:] + (1,) ob.data.materials.append(mat) else: count = len(ob.material_slots) faces_weight = [] for p in ob.data.polygons: verts_id = np.array([v for v in p.vertices]) face_weight = weight[verts_id] if self.mode == 'MIN': w = face_weight.min() if self.mode == 'MAX': w = face_weight.max() if self.mode == 'MEAN': w = face_weight.mean() faces_weight.append(w) faces_weight = np.array(faces_weight) faces_weight = faces_weight * count faces_weight.astype('int') ob.data.polygons.foreach_set('material_index',list(faces_weight)) ob.data.update() bpy.ops.object.mode_set(mode='OBJECT') return {'FINISHED'}