b69a4fcc76955fc911435b84249ef3a26dc3b696
[blender.git] / release / scripts / startup / bl_ui / properties_physics_field.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
21 import bpy
22 from bpy.types import (
23     Panel,
24 )
25 from .properties_physics_common import (
26     basic_force_field_settings_ui,
27     basic_force_field_falloff_ui,
28 )
29
30
31 class PhysicButtonsPanel:
32     bl_space_type = 'PROPERTIES'
33     bl_region_type = 'WINDOW'
34     bl_context = "physics"
35
36     def poll_force_field(context):
37         ob = context.object
38         return (ob and (ob.field) and (ob.field.type != 'NONE'))
39
40     def poll_collision(context):
41         ob = context.object
42         return (ob and ob.type == 'MESH') and (context.collision)
43
44
45 class PHYSICS_PT_field(PhysicButtonsPanel, Panel):
46     bl_label = "Force Fields"
47     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
48
49     @classmethod
50     def poll(cls, context):
51         if not PhysicButtonsPanel.poll_force_field(context):
52             return False
53
54         return (context.engine in cls.COMPAT_ENGINES)
55
56     def draw(self, context):
57         layout = self.layout
58         layout.use_property_split = True
59
60         ob = context.object
61         field = ob.field
62
63         layout.prop(field, "type")
64
65
66 class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel):
67     bl_label = "Settings"
68     bl_parent_id = 'PHYSICS_PT_field'
69     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
70
71     @classmethod
72     def poll(cls, context):
73         if not PhysicButtonsPanel.poll_force_field(context):
74             return False
75
76         return (context.engine in cls.COMPAT_ENGINES)
77
78     def draw(self, context):
79         layout = self.layout
80         layout.use_property_split = True
81
82         ob = context.object
83         field = ob.field
84
85         if field.type not in {'NONE', 'GUIDE', 'TEXTURE'}:
86             layout.prop(field, "shape", text="Shape")
87
88         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
89
90         if field.type == 'NONE':
91             return  # nothing to draw.
92
93         elif field.type == 'GUIDE':
94             col = flow.column()
95             col.prop(field, "guide_minimum")
96             col.prop(field, "guide_free")
97             col.prop(field, "falloff_power")
98             col.prop(field, "use_guide_path_add")
99             col.prop(field, "use_guide_path_weight")
100
101             col.separator()
102
103             col = flow.column()
104             col.prop(field, "guide_clump_amount", text="Clumping amount")
105             col.prop(field, "guide_clump_shape")
106             col.prop(field, "use_max_distance")
107
108             sub = col.column()
109             sub.active = field.use_max_distance
110             sub.prop(field, "distance_max")
111
112         elif field.type == 'TEXTURE':
113             col = flow.column()
114             col.prop(field, "texture_mode")
115
116             col.separator()
117
118             col.prop(field, "strength")
119
120             col = flow.column()
121             col.prop(field, "texture_nabla")
122             col.prop(field, "use_object_coords")
123             col.prop(field, "use_2d_force")
124
125         elif field.type == 'SMOKE_FLOW':
126             col = flow.column()
127             col.prop(field, "strength")
128             col.prop(field, "flow")
129
130             col = flow.column()
131             col.prop(field, "source_object")
132             col.prop(field, "use_smoke_density")
133         else:
134             del flow
135             basic_force_field_settings_ui(self, context, field)
136
137
138 class PHYSICS_PT_field_settings_kink(PhysicButtonsPanel, Panel):
139     bl_label = "Kink"
140     bl_parent_id = 'PHYSICS_PT_field_settings'
141     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
142
143     @classmethod
144     def poll(cls, context):
145         if not PhysicButtonsPanel.poll_force_field(context):
146             return False
147
148         ob = context.object
149         return ((ob.field.type == 'GUIDE') and (context.engine in cls.COMPAT_ENGINES))
150
151     def draw(self, context):
152         layout = self.layout
153         layout.use_property_split = True
154
155         ob = context.object
156         field = ob.field
157
158         layout.prop(field, "guide_kink_type", text="Type")
159
160         if field.guide_kink_type != 'NONE':
161             flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
162
163             col = flow.column()
164             col.prop(field, "guide_kink_axis")
165             col.prop(field, "guide_kink_frequency")
166
167             col = flow.column()
168             col.prop(field, "guide_kink_shape")
169             col.prop(field, "guide_kink_amplitude")
170
171
172 class PHYSICS_PT_field_settings_texture_select(PhysicButtonsPanel, Panel):
173     bl_label = "Texture"
174     bl_parent_id = 'PHYSICS_PT_field_settings'
175     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
176
177     @classmethod
178     def poll(cls, context):
179         if not PhysicButtonsPanel.poll_force_field(context):
180             return False
181
182         ob = context.object
183         return ((ob.field.type == 'TEXTURE') and (context.engine in cls.COMPAT_ENGINES))
184
185     def draw(self, context):
186         layout = self.layout
187
188         ob = context.object
189         field = ob.field
190
191         layout.row().template_ID(field, "texture", new="texture.new")
192
193
194 class PHYSICS_PT_field_falloff(PhysicButtonsPanel, Panel):
195     bl_label = "Falloff"
196     bl_parent_id = "PHYSICS_PT_field"
197     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
198
199     @classmethod
200     def poll(cls, context):
201         if not PhysicButtonsPanel.poll_force_field(context):
202             return False
203
204         ob = context.object
205         return ((ob.field.type not in {'NONE', 'GUIDE'}) and (context.engine in cls.COMPAT_ENGINES))
206
207     def draw(self, context):
208         layout = self.layout
209         layout.use_property_split = True
210
211         ob = context.object
212         field = ob.field
213
214         layout.prop(field, "falloff_type", text="Shape")
215
216         basic_force_field_falloff_ui(self, context, field)
217
218
219 class PHYSICS_PT_field_falloff_angular(PhysicButtonsPanel, Panel):
220     bl_label = "Angular"
221     bl_parent_id = "PHYSICS_PT_field_falloff"
222     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
223
224     @classmethod
225     def poll(cls, context):
226         if not PhysicButtonsPanel.poll_force_field(context):
227             return False
228
229         ob = context.object
230         return ((ob.field.falloff_type == 'CONE') and (context.engine in cls.COMPAT_ENGINES))
231
232     def draw(self, context):
233         layout = self.layout
234         layout.use_property_split = True
235         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
236
237         ob = context.object
238         field = ob.field
239
240         col = flow.column()
241         col.prop(field, "radial_falloff", text="Power")
242
243         col = flow.column()
244         col.prop(field, "use_radial_min", text="Use Min Angle")
245
246         sub = col.column()
247         sub.active = field.use_radial_min
248         sub.prop(field, "radial_min", text="Min Angle")
249
250         col = flow.column()
251         col.prop(field, "use_radial_max", text="Use Max Angle")
252
253         sub = col.column()
254         sub.active = field.use_radial_max
255         sub.prop(field, "radial_max", text="Max Angle")
256
257
258 class PHYSICS_PT_field_falloff_radial(PhysicButtonsPanel, Panel):
259     bl_label = "Radial"
260     bl_parent_id = "PHYSICS_PT_field_falloff"
261     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
262
263     @classmethod
264     def poll(cls, context):
265         if not PhysicButtonsPanel.poll_force_field(context):
266             return False
267
268         ob = context.object
269         return ((ob.field.falloff_type == 'TUBE') and (context.engine in cls.COMPAT_ENGINES))
270
271     def draw(self, context):
272         layout = self.layout
273         layout.use_property_split = True
274         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
275
276         ob = context.object
277         field = ob.field
278
279         col = flow.column()
280         col.prop(field, "radial_falloff", text="Power")
281
282         col = flow.column()
283         col.prop(field, "use_radial_min", text="Use Minimum")
284
285         sub = col.column()
286         sub.active = field.use_radial_min
287         sub.prop(field, "radial_min", text="Min Distance")
288
289         col = flow.column()
290         col.prop(field, "use_radial_max", text="Use Maximum")
291
292         sub = col.column()
293         sub.active = field.use_radial_max
294         sub.prop(field, "radial_max", text="Max Distance")
295
296
297 def collision_warning(layout):
298     row = layout.row(align=True)
299     row.alignment = 'RIGHT'
300     row.label(text="No collision settings available")
301
302
303 class PHYSICS_PT_collision(PhysicButtonsPanel, Panel):
304     bl_label = "Collision"
305     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
306
307     @classmethod
308     def poll(cls, context):
309         if not PhysicButtonsPanel.poll_collision(context):
310             return False
311
312         return (context.engine in cls.COMPAT_ENGINES)
313
314     def draw(self, context):
315         layout = self.layout
316         layout.use_property_split = True
317
318         md = context.collision
319         coll = md.settings
320
321         if not coll:
322             collision_warning(layout)
323             return
324
325         settings = context.object.collision
326
327         layout.active = settings.use
328
329         col = layout.column()
330         col.prop(settings, "absorption", text="Field Absorption")
331
332
333 class PHYSICS_PT_collision_particle(PhysicButtonsPanel, Panel):
334     bl_label = "Particle"
335     bl_parent_id = "PHYSICS_PT_collision"
336     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
337
338     @classmethod
339     def poll(cls, context):
340         if not PhysicButtonsPanel.poll_collision(context):
341             return False
342
343         return (context.engine in cls.COMPAT_ENGINES)
344
345     def draw(self, context):
346         layout = self.layout
347
348         md = context.collision
349
350         layout.use_property_split = True
351         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
352
353         coll = md.settings
354
355         if not coll:
356             collision_warning(layout)
357             return
358
359         settings = context.object.collision
360
361         layout.active = settings.use
362
363         col = flow.column()
364         col.prop(settings, "permeability", slider=True)
365         col.prop(settings, "stickiness")
366         col.prop(settings, "use_particle_kill")
367
368         col = flow.column()
369         sub = col.column(align=True)
370         sub.prop(settings, "damping_factor", text="Damping", slider=True)
371         sub.prop(settings, "damping_random", text="Randomize", slider=True)
372
373         col = flow.column()
374         sub = col.column(align=True)
375         sub.prop(settings, "friction_factor", text="Friction", slider=True)
376         sub.prop(settings, "friction_random", text="Randomize", slider=True)
377
378
379 class PHYSICS_PT_collision_softbody(PhysicButtonsPanel, Panel):
380     bl_label = "Softbody And Cloth"
381     bl_parent_id = "PHYSICS_PT_collision"
382     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
383
384     @classmethod
385     def poll(cls, context):
386         if not PhysicButtonsPanel.poll_collision(context):
387             return False
388
389         return (context.engine in cls.COMPAT_ENGINES)
390
391     def draw(self, context):
392         layout = self.layout
393
394         layout.use_property_split = True
395         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
396
397         md = context.collision
398         coll = md.settings
399
400         if not coll:
401             collision_warning(layout)
402             return
403
404         settings = context.object.collision
405
406         layout.active = settings.use
407
408         col = flow.column()
409         col.prop(settings, "damping", text="Damping", slider=True)
410
411         col = flow.column()
412         col.prop(settings, "thickness_outer", text="Thickness Outer", slider=True)
413
414         col = flow.column()
415         col.prop(settings, "thickness_inner", text="Inner", slider=True)
416
417         col = flow.column()
418         col.prop(settings, "cloth_friction")
419
420         col = flow.column()
421         col.prop(settings, "use_culling")
422
423         col = flow.column()
424         col.prop(settings, "use_normal")
425
426
427 classes = (
428     PHYSICS_PT_field,
429     PHYSICS_PT_field_settings,
430     PHYSICS_PT_field_settings_kink,
431     PHYSICS_PT_field_settings_texture_select,
432     PHYSICS_PT_field_falloff,
433     PHYSICS_PT_field_falloff_angular,
434     PHYSICS_PT_field_falloff_radial,
435     PHYSICS_PT_collision,
436     PHYSICS_PT_collision_particle,
437     PHYSICS_PT_collision_softbody,
438 )
439
440
441 if __name__ == "__main__":  # only for live edit.
442     from bpy.utils import register_class
443     for cls in classes:
444         register_class(cls)