Merge branch 'master' into blender2.8
[blender.git] / release / scripts / startup / bl_ui / properties_paint_common.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 from bpy.types import Menu
21
22
23 class UnifiedPaintPanel:
24     # subclass must set
25     # bl_space_type = 'IMAGE_EDITOR'
26     # bl_region_type = 'UI'
27
28     @staticmethod
29     def paint_settings(context):
30         toolsettings = context.tool_settings
31
32         if context.sculpt_object:
33             return toolsettings.sculpt
34         elif context.vertex_paint_object:
35             return toolsettings.vertex_paint
36         elif context.weight_paint_object:
37             return toolsettings.weight_paint
38         elif context.image_paint_object:
39             if (toolsettings.image_paint and toolsettings.image_paint.detect_data()):
40                 return toolsettings.image_paint
41
42             return None
43
44         return None
45
46     @staticmethod
47     def unified_paint_settings(parent, context):
48         ups = context.tool_settings.unified_paint_settings
49         parent.label(text="Unified Settings:")
50         row = parent.row()
51         row.prop(ups, "use_unified_size", text="Size")
52         row.prop(ups, "use_unified_strength", text="Strength")
53         if context.weight_paint_object:
54             parent.prop(ups, "use_unified_weight", text="Weight")
55         elif context.vertex_paint_object or context.image_paint_object:
56             parent.prop(ups, "use_unified_color", text="Color")
57         else:
58             parent.prop(ups, "use_unified_color", text="Color")
59
60     @staticmethod
61     def prop_unified_size(parent, context, brush, prop_name, icon='NONE', text="", slider=False):
62         ups = context.tool_settings.unified_paint_settings
63         ptr = ups if ups.use_unified_size else brush
64         parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider)
65
66     @staticmethod
67     def prop_unified_strength(parent, context, brush, prop_name, icon='NONE', text="", slider=False):
68         ups = context.tool_settings.unified_paint_settings
69         ptr = ups if ups.use_unified_strength else brush
70         parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider)
71
72     @staticmethod
73     def prop_unified_weight(parent, context, brush, prop_name, icon='NONE', text="", slider=False):
74         ups = context.tool_settings.unified_paint_settings
75         ptr = ups if ups.use_unified_weight else brush
76         parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider)
77
78     @staticmethod
79     def prop_unified_color(parent, context, brush, prop_name, text=""):
80         ups = context.tool_settings.unified_paint_settings
81         ptr = ups if ups.use_unified_color else brush
82         parent.prop(ptr, prop_name, text=text)
83
84     @staticmethod
85     def prop_unified_color_picker(parent, context, brush, prop_name, value_slider=True):
86         ups = context.tool_settings.unified_paint_settings
87         ptr = ups if ups.use_unified_color else brush
88         parent.template_color_picker(ptr, prop_name, value_slider=value_slider)
89
90
91 class VIEW3D_MT_tools_projectpaint_clone(Menu):
92     bl_label = "Clone Layer"
93
94     def draw(self, context):
95         layout = self.layout
96
97         for i, tex in enumerate(context.active_object.data.uv_textures):
98             props = layout.operator("wm.context_set_int", text=tex.name, translate=False)
99             props.data_path = "active_object.data.uv_texture_clone_index"
100             props.value = i
101
102
103 def brush_texpaint_common(panel, context, layout, brush, settings, projpaint=False):
104     capabilities = brush.image_paint_capabilities
105
106     col = layout.column()
107
108     if brush.image_tool in {'DRAW', 'FILL'}:
109         if brush.blend not in {'ERASE_ALPHA', 'ADD_ALPHA'}:
110             if not brush.use_gradient:
111                 panel.prop_unified_color_picker(col, context, brush, "color", value_slider=True)
112
113             if settings.palette:
114                 col.template_palette(settings, "palette", color=True)
115
116             if brush.use_gradient:
117                 col.label("Gradient Colors")
118                 col.template_color_ramp(brush, "gradient", expand=True)
119
120                 if brush.image_tool == 'DRAW':
121                     col.label("Background Color")
122                     row = col.row(align=True)
123                     panel.prop_unified_color(row, context, brush, "secondary_color", text="")
124                     col.prop(brush, "gradient_stroke_mode", text="Mode")
125                     if brush.gradient_stroke_mode in {'SPACING_REPEAT', 'SPACING_CLAMP'}:
126                         col.prop(brush, "grad_spacing")
127                 else: # if brush.image_tool == 'FILL':
128                     col.prop(brush, "gradient_fill_mode")
129             else:
130                 row = col.row(align=True)
131                 panel.prop_unified_color(row, context, brush, "color", text="")
132                 if brush.image_tool == 'FILL' and not projpaint:
133                     col.prop(brush, "fill_threshold")
134                 else:
135                     panel.prop_unified_color(row, context, brush, "secondary_color", text="")
136                     row.separator()
137                     row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="")
138         else:
139             if brush.image_tool == 'FILL' and not projpaint:
140                 col.prop(brush, "fill_threshold")
141
142     elif brush.image_tool == 'SOFTEN':
143         col = layout.column(align=True)
144         col.row().prop(brush, "direction", expand=True)
145         col.separator()
146         col.prop(brush, "sharp_threshold")
147         if not projpaint:
148             col.prop(brush, "blur_kernel_radius")
149         col.separator()
150         col.prop(brush, "blur_mode")
151     elif brush.image_tool == 'MASK':
152         col.prop(brush, "weight", text="Mask Value", slider=True)
153
154     elif brush.image_tool == 'CLONE':
155         col.separator()
156         if projpaint:
157             if settings.mode == 'MATERIAL':
158                 col.prop(settings, "use_clone_layer", text="Clone from paint slot")
159             elif settings.mode == 'IMAGE':
160                 col.prop(settings, "use_clone_layer", text="Clone from image/UV map")
161
162             if settings.use_clone_layer:
163                 ob = context.active_object
164                 col = layout.column()
165
166                 if settings.mode == 'MATERIAL':
167                     if len(ob.material_slots) > 1:
168                         col.label("Materials")
169                         col.template_list("MATERIAL_UL_matslots", "",
170                                           ob, "material_slots",
171                                           ob, "active_material_index", rows=2)
172
173                     mat = ob.active_material
174                     if mat:
175                         col.label("Source Clone Slot")
176                         col.template_list("TEXTURE_UL_texpaintslots", "",
177                                           mat, "texture_paint_images",
178                                           mat, "paint_clone_slot", rows=2)
179
180                 elif settings.mode == 'IMAGE':
181                     mesh = ob.data
182
183                     clone_text = mesh.uv_texture_clone.name if mesh.uv_texture_clone else ""
184                     col.label("Source Clone Image")
185                     col.template_ID(settings, "clone_image")
186                     col.label("Source Clone UV Map")
187                     col.menu("VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False)
188         else:
189             col.prop(brush, "clone_image", text="Image")
190             col.prop(brush, "clone_alpha", text="Alpha")
191
192     col.separator()
193
194     if capabilities.has_radius:
195         row = col.row(align=True)
196         panel.prop_unified_size(row, context, brush, "size", slider=True, text="Radius")
197         panel.prop_unified_size(row, context, brush, "use_pressure_size")
198
199     row = col.row(align=True)
200
201     if capabilities.has_space_attenuation:
202         row.prop(brush, "use_space_attenuation", toggle=True, icon_only=True)
203
204     panel.prop_unified_strength(row, context, brush, "strength", text="Strength")
205     panel.prop_unified_strength(row, context, brush, "use_pressure_strength")
206
207     if brush.image_tool in {'DRAW', 'FILL'}:
208         col.separator()
209         col.prop(brush, "blend", text="Blend")
210
211     col = layout.column()
212
213     # use_accumulate
214     if capabilities.has_accumulate:
215         col = layout.column(align=True)
216         col.prop(brush, "use_accumulate")
217
218     if projpaint:
219         col.prop(brush, "use_alpha")
220
221     col.prop(brush, "use_gradient")
222
223     col.separator()
224     col.template_ID(settings, "palette", new="palette.new")
225
226
227 # Used in both the View3D toolbar and texture properties
228 def brush_texture_settings(layout, brush, sculpt):
229     tex_slot = brush.texture_slot
230
231     layout.label(text="Brush Mapping:")
232
233     # map_mode
234     if sculpt:
235         layout.row().prop(tex_slot, "map_mode", text="")
236         layout.separator()
237     else:
238         layout.row().prop(tex_slot, "tex_paint_map_mode", text="")
239         layout.separator()
240
241     if tex_slot.map_mode == 'STENCIL':
242         if brush.texture and brush.texture.type == 'IMAGE':
243             layout.operator("brush.stencil_fit_image_aspect")
244         layout.operator("brush.stencil_reset_transform")
245
246     # angle and texture_angle_source
247     if tex_slot.has_texture_angle:
248         col = layout.column()
249         col.label(text="Angle:")
250         col.prop(tex_slot, "angle", text="")
251         if tex_slot.has_texture_angle_source:
252             col.prop(tex_slot, "use_rake", text="Rake")
253
254             if brush.brush_capabilities.has_random_texture_angle and tex_slot.has_random_texture_angle:
255                 if sculpt:
256                     if brush.sculpt_capabilities.has_random_texture_angle:
257                         col.prop(tex_slot, "use_random", text="Random")
258                         if tex_slot.use_random:
259                             col.prop(tex_slot, "random_angle", text="")
260                 else:
261                     col.prop(tex_slot, "use_random", text="Random")
262                     if tex_slot.use_random:
263                         col.prop(tex_slot, "random_angle", text="")
264
265     # scale and offset
266     split = layout.split()
267     split.prop(tex_slot, "offset")
268     split.prop(tex_slot, "scale")
269
270     if sculpt:
271         # texture_sample_bias
272         col = layout.column(align=True)
273         col.label(text="Sample Bias:")
274         col.prop(brush, "texture_sample_bias", slider=True, text="")
275
276
277 def brush_mask_texture_settings(layout, brush):
278     mask_tex_slot = brush.mask_texture_slot
279
280     layout.label(text="Mask Mapping:")
281
282     # map_mode
283     layout.row().prop(mask_tex_slot, "mask_map_mode", text="")
284     layout.separator()
285
286     if mask_tex_slot.map_mode == 'STENCIL':
287         if brush.mask_texture and brush.mask_texture.type == 'IMAGE':
288             layout.operator("brush.stencil_fit_image_aspect").mask = True
289         layout.operator("brush.stencil_reset_transform").mask = True
290
291     col = layout.column()
292     col.prop(brush, "use_pressure_masking", text="")
293     # angle and texture_angle_source
294     if mask_tex_slot.has_texture_angle:
295         col = layout.column()
296         col.label(text="Angle:")
297         col.prop(mask_tex_slot, "angle", text="")
298         if mask_tex_slot.has_texture_angle_source:
299             col.prop(mask_tex_slot, "use_rake", text="Rake")
300
301             if brush.brush_capabilities.has_random_texture_angle and mask_tex_slot.has_random_texture_angle:
302                 col.prop(mask_tex_slot, "use_random", text="Random")
303                 if mask_tex_slot.use_random:
304                     col.prop(mask_tex_slot, "random_angle", text="")
305
306     # scale and offset
307     split = layout.split()
308     split.prop(mask_tex_slot, "offset")
309     split.prop(mask_tex_slot, "scale")