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