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