Add a menu similar to the "Object > Constraints" menu that allows adding, copying, and clearing modifiers. The "copy all modifiers to selected" and "clear modifiers" operators are new, to mirror the functionality we already have for constraints. The "Add" menu is the same that's used in the property editor. In the 3D view, modifiers are always added to all selected objects. Part of #120230 Pull Request: https://projects.blender.org/blender/blender/pulls/121286
305 lines
13 KiB
Python
305 lines
13 KiB
Python
# SPDX-FileCopyrightText: 2009-2023 Blender Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
import bpy
|
|
from bpy.types import Panel, Menu, Operator
|
|
|
|
|
|
class ModifierButtonsPanel:
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "modifier"
|
|
bl_options = {'HIDE_HEADER'}
|
|
|
|
|
|
class ModifierAddMenu:
|
|
MODIFIER_TYPES_TO_LABELS = {
|
|
enum_it.identifier: enum_it.name
|
|
for enum_it in bpy.types.Modifier.bl_rna.properties["type"].enum_items_static
|
|
}
|
|
MODIFIER_TYPES_TO_ICONS = {
|
|
enum_it.identifier: enum_it.icon
|
|
for enum_it in bpy.types.Modifier.bl_rna.properties["type"].enum_items_static
|
|
}
|
|
MODIFIER_TYPES_I18N_CONTEXT = bpy.types.Modifier.bl_rna.properties["type"].translation_context
|
|
|
|
@classmethod
|
|
def operator_modifier_add(cls, layout, mod_type):
|
|
layout.operator(
|
|
"object.modifier_add",
|
|
text=cls.MODIFIER_TYPES_TO_LABELS[mod_type],
|
|
# Although these are operators, the label actually comes from an (enum) property,
|
|
# so the property's translation context must be used here.
|
|
text_ctxt=cls.MODIFIER_TYPES_I18N_CONTEXT,
|
|
icon=cls.MODIFIER_TYPES_TO_ICONS[mod_type],
|
|
).type = mod_type
|
|
|
|
|
|
class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
|
bl_label = "Modifiers"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
return ob and ob.type != 'GPENCIL'
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
layout.operator("wm.call_menu", text="Add Modifier", icon='ADD').name = "OBJECT_MT_modifier_add"
|
|
layout.template_modifiers()
|
|
|
|
|
|
class OBJECT_MT_modifier_add(ModifierAddMenu, Menu):
|
|
bl_label = "Add Modifier"
|
|
bl_options = {'SEARCH_ON_KEY_PRESS'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
ob = context.object
|
|
if not ob:
|
|
return
|
|
ob_type = ob.type
|
|
geometry_nodes_supported = ob_type in {
|
|
'MESH', 'CURVE', 'CURVES',
|
|
'FONT', 'VOLUME', 'POINTCLOUD', 'GREASEPENCIL',
|
|
}
|
|
|
|
if layout.operator_context == 'EXEC_REGION_WIN':
|
|
layout.operator_context = 'INVOKE_REGION_WIN'
|
|
layout.operator("WM_OT_search_single_menu", text="Search...",
|
|
icon='VIEWZOOM').menu_idname = "OBJECT_MT_modifier_add"
|
|
layout.separator()
|
|
|
|
layout.operator_context = 'INVOKE_REGION_WIN'
|
|
|
|
if geometry_nodes_supported:
|
|
self.operator_modifier_add(layout, 'NODES')
|
|
layout.separator()
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE', 'GREASEPENCIL'}:
|
|
layout.menu("OBJECT_MT_modifier_add_edit")
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'VOLUME', 'GREASEPENCIL'}:
|
|
layout.menu("OBJECT_MT_modifier_add_generate")
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE', 'VOLUME', 'GREASEPENCIL'}:
|
|
layout.menu("OBJECT_MT_modifier_add_deform")
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
|
layout.menu("OBJECT_MT_modifier_add_physics")
|
|
if ob_type in {'GREASEPENCIL'}:
|
|
layout.menu("OBJECT_MT_modifier_add_color")
|
|
|
|
if geometry_nodes_supported:
|
|
layout.menu_contents("OBJECT_MT_modifier_add_root_catalogs")
|
|
|
|
|
|
class OBJECT_MT_modifier_add_edit(ModifierAddMenu, Menu):
|
|
bl_label = "Edit"
|
|
bl_options = {'SEARCH_ON_KEY_PRESS'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
ob_type = context.object.type
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'DATA_TRANSFER')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
|
self.operator_modifier_add(layout, 'MESH_CACHE')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
|
self.operator_modifier_add(layout, 'MESH_SEQUENCE_CACHE')
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'NORMAL_EDIT')
|
|
self.operator_modifier_add(layout, 'WEIGHTED_NORMAL')
|
|
self.operator_modifier_add(layout, 'UV_PROJECT')
|
|
self.operator_modifier_add(layout, 'UV_WARP')
|
|
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_EDIT')
|
|
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_MIX')
|
|
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_PROXIMITY')
|
|
if ob_type == 'GREASEPENCIL':
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_TEXTURE')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_TIME')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_VERTEX_WEIGHT_PROXIMITY')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_VERTEX_WEIGHT_ANGLE')
|
|
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
|
|
|
|
|
|
class OBJECT_MT_modifier_add_generate(ModifierAddMenu, Menu):
|
|
bl_label = "Generate"
|
|
bl_options = {'SEARCH_ON_KEY_PRESS'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
ob_type = context.object.type
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
|
self.operator_modifier_add(layout, 'ARRAY')
|
|
self.operator_modifier_add(layout, 'BEVEL')
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'BOOLEAN')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
|
self.operator_modifier_add(layout, 'BUILD')
|
|
self.operator_modifier_add(layout, 'DECIMATE')
|
|
self.operator_modifier_add(layout, 'EDGE_SPLIT')
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'MASK')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
|
self.operator_modifier_add(layout, 'MIRROR')
|
|
if ob_type == 'VOLUME':
|
|
self.operator_modifier_add(layout, 'MESH_TO_VOLUME')
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'MULTIRES')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
|
self.operator_modifier_add(layout, 'REMESH')
|
|
self.operator_modifier_add(layout, 'SCREW')
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'SKIN')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
|
self.operator_modifier_add(layout, 'SOLIDIFY')
|
|
self.operator_modifier_add(layout, 'SUBSURF')
|
|
self.operator_modifier_add(layout, 'TRIANGULATE')
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'VOLUME_TO_MESH')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
|
self.operator_modifier_add(layout, 'WELD')
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'WIREFRAME')
|
|
if ob_type == 'GREASEPENCIL':
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_ARRAY')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_BUILD')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_DASH')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_ENVELOPE')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_LENGTH')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_MIRROR')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_MULTIPLY')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_OUTLINE')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_SIMPLIFY')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_SUBDIV')
|
|
self.operator_modifier_add(layout, 'LINEART')
|
|
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
|
|
|
|
|
|
class OBJECT_MT_modifier_add_deform(ModifierAddMenu, Menu):
|
|
bl_label = "Deform"
|
|
bl_options = {'SEARCH_ON_KEY_PRESS'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
ob_type = context.object.type
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
|
self.operator_modifier_add(layout, 'ARMATURE')
|
|
self.operator_modifier_add(layout, 'CAST')
|
|
self.operator_modifier_add(layout, 'CURVE')
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'DISPLACE')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
|
self.operator_modifier_add(layout, 'HOOK')
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'LAPLACIANDEFORM')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
|
self.operator_modifier_add(layout, 'LATTICE')
|
|
self.operator_modifier_add(layout, 'MESH_DEFORM')
|
|
self.operator_modifier_add(layout, 'SHRINKWRAP')
|
|
self.operator_modifier_add(layout, 'SIMPLE_DEFORM')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
|
|
self.operator_modifier_add(layout, 'SMOOTH')
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'CORRECTIVE_SMOOTH')
|
|
self.operator_modifier_add(layout, 'LAPLACIANSMOOTH')
|
|
self.operator_modifier_add(layout, 'SURFACE_DEFORM')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
|
self.operator_modifier_add(layout, 'WARP')
|
|
self.operator_modifier_add(layout, 'WAVE')
|
|
if ob_type == 'VOLUME':
|
|
self.operator_modifier_add(layout, 'VOLUME_DISPLACE')
|
|
if ob_type == 'GREASEPENCIL':
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_ARMATURE')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_HOOK')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_LATTICE')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_NOISE')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_OFFSET')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_SHRINKWRAP')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_SMOOTH')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_THICKNESS')
|
|
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
|
|
|
|
|
|
class OBJECT_MT_modifier_add_physics(ModifierAddMenu, Menu):
|
|
bl_label = "Physics"
|
|
bl_options = {'SEARCH_ON_KEY_PRESS'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
ob_type = context.object.type
|
|
if ob_type == 'MESH':
|
|
self.operator_modifier_add(layout, 'CLOTH')
|
|
self.operator_modifier_add(layout, 'COLLISION')
|
|
self.operator_modifier_add(layout, 'DYNAMIC_PAINT')
|
|
self.operator_modifier_add(layout, 'EXPLODE')
|
|
self.operator_modifier_add(layout, 'FLUID')
|
|
self.operator_modifier_add(layout, 'OCEAN')
|
|
self.operator_modifier_add(layout, 'PARTICLE_INSTANCE')
|
|
self.operator_modifier_add(layout, 'PARTICLE_SYSTEM')
|
|
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
|
|
self.operator_modifier_add(layout, 'SOFT_BODY')
|
|
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
|
|
|
|
|
|
class OBJECT_MT_modifier_add_color(ModifierAddMenu, Menu):
|
|
bl_label = "Color"
|
|
bl_options = {'SEARCH_ON_KEY_PRESS'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
ob_type = context.object.type
|
|
if ob_type == 'GREASEPENCIL':
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_COLOR')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_TINT')
|
|
self.operator_modifier_add(layout, 'GREASE_PENCIL_OPACITY')
|
|
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
|
|
|
|
|
|
class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
|
|
bl_label = "Modifiers"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
return ob and ob.type == 'GPENCIL'
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
layout.operator_menu_enum("object.gpencil_modifier_add", "type")
|
|
layout.template_grease_pencil_modifiers()
|
|
|
|
|
|
class AddModifierMenu(Operator):
|
|
bl_idname = "object.add_modifier_menu"
|
|
bl_label = "Add Modifier"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
# NOTE: This operator only exists to add a poll to the add modifier shortcut in the property editor.
|
|
object = context.object
|
|
space = context.space_data
|
|
if object and object.type == 'GPENCIL':
|
|
return False
|
|
return space and space.type == 'PROPERTIES' and space.context == 'MODIFIER'
|
|
|
|
def invoke(self, context, event):
|
|
return bpy.ops.wm.call_menu(name="OBJECT_MT_modifier_add")
|
|
|
|
|
|
classes = (
|
|
DATA_PT_modifiers,
|
|
OBJECT_MT_modifier_add,
|
|
OBJECT_MT_modifier_add_edit,
|
|
OBJECT_MT_modifier_add_generate,
|
|
OBJECT_MT_modifier_add_deform,
|
|
OBJECT_MT_modifier_add_physics,
|
|
OBJECT_MT_modifier_add_color,
|
|
DATA_PT_gpencil_modifiers,
|
|
AddModifierMenu,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
|
|
for cls in classes:
|
|
register_class(cls)
|