Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormeta-androcto <meta.androcto1@gmail.com>2019-08-17 03:19:02 +0300
committermeta-androcto <meta.androcto1@gmail.com>2019-08-17 03:19:02 +0300
commit4fa93f6eb1b63c2662190336d685358c34209cb2 (patch)
treefe7897ecac117a6c3e2e97c48fdd8085438a26f4 /materials_utils
parent6c882f7d817ad092f72d53b37daefc8622e9e74e (diff)
materials_utils: add search and preferences T67990
Diffstat (limited to 'materials_utils')
-rw-r--r--materials_utils/__init__.py65
-rw-r--r--materials_utils/enum_values.py24
-rw-r--r--materials_utils/functions.py102
-rw-r--r--materials_utils/menus.py94
-rw-r--r--materials_utils/operators.py249
-rw-r--r--materials_utils/preferences.py118
6 files changed, 587 insertions, 65 deletions
diff --git a/materials_utils/__init__.py b/materials_utils/__init__.py
index 40f976d7..2a4561f4 100644
--- a/materials_utils/__init__.py
+++ b/materials_utils/__init__.py
@@ -1,3 +1,19 @@
+# Material Utilities v2.2.0-Beta
+#
+# Usage: Shift + Q in the 3D viewport
+#
+# Ported from 2.6/2.7 to 2.8x by
+# Christopher Hindefjord (chrishinde) 2019
+#
+# ## Port based on 2010 version by MichaelW with some code added from latest 2.7x version
+# ## Same code may be attributed to one of the following awesome people!
+# (c) 2016 meta-androcto, parts based on work by Saidenka, lijenstina
+# Materials Utils: by MichaleW, lijenstina,
+# (some code thanks to: CoDEmanX, SynaGl0w, ideasman42)
+# Link to base names: Sybren, Texture renamer: Yadoob
+# ###
+#
+#
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
@@ -15,22 +31,15 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
-
-# Based on 2010 version by MichaelW
-# (c) 2016 meta-androcto, parts based on work by Saidenka, lijenstina
-# Materials Utils: by MichaleW, meta-androcto, lijenstina,
-# (some code thanks to: CoDEmanX, SynaGl0w, ideasman42)
-# Link to base names: Sybren, Texture renamer: Yadoob
-# Ported from 2.6/2.7 to 2.8x by Christopher Hindefjord (chrishinde) 2019
bl_info = {
- "name": "Material Utils",
+ "name": "Material Utilities",
"author": "MichaleW, ChrisHinde",
- "version": (1, 0, 6),
+ "version": (2, 2, 0),
"blender": (2, 80, 0),
"location": "View3D > Shift + Q key",
"description": "Menu of material tools (assign, select..) in the 3D View",
- "warning": "",
+ "warning": "Beta",
"wiki_url": "https://github.com/ChrisHinde/MaterialUtilities",
"category": "Material"
}
@@ -78,13 +87,34 @@ This script has several functions and operators, grouped for convenience:
"""
+if "bpy" in locals():
+ import importlib
+ if "enum_values" in locals():
+ importlib.reload(enum_values)
+ if "functions" in locals():
+ importlib.reload(functions)
+ if "operators" in locals():
+ importlib.reload(operators)
+ if "menues" in locals():
+ importlib.reload(menus)
+ if "preferences" in locals():
+ importlib.reload(preferences)
+else:
+ from .enum_values import *
+ from .functions import *
+ from .operators import *
+ from .menus import *
+ from .preferences import *
import bpy
+from bpy.props import (
+ PointerProperty,
+ )
+from bpy.types import (
+ AddonPreferences,
+ PropertyGroup,
+ )
-from .enum_values import *
-from .functions import *
-from .operators import *
-from .menus import *
# All classes used by Material Utilities, that need to be registred
classes = (
@@ -102,6 +132,8 @@ classes = (
VIEW3D_OT_materialutilities_change_material_link,
MATERIAL_OT_materialutilities_merge_base_names,
+ MATERIAL_OT_materialutilities_join_objects,
+ MATERIAL_OT_materialutilities_auto_smooth_angle,
MATERIAL_OT_materialutilities_material_slot_move,
@@ -112,12 +144,13 @@ classes = (
VIEW3D_MT_materialutilities_specials,
VIEW3D_MT_materialutilities_main,
+
+ VIEW3D_MT_materialutilities_preferences
)
# This allows you to right click on a button and link to the manual
def materialutilities_manual_map():
- print("ManMap")
url_manual_prefix = "https://github.com/ChrisHinde/MaterialUtilities"
url_manual_map = []
#url_manual_mapping = ()
@@ -156,7 +189,6 @@ def register():
def unregister():
"""Unregister the classes of Material Utilities together with the default shortcut for the menu"""
- mu_classes_unregister()
bpy.utils.unregister_manual_map(materialutilities_manual_map)
@@ -174,6 +206,7 @@ def unregister():
km.keymap_items.remove(kmi)
break
+ mu_classes_unregister()
if __name__ == "__main__":
register()
diff --git a/materials_utils/enum_values.py b/materials_utils/enum_values.py
index f7f8f65b..2f257400 100644
--- a/materials_utils/enum_values.py
+++ b/materials_utils/enum_values.py
@@ -4,21 +4,33 @@ import bpy
mu_override_type_enums = [
('OVERRIDE_ALL', "Override all assigned slots",
"Remove any current material slots, and assign the current material"),
+ ('OVERRIDE_CURRENT', 'Assign material to currently selected slot',
+ 'Only assign the material to the material slot that\'s currently selected'),
('OVERRIDE_SLOTS', 'Assign material to each slot',
'Keep the material slots, but assign the selected material in each slot'),
('APPEND_MATERIAL', 'Append Material',
'Add the material in a new slot, and assign it to the whole object')
]
+mu_clean_slots_enums = (('ACTIVE', "Active object", "Materials of active object only"),
+ ('SELECTED', "Selected objects", "Materials of selected objects"),
+ ('SCENE', "Scene objects", "Materials of objects in current scene"),
+ ('ALL', "All", "All materials in this blend file"))
+
+mu_affect_enums = (('ACTIVE', "Active object", "Affect the active object only"),
+ ('SELECTED', "Selected objects", "Affect all selected objects"),
+ ('SCENE', "Scene objects", "Affect all objects in the current scene"),
+ ('ALL', "All", "All objects in this blend file"))
+
mu_fake_user_set_enums = (('ON', "On", "Enable fake user"),
('OFF', "Off", "Disable fake user"),
('TOGGLE', "Toggle", "Toggle fake user"))
-mu_fake_user_materials_enums = (('ACTIVE', "Active object", "Materials of active object only"),
- ('SELECTED', "Selected objects", "Materials of selected objects"),
- ('SCENE', "Scene objects", "Materials of objects in current scene"),
- ('USED', "Used", "All materials used by objects"),
- ('UNUSED', "Unused", "Currently unused materials"),
- ('ALL', "All", "All materials in this blend file"))
+mu_fake_user_affect_enums = (('ACTIVE', "Active object", "Materials of active object only"),
+ ('SELECTED', "Selected objects", "Materials of selected objects"),
+ ('SCENE', "Scene objects", "Materials of objects in current scene"),
+ ('USED', "Used", "All materials used by objects"),
+ ('UNUSED', "Unused", "Currently unused materials"),
+ ('ALL', "All", "All materials in this blend file"))
mu_link_to_enums = (('DATA', "Data", "Link the materials to the data"),
('OBJECT', "Object", "Link the materials to the object"),
diff --git a/materials_utils/functions.py b/materials_utils/functions.py
index 6c8c72bd..b8afc04f 100644
--- a/materials_utils/functions.py
+++ b/materials_utils/functions.py
@@ -1,4 +1,5 @@
import bpy
+from math import radians, degrees
# -----------------------------------------------------------------------------
# utility functions
@@ -54,6 +55,19 @@ def mu_assign_to_data(object, material, index, edit_mode, all = True):
bpy.ops.object.mode_set(mode = 'OBJECT')
def mu_new_material_name(material):
+ for mat in bpy.data.materials:
+ name = mat.name
+
+ if (name == material):
+ try:
+ base, suffix = name.rsplit('.', 1)
+
+ # trigger the exception
+ num = int(suffix, 10)
+ material = base + "." + '%03d' % (num + 1)
+ except ValueError:
+ material = material + ".001"
+
return material
@@ -139,6 +153,15 @@ def mu_assign_material(self, material_name = "Default", override_type = 'APPEND_
obj.material_slots[i].material = target
i += 1
+ elif override_type == 'OVERRIDE_CURRENT':
+ active_slot = obj.active_material_index
+
+ if len(obj.material_slots) == 0:
+ self.report({'INFO'}, 'No material slots found! A material slot was added!')
+ bpy.ops.object.material_slot_add()
+
+ obj.material_slots[active_slot].material = target
+
# if we should keep the material slots and just append the selected material (if not already assigned)
elif override_type == 'APPEND_MATERIAL':
found = False
@@ -184,7 +207,7 @@ def mu_assign_material(self, material_name = "Default", override_type = 'APPEND_
return {'FINISHED'}
-def mu_select_by_material_name(self, find_material_name, extend_selection = False):
+def mu_select_by_material_name(self, find_material_name, extend_selection = False, internal = False):
"""Searches through all objects, or the polygons/curves of the current object
to find and select objects/data with the desired material"""
@@ -195,7 +218,7 @@ def mu_select_by_material_name(self, find_material_name, extend_selection = Fals
if find_material is None:
self.report({'INFO'}, "The material " + find_material_name + " doesn't exists!")
- return {'CANCELLED'}
+ return {'CANCELLED'} if not internal else -1
# check for edit_mode
edit_mode = False
@@ -236,9 +259,10 @@ def mu_select_by_material_name(self, find_material_name, extend_selection = Fals
obj.select_set(state=False)
if not found_material:
- self.report({'INFO'}, "No objects found with the material " +
- find_material_name + "!")
- return {'FINISHED'}
+ if not internal:
+ self.report({'INFO'}, "No objects found with the material " +
+ find_material_name + "!")
+ return {'FINISHED'} if not internal else 0
else:
# it's edit_mode, so select the polygons
@@ -253,7 +277,6 @@ def mu_select_by_material_name(self, find_material_name, extend_selection = Fals
objects = bpy.context.selected_editable_objects
for obj in objects:
- print("Obj:" + obj.name)
bpy.context.view_layer.objects.active = obj
if obj.type == 'MESH':
@@ -305,7 +328,7 @@ def mu_select_by_material_name(self, find_material_name, extend_selection = Fals
i += 1
- else:
+ elif not internal:
# Some object types are not supported
# mostly because don't really support selecting by material (like Font/Text objects)
# ore that they don't support multiple materials/are just "weird" (i.e. Meta balls)
@@ -316,10 +339,10 @@ def mu_select_by_material_name(self, find_material_name, extend_selection = Fals
bpy.context.view_layer.objects.active = active_object
- if not found_material:
+ if (not found_material) and (not internal):
self.report({'INFO'}, "Material " + find_material_name + " isn't assigned to anything!")
- return {'FINISHED'}
+ return {'FINISHED'} if not internal else 1
def mu_copy_material_to_others(self):
@@ -334,7 +357,7 @@ def mu_copy_material_to_others(self):
return {'FINISHED'}
-def mu_cleanmatslots(self):
+def mu_cleanmatslots(self, affect):
"""Clean the material slots of the seleceted objects"""
# check for edit mode
@@ -344,7 +367,16 @@ def mu_cleanmatslots(self):
edit_mode = True
bpy.ops.object.mode_set()
- objects = bpy.context.selected_editable_objects
+ objects = []
+
+ if affect == 'ACTIVE':
+ objects = [active_object]
+ elif affect == 'SELECTED':
+ objects = bpy.context.selected_editable_objects
+ elif affect == 'SCENE':
+ objects = bpy.context.scene.objects
+ else: # affect == 'ALL'
+ objects = bpy.data.objects
for obj in objects:
used_mat_index = [] # we'll store used materials indices here
@@ -602,3 +634,51 @@ def mu_change_material_link(self, link, affect, override_data_material = False):
index = index + 1
return {'FINISHED'}
+
+def mu_join_objects(self, materials):
+ """Join objects together based on their material"""
+
+ for material in materials:
+ mu_select_by_material_name(self, material, False, True)
+
+ bpy.ops.object.join()
+
+ return {'FINISHED'}
+
+def mu_set_auto_smooth(self, angle, affect, set_smooth_shading):
+ """Set Auto smooth values for selected objects"""
+ # Inspired by colkai
+
+ objects = []
+ objects_affected = 0
+
+ if affect == "ACTIVE":
+ objects = [bpy.context.active_object]
+ elif affect == "SELECTED":
+ objects = bpy.context.selected_editable_objects
+ elif affect == "SCENE":
+ objects = bpy.context.scene.objects
+ elif affect == "ALL":
+ objects = bpy.data.objects
+
+ if len(objects) == 0:
+ self.report({'WARNING'}, 'No objects available to set Auto Smooth on')
+ return {'CANCELLED'}
+
+ for object in objects:
+ if object.type == "MESH":
+ if set_smooth_shading:
+ for poly in object.data.polygons:
+ poly.use_smooth = True
+
+ #bpy.ops.object.shade_smooth()
+
+ object.data.use_auto_smooth = 1
+ object.data.auto_smooth_angle = angle # 35 degrees as radians
+
+ objects_affected += 1
+
+ self.report({'INFO'}, 'Auto smooth angle set to %.0f° on %d of %d objects' %
+ (degrees(angle), objects_affected, len(objects)))
+
+ return {'FINISHED'}
diff --git a/materials_utils/menus.py b/materials_utils/menus.py
index faf6da0c..2e444298 100644
--- a/materials_utils/menus.py
+++ b/materials_utils/menus.py
@@ -2,6 +2,7 @@ import bpy
from .functions import *
from .operators import *
+from .preferences import *
# -----------------------------------------------------------------------------
# menu classes
@@ -16,21 +17,48 @@ class VIEW3D_MT_materialutilities_assign_material(bpy.types.Menu):
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
+ edit_mode = False
+
+ materials = bpy.data.materials.items()
bl_id = VIEW3D_OT_materialutilities_assign_material_object.bl_idname
obj = context.object
+ mu_prefs = materialutilities_get_preferences(context)
if (not obj is None) and obj.mode == 'EDIT':
bl_id = VIEW3D_OT_materialutilities_assign_material_edit.bl_idname
+ edit_mode = True
+
+ if len(materials) > mu_prefs.search_show_limit:
+ op = layout.operator(bl_id,
+ text = 'Search',
+ icon = 'VIEWZOOM')
+ op.material_name = ""
+ op.new_material = False
+ op.show_dialog = True
+ if not edit_mode:
+ op.override_type = mu_prefs.override_type
+
+ op = layout.operator(bl_id,
+ text = "Add New Material",
+ icon = 'ADD')
+ op.material_name = mu_new_material_name(mu_prefs.new_material_name)
+ op.new_material = True
+ op.show_dialog = True
+ if not edit_mode:
+ op.override_type = mu_prefs.override_type
- for material_name, material in bpy.data.materials.items():
- layout.operator(bl_id,
- text = material_name,
- icon_value = material.preview.icon_id).material_name = material_name
+ layout.separator()
- layout.operator(bl_id,
- text = "Add New Material",
- icon = 'ADD').material_name = "Unnamed material"
+ for material_name, material in materials:
+ op = layout.operator(bl_id,
+ text = material_name,
+ icon_value = material.preview.icon_id)
+ op.material_name = material_name
+ op.new_material = False
+ op.show_dialog = False
+ if not edit_mode:
+ op.override_type = mu_prefs.override_type
class VIEW3D_MT_materialutilities_clean_slots(bpy.types.Menu):
@@ -65,19 +93,34 @@ class VIEW3D_MT_materialutilities_select_by_material(bpy.types.Menu):
def draw(self, context):
layout = self.layout
+ bl_id = VIEW3D_OT_materialutilities_select_by_material_name.bl_idname
obj = context.object
+ mu_prefs = materialutilities_get_preferences(context)
+
layout.label
if obj is None or obj.mode == 'OBJECT':
+ materials = bpy.data.materials.items()
+
+ if len(materials) > mu_prefs.search_show_limit:
+ layout.operator(bl_id,
+ text = 'Search',
+ icon = 'VIEWZOOM'
+ ).show_dialog = True
+
+ layout.separator()
+
#show all used materials in entire blend file
- for material_name, material in bpy.data.materials.items():
+ for material_name, material in materials:
# There's no point in showing materials with 0 users
# (It will still show materials with fake user though)
if material.users > 0:
- layout.operator(VIEW3D_OT_materialutilities_select_by_material_name.bl_idname,
+ op = layout.operator(bl_id,
text = material_name,
icon_value = material.preview.icon_id
- ).material_name = material_name
+ )
+ op.material_name = material_name
+ op.show_dialog = False
elif obj.mode == 'EDIT':
objects = context.selected_editable_objects
@@ -93,10 +136,12 @@ class VIEW3D_MT_materialutilities_select_by_material(bpy.types.Menu):
if material.name in materials_added:
continue
- layout.operator(VIEW3D_OT_materialutilities_select_by_material_name.bl_idname,
- text = material.name,
- icon_value = material.preview.icon_id
- ).material_name = material.name
+ op = layout.operator(bl_id,
+ text = material.name,
+ icon_value = material.preview.icon_id
+ )
+ op.material_name = material.name
+ op.show_dialog = False
materials_added.append(material.name)
@@ -107,6 +152,7 @@ class VIEW3D_MT_materialutilities_specials(bpy.types.Menu):
bl_label = "Specials"
def draw(self, context):
+ mu_prefs = materialutilities_get_preferences(context)
layout = self.layout
#layout.operator(VIEW3D_OT_materialutilities_set_new_material_name.bl_idname, icon = "SETTINGS")
@@ -117,6 +163,17 @@ class VIEW3D_MT_materialutilities_specials(bpy.types.Menu):
text = "Merge Base Names",
icon = "GREASEPENCIL")
+ layout.operator(MATERIAL_OT_materialutilities_join_objects.bl_idname,
+ text = "Join by material",
+ icon = "OBJECT_DATAMODE")
+
+ layout.separator()
+
+ op = layout.operator(MATERIAL_OT_materialutilities_auto_smooth_angle.bl_idname,
+ text = "Set Auto Smooth",
+ icon = "SHADING_SOLID")
+ op.affect = mu_prefs.set_smooth_affect
+ op.angle = mu_prefs.auto_smooth_angle
class VIEW3D_MT_materialutilities_main(bpy.types.Menu):
"""Main menu for Material Utilities"""
@@ -126,6 +183,7 @@ class VIEW3D_MT_materialutilities_main(bpy.types.Menu):
def draw(self, context):
obj = context.object
+ mu_prefs = materialutilities_get_preferences(context)
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
@@ -150,13 +208,17 @@ class VIEW3D_MT_materialutilities_main(bpy.types.Menu):
text = 'Replace Material',
icon = 'OVERLAY')
- layout.operator(VIEW3D_OT_materialutilities_fake_user_set.bl_idname,
+ op = layout.operator(VIEW3D_OT_materialutilities_fake_user_set.bl_idname,
text = 'Set Fake User',
icon = 'FAKE_USER_OFF')
+ op.fake_user = mu_prefs.fake_user
+ op.affect = mu_prefs.fake_user_affect
- layout.operator(VIEW3D_OT_materialutilities_change_material_link.bl_idname,
+ op = layout.operator(VIEW3D_OT_materialutilities_change_material_link.bl_idname,
text = 'Change Material Link',
icon = 'LINKED')
+ op.link_to = mu_prefs.link_to
+ op.affect = mu_prefs.link_to_affect
layout.separator()
layout.menu(VIEW3D_MT_materialutilities_specials.bl_idname,
diff --git a/materials_utils/operators.py b/materials_utils/operators.py
index 6bb451e6..a91c2301 100644
--- a/materials_utils/operators.py
+++ b/materials_utils/operators.py
@@ -1,12 +1,20 @@
import bpy
from bpy.types import Operator
-from bpy.props import StringProperty, BoolProperty, EnumProperty
+from bpy.props import (
+ StringProperty,
+ BoolProperty,
+ EnumProperty,
+ IntProperty,
+ FloatProperty
+ )
from .enum_values import *
from .functions import *
+from math import radians
+
# -----------------------------------------------------------------------------
# operator classes
@@ -23,15 +31,49 @@ class VIEW3D_OT_materialutilities_assign_material_edit(bpy.types.Operator):
default = "",
maxlen = 63
)
+ new_material: BoolProperty(
+ name = '',
+ description = 'Add a new material, enter the name in the box',
+ default = False
+ )
+ show_dialog: BoolProperty(
+ name = 'Show Dialog',
+ default = False
+ )
@classmethod
def poll(cls, context):
return context.active_object is not None
+ def invoke(self, context, event):
+ if self.show_dialog:
+ return context.window_manager.invoke_props_dialog(self)
+ else:
+ return self.execute(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ col = layout.column()
+ row = col.split(factor = 0.9, align = True)
+
+ if self.new_material:
+ row.prop(self, "material_name")
+ else:
+ row.prop_search(self, "material_name", bpy.data, "materials")
+
+ row.prop(self, "new_material", expand = True, icon = 'ADD')
+
def execute(self, context):
material_name = self.material_name
- return mu_assign_material(self, material_name, 'APPEND_MATERIAL')
+ if self.new_material:
+ material_name = mu_new_material_name(material_name)
+ elif material_name == "":
+ self.report({'WARNING'}, "No Material Name given!")
+ return {'CANCELLED'}
+
+ return mu_assign_material(self, material_name, 'APPEND_MATERIAL')
class VIEW3D_OT_materialutilities_assign_material_object(bpy.types.Operator):
@@ -45,7 +87,7 @@ class VIEW3D_OT_materialutilities_assign_material_object(bpy.types.Operator):
material_name: StringProperty(
name = 'Material Name',
description = 'Name of Material to assign to current selection',
- default = "Unnamed Material",
+ default = "",
maxlen = 63
)
override_type: EnumProperty(
@@ -53,24 +95,55 @@ class VIEW3D_OT_materialutilities_assign_material_object(bpy.types.Operator):
description = '',
items = mu_override_type_enums
)
+ new_material: BoolProperty(
+ name = '',
+ description = 'Add a new material, enter the name in the box',
+ default = False
+ )
+ show_dialog: BoolProperty(
+ name = 'Show Dialog',
+ default = False
+ )
@classmethod
def poll(cls, context):
return len(context.selected_editable_objects) > 0
+ def invoke(self, context, event):
+ if self.show_dialog:
+ return context.window_manager.invoke_props_dialog(self)
+ else:
+ return self.execute(context)
+
def draw(self, context):
layout = self.layout
- layout.prop_search(self, "material_name", bpy.data, "materials")
+
+ col = layout.column()
+ row = col.split(factor=0.9, align = True)
+
+ if self.new_material:
+ row.prop(self, "material_name")
+ else:
+ row.prop_search(self, "material_name", bpy.data, "materials")
+
+ row.prop(self, "new_material", expand = True, icon = 'ADD')
layout.prop(self, "override_type")
+
def execute(self, context):
material_name = self.material_name
override_type = self.override_type
+
+ if self.new_material:
+ material_name = mu_new_material_name(material_name)
+ elif material_name == "":
+ self.report({'WARNING'}, "No Material Name given!")
+ return {'CANCELLED'}
+
result = mu_assign_material(self, material_name, override_type)
return result
-
class VIEW3D_OT_materialutilities_select_by_material_name(bpy.types.Operator):
"""Select geometry that has the chosen material assigned to it
(See the operator panel [F9] for more options)"""
@@ -88,11 +161,21 @@ class VIEW3D_OT_materialutilities_select_by_material_name(bpy.types.Operator):
description = 'Name of Material to find and Select',
maxlen = 63
)
+ show_dialog: BoolProperty(
+ name = 'Show Dialog',
+ default = False
+ )
@classmethod
def poll(cls, context):
return len(context.visible_objects) > 0
+ def invoke(self, context, event):
+ if self.show_dialog:
+ return context.window_manager.invoke_props_dialog(self)
+ else:
+ return self.execute(context)
+
def draw(self, context):
layout = self.layout
layout.prop_search(self, "material_name", bpy.data, "materials")
@@ -127,12 +210,32 @@ class VIEW3D_OT_materialutilities_clean_material_slots(bpy.types.Operator):
bl_label = "Clean Material Slots (Material Utilities)"
bl_options = {'REGISTER', 'UNDO'}
+ # affect: EnumProperty(
+ # name = "Affect",
+ # description = "Which objects material slots should be cleaned",
+ # items = mu_clean_slots_enums,
+ # default = 'ACTIVE'
+ # )
+
+ only_active: BoolProperty(
+ name = 'Only active object',
+ description = 'Only remove the material slots for the active object ' +
+ '(otherwise do it for every selected object)',
+ default = True
+ )
+
@classmethod
def poll(cls, context):
return len(context.selected_editable_objects) > 0
+ def draw(self, context):
+ layout = self.layout
+ layout.prop(self, "only_active", icon = "PIVOT_ACTIVE")
+
def execute(self, context):
- return mu_cleanmatslots(self)
+ affect = "ACTIVE" if self.only_active else "SELECTED"
+
+ return mu_cleanmatslots(self, affect)
class VIEW3D_OT_materialutilities_remove_material_slot(bpy.types.Operator):
@@ -146,7 +249,8 @@ class VIEW3D_OT_materialutilities_remove_material_slot(bpy.types.Operator):
only_active: BoolProperty(
name = 'Only active object',
description = 'Only remove the active material slot for the active object ' +
- '(otherwise do it for every selected object)'
+ '(otherwise do it for every selected object)',
+ default = True
)
@classmethod
@@ -171,7 +275,8 @@ class VIEW3D_OT_materialutilities_remove_all_material_slots(bpy.types.Operator):
only_active: BoolProperty(
name = 'Only active object',
description = 'Only remove the material slots for the active object ' +
- '(otherwise do it for every selected object)'
+ '(otherwise do it for every selected object)',
+ default = True
)
@classmethod
@@ -243,10 +348,10 @@ class VIEW3D_OT_materialutilities_fake_user_set(bpy.types.Operator):
default = 'TOGGLE'
)
- materials: EnumProperty(
- name = "Materials",
+ affect: EnumProperty(
+ name = "Affect",
description = "Which materials of objects to affect",
- items = mu_fake_user_materials_enums,
+ items = mu_fake_user_affect_enums,
default = 'UNUSED'
)
@@ -259,13 +364,13 @@ class VIEW3D_OT_materialutilities_fake_user_set(bpy.types.Operator):
layout.prop(self, "fake_user", expand = True)
layout.separator()
- layout.prop(self, "materials")
+ layout.prop(self, "affect")
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
def execute(self, context):
- return mu_set_fake_user(self, self.fake_user, self.materials)
+ return mu_set_fake_user(self, self.fake_user, self.affect)
class VIEW3D_OT_materialutilities_change_material_link(bpy.types.Operator):
@@ -290,7 +395,7 @@ class VIEW3D_OT_materialutilities_change_material_link(bpy.types.Operator):
)
affect: EnumProperty(
- name = "Materials",
+ name = "Affect",
description = "Which materials of objects to affect",
items = mu_link_affect_enums,
default = 'SELECTED'
@@ -428,7 +533,7 @@ class MATERIAL_OT_materialutilities_merge_base_names(bpy.types.Operator):
@classmethod
def poll(self, context):
- return len(context.selected_editable_objects) > 0
+ return (context.mode == 'OBJECT') and (len(context.visible_objects) > 0)
def draw(self, context):
layout = self.layout
@@ -492,7 +597,7 @@ class MATERIAL_OT_materialutilities_material_slot_move(bpy.types.Operator):
@classmethod
def poll(self, context):
- # would prefer to access sely.movement here, but can'-'t..
+ # would prefer to access self.movement here, but can't..
obj = context.active_object
if not obj:
return False
@@ -523,3 +628,115 @@ class MATERIAL_OT_materialutilities_material_slot_move(bpy.types.Operator):
self.report({'INFO'}, active_material.name + ' moved to ' + self.movement.lower())
return {'FINISHED'}
+
+
+
+class MATERIAL_OT_materialutilities_join_objects(bpy.types.Operator):
+ """Join objects that have the same (selected) material(s)"""
+
+ bl_idname = "material.materialutilities_join_objects"
+ bl_label = "Join by material (Material Utilities)"
+ bl_description = "Join objects that share the same material"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ material_name: StringProperty(
+ name = "Material",
+ default = "",
+ description = 'Material to use to join objects'
+ )
+ is_auto: BoolProperty(
+ name = "Auto Join",
+ description = "Join objects for all materials"
+ )
+
+ is_not_undo = True
+ material_error = [] # collect mat for warning messages
+
+
+ @classmethod
+ def poll(self, context):
+ # This operator only works in Object mode
+ return (context.mode == 'OBJECT') and (len(context.visible_objects) > 0)
+
+ def draw(self, context):
+ layout = self.layout
+
+ box_1 = layout.box()
+ box_1.prop_search(self, "material_name", bpy.data, "materials")
+ box_1.enabled = not self.is_auto
+ layout.separator()
+
+ layout.prop(self, "is_auto", text = "Auto Join", icon = "SYNTAX_ON")
+
+ def invoke(self, context, event):
+ self.is_not_undo = True
+ return context.window_manager.invoke_props_dialog(self)
+
+ def execute(self, context):
+ # Reset Material errors, otherwise we risk reporting errors erroneously..
+ self.material_error = []
+ materials = []
+
+ if not self.is_auto:
+ if self.material_name == "":
+ self.report({'WARNING'}, "No Material Name given!")
+
+ self.is_not_undo = False
+ return {'CANCELLED'}
+ materials = [self.material_name]
+ else:
+ materials = bpy.data.materials.keys()
+
+ result = mu_join_objects(self, materials)
+ self.is_not_undo = False
+
+ return result
+
+
+class MATERIAL_OT_materialutilities_auto_smooth_angle(bpy.types.Operator):
+ """Set Auto smooth values for selected objects"""
+ # Inspired by colkai
+
+ bl_idname = "view3d.materialutilities_auto_smooth_angle"
+ bl_label = "Set Auto Smooth Angle (Material Utilities)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ affect: EnumProperty(
+ name = "Affect",
+ description = "Which objects of to affect",
+ items = mu_affect_enums,
+ default = 'SELECTED'
+ )
+ angle: FloatProperty(
+ name = "Angle",
+ description = "Maximum angle between face normals that will be considered as smooth",
+ subtype = 'ANGLE',
+ min = 0,
+ max = radians(180),
+ default = radians(35)
+ )
+ set_smooth_shading: BoolProperty(
+ name = "Set Smooth",
+ description = "Set Smooth shading for the affected objects\n"
+ "This overrides the currenth smooth/flat shading that might be set to different parts of the object",
+ default = True
+ )
+
+ @classmethod
+ def poll(cls, context):
+ return (len(bpy.data.objects) > 0) and (context.mode == 'OBJECT')
+
+ def invoke(self, context, event):
+ self.is_not_undo = True
+ return context.window_manager.invoke_props_dialog(self)
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.prop(self, "angle")
+ layout.prop(self, "affect")
+
+ layout.prop(self, "set_smooth_shading", icon = "BLANK1")
+
+ def execute(self, context):
+ return mu_set_auto_smooth(self, self.angle, self.affect, self.set_smooth_shading)
diff --git a/materials_utils/preferences.py b/materials_utils/preferences.py
new file mode 100644
index 00000000..7fdbd9ff
--- /dev/null
+++ b/materials_utils/preferences.py
@@ -0,0 +1,118 @@
+import bpy
+
+from bpy.types import (
+ AddonPreferences,
+ PropertyGroup,
+ )
+from bpy.props import (
+ StringProperty,
+ BoolProperty,
+ EnumProperty,
+ IntProperty,
+ FloatProperty
+ )
+from math import radians
+
+from .enum_values import *
+
+# Addon Preferences
+class VIEW3D_MT_materialutilities_preferences(AddonPreferences):
+ bl_idname = __package__
+
+ new_material_name: StringProperty(
+ name = "New Material name",
+ description = "What Base name pattern to use for a new created Material\n"
+ "It is appended by an automatic numeric pattern depending\n"
+ "on the number of Scene's materials containing the Base",
+ default = "Unnamed Material",
+ )
+ override_type: EnumProperty(
+ name = 'Assignment method',
+ description = '',
+ items = mu_override_type_enums
+ )
+ fake_user: EnumProperty(
+ name = "Set Fake User",
+ description = "Default option for the Set Fake User (Turn fake user on or off)",
+ items = mu_fake_user_set_enums,
+ default = 'TOGGLE'
+ )
+ fake_user_affect: EnumProperty(
+ name = "Affect",
+ description = "Which materials of objects to affect",
+ items = mu_fake_user_affect_enums,
+ default = 'UNUSED'
+ )
+ link_to: EnumProperty(
+ name = "Change Material Link To",
+ description = "Default option for the Change Material Link operator",
+ items = mu_link_to_enums,
+ default = 'OBJECT'
+ )
+ link_to_affect: EnumProperty(
+ name = "Affect",
+ description = "Which materials of objects to affect by default with Change Material Link",
+ items = mu_link_affect_enums,
+ default = 'SELECTED'
+ )
+ search_show_limit: IntProperty(
+ name = "Show 'Search' Limit",
+ description = "How many materials should there be before the 'Search' option is shown "
+ "in the Assign Material and Select By Material menus\n"
+ "Set it to 0 to always show 'Search'",
+ min = 0,
+ default = 0
+ )
+
+ set_smooth_affect: EnumProperty(
+ name = "Set Auto Smooth Affect",
+ description = "Which objects to affect",
+ items = mu_affect_enums,
+ default = 'SELECTED'
+ )
+ auto_smooth_angle: FloatProperty(
+ name = "Auto Smooth Angle",
+ description = "Maximum angle between face normals that will be considered as smooth",
+ subtype = 'ANGLE',
+ min = 0,
+ max = radians(180),
+ default = radians(35)
+ )
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ box = layout.box()
+ box.label(text = "Defaults")
+
+ a = box.box()
+ a.label(text = "Assign Material")
+ a.prop(self, "new_material_name", icon = "MATERIAL")
+ a.prop(self, "override_type", expand = False)
+
+ b = box.box()
+ b.label(text = "Set Fake User")
+ b.row().prop(self, "fake_user", expand = False)
+ b.row().prop(self, "fake_user_affect", expand = False)
+
+ c = box.box()
+ c.label(text = "Set Link To")
+ c.row().prop(self, "link_to", expand = False)
+ c.row().prop(self, "link_to_affect", expand = False)
+
+ d = box.box()
+ d.label(text = "Set Auto Smooth")
+ d.row().prop(self, "auto_smooth_angle", expand = False)
+ d.row().prop(self, "set_smooth_affect", expand = False)
+
+ box = layout.box()
+ box.label(text = "Miscellaneous")
+
+ #col = box.column()
+ #row = col.split(factor = 0.5)
+ box.prop(self, "search_show_limit", expand = False)
+
+
+def materialutilities_get_preferences(context):
+ return context.preferences.addons[__package__].preferences