2023-08-16 00:20:26 +10:00
|
|
|
# SPDX-FileCopyrightText: 2009-2023 Blender Authors
|
2023-06-15 13:09:04 +10:00
|
|
|
#
|
2022-02-11 09:07:11 +11:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2023-06-15 13:09:04 +10:00
|
|
|
|
2009-05-21 21:23:36 +00:00
|
|
|
import bpy
|
This commit frees list ui items from their dependencies to Panel, and hence from all the limitations this implied (mostly, the "only one list per panel" one).
It introduces a new (py-extendable and registrable) RNA type, UIList (roughly similar to Panel one), which currently contains only "standard" list's scroll pos and size (but may be expended to include e.g. some filtering data, etc.). This now makes lists completely independent from Panels!
This UIList has a draw_item callback which allows to customize items' drawing from python, that all addons can now use. Incidentally, this also greatly simplifies the C code of this widget, as we do not code any "special case" here anymore!
To make all this work, other changes were also necessary:
* Now all buttons (uiBut struct) have a 'custom_data' void pointer, used currently to store the uiList struct associated with a given uiLayoutListBox.
* DynamicPaintSurface now exposes a new bool, use_color_preview (readonly), saying whether that surface has some 3D view preview data or not.
* UILayout class has now four new (static) functions, to get the actual icon of any RNA object (important e.g. with materials or textures), and to get an enum item's UI name, description and icon.
* UILayout's label() func now takes an optional 'icon_value' integer parameter, which if not zero will override the 'icon' one (mandatory to use "custom" icons as generated for material/texture/... previews).
Note: not sure whether we should add that one to all UILayout's prop funcs?
Note: will update addons using template list asap.
2012-12-28 09:20:16 +00:00
|
|
|
from bpy.types import Menu, Panel, UIList
|
2010-01-08 08:54:41 +00:00
|
|
|
from rna_prop_ui import PropertyPanel
|
2025-05-04 13:26:03 +02:00
|
|
|
from bl_ui.space_properties import PropertiesAnimationMixin
|
2009-11-14 13:35:44 +00:00
|
|
|
|
2022-11-16 12:06:14 +01:00
|
|
|
from bpy.app.translations import (
|
2023-04-03 20:55:22 +02:00
|
|
|
pgettext_iface as iface_,
|
2024-01-11 19:49:03 +01:00
|
|
|
pgettext_tip as rpt_,
|
2022-11-16 12:06:14 +01:00
|
|
|
)
|
2011-09-20 18:29:19 +00:00
|
|
|
|
2022-12-05 12:54:00 +11:00
|
|
|
|
2019-03-12 10:59:57 +11:00
|
|
|
class MESH_MT_vertex_group_context_menu(Menu):
|
2011-09-21 15:18:38 +00:00
|
|
|
bl_label = "Vertex Group Specials"
|
2010-02-05 14:29:05 +00:00
|
|
|
|
2019-04-19 07:32:24 +02:00
|
|
|
def draw(self, _context):
|
2010-02-05 14:29:05 +00:00
|
|
|
layout = self.layout
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2019-05-22 00:27:01 +10:00
|
|
|
layout.operator(
|
|
|
|
|
"object.vertex_group_sort",
|
|
|
|
|
icon='SORTALPHA',
|
|
|
|
|
text="Sort by Name",
|
|
|
|
|
).sort_type = 'NAME'
|
|
|
|
|
layout.operator(
|
|
|
|
|
"object.vertex_group_sort",
|
|
|
|
|
icon='BONE_DATA',
|
|
|
|
|
text="Sort by Bone Hierarchy",
|
|
|
|
|
).sort_type = 'BONE_HIERARCHY'
|
2018-10-31 18:01:23 +01:00
|
|
|
layout.separator()
|
2018-10-31 17:30:47 +01:00
|
|
|
layout.operator("object.vertex_group_copy", icon='DUPLICATE')
|
2018-10-31 18:01:23 +01:00
|
|
|
layout.operator("object.vertex_group_copy_to_selected")
|
|
|
|
|
layout.separator()
|
2013-07-04 11:37:32 +00:00
|
|
|
layout.operator("object.vertex_group_mirror", icon='ARROW_LEFTRIGHT').use_topology = False
|
2018-10-31 18:01:23 +01:00
|
|
|
layout.operator("object.vertex_group_mirror", text="Mirror Vertex Group (Topology)").use_topology = True
|
|
|
|
|
layout.separator()
|
2019-10-30 05:40:36 +11:00
|
|
|
layout.operator(
|
|
|
|
|
"object.vertex_group_remove_from",
|
|
|
|
|
icon='X',
|
|
|
|
|
text="Remove from All Groups",
|
|
|
|
|
).use_all_groups = True
|
2018-10-31 18:01:23 +01:00
|
|
|
layout.operator("object.vertex_group_remove_from", text="Clear Active Group").use_all_verts = True
|
|
|
|
|
layout.operator("object.vertex_group_remove", text="Delete All Unlocked Groups").all_unlocked = True
|
|
|
|
|
layout.operator("object.vertex_group_remove", text="Delete All Groups").all = True
|
2011-09-14 08:21:21 +00:00
|
|
|
layout.separator()
|
2019-12-23 12:46:05 +03:00
|
|
|
props = layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All")
|
|
|
|
|
props.action, props.mask = 'LOCK', 'ALL'
|
2020-10-24 11:42:17 -07:00
|
|
|
props = layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock All")
|
2019-12-23 12:46:05 +03:00
|
|
|
props.action, props.mask = 'UNLOCK', 'ALL'
|
|
|
|
|
props = layout.operator("object.vertex_group_lock", text="Lock Invert All")
|
|
|
|
|
props.action, props.mask = 'INVERT', 'ALL'
|
2010-02-05 14:29:05 +00:00
|
|
|
|
|
|
|
|
|
2019-03-12 10:59:57 +11:00
|
|
|
class MESH_MT_shape_key_context_menu(Menu):
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Shape Key Specials"
|
2010-02-05 14:29:05 +00:00
|
|
|
|
2019-04-19 07:32:24 +02:00
|
|
|
def draw(self, _context):
|
2010-02-05 14:29:05 +00:00
|
|
|
layout = self.layout
|
|
|
|
|
|
2025-08-08 17:43:17 +02:00
|
|
|
layout.operator("object.shape_key_add", icon='ADD', text="New Combined").from_mix = True
|
|
|
|
|
layout.operator("object.shape_key_copy", icon='DUPLICATE', text="Duplicate")
|
|
|
|
|
layout.operator("object.shape_key_transfer", text="Copy from Objects")
|
2018-10-31 18:01:23 +01:00
|
|
|
layout.separator()
|
2025-08-08 17:43:17 +02:00
|
|
|
layout.operator("object.join_shapes", text="New from Objects")
|
2025-09-23 22:12:10 +02:00
|
|
|
layout.operator("object.join_shapes", text="New from Objects Flipped").use_mirror = True
|
2025-08-08 17:43:17 +02:00
|
|
|
layout.operator("object.update_shapes", icon='FILE_REFRESH')
|
2025-09-23 22:12:10 +02:00
|
|
|
layout.operator("object.update_shapes", text="Update from Objects Flipped").use_mirror = True
|
2018-10-31 18:01:23 +01:00
|
|
|
layout.separator()
|
2025-08-08 17:43:17 +02:00
|
|
|
layout.operator("object.shape_key_mirror", icon='ARROW_LEFTRIGHT', text="Flip").use_topology = False
|
|
|
|
|
layout.operator("object.shape_key_mirror", text="Flip (Topology)").use_topology = True
|
2018-10-31 18:01:23 +01:00
|
|
|
layout.separator()
|
2023-07-14 20:57:28 +03:00
|
|
|
layout.operator("object.shape_key_lock", icon='LOCKED', text="Lock All").action = 'LOCK'
|
|
|
|
|
layout.operator("object.shape_key_lock", icon='UNLOCKED', text="Unlock All").action = 'UNLOCK'
|
|
|
|
|
layout.separator()
|
2025-07-28 15:28:21 +02:00
|
|
|
layout.operator("object.shape_key_make_basis", text="Make Basis")
|
2025-08-08 17:43:17 +02:00
|
|
|
layout.separator()
|
|
|
|
|
props = layout.operator("object.shape_key_remove", text="Apply All")
|
|
|
|
|
props.all = True
|
|
|
|
|
props.apply_mix = True
|
|
|
|
|
props = layout.operator("object.shape_key_remove", icon='X', text="Delete All")
|
|
|
|
|
props.all = True
|
|
|
|
|
props.apply_mix = False
|
2010-02-05 14:29:05 +00:00
|
|
|
|
2022-04-19 15:05:55 +10:00
|
|
|
|
2025-08-07 12:41:26 +02:00
|
|
|
class MESH_MT_shape_key_tree_context_menu(Menu):
|
|
|
|
|
bl_label = "Shape Key context menu"
|
|
|
|
|
|
|
|
|
|
def draw(self, _context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
layout.operator("object.shape_key_make_basis", text="Make Basis")
|
|
|
|
|
layout.operator("object.shape_key_copy", icon='DUPLICATE', text="Duplicate")
|
|
|
|
|
layout.separator()
|
|
|
|
|
layout.operator("object.shape_key_move", icon='TRIA_UP_BAR', text="Move After Basis").type = 'TOP'
|
|
|
|
|
layout.operator("object.shape_key_move", icon='TRIA_DOWN_BAR', text="Move to Last").type = 'BOTTOM'
|
|
|
|
|
|
|
|
|
|
|
2022-06-08 12:10:32 -07:00
|
|
|
class MESH_MT_color_attribute_context_menu(Menu):
|
|
|
|
|
bl_label = "Color Attribute Specials"
|
|
|
|
|
|
|
|
|
|
def draw(self, _context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
|
2022-06-14 14:30:09 +10:00
|
|
|
layout.operator(
|
2022-06-08 12:10:32 -07:00
|
|
|
"geometry.color_attribute_duplicate",
|
|
|
|
|
icon='DUPLICATE',
|
|
|
|
|
)
|
2022-11-10 14:11:11 -06:00
|
|
|
layout.operator("geometry.color_attribute_convert")
|
2022-06-08 12:10:32 -07:00
|
|
|
|
|
|
|
|
|
2022-01-21 12:47:35 +01:00
|
|
|
class MESH_MT_attribute_context_menu(Menu):
|
|
|
|
|
bl_label = "Attribute Specials"
|
|
|
|
|
|
2022-04-06 09:40:14 +10:00
|
|
|
def draw(self, _context):
|
2022-01-21 12:47:35 +01:00
|
|
|
layout = self.layout
|
|
|
|
|
|
|
|
|
|
layout.operator("geometry.attribute_convert")
|
|
|
|
|
|
2010-02-05 14:29:05 +00:00
|
|
|
|
This commit frees list ui items from their dependencies to Panel, and hence from all the limitations this implied (mostly, the "only one list per panel" one).
It introduces a new (py-extendable and registrable) RNA type, UIList (roughly similar to Panel one), which currently contains only "standard" list's scroll pos and size (but may be expended to include e.g. some filtering data, etc.). This now makes lists completely independent from Panels!
This UIList has a draw_item callback which allows to customize items' drawing from python, that all addons can now use. Incidentally, this also greatly simplifies the C code of this widget, as we do not code any "special case" here anymore!
To make all this work, other changes were also necessary:
* Now all buttons (uiBut struct) have a 'custom_data' void pointer, used currently to store the uiList struct associated with a given uiLayoutListBox.
* DynamicPaintSurface now exposes a new bool, use_color_preview (readonly), saying whether that surface has some 3D view preview data or not.
* UILayout class has now four new (static) functions, to get the actual icon of any RNA object (important e.g. with materials or textures), and to get an enum item's UI name, description and icon.
* UILayout's label() func now takes an optional 'icon_value' integer parameter, which if not zero will override the 'icon' one (mandatory to use "custom" icons as generated for material/texture/... previews).
Note: not sure whether we should add that one to all UILayout's prop funcs?
Note: will update addons using template list asap.
2012-12-28 09:20:16 +00:00
|
|
|
class MESH_UL_vgroups(UIList):
|
2019-04-19 07:32:24 +02:00
|
|
|
def draw_item(self, _context, layout, _data, item, icon, _active_data_, _active_propname, _index):
|
Fix T39897: shape keys created while the Relative checkbox is unchecked start out with frame=0
So! First, frame for absolute shape keys: never allow a new key to have the same pos as an
existing one (this does not make sense). This way, the two workflows are possible (create
all keys and then animate ctime, or animate ctime and then create keys where you need them).
Also, fixed UIList for shapekeys, the "absolute" test was wrong, and better to show frame
value, even though not editable, than nothing in case of absolute keys.
And finally, add getter to RNA 'frame' readonly value, so that we output real frame values,
and not dummy internal ones (which are /100) in our API.
2014-05-18 22:05:21 +02:00
|
|
|
# assert(isinstance(item, bpy.types.VertexGroup))
|
This commit frees list ui items from their dependencies to Panel, and hence from all the limitations this implied (mostly, the "only one list per panel" one).
It introduces a new (py-extendable and registrable) RNA type, UIList (roughly similar to Panel one), which currently contains only "standard" list's scroll pos and size (but may be expended to include e.g. some filtering data, etc.). This now makes lists completely independent from Panels!
This UIList has a draw_item callback which allows to customize items' drawing from python, that all addons can now use. Incidentally, this also greatly simplifies the C code of this widget, as we do not code any "special case" here anymore!
To make all this work, other changes were also necessary:
* Now all buttons (uiBut struct) have a 'custom_data' void pointer, used currently to store the uiList struct associated with a given uiLayoutListBox.
* DynamicPaintSurface now exposes a new bool, use_color_preview (readonly), saying whether that surface has some 3D view preview data or not.
* UILayout class has now four new (static) functions, to get the actual icon of any RNA object (important e.g. with materials or textures), and to get an enum item's UI name, description and icon.
* UILayout's label() func now takes an optional 'icon_value' integer parameter, which if not zero will override the 'icon' one (mandatory to use "custom" icons as generated for material/texture/... previews).
Note: not sure whether we should add that one to all UILayout's prop funcs?
Note: will update addons using template list asap.
2012-12-28 09:20:16 +00:00
|
|
|
vgroup = item
|
2025-06-18 13:45:06 +02:00
|
|
|
layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon)
|
|
|
|
|
icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED'
|
|
|
|
|
layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False)
|
This commit frees list ui items from their dependencies to Panel, and hence from all the limitations this implied (mostly, the "only one list per panel" one).
It introduces a new (py-extendable and registrable) RNA type, UIList (roughly similar to Panel one), which currently contains only "standard" list's scroll pos and size (but may be expended to include e.g. some filtering data, etc.). This now makes lists completely independent from Panels!
This UIList has a draw_item callback which allows to customize items' drawing from python, that all addons can now use. Incidentally, this also greatly simplifies the C code of this widget, as we do not code any "special case" here anymore!
To make all this work, other changes were also necessary:
* Now all buttons (uiBut struct) have a 'custom_data' void pointer, used currently to store the uiList struct associated with a given uiLayoutListBox.
* DynamicPaintSurface now exposes a new bool, use_color_preview (readonly), saying whether that surface has some 3D view preview data or not.
* UILayout class has now four new (static) functions, to get the actual icon of any RNA object (important e.g. with materials or textures), and to get an enum item's UI name, description and icon.
* UILayout's label() func now takes an optional 'icon_value' integer parameter, which if not zero will override the 'icon' one (mandatory to use "custom" icons as generated for material/texture/... previews).
Note: not sure whether we should add that one to all UILayout's prop funcs?
Note: will update addons using template list asap.
2012-12-28 09:20:16 +00:00
|
|
|
|
|
|
|
|
|
2018-10-01 10:45:50 +02:00
|
|
|
class MESH_UL_uvmaps(UIList):
|
2019-04-19 07:32:24 +02:00
|
|
|
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
|
Fix T39897: shape keys created while the Relative checkbox is unchecked start out with frame=0
So! First, frame for absolute shape keys: never allow a new key to have the same pos as an
existing one (this does not make sense). This way, the two workflows are possible (create
all keys and then animate ctime, or animate ctime and then create keys where you need them).
Also, fixed UIList for shapekeys, the "absolute" test was wrong, and better to show frame
value, even though not editable, than nothing in case of absolute keys.
And finally, add getter to RNA 'frame' readonly value, so that we output real frame values,
and not dummy internal ones (which are /100) in our API.
2014-05-18 22:05:21 +02:00
|
|
|
# assert(isinstance(item, (bpy.types.MeshTexturePolyLayer, bpy.types.MeshLoopColorLayer)))
|
2025-06-18 13:45:06 +02:00
|
|
|
layout.prop(item, "name", text="", emboss=False, icon='GROUP_UVS')
|
|
|
|
|
icon = 'RESTRICT_RENDER_OFF' if item.active_render else 'RESTRICT_RENDER_ON'
|
|
|
|
|
layout.prop(item, "active_render", text="", icon=icon, emboss=False)
|
2018-10-01 10:45:50 +02:00
|
|
|
|
2022-04-19 15:05:55 +10:00
|
|
|
|
2015-01-29 15:35:06 +11:00
|
|
|
class MeshButtonsPanel:
|
2009-10-31 19:31:45 +00:00
|
|
|
bl_space_type = 'PROPERTIES'
|
|
|
|
|
bl_region_type = 'WINDOW'
|
|
|
|
|
bl_context = "data"
|
|
|
|
|
|
2010-08-09 01:37:09 +00:00
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
2017-10-16 17:15:03 -02:00
|
|
|
engine = context.engine
|
2010-08-09 01:37:09 +00:00
|
|
|
return context.mesh and (engine in cls.COMPAT_ENGINES)
|
|
|
|
|
|
2009-10-31 23:35:56 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class DATA_PT_context_mesh(MeshButtonsPanel, Panel):
|
2009-10-31 19:31:45 +00:00
|
|
|
bl_label = ""
|
2010-08-26 01:05:37 +00:00
|
|
|
bl_options = {'HIDE_HEADER'}
|
2023-09-26 14:08:31 +02:00
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2023-09-29 14:32:54 +10:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
2009-10-31 19:31:45 +00:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
|
|
|
|
|
ob = context.object
|
|
|
|
|
mesh = context.mesh
|
|
|
|
|
space = context.space_data
|
2010-08-06 15:17:44 +00:00
|
|
|
|
|
|
|
|
if ob:
|
2010-12-30 12:22:28 +00:00
|
|
|
layout.template_ID(ob, "data")
|
2010-08-06 15:17:44 +00:00
|
|
|
elif mesh:
|
2010-12-30 12:22:28 +00:00
|
|
|
layout.template_ID(space, "pin_id")
|
2009-06-07 13:36:12 +00:00
|
|
|
|
2009-10-31 23:35:56 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class DATA_PT_texture_space(MeshButtonsPanel, Panel):
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Texture Space"
|
2011-06-06 19:44:28 +00:00
|
|
|
bl_options = {'DEFAULT_CLOSED'}
|
2023-09-26 14:08:31 +02:00
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2023-09-29 14:32:54 +10:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
2009-10-31 19:31:45 +00:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
2018-06-01 18:44:06 +02:00
|
|
|
layout.use_property_split = True
|
2009-10-31 19:31:45 +00:00
|
|
|
|
|
|
|
|
mesh = context.mesh
|
|
|
|
|
|
2009-11-23 00:27:30 +00:00
|
|
|
layout.prop(mesh, "texture_mesh")
|
2011-06-06 19:44:28 +00:00
|
|
|
|
|
|
|
|
layout.separator()
|
|
|
|
|
|
2011-01-06 09:55:20 +00:00
|
|
|
layout.prop(mesh, "use_auto_texspace")
|
2018-06-01 18:44:06 +02:00
|
|
|
|
|
|
|
|
layout.prop(mesh, "texspace_location", text="Location")
|
|
|
|
|
layout.prop(mesh, "texspace_size", text="Size")
|
2009-10-07 09:23:29 +00:00
|
|
|
|
2011-06-21 17:17:51 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class DATA_PT_vertex_groups(MeshButtonsPanel, Panel):
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Vertex Groups"
|
2023-09-26 14:08:31 +02:00
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2023-09-29 14:32:54 +10:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2010-08-09 01:37:09 +00:00
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
2017-10-16 17:15:03 -02:00
|
|
|
engine = context.engine
|
2010-08-05 16:05:30 +00:00
|
|
|
obj = context.object
|
2024-01-31 17:45:59 +01:00
|
|
|
return (obj and obj.type in {'MESH', 'LATTICE', 'GREASEPENCIL'} and (engine in cls.COMPAT_ENGINES))
|
2009-10-31 19:31:45 +00:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
|
|
|
|
|
ob = context.object
|
2010-08-24 04:02:50 +00:00
|
|
|
group = ob.vertex_groups.active
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2018-10-31 18:01:23 +01:00
|
|
|
rows = 3
|
2009-10-31 19:31:45 +00:00
|
|
|
if group:
|
2018-10-31 18:01:23 +01:00
|
|
|
rows = 5
|
2009-10-31 19:31:45 +00:00
|
|
|
|
|
|
|
|
row = layout.row()
|
This commit frees list ui items from their dependencies to Panel, and hence from all the limitations this implied (mostly, the "only one list per panel" one).
It introduces a new (py-extendable and registrable) RNA type, UIList (roughly similar to Panel one), which currently contains only "standard" list's scroll pos and size (but may be expended to include e.g. some filtering data, etc.). This now makes lists completely independent from Panels!
This UIList has a draw_item callback which allows to customize items' drawing from python, that all addons can now use. Incidentally, this also greatly simplifies the C code of this widget, as we do not code any "special case" here anymore!
To make all this work, other changes were also necessary:
* Now all buttons (uiBut struct) have a 'custom_data' void pointer, used currently to store the uiList struct associated with a given uiLayoutListBox.
* DynamicPaintSurface now exposes a new bool, use_color_preview (readonly), saying whether that surface has some 3D view preview data or not.
* UILayout class has now four new (static) functions, to get the actual icon of any RNA object (important e.g. with materials or textures), and to get an enum item's UI name, description and icon.
* UILayout's label() func now takes an optional 'icon_value' integer parameter, which if not zero will override the 'icon' one (mandatory to use "custom" icons as generated for material/texture/... previews).
Note: not sure whether we should add that one to all UILayout's prop funcs?
Note: will update addons using template list asap.
2012-12-28 09:20:16 +00:00
|
|
|
row.template_list("MESH_UL_vgroups", "", ob, "vertex_groups", ob.vertex_groups, "active_index", rows=rows)
|
|
|
|
|
|
2009-10-31 19:31:45 +00:00
|
|
|
col = row.column(align=True)
|
2018-10-31 18:01:23 +01:00
|
|
|
|
2018-10-01 10:45:50 +02:00
|
|
|
col.operator("object.vertex_group_add", icon='ADD', text="")
|
|
|
|
|
props = col.operator("object.vertex_group_remove", icon='REMOVE', text="")
|
2017-11-29 20:19:07 +01:00
|
|
|
props.all_unlocked = props.all = False
|
2018-10-31 18:01:23 +01:00
|
|
|
|
|
|
|
|
col.separator()
|
|
|
|
|
|
2019-03-12 10:59:57 +11:00
|
|
|
col.menu("MESH_MT_vertex_group_context_menu", icon='DOWNARROW_HLT', text="")
|
2018-10-31 18:01:23 +01:00
|
|
|
|
2010-05-04 12:31:24 +00:00
|
|
|
if group:
|
2012-07-11 10:41:26 +00:00
|
|
|
col.separator()
|
2010-05-04 12:31:24 +00:00
|
|
|
col.operator("object.vertex_group_move", icon='TRIA_UP', text="").direction = 'UP'
|
|
|
|
|
col.operator("object.vertex_group_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2019-05-22 00:27:01 +10:00
|
|
|
if (
|
|
|
|
|
ob.vertex_groups and
|
|
|
|
|
(ob.mode == 'EDIT' or
|
|
|
|
|
(ob.mode == 'WEIGHT_PAINT' and ob.type == 'MESH' and ob.data.use_paint_mask_vertex))
|
|
|
|
|
):
|
2009-10-31 19:31:45 +00:00
|
|
|
row = layout.row()
|
|
|
|
|
|
|
|
|
|
sub = row.row(align=True)
|
2013-07-01 13:02:53 +00:00
|
|
|
sub.operator("object.vertex_group_assign", text="Assign")
|
2011-09-21 15:18:38 +00:00
|
|
|
sub.operator("object.vertex_group_remove_from", text="Remove")
|
2009-10-31 19:31:45 +00:00
|
|
|
|
|
|
|
|
sub = row.row(align=True)
|
2011-09-21 15:18:38 +00:00
|
|
|
sub.operator("object.vertex_group_select", text="Select")
|
|
|
|
|
sub.operator("object.vertex_group_deselect", text="Deselect")
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2025-05-01 16:08:10 +02:00
|
|
|
col = layout.column(align=True)
|
|
|
|
|
col.separator()
|
2025-05-19 05:20:11 +02:00
|
|
|
col.use_property_split = True
|
|
|
|
|
col.use_property_decorate = False
|
2025-05-01 16:08:10 +02:00
|
|
|
col.prop(context.tool_settings, "vertex_group_weight", text="Weight")
|
|
|
|
|
col.prop(context.tool_settings, "use_auto_normalize", text="Auto Normalize")
|
2009-07-01 22:25:49 +00:00
|
|
|
|
2025-03-11 10:28:34 +01:00
|
|
|
draw_attribute_warnings(context, layout, None)
|
2023-09-26 15:36:40 +02:00
|
|
|
|
2009-10-31 23:35:56 +00:00
|
|
|
|
2025-09-09 11:38:19 +02:00
|
|
|
def draw_shape_key_properties(context, layout):
|
|
|
|
|
layout.use_property_split = True
|
|
|
|
|
ob = context.object
|
|
|
|
|
key = ob.data.shape_keys
|
|
|
|
|
kb = ob.active_shape_key
|
|
|
|
|
enable_edit = ob.mode != 'EDIT'
|
|
|
|
|
enable_edit_value = False
|
|
|
|
|
|
|
|
|
|
if enable_edit or (ob.use_shape_key_edit_mode and ob.type == 'MESH'):
|
|
|
|
|
if ob.show_only_shape_key is False:
|
|
|
|
|
enable_edit_value = True
|
|
|
|
|
|
|
|
|
|
layout.use_property_split = True
|
|
|
|
|
if key.use_relative:
|
|
|
|
|
if ob.active_shape_key_index != 0:
|
|
|
|
|
row = layout.row()
|
|
|
|
|
row.active = enable_edit_value
|
|
|
|
|
row.prop(kb, "value")
|
|
|
|
|
|
|
|
|
|
col = layout.column()
|
|
|
|
|
sub = col.column(align=True)
|
|
|
|
|
sub.active = enable_edit_value
|
|
|
|
|
sub.prop(kb, "slider_min", text="Range Min")
|
|
|
|
|
sub.prop(kb, "slider_max", text="Max")
|
|
|
|
|
|
|
|
|
|
col.prop_search(kb, "vertex_group", ob, "vertex_groups", text="Vertex Group")
|
|
|
|
|
col.prop_search(kb, "relative_key", key, "key_blocks", text="Relative To")
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
layout.prop(kb, "interpolation")
|
|
|
|
|
row = layout.column()
|
|
|
|
|
row.active = enable_edit_value
|
|
|
|
|
row.prop(key, "eval_time")
|
|
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class DATA_PT_shape_keys(MeshButtonsPanel, Panel):
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Shape Keys"
|
2023-09-26 14:08:31 +02:00
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2023-09-29 14:32:54 +10:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2010-08-09 01:37:09 +00:00
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
2017-10-16 17:15:03 -02:00
|
|
|
engine = context.engine
|
2010-08-05 16:05:30 +00:00
|
|
|
obj = context.object
|
2011-03-07 13:23:45 +00:00
|
|
|
return (obj and obj.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'} and (engine in cls.COMPAT_ENGINES))
|
2009-10-31 19:31:45 +00:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
|
|
|
|
|
ob = context.object
|
|
|
|
|
key = ob.data.shape_keys
|
2010-02-01 14:25:38 +00:00
|
|
|
kb = ob.active_shape_key
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2018-04-05 18:20:27 +02:00
|
|
|
enable_edit = ob.mode != 'EDIT'
|
2020-07-24 15:55:11 +10:00
|
|
|
enable_pin = False
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2020-07-24 15:55:11 +10:00
|
|
|
if enable_edit or (ob.use_shape_key_edit_mode and ob.type == 'MESH'):
|
|
|
|
|
enable_pin = True
|
2009-10-31 19:31:45 +00:00
|
|
|
|
|
|
|
|
row = layout.row()
|
|
|
|
|
|
2025-06-06 12:03:47 +02:00
|
|
|
row.template_shape_key_tree()
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2018-10-31 18:01:23 +01:00
|
|
|
col = row.column(align=True)
|
|
|
|
|
|
|
|
|
|
col.operator("object.shape_key_add", icon='ADD', text="").from_mix = False
|
|
|
|
|
col.operator("object.shape_key_remove", icon='REMOVE', text="").all = False
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2018-10-31 18:01:23 +01:00
|
|
|
col.separator()
|
|
|
|
|
|
2019-03-12 10:59:57 +11:00
|
|
|
col.menu("MESH_MT_shape_key_context_menu", icon='DOWNARROW_HLT', text="")
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2025-10-17 15:32:49 +02:00
|
|
|
if ob.type == 'MESH':
|
|
|
|
|
row = layout.row(align=True)
|
|
|
|
|
row.use_property_split = False
|
|
|
|
|
row.alignment = 'LEFT'
|
|
|
|
|
row.prop(ob, "add_rest_position_attribute")
|
|
|
|
|
|
2009-10-31 19:31:45 +00:00
|
|
|
if kb:
|
2009-11-23 00:27:30 +00:00
|
|
|
col.separator()
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2009-11-08 11:07:00 +00:00
|
|
|
sub = col.column(align=True)
|
2009-12-10 10:23:53 +00:00
|
|
|
sub.operator("object.shape_key_move", icon='TRIA_UP', text="").type = 'UP'
|
|
|
|
|
sub.operator("object.shape_key_move", icon='TRIA_DOWN', text="").type = 'DOWN'
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2018-08-28 12:38:54 +10:00
|
|
|
split = layout.split(factor=0.4)
|
2009-11-08 11:07:00 +00:00
|
|
|
row = split.row()
|
|
|
|
|
row.enabled = enable_edit
|
2010-08-18 07:14:10 +00:00
|
|
|
row.prop(key, "use_relative")
|
2009-10-31 19:31:45 +00:00
|
|
|
|
2009-11-08 11:07:00 +00:00
|
|
|
row = split.row()
|
|
|
|
|
row.alignment = 'RIGHT'
|
2009-11-14 13:35:44 +00:00
|
|
|
|
2009-11-08 11:07:00 +00:00
|
|
|
sub = row.row(align=True)
|
2012-01-30 09:49:30 +00:00
|
|
|
sub.label() # XXX, for alignment only
|
2009-11-08 11:07:00 +00:00
|
|
|
subsub = sub.row(align=True)
|
2020-07-24 15:55:11 +10:00
|
|
|
subsub.active = enable_pin
|
2010-10-12 22:20:10 +00:00
|
|
|
subsub.prop(ob, "show_only_shape_key", text="")
|
2010-08-20 06:09:58 +00:00
|
|
|
sub.prop(ob, "use_shape_key_edit_mode", text="")
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2010-02-05 14:29:05 +00:00
|
|
|
sub = row.row()
|
2012-04-05 06:10:15 +00:00
|
|
|
if key.use_relative:
|
|
|
|
|
sub.operator("object.shape_key_clear", icon='X', text="")
|
|
|
|
|
else:
|
|
|
|
|
sub.operator("object.shape_key_retime", icon='RECOVER_LAST', text="")
|
2009-11-07 22:07:46 +00:00
|
|
|
|
2025-09-09 11:38:19 +02:00
|
|
|
draw_shape_key_properties(context, layout)
|
2022-07-08 14:45:48 +02:00
|
|
|
|
2009-10-31 23:35:56 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class DATA_PT_uv_texture(MeshButtonsPanel, Panel):
|
2011-11-23 17:25:25 +00:00
|
|
|
bl_label = "UV Maps"
|
2018-10-18 12:13:06 +02:00
|
|
|
bl_options = {'DEFAULT_CLOSED'}
|
2023-09-26 14:08:31 +02:00
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2023-09-29 14:32:54 +10:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
2009-10-31 19:31:45 +00:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
|
|
|
|
|
me = context.mesh
|
|
|
|
|
|
|
|
|
|
row = layout.row()
|
|
|
|
|
col = row.column()
|
|
|
|
|
|
2018-10-31 18:01:23 +01:00
|
|
|
col.template_list("MESH_UL_uvmaps", "uvmaps", me, "uv_layers", me.uv_layers, "active_index", rows=2)
|
2009-10-31 19:31:45 +00:00
|
|
|
|
|
|
|
|
col = row.column(align=True)
|
2018-10-01 10:45:50 +02:00
|
|
|
col.operator("mesh.uv_texture_add", icon='ADD', text="")
|
|
|
|
|
col.operator("mesh.uv_texture_remove", icon='REMOVE', text="")
|
2013-11-20 03:38:18 +11:00
|
|
|
|
2025-03-11 10:28:34 +01:00
|
|
|
draw_attribute_warnings(context, layout, me.uv_layers)
|
2023-09-26 15:36:40 +02:00
|
|
|
|
2022-04-19 15:05:55 +10:00
|
|
|
|
2019-08-14 18:37:46 +02:00
|
|
|
class DATA_PT_remesh(MeshButtonsPanel, Panel):
|
|
|
|
|
bl_label = "Remesh"
|
|
|
|
|
bl_options = {'DEFAULT_CLOSED'}
|
2023-09-26 14:08:31 +02:00
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2023-09-29 14:32:54 +10:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
2019-08-14 18:37:46 +02:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
layout.use_property_split = True
|
2019-09-13 14:06:41 +02:00
|
|
|
layout.use_property_decorate = False
|
2019-08-26 18:34:11 +02:00
|
|
|
row = layout.row()
|
2019-08-14 18:37:46 +02:00
|
|
|
|
|
|
|
|
mesh = context.mesh
|
2019-08-26 18:34:11 +02:00
|
|
|
row.prop(mesh, "remesh_mode", text="Mode", expand=True)
|
|
|
|
|
col = layout.column()
|
2019-10-30 05:40:36 +11:00
|
|
|
if mesh.remesh_mode == 'VOXEL':
|
2019-08-26 18:34:11 +02:00
|
|
|
col.prop(mesh, "remesh_voxel_size")
|
2019-09-27 17:41:05 +02:00
|
|
|
col.prop(mesh, "remesh_voxel_adaptivity")
|
2019-10-30 05:30:38 +11:00
|
|
|
col.prop(mesh, "use_remesh_fix_poles")
|
UI: Layout changes for new checkbox layout possibilities
Follow-up to previous commit.
Some examples:
{F8473507} {F8473508} {F8473509} {F8473510}
For more screenshots, please see D7430.
We use column or row headings here to bring more structure, and to give
the eye visual anchors which aid eye-scanning. The left-aligned
checkboxes likewise help with this. And we keep the adherence to the
center line, so the alignment matches up between the various buttons and
controls.
* Changes the property split percentage from 50/50% to 40/60%. This is
needed to give enough space for the checkboxes. But in most cases this
looks better anyway - see Transform panel. In some cases it simply
fills out the available space more efficently.
* Fix various hacks where we previously used manually defined splits.
When we did this, the alignment was never quite right, and the layout
code was a mess.
* Adds column headings to many places where a list of checkboxes all
share a common purpose or leading text.
* Add checkbox + value configurations various places where a checkbox
only serves to enable the value slider
* Removes most uses of grid flow layout. The grid flow layouts combine
poorly with column headings, and also they would mess alignment up
badly. The grid flow layouts also often made buttons and controls jump
around on the screen if you would just resize editors slightly,
causing visual confusion, making users lose their place. The logic for
at what time the list of items would re-flow was often flawed, jumping
to multiple columns too fast or too late - and frankly, the grid flow
layouts would often just look bad.
Maniphest Task: https://developer.blender.org/T65965
Differential Revision: https://developer.blender.org/D7430
Reviewed by: Brecht Van Lommel, Pablo Vazquez.
Most work here by William Reynish, few changes by Julian Eisel.
2020-04-17 16:54:03 +02:00
|
|
|
|
|
|
|
|
col = layout.column(heading="Preserve")
|
|
|
|
|
col.prop(mesh, "use_remesh_preserve_volume", text="Volume")
|
2023-11-22 15:21:58 +01:00
|
|
|
col.prop(mesh, "use_remesh_preserve_attributes", text="Attributes")
|
Sculpt Vertex Colors: Initial implementation
Sculpt Vertex Colors is a painting system that runs inside sculpt mode, reusing all its tools and optimizations. This provides much better performance, easier to maintain code and more advanced features (new brush engine, filters, symmetry options, masks and face sets compatibility...). This is also the initial step for future features like vertex painting in Multires and brushes that can sculpt and paint at the same time.
This commit includes:
- SCULPT_UNDO_COLOR for undo support in sculpt mode
- SCULPT_UPDATE_COLOR and PBVH flags and rendering
- Sculpt Color API functions
- Sculpt capability for sculpt tools (only enabled in the Paint Brush for now)
- Rendering support in workbench (default to Sculpt Vertex Colors except in Vertex Paint)
- Conversion operator between MPropCol (Sculpt Vertex Colors) and MLoopCol (Vertex Paint)
- Remesher reprojection in the Voxel Remehser
- Paint Brush and Smear Brush with color smoothing in alt-smooth mode
- Parameters for the new brush engine (density, opacity, flow, wet paint mixing, tip scale) implemented in Sculpt Vertex Colors
- Color Filter
- Color picker (uses S shortcut, replaces smooth)
- Color selector in the top bar
Reviewed By: brecht
Maniphest Tasks: T72866
Differential Revision: https://developer.blender.org/D5975
2020-06-22 20:05:28 +02:00
|
|
|
|
2019-08-26 18:34:11 +02:00
|
|
|
col.operator("object.voxel_remesh", text="Voxel Remesh")
|
|
|
|
|
else:
|
|
|
|
|
col.operator("object.quadriflow_remesh", text="QuadriFlow Remesh")
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2019-10-30 05:40:36 +11:00
|
|
|
|
2012-09-21 03:41:59 +00:00
|
|
|
class DATA_PT_customdata(MeshButtonsPanel, Panel):
|
|
|
|
|
bl_label = "Geometry Data"
|
|
|
|
|
bl_options = {'DEFAULT_CLOSED'}
|
2023-09-26 14:08:31 +02:00
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2023-09-29 14:32:54 +10:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
2012-09-21 03:41:59 +00:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
2018-06-25 16:57:44 +02:00
|
|
|
layout.use_property_split = True
|
2019-09-13 14:06:41 +02:00
|
|
|
layout.use_property_decorate = False
|
2012-09-21 03:41:59 +00:00
|
|
|
|
2013-01-10 04:43:31 +00:00
|
|
|
me = context.mesh
|
2012-09-21 22:33:43 +00:00
|
|
|
col = layout.column()
|
2013-01-10 04:43:31 +00:00
|
|
|
|
2015-05-03 15:18:27 +02:00
|
|
|
col.operator("mesh.customdata_mask_clear", icon='X')
|
|
|
|
|
col.operator("mesh.customdata_skin_clear", icon='X')
|
2025-07-04 20:02:37 +02:00
|
|
|
col.operator("mesh.reorder_vertices_spatial")
|
Add Custom Loop Normals.
This is the core code for it, tools (datatransfer and modifier) will come in next commits).
RNA api is already there, though.
See the code for details, but basically, we define, for each 'smooth fan'
(which is a set of adjacent loops around a same vertex that are smooth, i.e. have a single same normal),
a 'loop normal space' (or lnor space), using auto-computed normal and relevant edges, and store
custom normal as two angular factors inside that space. This allows to have custom normals
'following' deformations of the geometry, and to only save two shorts per loop in new clnor CDLayer.
Normal manipulation (editing, mixing, interpolating, etc.) shall always happen with plain 3D vectors normals,
and be converted back into storage format at the end.
Clnor computation has also been threaded (at least for Mesh case, not for BMesh), since the process can
be rather heavy with high poly meshes.
Also, bumping subversion, and fix mess in 2.70 versioning code.
2015-02-05 14:24:48 +01:00
|
|
|
if me.has_custom_normals:
|
|
|
|
|
col.operator("mesh.customdata_custom_splitnormals_clear", icon='X')
|
|
|
|
|
else:
|
2018-10-01 10:45:50 +02:00
|
|
|
col.operator("mesh.customdata_custom_splitnormals_add", icon='ADD')
|
Add Custom Loop Normals.
This is the core code for it, tools (datatransfer and modifier) will come in next commits).
RNA api is already there, though.
See the code for details, but basically, we define, for each 'smooth fan'
(which is a set of adjacent loops around a same vertex that are smooth, i.e. have a single same normal),
a 'loop normal space' (or lnor space), using auto-computed normal and relevant edges, and store
custom normal as two angular factors inside that space. This allows to have custom normals
'following' deformations of the geometry, and to only save two shorts per loop in new clnor CDLayer.
Normal manipulation (editing, mixing, interpolating, etc.) shall always happen with plain 3D vectors normals,
and be converted back into storage format at the end.
Clnor computation has also been threaded (at least for Mesh case, not for BMesh), since the process can
be rather heavy with high poly meshes.
Also, bumping subversion, and fix mess in 2.70 versioning code.
2015-02-05 14:24:48 +01:00
|
|
|
|
2012-09-21 03:41:59 +00:00
|
|
|
|
2024-09-05 12:11:35 +02:00
|
|
|
class DATA_PT_mesh_animation(MeshButtonsPanel, PropertiesAnimationMixin, PropertyPanel, Panel):
|
|
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2024-09-05 12:11:35 +02:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
layout.use_property_split = True
|
2024-09-27 16:29:32 +02:00
|
|
|
layout.use_property_decorate = False
|
2024-09-05 12:11:35 +02:00
|
|
|
|
|
|
|
|
# MeshButtonsPanel.poll ensures this is not None.
|
|
|
|
|
mesh = context.mesh
|
|
|
|
|
|
|
|
|
|
col = layout.column(align=True)
|
|
|
|
|
col.label(text="Mesh")
|
|
|
|
|
self.draw_action_and_slot_selector(context, col, mesh)
|
|
|
|
|
|
|
|
|
|
if shape_keys := mesh.shape_keys:
|
|
|
|
|
col = layout.column(align=True)
|
|
|
|
|
col.label(text="Shape Keys")
|
|
|
|
|
self.draw_action_and_slot_selector(context, col, shape_keys)
|
|
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class DATA_PT_custom_props_mesh(MeshButtonsPanel, PropertyPanel, Panel):
|
2023-09-26 14:08:31 +02:00
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2023-09-29 14:32:54 +10:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
2010-08-12 19:36:10 +00:00
|
|
|
_context_path = "object.data"
|
2010-12-17 10:33:28 +00:00
|
|
|
_property_type = bpy.types.Mesh
|
2011-04-04 10:13:04 +00:00
|
|
|
|
2012-09-21 03:41:59 +00:00
|
|
|
|
2021-06-28 16:52:49 +02:00
|
|
|
class MESH_UL_attributes(UIList):
|
|
|
|
|
display_domain_names = {
|
|
|
|
|
'POINT': "Vertex",
|
|
|
|
|
'EDGE': "Edge",
|
|
|
|
|
'FACE': "Face",
|
|
|
|
|
'CORNER': "Face Corner",
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-14 14:30:09 +10:00
|
|
|
def filter_items(self, _context, data, property):
|
2022-05-31 13:20:16 +02:00
|
|
|
attributes = getattr(data, property)
|
|
|
|
|
flags = []
|
|
|
|
|
indices = [i for i in range(len(attributes))]
|
|
|
|
|
|
2022-12-02 12:50:00 +01:00
|
|
|
# Filtering by name
|
|
|
|
|
if self.filter_name:
|
|
|
|
|
flags = bpy.types.UI_UL_list.filter_items_by_name(
|
2025-01-14 12:46:40 +11:00
|
|
|
self.filter_name, self.bitflag_filter_item, attributes, "name", reverse=self.use_filter_invert,
|
|
|
|
|
)
|
2022-12-02 12:50:00 +01:00
|
|
|
if not flags:
|
|
|
|
|
flags = [self.bitflag_filter_item] * len(attributes)
|
|
|
|
|
|
|
|
|
|
# Filtering internal attributes
|
|
|
|
|
for idx, item in enumerate(attributes):
|
2025-05-16 16:08:30 +02:00
|
|
|
flags[idx] = self.bitflag_item_never_show if item.is_internal else flags[idx]
|
2022-06-01 14:38:06 +10:00
|
|
|
|
2024-01-09 09:26:06 +01:00
|
|
|
# Reorder by name.
|
|
|
|
|
if self.use_filter_sort_alpha:
|
|
|
|
|
indices = bpy.types.UI_UL_list.sort_items_by_name(attributes, "name")
|
|
|
|
|
|
2022-05-31 13:20:16 +02:00
|
|
|
return flags, indices
|
|
|
|
|
|
2021-06-28 16:52:49 +02:00
|
|
|
def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index):
|
2023-01-06 13:52:57 +11:00
|
|
|
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
|
2021-06-28 16:52:49 +02:00
|
|
|
|
|
|
|
|
domain_name = self.display_domain_names.get(attribute.domain, "")
|
|
|
|
|
|
|
|
|
|
split = layout.split(factor=0.50)
|
|
|
|
|
split.emboss = 'NONE'
|
|
|
|
|
split.prop(attribute, "name", text="")
|
|
|
|
|
sub = split.row()
|
|
|
|
|
sub.alignment = 'RIGHT'
|
|
|
|
|
sub.active = False
|
2024-04-27 16:02:36 +10:00
|
|
|
sub.label(
|
2024-07-25 17:15:46 +02:00
|
|
|
text="{:s} - {:s}".format(iface_(domain_name), iface_(data_type.name)),
|
2024-04-27 16:02:36 +10:00
|
|
|
translate=False,
|
|
|
|
|
)
|
2021-06-28 16:52:49 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
|
|
|
|
|
bl_label = "Attributes"
|
|
|
|
|
bl_options = {'DEFAULT_CLOSED'}
|
2023-09-26 14:08:31 +02:00
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2023-09-29 14:32:54 +10:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
2021-06-28 16:52:49 +02:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
mesh = context.mesh
|
|
|
|
|
|
|
|
|
|
layout = self.layout
|
|
|
|
|
row = layout.row()
|
|
|
|
|
|
|
|
|
|
col = row.column()
|
|
|
|
|
col.template_list(
|
|
|
|
|
"MESH_UL_attributes",
|
|
|
|
|
"attributes",
|
|
|
|
|
mesh,
|
|
|
|
|
"attributes",
|
|
|
|
|
mesh.attributes,
|
|
|
|
|
"active_index",
|
|
|
|
|
rows=3,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
col = row.column(align=True)
|
|
|
|
|
col.operator("geometry.attribute_add", icon='ADD', text="")
|
|
|
|
|
col.operator("geometry.attribute_remove", icon='REMOVE', text="")
|
|
|
|
|
|
2022-01-21 12:47:35 +01:00
|
|
|
col.separator()
|
|
|
|
|
|
|
|
|
|
col.menu("MESH_MT_attribute_context_menu", icon='DOWNARROW_HLT', text="")
|
|
|
|
|
|
2025-03-11 10:28:34 +01:00
|
|
|
draw_attribute_warnings(context, layout, None)
|
2021-06-28 16:52:49 +02:00
|
|
|
|
|
|
|
|
|
2025-03-11 10:28:34 +01:00
|
|
|
# `attribute` is list of attributes in current UI list
|
|
|
|
|
# None for vgroup and mesh. Those are already utilized in comparison.
|
|
|
|
|
def draw_attribute_warnings(context, layout, attributes):
|
2023-09-26 15:36:40 +02:00
|
|
|
ob = context.object
|
|
|
|
|
mesh = context.mesh
|
|
|
|
|
|
2023-09-28 14:20:03 +02:00
|
|
|
if not mesh:
|
|
|
|
|
return
|
|
|
|
|
|
2023-09-26 15:36:40 +02:00
|
|
|
unique_names = set()
|
|
|
|
|
colliding_names = []
|
|
|
|
|
for collection in (
|
|
|
|
|
mesh.attributes,
|
|
|
|
|
None if ob is None else ob.vertex_groups,
|
|
|
|
|
):
|
|
|
|
|
if collection is None:
|
|
|
|
|
colliding_names.append("Cannot check for object vertex groups when pinning mesh")
|
|
|
|
|
continue
|
|
|
|
|
for name in collection.keys():
|
|
|
|
|
unique_names_len = len(unique_names)
|
|
|
|
|
unique_names.add(name)
|
2025-03-11 10:28:34 +01:00
|
|
|
if (len(unique_names) == unique_names_len):
|
|
|
|
|
if (not attributes or attributes.get(name)):
|
|
|
|
|
# Print colliding names if they exist in current attribute list, see: !135495
|
|
|
|
|
colliding_names.append(name)
|
2021-06-28 16:52:49 +02:00
|
|
|
|
2023-09-26 15:36:40 +02:00
|
|
|
if not colliding_names:
|
|
|
|
|
return
|
2021-06-28 16:52:49 +02:00
|
|
|
|
2025-01-14 12:46:40 +11:00
|
|
|
layout.label(text=rpt_("Name collisions: ") + ", ".join(set(colliding_names)), icon='ERROR', translate=False)
|
2021-06-28 16:52:49 +02:00
|
|
|
|
|
|
|
|
|
2025-02-04 14:51:17 +11:00
|
|
|
class ColorAttributesListBase:
|
2022-04-05 11:42:55 -07:00
|
|
|
display_domain_names = {
|
|
|
|
|
'POINT': "Vertex",
|
|
|
|
|
'EDGE': "Edge",
|
|
|
|
|
'FACE': "Face",
|
|
|
|
|
'CORNER': "Face Corner",
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-14 14:30:09 +10:00
|
|
|
def filter_items(self, _context, data, property):
|
2022-12-02 12:50:00 +01:00
|
|
|
attributes = getattr(data, property)
|
|
|
|
|
flags = []
|
|
|
|
|
indices = [i for i in range(len(attributes))]
|
|
|
|
|
|
|
|
|
|
# Filtering by name
|
|
|
|
|
if self.filter_name:
|
|
|
|
|
flags = bpy.types.UI_UL_list.filter_items_by_name(
|
2025-01-14 12:46:40 +11:00
|
|
|
self.filter_name, self.bitflag_filter_item, attributes, "name", reverse=self.use_filter_invert,
|
|
|
|
|
)
|
2022-12-02 12:50:00 +01:00
|
|
|
if not flags:
|
|
|
|
|
flags = [self.bitflag_filter_item] * len(attributes)
|
2022-04-05 11:42:55 -07:00
|
|
|
|
2022-12-02 12:50:00 +01:00
|
|
|
for idx, item in enumerate(attributes):
|
2022-04-06 09:40:14 +10:00
|
|
|
skip = (
|
2023-09-13 13:36:00 +10:00
|
|
|
(item.domain not in {'POINT', 'CORNER'}) or
|
|
|
|
|
(item.data_type not in {'FLOAT_COLOR', 'BYTE_COLOR'}) or
|
2022-12-02 11:21:54 -06:00
|
|
|
item.is_internal
|
2022-04-06 09:40:14 +10:00
|
|
|
)
|
2022-12-02 12:50:00 +01:00
|
|
|
flags[idx] = 0 if skip else flags[idx]
|
2022-04-05 11:42:55 -07:00
|
|
|
|
2024-01-09 09:26:06 +01:00
|
|
|
# Reorder by name.
|
|
|
|
|
if self.use_filter_sort_alpha:
|
|
|
|
|
indices = bpy.types.UI_UL_list.sort_items_by_name(attributes, "name")
|
|
|
|
|
|
2022-12-02 12:50:00 +01:00
|
|
|
return flags, indices
|
2022-04-05 11:42:55 -07:00
|
|
|
|
2022-04-08 16:37:35 +02:00
|
|
|
|
|
|
|
|
class MESH_UL_color_attributes(UIList, ColorAttributesListBase):
|
2022-04-05 11:42:55 -07:00
|
|
|
def draw_item(self, _context, layout, data, attribute, _icon, _active_data, _active_propname, _index):
|
2023-01-06 13:52:57 +11:00
|
|
|
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
|
2022-04-05 11:42:55 -07:00
|
|
|
|
|
|
|
|
domain_name = self.display_domain_names.get(attribute.domain, "")
|
|
|
|
|
|
|
|
|
|
split = layout.split(factor=0.50)
|
|
|
|
|
split.emboss = 'NONE'
|
2022-04-28 13:08:34 -05:00
|
|
|
split.prop(attribute, "name", text="", icon='GROUP_VCOL')
|
2022-04-05 11:42:55 -07:00
|
|
|
|
2022-04-11 23:08:23 -07:00
|
|
|
sub = split.row()
|
|
|
|
|
sub.alignment = 'RIGHT'
|
|
|
|
|
sub.active = False
|
2024-07-25 17:15:46 +02:00
|
|
|
sub.label(text="{:s} - {:s}".format(iface_(domain_name), iface_(data_type.name)), translate=False)
|
2022-04-05 11:42:55 -07:00
|
|
|
|
2022-04-11 23:08:23 -07:00
|
|
|
active_render = _index == data.color_attributes.render_color_index
|
2022-04-19 15:05:55 +10:00
|
|
|
|
2022-04-11 23:08:23 -07:00
|
|
|
row = layout.row()
|
|
|
|
|
row.emboss = 'NONE'
|
2023-04-13 13:14:01 +10:00
|
|
|
props = row.operator(
|
2022-04-06 09:40:14 +10:00
|
|
|
"geometry.color_attribute_render_set",
|
|
|
|
|
text="",
|
|
|
|
|
icon='RESTRICT_RENDER_OFF' if active_render else 'RESTRICT_RENDER_ON',
|
|
|
|
|
)
|
2023-04-13 13:14:01 +10:00
|
|
|
props.name = attribute.name
|
2022-04-05 11:42:55 -07:00
|
|
|
|
|
|
|
|
|
2022-04-08 16:37:35 +02:00
|
|
|
class MESH_UL_color_attributes_selector(UIList, ColorAttributesListBase):
|
2022-04-28 13:08:34 -05:00
|
|
|
def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index):
|
2022-04-08 16:37:35 +02:00
|
|
|
layout.emboss = 'NONE'
|
2022-04-28 13:08:34 -05:00
|
|
|
layout.prop(attribute, "name", text="", icon='GROUP_VCOL')
|
2022-04-08 16:37:35 +02:00
|
|
|
|
|
|
|
|
|
2025-02-13 10:43:12 +11:00
|
|
|
class DATA_PT_vertex_colors(MeshButtonsPanel, Panel):
|
2022-04-05 11:42:55 -07:00
|
|
|
bl_label = "Color Attributes"
|
|
|
|
|
bl_options = {'DEFAULT_CLOSED'}
|
2023-09-26 14:08:31 +02:00
|
|
|
COMPAT_ENGINES = {
|
|
|
|
|
'BLENDER_RENDER',
|
2025-06-13 12:36:14 +02:00
|
|
|
'BLENDER_EEVEE',
|
2023-09-29 14:32:54 +10:00
|
|
|
'BLENDER_WORKBENCH',
|
|
|
|
|
}
|
2022-04-05 11:42:55 -07:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
mesh = context.mesh
|
|
|
|
|
|
|
|
|
|
layout = self.layout
|
|
|
|
|
row = layout.row()
|
|
|
|
|
|
|
|
|
|
col = row.column()
|
|
|
|
|
col.template_list(
|
|
|
|
|
"MESH_UL_color_attributes",
|
|
|
|
|
"color_attributes",
|
|
|
|
|
mesh,
|
|
|
|
|
"color_attributes",
|
|
|
|
|
mesh.color_attributes,
|
|
|
|
|
"active_color_index",
|
|
|
|
|
rows=3,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
col = row.column(align=True)
|
|
|
|
|
col.operator("geometry.color_attribute_add", icon='ADD', text="")
|
|
|
|
|
col.operator("geometry.color_attribute_remove", icon='REMOVE', text="")
|
2022-04-06 09:40:14 +10:00
|
|
|
|
2022-06-08 12:10:32 -07:00
|
|
|
col.separator()
|
2022-04-05 11:42:55 -07:00
|
|
|
|
2022-06-08 12:10:32 -07:00
|
|
|
col.menu("MESH_MT_color_attribute_context_menu", icon='DOWNARROW_HLT', text="")
|
|
|
|
|
|
2025-03-11 10:28:34 +01:00
|
|
|
draw_attribute_warnings(context, layout, mesh.color_attributes)
|
2022-04-19 15:05:55 +10:00
|
|
|
|
2022-06-09 10:10:37 +10:00
|
|
|
|
2017-03-18 20:03:24 +11:00
|
|
|
classes = (
|
2019-03-12 10:59:57 +11:00
|
|
|
MESH_MT_vertex_group_context_menu,
|
|
|
|
|
MESH_MT_shape_key_context_menu,
|
2025-08-07 12:41:26 +02:00
|
|
|
MESH_MT_shape_key_tree_context_menu,
|
2022-06-08 12:10:32 -07:00
|
|
|
MESH_MT_color_attribute_context_menu,
|
2022-01-21 12:47:35 +01:00
|
|
|
MESH_MT_attribute_context_menu,
|
2017-03-20 02:34:32 +11:00
|
|
|
MESH_UL_vgroups,
|
2018-10-01 10:45:50 +02:00
|
|
|
MESH_UL_uvmaps,
|
2021-06-28 16:52:49 +02:00
|
|
|
MESH_UL_attributes,
|
2017-03-18 20:03:24 +11:00
|
|
|
DATA_PT_context_mesh,
|
2017-03-20 02:34:32 +11:00
|
|
|
DATA_PT_vertex_groups,
|
|
|
|
|
DATA_PT_shape_keys,
|
2017-03-18 20:03:24 +11:00
|
|
|
DATA_PT_uv_texture,
|
|
|
|
|
DATA_PT_vertex_colors,
|
2021-06-28 16:52:49 +02:00
|
|
|
DATA_PT_mesh_attributes,
|
2018-07-25 16:48:04 +02:00
|
|
|
DATA_PT_texture_space,
|
2019-08-14 18:37:46 +02:00
|
|
|
DATA_PT_remesh,
|
2017-03-20 02:34:32 +11:00
|
|
|
DATA_PT_customdata,
|
2024-09-05 12:11:35 +02:00
|
|
|
DATA_PT_mesh_animation,
|
2017-03-20 02:34:32 +11:00
|
|
|
DATA_PT_custom_props_mesh,
|
2022-04-05 11:42:55 -07:00
|
|
|
MESH_UL_color_attributes,
|
2022-04-08 16:37:35 +02:00
|
|
|
MESH_UL_color_attributes_selector,
|
2017-03-18 20:03:24 +11:00
|
|
|
)
|
|
|
|
|
|
2011-04-04 10:13:04 +00:00
|
|
|
if __name__ == "__main__": # only for live edit.
|
2017-03-18 20:03:24 +11:00
|
|
|
from bpy.utils import register_class
|
|
|
|
|
for cls in classes:
|
|
|
|
|
register_class(cls)
|