Pose Library: update for rename of asset_library to asset_library_ref
[blender-addons.git] / space_view3d_modifier_tools.py
blob1fb64635ccfdd28d42b41c3054dd7c36364c1ea8
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
18 # by meta-androcto, saidenka #
20 bl_info = {
21 "name": "Modifier Tools",
22 "author": "Meta Androcto, saidenka",
23 "version": (0, 2, 6),
24 "blender": (2, 80, 0),
25 "location": "Properties > Modifiers",
26 "description": "Modifiers Specials Show/Hide/Apply Selected",
27 "warning": "",
28 "doc_url": "{BLENDER_MANUAL_URL}/addons/interface/modifier_tools.html",
29 "category": "Interface"
32 import bpy
33 from bpy.types import Operator
36 class ApplyAllModifiers(Operator):
37 bl_idname = "object.apply_all_modifiers"
38 bl_label = "Apply All Modifiers"
39 bl_description = ("Apply All modifiers of the selected object(s) \n"
40 "Active object has to have modifiers for the option to show up")
41 bl_options = {'REGISTER', 'UNDO'}
43 def execute(self, context):
44 is_select, is_mod = False, False
45 message_a, message_b = "", ""
46 # collect names for objects failed to apply modifiers
47 collect_names = []
49 for obj in bpy.context.selected_objects:
50 is_select = True
52 # copying context for the operator's override
53 contx = bpy.context.copy()
54 contx['object'] = obj
56 for mod in obj.modifiers[:]:
57 contx['modifier'] = mod
58 is_mod = True
59 try:
60 bpy.ops.object.modifier_apply(
61 contx,
62 modifier=contx['modifier'].name
64 except:
65 obj_name = getattr(obj, "name", "NO NAME")
66 collect_names.append(obj_name)
67 message_b = True
68 pass
70 if is_select:
71 if is_mod:
72 message_a = "Applying modifiers on all Selected Objects"
73 else:
74 message_a = "No Modifiers on Selected Objects"
75 else:
76 self.report({"INFO"}, "No Selection. No changes applied")
77 return {'CANCELLED'}
79 # applying failed for some objects, show report
80 message_obj = (",".join(collect_names) if collect_names and
81 len(collect_names) < 8 else "some objects (Check System Console)")
83 self.report({"INFO"},
84 (message_a if not message_b else
85 "Applying modifiers failed for {}".format(message_obj)))
87 if (collect_names and message_obj == "some objects (Check System Console)"):
88 print("\n[Modifier Tools]\n\nApplying failed on:"
89 "\n\n{} \n".format(", ".join(collect_names)))
91 return {'FINISHED'}
94 class DeleteAllModifiers(Operator):
95 bl_idname = "object.delete_all_modifiers"
96 bl_label = "Remove All Modifiers"
97 bl_description = "Remove All modifiers of the selected object(s)"
98 bl_options = {'REGISTER', 'UNDO'}
100 def invoke(self, context, event):
101 return context.window_manager.invoke_confirm(self, event)
103 def execute(self, context):
104 is_select, is_mod = False, False
105 message_a = ""
107 for obj in context.selected_objects:
108 is_select = True
109 modifiers = obj.modifiers[:]
110 for modi in modifiers:
111 is_mod = True
112 obj.modifiers.remove(modi)
114 if is_select:
115 if is_mod:
116 message_a = "Removing modifiers on all Selected Objects"
117 else:
118 message_a = "No Modifiers on Selected Objects"
119 else:
120 self.report({"INFO"}, "No Selection. No changes applied")
121 return {'CANCELLED'}
123 self.report({"INFO"}, message_a)
124 return {'FINISHED'}
127 class ToggleApplyModifiersView(Operator):
128 bl_idname = "object.toggle_apply_modifiers_view"
129 bl_label = "Toggle Visibility of Modifiers"
130 bl_description = "Shows/Hide modifiers of the active / selected object(s) in the 3D View"
131 bl_options = {'REGISTER'}
133 @classmethod
134 def poll(cls, context):
135 return context.active_object is not None
137 def execute(self, context):
138 is_apply = True
139 message_a = ""
141 # avoid toggling not exposed modifiers (currently only Collision, see T53406)
142 skip_type = ["COLLISION"] # types of modifiers to skip
143 skipped = set() # collect names
144 count_modifiers = 0 # check for message_a (all skipped)
146 # check if the active object has only one non exposed modifier as the logic will fail
147 if len(context.active_object.modifiers) == 1 and \
148 context.active_object.modifiers[0].type in skip_type:
150 for obj in context.selected_objects:
151 for mod in obj.modifiers:
152 if mod.type in skip_type:
153 skipped.add(mod.name)
154 continue
156 if mod.show_viewport:
157 is_apply = False
158 break
159 else:
160 for mod in context.active_object.modifiers:
161 if mod.type in skip_type:
162 skipped.add(mod.name)
163 continue
165 if mod.show_viewport:
166 is_apply = False
167 break
169 count_modifiers = len(context.active_object.modifiers)
170 # active object - no selection
171 for mod in context.active_object.modifiers:
172 if mod.type in skip_type:
173 count_modifiers -= 1
174 skipped.add(mod.name)
175 continue
177 mod.show_viewport = is_apply
179 for obj in context.selected_objects:
180 count_modifiers += len(obj.modifiers)
182 for mod in obj.modifiers:
183 if mod.type in skip_type:
184 skipped.add(mod.name)
185 count_modifiers -= 1
186 continue
188 mod.show_viewport = is_apply
190 message_a = "{} modifiers in the 3D View".format("Displaying" if is_apply else "Hiding")
192 if skipped:
193 message_a = "{}, {}".format(message_a, "skipping: " + ", ".join(skipped)) if \
194 count_modifiers > 0 else "No change of Modifiers' Visibility, all skipped"
196 self.report({"INFO"}, message_a)
198 return {'FINISHED'}
201 class ToggleAllShowExpanded(Operator):
202 bl_idname = "wm.toggle_all_show_expanded"
203 bl_label = "Expand/Collapse All Modifiers"
204 bl_description = "Expand/Collapse Modifier Stack"
205 bl_options = {'REGISTER'}
207 @classmethod
208 def poll(cls, context):
209 return context.active_object is not None
211 def execute(self, context):
212 obj = context.active_object
213 if (len(obj.modifiers)):
214 vs = 0
215 for mod in obj.modifiers:
216 if (mod.show_expanded):
217 vs += 1
218 else:
219 vs -= 1
220 is_close = False
221 if (0 < vs):
222 is_close = True
223 for mod in obj.modifiers:
224 mod.show_expanded = not is_close
225 else:
226 self.report({'WARNING'}, "Not a single modifier to Expand/Collapse")
227 return {'CANCELLED'}
229 for area in context.screen.areas:
230 area.tag_redraw()
231 return {'FINISHED'}
234 # Menus #
235 def menu(self, context):
236 if (context.active_object):
237 if (len(context.active_object.modifiers)):
238 col = self.layout.column(align=True)
240 row = col.row(align=True)
241 row.operator(ApplyAllModifiers.bl_idname,
242 icon='IMPORT', text="Apply All")
243 row.operator(DeleteAllModifiers.bl_idname,
244 icon='X', text="Delete All")
246 row = col.row(align=True)
247 row.operator(ToggleApplyModifiersView.bl_idname,
248 icon='RESTRICT_VIEW_OFF',
249 text="Viewport Vis")
250 row.operator(ToggleAllShowExpanded.bl_idname,
251 icon='FULLSCREEN_ENTER',
252 text="Toggle Stack")
255 def menu_func(self, context):
256 if (context.active_object):
257 if (len(context.active_object.modifiers)):
258 layout = self.layout
259 layout.separator()
260 layout.operator(ApplyAllModifiers.bl_idname,
261 icon='IMPORT',
262 text="Apply All Modifiers")
264 # Register
265 classes = [
266 ApplyAllModifiers,
267 DeleteAllModifiers,
268 ToggleApplyModifiersView,
269 ToggleAllShowExpanded,
272 def register():
273 from bpy.utils import register_class
274 for cls in classes:
275 register_class(cls)
277 # Add "Specials" menu to the "Modifiers" menu
278 bpy.types.DATA_PT_modifiers.prepend(menu)
280 # Add apply operator to the Apply 3D View Menu
281 bpy.types.VIEW3D_MT_object_apply.append(menu_func)
284 def unregister():
285 # Remove "Specials" menu from the "Modifiers" menu.
286 bpy.types.DATA_PT_modifiers.remove(menu)
288 # Remove apply operator to the Apply 3D View Menu
289 bpy.types.VIEW3D_MT_object_apply.remove(menu_func)
291 from bpy.utils import unregister_class
292 for cls in reversed(classes):
293 unregister_class(cls)
295 if __name__ == "__main__":
296 register()