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