Rename operator 'Join Shapes' to 'Transfer Mix'.
[blender.git] / release / scripts / startup / bl_ui / properties_data_mesh.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
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.
7 #
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.
12 #
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.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20 import bpy
21 from bpy.types import Menu, Panel, UIList
22 from rna_prop_ui import PropertyPanel
23
24
25 class MESH_MT_vertex_group_context_menu(Menu):
26     bl_label = "Vertex Group Specials"
27
28     def draw(self, _context):
29         layout = self.layout
30
31         layout.operator(
32             "object.vertex_group_sort",
33             icon='SORTALPHA',
34             text="Sort by Name",
35         ).sort_type = 'NAME'
36         layout.operator(
37             "object.vertex_group_sort",
38             icon='BONE_DATA',
39             text="Sort by Bone Hierarchy",
40         ).sort_type = 'BONE_HIERARCHY'
41         layout.separator()
42         layout.operator("object.vertex_group_copy", icon='DUPLICATE')
43         layout.operator("object.vertex_group_copy_to_linked")
44         layout.operator("object.vertex_group_copy_to_selected")
45         layout.separator()
46         layout.operator("object.vertex_group_mirror", icon='ARROW_LEFTRIGHT').use_topology = False
47         layout.operator("object.vertex_group_mirror", text="Mirror Vertex Group (Topology)").use_topology = True
48         layout.separator()
49         layout.operator("object.vertex_group_remove_from", icon='X', text="Remove from All Groups").use_all_groups = True
50         layout.operator("object.vertex_group_remove_from", text="Clear Active Group").use_all_verts = True
51         layout.operator("object.vertex_group_remove", text="Delete All Unlocked Groups").all_unlocked = True
52         layout.operator("object.vertex_group_remove", text="Delete All Groups").all = True
53         layout.separator()
54         layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All").action = 'LOCK'
55         layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="UnLock All").action = 'UNLOCK'
56         layout.operator("object.vertex_group_lock", text="Lock Invert All").action = 'INVERT'
57
58
59 class MESH_MT_shape_key_context_menu(Menu):
60     bl_label = "Shape Key Specials"
61
62     def draw(self, _context):
63         layout = self.layout
64
65         layout.operator("object.shape_key_add", icon='ADD', text="New Shape From Mix").from_mix = True
66         layout.separator()
67         layout.operator("object.shape_key_mirror", icon='ARROW_LEFTRIGHT').use_topology = False
68         layout.operator("object.shape_key_mirror", text="Mirror Shape Key (Topology)").use_topology = True
69         layout.separator()
70         layout.operator("object.shape_key_transfer_mix")
71         layout.operator("object.shape_key_transfer_active")
72         layout.separator()
73         layout.operator("object.shape_key_remove", icon='X', text="Delete All Shape Keys").all = True
74         layout.separator()
75         layout.operator("object.shape_key_move", icon='TRIA_UP_BAR', text="Move To Top").type = 'TOP'
76         layout.operator("object.shape_key_move", icon='TRIA_DOWN_BAR', text="Move To Bottom").type = 'BOTTOM'
77
78
79 class MESH_UL_vgroups(UIList):
80     def draw_item(self, _context, layout, _data, item, icon, _active_data_, _active_propname, _index):
81         # assert(isinstance(item, bpy.types.VertexGroup))
82         vgroup = item
83         if self.layout_type in {'DEFAULT', 'COMPACT'}:
84             layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon)
85             icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED'
86             layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False)
87         elif self.layout_type == 'GRID':
88             layout.alignment = 'CENTER'
89             layout.label(text="", icon_value=icon)
90
91
92 class MESH_UL_fmaps(UIList):
93     def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
94         # assert(isinstance(item, bpy.types.FaceMap))
95         fmap = item
96         if self.layout_type in {'DEFAULT', 'COMPACT'}:
97             layout.prop(fmap, "name", text="", emboss=False, icon='FACE_MAPS')
98         elif self.layout_type == 'GRID':
99             layout.alignment = 'CENTER'
100             layout.label(text="", icon_value=icon)
101
102
103 class MESH_UL_shape_keys(UIList):
104     def draw_item(self, _context, layout, _data, item, icon, active_data, _active_propname, index):
105         # assert(isinstance(item, bpy.types.ShapeKey))
106         obj = active_data
107         # key = data
108         key_block = item
109         if self.layout_type in {'DEFAULT', 'COMPACT'}:
110             split = layout.split(factor=0.66, align=False)
111             split.prop(key_block, "name", text="", emboss=False, icon_value=icon)
112             row = split.row(align=True)
113             if key_block.mute or (obj.mode == 'EDIT' and not (obj.use_shape_key_edit_mode and obj.type == 'MESH')):
114                 row.active = False
115             if not item.id_data.use_relative:
116                 row.prop(key_block, "frame", text="", emboss=False)
117             elif index > 0:
118                 row.prop(key_block, "value", text="", emboss=False)
119             else:
120                 row.label(text="")
121             row.prop(key_block, "mute", text="", emboss=False)
122         elif self.layout_type == 'GRID':
123             layout.alignment = 'CENTER'
124             layout.label(text="", icon_value=icon)
125
126
127 class MESH_UL_uvmaps(UIList):
128     def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
129         # assert(isinstance(item, (bpy.types.MeshTexturePolyLayer, bpy.types.MeshLoopColorLayer)))
130         if self.layout_type in {'DEFAULT', 'COMPACT'}:
131             layout.prop(item, "name", text="", emboss=False, icon='GROUP_UVS')
132             icon = 'RESTRICT_RENDER_OFF' if item.active_render else 'RESTRICT_RENDER_ON'
133             layout.prop(item, "active_render", text="", icon=icon, emboss=False)
134         elif self.layout_type == 'GRID':
135             layout.alignment = 'CENTER'
136             layout.label(text="", icon_value=icon)
137
138
139 class MESH_UL_vcols(UIList):
140     def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
141         # assert(isinstance(item, (bpy.types.MeshTexturePolyLayer, bpy.types.MeshLoopColorLayer)))
142         if self.layout_type in {'DEFAULT', 'COMPACT'}:
143             layout.prop(item, "name", text="", emboss=False, icon='GROUP_VCOL')
144             icon = 'RESTRICT_RENDER_OFF' if item.active_render else 'RESTRICT_RENDER_ON'
145             layout.prop(item, "active_render", text="", icon=icon, emboss=False)
146         elif self.layout_type == 'GRID':
147             layout.alignment = 'CENTER'
148             layout.label(text="", icon_value=icon)
149
150
151 class MeshButtonsPanel:
152     bl_space_type = 'PROPERTIES'
153     bl_region_type = 'WINDOW'
154     bl_context = "data"
155
156     @classmethod
157     def poll(cls, context):
158         engine = context.engine
159         return context.mesh and (engine in cls.COMPAT_ENGINES)
160
161
162 class DATA_PT_context_mesh(MeshButtonsPanel, Panel):
163     bl_label = ""
164     bl_options = {'HIDE_HEADER'}
165     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
166
167     def draw(self, context):
168         layout = self.layout
169
170         ob = context.object
171         mesh = context.mesh
172         space = context.space_data
173
174         if ob:
175             layout.template_ID(ob, "data")
176         elif mesh:
177             layout.template_ID(space, "pin_id")
178
179
180 class DATA_PT_normals(MeshButtonsPanel, Panel):
181     bl_label = "Normals"
182     bl_options = {'DEFAULT_CLOSED'}
183     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
184
185     def draw(self, context):
186         pass
187
188
189 class DATA_PT_normals_auto_smooth(MeshButtonsPanel, Panel):
190     bl_label = "Auto Smooth"
191     bl_parent_id = "DATA_PT_normals"
192     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
193
194     def draw_header(self, context):
195         mesh = context.mesh
196
197         self.layout.prop(mesh, "use_auto_smooth", text="")
198
199     def draw(self, context):
200         layout = self.layout
201         layout.use_property_split = True
202
203         mesh = context.mesh
204
205         layout.active = mesh.use_auto_smooth and not mesh.has_custom_normals
206         layout.prop(mesh, "auto_smooth_angle", text="Angle")
207
208
209 class DATA_PT_texture_space(MeshButtonsPanel, Panel):
210     bl_label = "Texture Space"
211     bl_options = {'DEFAULT_CLOSED'}
212     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
213
214     def draw(self, context):
215         layout = self.layout
216         layout.use_property_split = True
217
218         mesh = context.mesh
219
220         layout.prop(mesh, "texture_mesh")
221
222         layout.separator()
223
224         layout.prop(mesh, "use_auto_texspace")
225
226         layout.prop(mesh, "texspace_location", text="Location")
227         layout.prop(mesh, "texspace_size", text="Size")
228
229
230 class DATA_PT_vertex_groups(MeshButtonsPanel, Panel):
231     bl_label = "Vertex Groups"
232     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
233
234     @classmethod
235     def poll(cls, context):
236         engine = context.engine
237         obj = context.object
238         return (obj and obj.type in {'MESH', 'LATTICE'} and (engine in cls.COMPAT_ENGINES))
239
240     def draw(self, context):
241         layout = self.layout
242
243         ob = context.object
244         group = ob.vertex_groups.active
245
246         rows = 3
247         if group:
248             rows = 5
249
250         row = layout.row()
251         row.template_list("MESH_UL_vgroups", "", ob, "vertex_groups", ob.vertex_groups, "active_index", rows=rows)
252
253         col = row.column(align=True)
254
255         col.operator("object.vertex_group_add", icon='ADD', text="")
256         props = col.operator("object.vertex_group_remove", icon='REMOVE', text="")
257         props.all_unlocked = props.all = False
258
259         col.separator()
260
261         col.menu("MESH_MT_vertex_group_context_menu", icon='DOWNARROW_HLT', text="")
262
263         if group:
264             col.separator()
265             col.operator("object.vertex_group_move", icon='TRIA_UP', text="").direction = 'UP'
266             col.operator("object.vertex_group_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
267
268         if (
269                 ob.vertex_groups and
270                 (ob.mode == 'EDIT' or
271                  (ob.mode == 'WEIGHT_PAINT' and ob.type == 'MESH' and ob.data.use_paint_mask_vertex))
272         ):
273             row = layout.row()
274
275             sub = row.row(align=True)
276             sub.operator("object.vertex_group_assign", text="Assign")
277             sub.operator("object.vertex_group_remove_from", text="Remove")
278
279             sub = row.row(align=True)
280             sub.operator("object.vertex_group_select", text="Select")
281             sub.operator("object.vertex_group_deselect", text="Deselect")
282
283             layout.prop(context.tool_settings, "vertex_group_weight", text="Weight")
284
285
286 class DATA_PT_face_maps(MeshButtonsPanel, Panel):
287     bl_label = "Face Maps"
288     bl_options = {'DEFAULT_CLOSED'}
289     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
290
291     @classmethod
292     def poll(cls, context):
293         obj = context.object
294         return (obj and obj.type == 'MESH')
295
296     def draw(self, context):
297         layout = self.layout
298
299         ob = context.object
300         facemap = ob.face_maps.active
301
302         rows = 2
303         if facemap:
304             rows = 4
305
306         row = layout.row()
307         row.template_list("MESH_UL_fmaps", "", ob, "face_maps", ob.face_maps, "active_index", rows=rows)
308
309         col = row.column(align=True)
310         col.operator("object.face_map_add", icon='ADD', text="")
311         col.operator("object.face_map_remove", icon='REMOVE', text="")
312
313         if facemap:
314             col.separator()
315             col.operator("object.face_map_move", icon='TRIA_UP', text="").direction = 'UP'
316             col.operator("object.face_map_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
317
318         if ob.face_maps and (ob.mode == 'EDIT' and ob.type == 'MESH'):
319             row = layout.row()
320
321             sub = row.row(align=True)
322             sub.operator("object.face_map_assign", text="Assign")
323             sub.operator("object.face_map_remove_from", text="Remove")
324
325             sub = row.row(align=True)
326             sub.operator("object.face_map_select", text="Select")
327             sub.operator("object.face_map_deselect", text="Deselect")
328
329
330 class DATA_PT_shape_keys(MeshButtonsPanel, Panel):
331     bl_label = "Shape Keys"
332     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
333
334     @classmethod
335     def poll(cls, context):
336         engine = context.engine
337         obj = context.object
338         return (obj and obj.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'} and (engine in cls.COMPAT_ENGINES))
339
340     def draw(self, context):
341         layout = self.layout
342
343         ob = context.object
344         key = ob.data.shape_keys
345         kb = ob.active_shape_key
346
347         enable_edit = ob.mode != 'EDIT'
348         enable_edit_value = False
349
350         if ob.show_only_shape_key is False:
351             if enable_edit or (ob.type == 'MESH' and ob.use_shape_key_edit_mode):
352                 enable_edit_value = True
353
354         row = layout.row()
355
356         rows = 3
357         if kb:
358             rows = 5
359
360         row.template_list("MESH_UL_shape_keys", "", key, "key_blocks", ob, "active_shape_key_index", rows=rows)
361
362         col = row.column(align=True)
363
364         col.operator("object.shape_key_add", icon='ADD', text="").from_mix = False
365         col.operator("object.shape_key_remove", icon='REMOVE', text="").all = False
366
367         col.separator()
368
369         col.menu("MESH_MT_shape_key_context_menu", icon='DOWNARROW_HLT', text="")
370
371         if kb:
372             col.separator()
373
374             sub = col.column(align=True)
375             sub.operator("object.shape_key_move", icon='TRIA_UP', text="").type = 'UP'
376             sub.operator("object.shape_key_move", icon='TRIA_DOWN', text="").type = 'DOWN'
377
378             split = layout.split(factor=0.4)
379             row = split.row()
380             row.enabled = enable_edit
381             row.prop(key, "use_relative")
382
383             row = split.row()
384             row.alignment = 'RIGHT'
385
386             sub = row.row(align=True)
387             sub.label()  # XXX, for alignment only
388             subsub = sub.row(align=True)
389             subsub.active = enable_edit_value
390             subsub.prop(ob, "show_only_shape_key", text="")
391             sub.prop(ob, "use_shape_key_edit_mode", text="")
392
393             sub = row.row()
394             if key.use_relative:
395                 sub.operator("object.shape_key_clear", icon='X', text="")
396             else:
397                 sub.operator("object.shape_key_retime", icon='RECOVER_LAST', text="")
398
399             if key.use_relative:
400                 if ob.active_shape_key_index != 0:
401                     layout.use_property_split = True
402
403                     row = layout.row()
404                     row.active = enable_edit_value
405                     row.prop(kb, "value")
406
407                     col = layout.column()
408                     sub.active = enable_edit_value
409                     sub = col.column(align=True)
410                     sub.prop(kb, "slider_min", text="Range Min")
411                     sub.prop(kb, "slider_max", text="Max")
412
413                     col.prop_search(kb, "vertex_group", ob, "vertex_groups", text="Vertex Group")
414                     col.prop_search(kb, "relative_key", key, "key_blocks", text="Relative To")
415
416             else:
417                 layout.prop(kb, "interpolation")
418                 row = layout.column()
419                 row.active = enable_edit_value
420                 row.prop(key, "eval_time")
421
422
423 class DATA_PT_uv_texture(MeshButtonsPanel, Panel):
424     bl_label = "UV Maps"
425     bl_options = {'DEFAULT_CLOSED'}
426     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
427
428     def draw(self, context):
429         layout = self.layout
430
431         me = context.mesh
432
433         row = layout.row()
434         col = row.column()
435
436         col.template_list("MESH_UL_uvmaps", "uvmaps", me, "uv_layers", me.uv_layers, "active_index", rows=2)
437
438         col = row.column(align=True)
439         col.operator("mesh.uv_texture_add", icon='ADD', text="")
440         col.operator("mesh.uv_texture_remove", icon='REMOVE', text="")
441
442
443 class DATA_PT_vertex_colors(MeshButtonsPanel, Panel):
444     bl_label = "Vertex Colors"
445     bl_options = {'DEFAULT_CLOSED'}
446     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
447
448     def draw(self, context):
449         layout = self.layout
450
451         me = context.mesh
452
453         row = layout.row()
454         col = row.column()
455
456         col.template_list("MESH_UL_vcols", "vcols", me, "vertex_colors", me.vertex_colors, "active_index", rows=2)
457
458         col = row.column(align=True)
459         col.operator("mesh.vertex_color_add", icon='ADD', text="")
460         col.operator("mesh.vertex_color_remove", icon='REMOVE', text="")
461
462
463 class DATA_PT_customdata(MeshButtonsPanel, Panel):
464     bl_label = "Geometry Data"
465     bl_options = {'DEFAULT_CLOSED'}
466     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
467
468     def draw(self, context):
469         layout = self.layout
470         layout.use_property_split = True
471
472         obj = context.object
473         me = context.mesh
474         col = layout.column()
475
476         col.operator("mesh.customdata_mask_clear", icon='X')
477         col.operator("mesh.customdata_skin_clear", icon='X')
478
479         if me.has_custom_normals:
480             col.operator("mesh.customdata_custom_splitnormals_clear", icon='X')
481         else:
482             col.operator("mesh.customdata_custom_splitnormals_add", icon='ADD')
483
484         col = layout.column()
485
486         col.enabled = (obj.mode != 'EDIT')
487         col.prop(me, "use_customdata_vertex_bevel")
488         col.prop(me, "use_customdata_edge_bevel")
489         col.prop(me, "use_customdata_edge_crease")
490
491
492 class DATA_PT_custom_props_mesh(MeshButtonsPanel, PropertyPanel, Panel):
493     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
494     _context_path = "object.data"
495     _property_type = bpy.types.Mesh
496
497
498 classes = (
499     MESH_MT_vertex_group_context_menu,
500     MESH_MT_shape_key_context_menu,
501     MESH_UL_vgroups,
502     MESH_UL_fmaps,
503     MESH_UL_shape_keys,
504     MESH_UL_uvmaps,
505     MESH_UL_vcols,
506     DATA_PT_context_mesh,
507     DATA_PT_vertex_groups,
508     DATA_PT_shape_keys,
509     DATA_PT_uv_texture,
510     DATA_PT_vertex_colors,
511     DATA_PT_face_maps,
512     DATA_PT_normals,
513     DATA_PT_normals_auto_smooth,
514     DATA_PT_texture_space,
515     DATA_PT_customdata,
516     DATA_PT_custom_props_mesh,
517 )
518
519 if __name__ == "__main__":  # only for live edit.
520     from bpy.utils import register_class
521     for cls in classes:
522         register_class(cls)