4758f72edb5d16d4e9bb6dd1d9acb45015875ce8
[blender-staging.git] / release / scripts / startup / bl_ui / properties_physics_dynamicpaint.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 Panel, UIList
22
23 from bl_ui.properties_physics_common import (
24         point_cache_ui,
25         effector_weights_ui,
26         )
27
28
29 class PHYSICS_UL_dynapaint_surfaces(UIList):
30     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
31         # assert(isinstance(item, bpy.types.DynamicPaintSurface)
32         surf = item
33         sticon = layout.enum_item_icon(surf, "surface_type", surf.surface_type)
34         if self.layout_type in {'DEFAULT', 'COMPACT'}:
35             row = layout.row(align=True)
36             row.label(text="", icon_value=icon)
37             row.prop(surf, "name", text="", emboss=False, icon_value=sticon)
38             row = layout.row(align=True)
39             if surf.use_color_preview:
40                 row.prop(surf, "show_preview", text="", emboss=False,
41                          icon='RESTRICT_VIEW_OFF' if surf.show_preview else 'RESTRICT_VIEW_ON')
42             row.prop(surf, "is_active", text="")
43         elif self.layout_type == 'GRID':
44             layout.alignment = 'CENTER'
45             row = layout.row(align=True)
46             row.label(text="", icon_value=icon)
47             row.label(text="", icon_value=sticon)
48
49
50 class PhysicButtonsPanel:
51     bl_space_type = 'PROPERTIES'
52     bl_region_type = 'WINDOW'
53     bl_context = "physics"
54
55     @classmethod
56     def poll(cls, context):
57         ob = context.object
58         rd = context.scene.render
59         return (ob and ob.type == 'MESH') and rd.engine in cls.COMPAT_ENGINES and context.dynamic_paint
60
61
62 class PHYSICS_PT_dynamic_paint(PhysicButtonsPanel, Panel):
63     bl_label = "Dynamic Paint"
64     COMPAT_ENGINES = {'BLENDER_RENDER'}
65
66     def draw(self, context):
67         layout = self.layout
68
69         md = context.dynamic_paint
70
71         layout.prop(md, "ui_type", expand=True)
72
73         if md.ui_type == 'CANVAS':
74             canvas = md.canvas_settings
75
76             if canvas is None:
77                 layout.operator("dpaint.type_toggle", text="Add Canvas").type = 'CANVAS'
78             else:
79                 layout.operator("dpaint.type_toggle", text="Remove Canvas", icon='X').type = 'CANVAS'
80
81                 surface = canvas.canvas_surfaces.active
82
83                 row = layout.row()
84                 row.template_list("PHYSICS_UL_dynapaint_surfaces", "", canvas, "canvas_surfaces",
85                                   canvas.canvas_surfaces, "active_index", rows=1)
86
87                 col = row.column(align=True)
88                 col.operator("dpaint.surface_slot_add", icon='ZOOMIN', text="")
89                 col.operator("dpaint.surface_slot_remove", icon='ZOOMOUT', text="")
90
91                 if surface:
92                     layout.prop(surface, "surface_format")
93
94                     col = layout.column()
95                     if surface.surface_format != 'VERTEX':
96                         col.label(text="Quality:")
97                         col.prop(surface, "image_resolution")
98                     col.prop(surface, "use_antialiasing")
99
100                     col = layout.column()
101                     col.label(text="Frames:")
102                     split = col.split()
103
104                     col = split.column(align=True)
105                     col.prop(surface, "frame_start", text="Start")
106                     col.prop(surface, "frame_end", text="End")
107
108                     split.prop(surface, "frame_substeps")
109
110         elif md.ui_type == 'BRUSH':
111             brush = md.brush_settings
112             use_shading_nodes = context.scene.render.use_shading_nodes
113
114             if brush is None:
115                 layout.operator("dpaint.type_toggle", text="Add Brush").type = 'BRUSH'
116             else:
117                 layout.operator("dpaint.type_toggle", text="Remove Brush", icon='X').type = 'BRUSH'
118
119                 split = layout.split()
120
121                 col = split.column()
122                 col.prop(brush, "use_absolute_alpha")
123                 col.prop(brush, "use_paint_erase")
124                 col.prop(brush, "paint_wetness", text="Wetness")
125
126                 col = split.column()
127                 if not use_shading_nodes:
128                     sub = col.column()
129                     sub.active = (brush.paint_source != 'PARTICLE_SYSTEM')
130                     sub.prop(brush, "use_material")
131                 if brush.use_material and brush.paint_source != 'PARTICLE_SYSTEM' and not use_shading_nodes:
132                     col.prop(brush, "material", text="")
133                     col.prop(brush, "paint_alpha", text="Alpha Factor")
134                 else:
135                     col.prop(brush, "paint_color", text="")
136                     col.prop(brush, "paint_alpha", text="Alpha")
137
138
139 class PHYSICS_PT_dp_advanced_canvas(PhysicButtonsPanel, Panel):
140     bl_label = "Dynamic Paint Advanced"
141     COMPAT_ENGINES = {'BLENDER_RENDER'}
142
143     @classmethod
144     def poll(cls, context):
145         md = context.dynamic_paint
146         rd = context.scene.render
147         return md and md.ui_type == 'CANVAS' and md.canvas_settings and md.canvas_settings.canvas_surfaces.active and rd.engine in cls.COMPAT_ENGINES
148
149     def draw(self, context):
150         layout = self.layout
151
152         canvas = context.dynamic_paint.canvas_settings
153         surface = canvas.canvas_surfaces.active
154
155         surface_type = surface.surface_type
156
157         layout.prop(surface, "surface_type")
158         layout.separator()
159
160         # dissolve
161         if surface_type == 'PAINT':
162             split = layout.split(percentage=0.35)
163             split.prop(surface, "use_drying", text="Dry:")
164
165             col = split.column()
166             col.active = surface.use_drying
167             split = col.split(percentage=0.7)
168             col = split.column(align=True)
169             col.prop(surface, "dry_speed", text="Time")
170             col.prop(surface, "color_dry_threshold")
171             split.prop(surface, "use_dry_log", text="Slow")
172
173         if surface_type != 'WAVE':
174             split = layout.split(percentage=0.35)
175             col = split.column()
176             if surface_type == 'WEIGHT':
177                 col.prop(surface, "use_dissolve", text="Fade:")
178             else:
179                 col.prop(surface, "use_dissolve", text="Dissolve:")
180             col = split.column()
181             col.active = surface.use_dissolve
182             split = col.split(percentage=0.7)
183             split.prop(surface, "dissolve_speed", text="Time")
184             split.prop(surface, "use_dissolve_log", text="Slow")
185
186         # per type settings
187         if surface_type == 'DISPLACE':
188             layout.prop(surface, "use_incremental_displace")
189             if surface.surface_format == 'VERTEX':
190                 row = layout.row()
191                 row.prop(surface, "depth_clamp")
192                 row.prop(surface, "displace_factor")
193
194         elif surface_type == 'WAVE':
195             layout.prop(surface, "use_wave_open_border")
196
197             split = layout.split()
198
199             col = split.column(align=True)
200             col.prop(surface, "wave_timescale")
201             col.prop(surface, "wave_speed")
202
203             col = split.column(align=True)
204             col.prop(surface, "wave_damping")
205             col.prop(surface, "wave_spring")
206             col.prop(surface, "wave_smoothness")
207
208         layout.separator()
209         layout.prop(surface, "brush_group")
210         row = layout.row()
211         row.prop(surface, "brush_influence_scale")
212         row.prop(surface, "brush_radius_scale")
213
214
215 class PHYSICS_PT_dp_canvas_output(PhysicButtonsPanel, Panel):
216     bl_label = "Dynamic Paint Output"
217     bl_options = {'DEFAULT_CLOSED'}
218     COMPAT_ENGINES = {'BLENDER_RENDER'}
219
220     @classmethod
221     def poll(cls, context):
222         md = context.dynamic_paint
223         rd = context.scene.render
224         if not (md and md.ui_type == 'CANVAS' and md.canvas_settings):
225             return 0
226         surface = context.dynamic_paint.canvas_settings.canvas_surfaces.active
227         return (surface and
228                 (not (surface.surface_format == 'VERTEX' and (surface.surface_type in {'DISPLACE', 'WAVE'}))) and
229                 (rd.engine in cls.COMPAT_ENGINES))
230
231     def draw(self, context):
232         layout = self.layout
233
234         canvas = context.dynamic_paint.canvas_settings
235         surface = canvas.canvas_surfaces.active
236         ob = context.object
237
238         surface_type = surface.surface_type
239
240         # vertex format outputs
241         if surface.surface_format == 'VERTEX':
242             if surface_type == 'PAINT':
243                 # toggle active preview
244                 layout.prop(surface, "preview_id")
245
246                 # paint-map output
247                 row = layout.row()
248                 row.prop_search(surface, "output_name_a", ob.data, "vertex_colors", text="Paintmap layer")
249                 if surface.output_exists(object=ob, index=0):
250                     ic = 'ZOOMOUT'
251                 else:
252                     ic = 'ZOOMIN'
253
254                 row.operator("dpaint.output_toggle", icon=ic, text="").output = 'A'
255
256                 # wet-map output
257                 row = layout.row()
258                 row.prop_search(surface, "output_name_b", ob.data, "vertex_colors", text="Wetmap layer")
259                 if surface.output_exists(object=ob, index=1):
260                     ic = 'ZOOMOUT'
261                 else:
262                     ic = 'ZOOMIN'
263
264                 row.operator("dpaint.output_toggle", icon=ic, text="").output = 'B'
265
266             elif surface_type == 'WEIGHT':
267                 row = layout.row()
268                 row.prop_search(surface, "output_name_a", ob, "vertex_groups", text="Vertex Group")
269                 if surface.output_exists(object=ob, index=0):
270                     ic = 'ZOOMOUT'
271                 else:
272                     ic = 'ZOOMIN'
273
274                 row.operator("dpaint.output_toggle", icon=ic, text="").output = 'A'
275
276         # image format outputs
277         if surface.surface_format == 'IMAGE':
278             layout.operator("dpaint.bake", text="Bake Image Sequence", icon='MOD_DYNAMICPAINT')
279             layout.prop_search(surface, "uv_layer", ob.data, "uv_textures", text="UV Map")
280             layout.separator()
281
282             layout.prop(surface, "image_output_path", text="")
283             row = layout.row()
284             row.prop(surface, "image_fileformat", text="")
285             row.prop(surface, "use_premultiply", text="Premultiply alpha")
286
287             if surface_type == 'PAINT':
288                 split = layout.split(percentage=0.4)
289                 split.prop(surface, "use_output_a", text="Paintmaps:")
290                 sub = split.row()
291                 sub.active = surface.use_output_a
292                 sub.prop(surface, "output_name_a", text="")
293
294                 split = layout.split(percentage=0.4)
295                 split.prop(surface, "use_output_b", text="Wetmaps:")
296                 sub = split.row()
297                 sub.active = surface.use_output_b
298                 sub.prop(surface, "output_name_b", text="")
299             else:
300                 col = layout.column()
301                 col.prop(surface, "output_name_a", text="Filename:")
302                 if surface_type == 'DISPLACE':
303                     col.prop(surface, "displace_type", text="Displace Type")
304                     col.prop(surface, "depth_clamp")
305                 elif surface_type == 'WAVE':
306                     col.prop(surface, "depth_clamp", text="Wave Clamp")
307
308
309 class PHYSICS_PT_dp_canvas_initial_color(PhysicButtonsPanel, Panel):
310     bl_label = "Dynamic Paint Initial Color"
311     bl_options = {'DEFAULT_CLOSED'}
312     COMPAT_ENGINES = {'BLENDER_RENDER'}
313
314     @classmethod
315     def poll(cls, context):
316         md = context.dynamic_paint
317         rd = context.scene.render
318         if not (md and md.ui_type == 'CANVAS' and md.canvas_settings):
319             return 0
320         surface = context.dynamic_paint.canvas_settings.canvas_surfaces.active
321         return (surface and surface.surface_type == 'PAINT') and (rd.engine in cls.COMPAT_ENGINES)
322
323     def draw(self, context):
324         layout = self.layout
325
326         canvas = context.dynamic_paint.canvas_settings
327         surface = canvas.canvas_surfaces.active
328         ob = context.object
329
330         layout.prop(surface, "init_color_type", expand=False)
331         if surface.init_color_type != 'NONE':
332             layout.separator()
333
334         # dissolve
335         if surface.init_color_type == 'COLOR':
336             layout.prop(surface, "init_color")
337
338         elif surface.init_color_type == 'TEXTURE':
339             layout.prop(surface, "init_texture")
340             layout.prop_search(surface, "init_layername", ob.data, "uv_textures", text="UV Map")
341
342         elif surface.init_color_type == 'VERTEX_COLOR':
343             layout.prop_search(surface, "init_layername", ob.data, "vertex_colors", text="Color Layer")
344
345
346 class PHYSICS_PT_dp_effects(PhysicButtonsPanel, Panel):
347     bl_label = "Dynamic Paint Effects"
348     bl_options = {'DEFAULT_CLOSED'}
349     COMPAT_ENGINES = {'BLENDER_RENDER'}
350
351     @classmethod
352     def poll(cls, context):
353         md = context.dynamic_paint
354         rd = context.scene.render
355         if not (md and md.ui_type == 'CANVAS' and md.canvas_settings):
356             return False
357         surface = context.dynamic_paint.canvas_settings.canvas_surfaces.active
358         return (surface and surface.surface_type == 'PAINT') and (rd.engine in cls.COMPAT_ENGINES)
359
360     def draw(self, context):
361         layout = self.layout
362
363         canvas = context.dynamic_paint.canvas_settings
364         surface = canvas.canvas_surfaces.active
365
366         layout.prop(surface, "effect_ui", expand=True)
367
368         if surface.effect_ui == 'SPREAD':
369             layout.prop(surface, "use_spread")
370
371             row = layout.row()
372             row.active = surface.use_spread
373             row.prop(surface, "spread_speed")
374             row.prop(surface, "color_spread_speed")
375
376         elif surface.effect_ui == 'DRIP':
377             layout.prop(surface, "use_drip")
378
379             col = layout.column()
380             col.active = surface.use_drip
381             effector_weights_ui(self, context, surface.effector_weights, 'DYNAMIC_PAINT')
382
383             layout.label(text="Surface Movement:")
384             row = layout.row()
385             row.prop(surface, "drip_velocity", slider=True)
386             row.prop(surface, "drip_acceleration", slider=True)
387
388         elif surface.effect_ui == 'SHRINK':
389             layout.prop(surface, "use_shrink")
390
391             row = layout.row()
392             row.active = surface.use_shrink
393             row.prop(surface, "shrink_speed")
394
395
396 class PHYSICS_PT_dp_cache(PhysicButtonsPanel, Panel):
397     bl_label = "Dynamic Paint Cache"
398     bl_options = {'DEFAULT_CLOSED'}
399     COMPAT_ENGINES = {'BLENDER_RENDER'}
400
401     @classmethod
402     def poll(cls, context):
403         md = context.dynamic_paint
404         rd = context.scene.render
405         return (md and
406                 md.ui_type == 'CANVAS' and
407                 md.canvas_settings and
408                 md.canvas_settings.canvas_surfaces.active and
409                 md.canvas_settings.canvas_surfaces.active.is_cache_user and
410                 (rd.engine in cls.COMPAT_ENGINES))
411
412     def draw(self, context):
413         surface = context.dynamic_paint.canvas_settings.canvas_surfaces.active
414         cache = surface.point_cache
415
416         point_cache_ui(self, context, cache, (cache.is_baked is False), 'DYNAMIC_PAINT')
417
418
419 class PHYSICS_PT_dp_brush_source(PhysicButtonsPanel, Panel):
420     bl_label = "Dynamic Paint Source"
421     COMPAT_ENGINES = {'BLENDER_RENDER'}
422
423     @classmethod
424     def poll(cls, context):
425         md = context.dynamic_paint
426         rd = context.scene.render
427         return md and md.ui_type == 'BRUSH' and md.brush_settings and (rd.engine in cls.COMPAT_ENGINES)
428
429     def draw(self, context):
430         layout = self.layout
431
432         brush = context.dynamic_paint.brush_settings
433         ob = context.object
434
435         split = layout.split()
436         col = split.column()
437         col.prop(brush, "paint_source")
438
439         if brush.paint_source == 'PARTICLE_SYSTEM':
440             col.prop_search(brush, "particle_system", ob, "particle_systems", text="")
441             if brush.particle_system:
442                 col.label(text="Particle effect:")
443                 sub = col.column()
444                 sub.active = not brush.use_particle_radius
445                 sub.prop(brush, "solid_radius", text="Solid Radius")
446                 col.prop(brush, "use_particle_radius", text="Use Particle's Radius")
447                 col.prop(brush, "smooth_radius", text="Smooth radius")
448
449         if brush.paint_source in {'DISTANCE', 'VOLUME_DISTANCE', 'POINT'}:
450             col.prop(brush, "paint_distance", text="Paint Distance")
451             split = layout.row().split(percentage=0.4)
452             sub = split.column()
453             if brush.paint_source in {'DISTANCE', 'VOLUME_DISTANCE'}:
454                 sub.prop(brush, "use_proximity_project")
455             if brush.paint_source == 'VOLUME_DISTANCE':
456                 sub.prop(brush, "invert_proximity")
457                 sub.prop(brush, "use_negative_volume")
458
459             sub = split.column()
460             if brush.paint_source in {'DISTANCE', 'VOLUME_DISTANCE'}:
461                 column = sub.column()
462                 column.active = brush.use_proximity_project
463                 column.prop(brush, "ray_direction")
464             sub.prop(brush, "proximity_falloff")
465             if brush.proximity_falloff == 'RAMP':
466                 col = layout.row().column()
467                 col.separator()
468                 col.prop(brush, "use_proximity_ramp_alpha", text="Only Use Alpha")
469                 col.template_color_ramp(brush, "paint_ramp", expand=True)
470
471
472 class PHYSICS_PT_dp_brush_velocity(PhysicButtonsPanel, Panel):
473     bl_label = "Dynamic Paint Velocity"
474     bl_options = {'DEFAULT_CLOSED'}
475     COMPAT_ENGINES = {'BLENDER_RENDER'}
476
477     @classmethod
478     def poll(cls, context):
479         md = context.dynamic_paint
480         rd = context.scene.render
481         return md and md.ui_type == 'BRUSH' and md.brush_settings and (rd.engine in cls.COMPAT_ENGINES)
482
483     def draw(self, context):
484         layout = self.layout
485
486         brush = context.dynamic_paint.brush_settings
487
488         split = layout.split()
489
490         col = split.column()
491         col.prop(brush, "use_velocity_alpha")
492         col.prop(brush, "use_velocity_color")
493
494         split.prop(brush, "use_velocity_depth")
495
496         col = layout.column()
497         col.active = (brush.use_velocity_alpha or brush.use_velocity_color or brush.use_velocity_depth)
498         col.prop(brush, "velocity_max")
499         col.template_color_ramp(brush, "velocity_ramp", expand=True)
500         layout.separator()
501
502         row = layout.row()
503         row.prop(brush, "use_smudge")
504         sub = row.row()
505         sub.active = brush.use_smudge
506         sub.prop(brush, "smudge_strength")
507
508
509 class PHYSICS_PT_dp_brush_wave(PhysicButtonsPanel, Panel):
510     bl_label = "Dynamic Paint Waves"
511     bl_options = {'DEFAULT_CLOSED'}
512     COMPAT_ENGINES = {'BLENDER_RENDER'}
513
514     @classmethod
515     def poll(cls, context):
516         md = context.dynamic_paint
517         rd = context.scene.render
518         return md and md.ui_type == 'BRUSH' and md.brush_settings and (rd.engine in cls.COMPAT_ENGINES)
519
520     def draw(self, context):
521         layout = self.layout
522
523         brush = context.dynamic_paint.brush_settings
524
525         layout.prop(brush, "wave_type")
526         if brush.wave_type != 'REFLECT':
527             row = layout.row()
528             row.prop(brush, "wave_factor")
529             row.prop(brush, "wave_clamp")
530
531
532 classes = (
533     PHYSICS_PT_dp_advanced_canvas,
534     PHYSICS_PT_dp_brush_source,
535     PHYSICS_PT_dp_brush_velocity,
536     PHYSICS_PT_dp_brush_wave,
537     PHYSICS_PT_dp_cache,
538     PHYSICS_PT_dp_canvas_initial_color,
539     PHYSICS_PT_dp_canvas_output,
540     PHYSICS_PT_dp_effects,
541     PHYSICS_PT_dynamic_paint,
542     PHYSICS_UL_dynapaint_surfaces,
543 )
544
545 if __name__ == "__main__":  # only for live edit.
546     from bpy.utils import register_class
547     for cls in classes:
548         register_class(cls)