materials_utils: add search and preferences T67990
This commit is contained in:
parent
6c882f7d81
commit
4fa93f6eb1
|
@ -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()
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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'}
|
||||
|
|
|
@ -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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
layout.operator(bl_id,
|
||||
text = "Add New Material",
|
||||
icon = 'ADD').material_name = "Unnamed material"
|
||||
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
|
||||
|
||||
layout.separator()
|
||||
|
||||
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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue