GPU: Fix broken assert
[blender.git] / release / scripts / startup / bl_ui / properties_physics_smoke.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 bl_ui.properties_physics_common import (
26     point_cache_ui,
27     effector_weights_ui,
28 )
29
30
31 class PhysicButtonsPanel:
32     bl_space_type = 'PROPERTIES'
33     bl_region_type = 'WINDOW'
34     bl_context = "physics"
35
36     @staticmethod
37     def poll_smoke(context):
38         ob = context.object
39         if not ((ob and ob.type == 'MESH') and (context.smoke)):
40             return False
41
42         md = context.smoke
43         return md and (context.smoke.smoke_type != 'NONE') and (bpy.app.build_options.mod_smoke)
44
45     @staticmethod
46     def poll_smoke_domain(context):
47         if not PhysicButtonsPanel.poll_smoke(context):
48             return False
49
50         md = context.smoke
51         return md and (md.smoke_type == 'DOMAIN')
52
53
54 class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
55     bl_label = "Smoke"
56     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
57
58     @classmethod
59     def poll(cls, context):
60         ob = context.object
61         return (ob and ob.type == 'MESH') and (context.engine in cls.COMPAT_ENGINES) and (context.smoke)
62
63     def draw(self, context):
64         layout = self.layout
65         layout.use_property_split = True
66
67         if not bpy.app.build_options.mod_smoke:
68             col = layout.column(align=True)
69             col.alignment = 'RIGHT'
70             col.label(text="Built without Smoke modifier")
71             return
72
73         md = context.smoke
74
75         layout.prop(md, "smoke_type")
76
77
78 class PHYSICS_PT_smoke_settings(PhysicButtonsPanel, Panel):
79     bl_label = "Settings"
80     bl_parent_id = 'PHYSICS_PT_smoke'
81     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
82
83     @classmethod
84     def poll(cls, context):
85         if not PhysicButtonsPanel.poll_smoke(context):
86             return False
87
88         return (context.engine in cls.COMPAT_ENGINES)
89
90     def draw(self, context):
91         layout = self.layout
92         layout.use_property_split = True
93
94         md = context.smoke
95         ob = context.object
96
97         if md.smoke_type == 'DOMAIN':
98             domain = md.domain_settings
99
100             flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
101
102             col = flow.column()
103             col.enabled = (not domain.point_cache.is_baked)
104             col.prop(domain, "resolution_max", text="Resolution Divisions")
105             col.prop(domain, "time_scale", text="Time Scale")
106
107             col.separator()
108
109             col = flow.column()
110             sub = col.row()
111             sub.enabled = (not domain.point_cache.is_baked)
112             sub.prop(domain, "collision_extents", text="Border Collisions")
113
114             # This can be tweaked after baking, for render.
115             col.prop(domain, "clipping", text="Empty Space")
116
117         elif md.smoke_type == 'FLOW':
118             flow_smoke = md.flow_settings
119
120             col = layout.column()
121             col.prop(flow_smoke, "smoke_flow_type", expand=False)
122
123             col.separator()
124
125             flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
126             col = flow.column()
127
128             if flow_smoke.smoke_flow_type != 'OUTFLOW':
129                 col.prop(flow_smoke, "smoke_flow_source", expand=False, text="Flow Source")
130
131                 if flow_smoke.smoke_flow_source == 'PARTICLES':
132                     col.prop_search(
133                         flow_smoke, "particle_system", ob, "particle_systems",
134                         text="Particle System"
135                     )
136                 else:
137                     col.prop(flow_smoke, "surface_distance")
138                     col.prop(flow_smoke, "volume_density")
139
140                 col = flow.column()
141                 col.prop(flow_smoke, "use_absolute")
142
143                 if flow_smoke.smoke_flow_type in {'SMOKE', 'BOTH'}:
144                     col.prop(flow_smoke, "density")
145                     col.prop(flow_smoke, "temperature", text="Temperature Diff.")
146
147                     col.separator()
148
149                     col = flow.column()
150                     col.prop(flow_smoke, "smoke_color")
151
152                 if flow_smoke.smoke_flow_type in {'FIRE', 'BOTH'}:
153                     col.prop(flow_smoke, "fuel_amount")
154
155                 col.prop(flow_smoke, "subframes", text="Sampling Subframes")
156
157             col.separator()
158
159             col.prop_search(flow_smoke, "density_vertex_group", ob, "vertex_groups", text="Vertex Group")
160
161         elif md.smoke_type == 'COLLISION':
162             coll = md.coll_settings
163
164             col = layout.column()
165             col.prop(coll, "collision_type")
166
167
168 class PHYSICS_PT_smoke_settings_initial_velocity(PhysicButtonsPanel, Panel):
169     bl_label = "Initial Velocity"
170     bl_parent_id = 'PHYSICS_PT_smoke_settings'
171     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
172
173     @classmethod
174     def poll(cls, context):
175         if not PhysicButtonsPanel.poll_smoke(context):
176             return False
177
178         md = context.smoke
179         return (md and (md.smoke_type == 'FLOW')
180                 and md.flow_settings and md.flow_settings.smoke_flow_type != 'OUTFLOW'
181                 and context.engine in cls.COMPAT_ENGINES)
182
183     def draw_header(self, context):
184         md = context.smoke
185         flow_smoke = md.flow_settings
186
187         self.layout.prop(flow_smoke, "use_initial_velocity", text="")
188
189     def draw(self, context):
190         layout = self.layout
191         layout.use_property_split = True
192         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
193
194         md = context.smoke
195         flow_smoke = md.flow_settings
196
197         flow.active = flow_smoke.use_initial_velocity
198
199         col = flow.column(align=True)
200         col.prop(flow_smoke, "velocity_factor")
201
202         if flow_smoke.smoke_flow_source == 'MESH':
203             col = flow.column()
204             col.prop(flow_smoke, "velocity_normal")
205             # sub.prop(flow_smoke, "velocity_random")
206
207
208 class PHYSICS_PT_smoke_settings_particle_size(PhysicButtonsPanel, Panel):
209     bl_label = "Particle Size"
210     bl_parent_id = 'PHYSICS_PT_smoke_settings'
211     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
212
213     @classmethod
214     def poll(cls, context):
215         if not PhysicButtonsPanel.poll_smoke(context):
216             return False
217
218         md = context.smoke
219         return (md and (md.smoke_type == 'FLOW')
220                 and md.flow_settings and md.flow_settings.smoke_flow_type != 'OUTFLOW'
221                 and md.flow_settings.smoke_flow_source == 'PARTICLES'
222                 and context.engine in cls.COMPAT_ENGINES)
223
224     def draw_header(self, context):
225         md = context.smoke
226         flow_smoke = md.flow_settings
227
228         self.layout.prop(flow_smoke, "use_particle_size", text="")
229
230     def draw(self, context):
231         layout = self.layout
232         layout.use_property_split = True
233
234         md = context.smoke
235         flow_smoke = md.flow_settings
236
237         layout.active = flow_smoke.use_particle_size
238
239         layout.prop(flow_smoke, "particle_size")
240
241
242 class PHYSICS_PT_smoke_behavior(PhysicButtonsPanel, Panel):
243     bl_label = "Behavior"
244     bl_parent_id = 'PHYSICS_PT_smoke_settings'
245     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
246
247     @classmethod
248     def poll(cls, context):
249         if not PhysicButtonsPanel.poll_smoke_domain(context):
250             return False
251
252         return (context.engine in cls.COMPAT_ENGINES)
253
254     def draw(self, context):
255         layout = self.layout
256         layout.use_property_split = True
257
258         md = context.smoke
259         domain = md.domain_settings
260
261         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
262         flow.enabled = (not domain.point_cache.is_baked)
263
264         col = flow.column()
265         col.prop(domain, "alpha")
266         col.prop(domain, "beta", text="Temperature Diff.")
267         col = flow.column()
268         col.prop(domain, "vorticity")
269
270
271 class PHYSICS_PT_smoke_behavior_dissolve(PhysicButtonsPanel, Panel):
272     bl_label = "Dissolve"
273     bl_parent_id = 'PHYSICS_PT_smoke_behavior'
274     bl_options = {'DEFAULT_CLOSED'}
275     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
276
277     @classmethod
278     def poll(cls, context):
279         if not PhysicButtonsPanel.poll_smoke_domain(context):
280             return False
281
282         return (context.engine in cls.COMPAT_ENGINES)
283
284     def draw_header(self, context):
285         md = context.smoke
286         domain = md.domain_settings
287
288         self.layout.prop(domain, "use_dissolve_smoke", text="")
289
290     def draw(self, context):
291         layout = self.layout
292         layout.use_property_split = True
293
294         md = context.smoke
295         domain = md.domain_settings
296
297         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
298         flow.enabled = (not domain.point_cache.is_baked)
299
300         layout.active = domain.use_dissolve_smoke
301
302         col = flow.column()
303         col.prop(domain, "dissolve_speed", text="Time")
304
305         col = flow.column()
306         col.prop(domain, "use_dissolve_smoke_log", text="Slow")
307
308
309 class PHYSICS_PT_smoke_flow_texture(PhysicButtonsPanel, Panel):
310     bl_label = "Texture"
311     bl_parent_id = 'PHYSICS_PT_smoke'
312     bl_options = {'DEFAULT_CLOSED'}
313     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
314
315     @classmethod
316     def poll(cls, context):
317         if not PhysicButtonsPanel.poll_smoke(context):
318             return False
319
320         md = context.smoke
321         return (md and (md.smoke_type == 'FLOW')
322                 and (md.flow_settings.smoke_flow_source == 'MESH')
323                 and (context.engine in cls.COMPAT_ENGINES))
324
325     def draw_header(self, context):
326         md = context.smoke
327         flow_smoke = md.flow_settings
328
329         self.layout.prop(flow_smoke, "use_texture", text="")
330
331     def draw(self, context):
332         layout = self.layout
333         layout.use_property_split = True
334         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
335
336         ob = context.object
337         flow_smoke = context.smoke.flow_settings
338
339         sub = flow.column()
340         sub.active = flow_smoke.use_texture
341         sub.prop(flow_smoke, "noise_texture")
342         sub.prop(flow_smoke, "texture_map_type", text="Mapping")
343
344         col = flow.column()
345         sub = col.column()
346         sub.active = flow_smoke.use_texture
347
348         if flow_smoke.texture_map_type == 'UV':
349             sub.prop_search(flow_smoke, "uv_layer", ob.data, "uv_layers")
350
351         if flow_smoke.texture_map_type == 'AUTO':
352             sub.prop(flow_smoke, "texture_size")
353
354         sub.prop(flow_smoke, "texture_offset")
355
356
357 class PHYSICS_PT_smoke_fire(PhysicButtonsPanel, Panel):
358     bl_label = "Flames"
359     bl_parent_id = 'PHYSICS_PT_smoke'
360     bl_options = {'DEFAULT_CLOSED'}
361     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
362
363     @classmethod
364     def poll(cls, context):
365         if not PhysicButtonsPanel.poll_smoke_domain(context):
366             return False
367
368         return (context.engine in cls.COMPAT_ENGINES)
369
370     def draw(self, context):
371         layout = self.layout
372         layout.use_property_split = True
373
374         domain = context.smoke.domain_settings
375
376         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
377         flow.enabled = (not domain.point_cache.is_baked)
378
379         col = flow.column()
380         col.prop(domain, "burning_rate", text="Reaction Speed")
381         col.prop(domain, "flame_smoke")
382         col.prop(domain, "flame_vorticity")
383
384         col.separator()
385
386         col = flow.column(align=True)
387         col.prop(domain, "flame_ignition", text="Temperature Ignition")
388         col.prop(domain, "flame_max_temp")
389
390         col.separator()
391
392         sub = col.column()
393         sub.prop(domain, "flame_smoke_color")
394
395
396 class PHYSICS_PT_smoke_adaptive_domain(PhysicButtonsPanel, Panel):
397     bl_label = "Adaptive Domain"
398     bl_parent_id = 'PHYSICS_PT_smoke'
399     bl_options = {'DEFAULT_CLOSED'}
400     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
401
402     @classmethod
403     def poll(cls, context):
404         if not PhysicButtonsPanel.poll_smoke_domain(context):
405             return False
406
407         return (context.engine in cls.COMPAT_ENGINES)
408
409     def draw_header(self, context):
410         md = context.smoke.domain_settings
411
412         self.layout.prop(md, "use_adaptive_domain", text="")
413
414     def draw(self, context):
415         layout = self.layout
416         layout.use_property_split = True
417
418         domain = context.smoke.domain_settings
419         layout.active = domain.use_adaptive_domain
420
421         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
422         flow.enabled = (not domain.point_cache.is_baked)
423
424         col = flow.column()
425         col.prop(domain, "additional_res", text="Add Resolution")
426         col.prop(domain, "adapt_margin")
427
428         col.separator()
429
430         col = flow.column()
431         col.prop(domain, "adapt_threshold", text="Threshold")
432
433
434 class PHYSICS_PT_smoke_highres(PhysicButtonsPanel, Panel):
435     bl_label = "High Resolution"
436     bl_parent_id = 'PHYSICS_PT_smoke'
437     bl_options = {'DEFAULT_CLOSED'}
438     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
439
440     @classmethod
441     def poll(cls, context):
442         if not PhysicButtonsPanel.poll_smoke_domain(context):
443             return False
444
445         return (context.engine in cls.COMPAT_ENGINES)
446
447     def draw_header(self, context):
448         md = context.smoke.domain_settings
449
450         self.layout.prop(md, "use_high_resolution", text="")
451
452     def draw(self, context):
453         layout = self.layout
454         layout.use_property_split = True
455
456         md = context.smoke.domain_settings
457         layout.active = md.use_high_resolution
458
459         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
460
461         col = flow.column()
462         col.enabled = not md.point_cache.is_baked
463         col.prop(md, "amplify", text="Resolution Divisions")
464         col.prop(md, "highres_sampling", text="Flow Sampling")
465
466         col.separator()
467
468         col = flow.column()
469         col.enabled = not md.point_cache.is_baked
470         col.prop(md, "noise_type", text="Noise Method")
471         col.prop(md, "strength")
472
473         layout.prop(md, "show_high_resolution")
474
475
476 class PHYSICS_PT_smoke_collections(PhysicButtonsPanel, Panel):
477     bl_label = "Collections"
478     bl_parent_id = 'PHYSICS_PT_smoke'
479     bl_options = {'DEFAULT_CLOSED'}
480     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
481
482     @classmethod
483     def poll(cls, context):
484         if not PhysicButtonsPanel.poll_smoke_domain(context):
485             return False
486
487         return (context.engine in cls.COMPAT_ENGINES)
488
489     def draw(self, context):
490         layout = self.layout
491         layout.use_property_split = True
492
493         domain = context.smoke.domain_settings
494
495         col = layout.column()
496         col.prop(domain, "fluid_collection", text="Flow")
497
498         # col = layout.column()
499         # col.prop(domain, "effector_collection", text="Effector")
500         col.prop(domain, "collision_collection", text="Collision")
501
502
503 class PHYSICS_PT_smoke_cache(PhysicButtonsPanel, Panel):
504     bl_label = "Cache"
505     bl_parent_id = 'PHYSICS_PT_smoke'
506     bl_options = {'DEFAULT_CLOSED'}
507     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
508
509     @classmethod
510     def poll(cls, context):
511         if not PhysicButtonsPanel.poll_smoke_domain(context):
512             return False
513
514         return (context.engine in cls.COMPAT_ENGINES)
515
516     def draw(self, context):
517         layout = self.layout
518         layout.use_property_split = True
519         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
520
521         domain = context.smoke.domain_settings
522         cache_file_format = domain.cache_file_format
523
524         col = flow.column()
525         col.prop(domain, "cache_file_format")
526
527         if cache_file_format == 'POINTCACHE':
528             col = flow.column()
529             col.prop(domain, "point_cache_compress_type", text="Compression")
530             col.separator()
531
532         elif cache_file_format == 'OPENVDB':
533             if not bpy.app.build_options.openvdb:
534                 row = layout.row(align=True)
535                 row.alignment = 'RIGHT'
536                 row.label(text="Built without OpenVDB support")
537                 return
538
539             col = flow.column()
540             col.prop(domain, "openvdb_cache_compress_type", text="Compression")
541             col.prop(domain, "data_depth", text="Data Depth")
542             col.separator()
543
544         cache = domain.point_cache
545         point_cache_ui(self, cache, (cache.is_baked is False), 'SMOKE')
546
547
548 class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel):
549     bl_label = "Field Weights"
550     bl_parent_id = 'PHYSICS_PT_smoke'
551     bl_options = {'DEFAULT_CLOSED'}
552     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
553
554     @classmethod
555     def poll(cls, context):
556         if not PhysicButtonsPanel.poll_smoke_domain(context):
557             return False
558
559         return (context.engine in cls.COMPAT_ENGINES)
560
561     def draw(self, context):
562         domain = context.smoke.domain_settings
563         effector_weights_ui(self, domain.effector_weights, 'SMOKE')
564
565
566 class PHYSICS_PT_smoke_viewport_display(PhysicButtonsPanel, Panel):
567     bl_label = "Viewport Display"
568     bl_parent_id = 'PHYSICS_PT_smoke'
569     bl_options = {'DEFAULT_CLOSED'}
570
571     @classmethod
572     def poll(cls, context):
573         return (PhysicButtonsPanel.poll_smoke_domain(context))
574
575     def draw(self, context):
576         layout = self.layout
577         layout.use_property_split = True
578         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
579
580         domain = context.smoke.domain_settings
581
582         col = flow.column()
583         col.prop(domain, "display_thickness")
584
585         col.separator()
586
587         col.prop(domain, "slice_method", text="Slicing")
588
589         slice_method = domain.slice_method
590         axis_slice_method = domain.axis_slice_method
591
592         do_axis_slicing = (slice_method == 'AXIS_ALIGNED')
593         do_full_slicing = (axis_slice_method == 'FULL')
594
595         col = col.column()
596         col.enabled = do_axis_slicing
597         col.prop(domain, "axis_slice_method")
598
599         col = flow.column()
600         sub = col.column()
601         sub.enabled = not do_full_slicing and do_axis_slicing
602         sub.prop(domain, "slice_axis")
603         sub.prop(domain, "slice_depth")
604
605         row = col.row()
606         row.enabled = do_full_slicing or not do_axis_slicing
607         row.prop(domain, "slice_per_voxel")
608
609         col.prop(domain, "display_interpolation")
610
611
612 class PHYSICS_PT_smoke_viewport_display_color(PhysicButtonsPanel, Panel):
613     bl_label = "Color Mapping"
614     bl_parent_id = 'PHYSICS_PT_smoke_viewport_display'
615     bl_options = {'DEFAULT_CLOSED'}
616
617     @classmethod
618     def poll(cls, context):
619         return (PhysicButtonsPanel.poll_smoke_domain(context))
620
621     def draw_header(self, context):
622         md = context.smoke.domain_settings
623
624         self.layout.prop(md, "use_color_ramp", text="")
625
626     def draw(self, context):
627         layout = self.layout
628         layout.use_property_split = True
629
630         domain = context.smoke.domain_settings
631         col = layout.column()
632         col.enabled = domain.use_color_ramp
633
634         col.prop(domain, "coba_field")
635
636         col.use_property_split = False
637
638         col = col.column()
639         col.template_color_ramp(domain, "color_ramp", expand=True)
640
641
642 class PHYSICS_PT_smoke_viewport_display_debug(PhysicButtonsPanel, Panel):
643     bl_label = "Debug Velocity"
644     bl_parent_id = 'PHYSICS_PT_smoke_viewport_display'
645     bl_options = {'DEFAULT_CLOSED'}
646
647     @classmethod
648     def poll(cls, context):
649         return (PhysicButtonsPanel.poll_smoke_domain(context))
650
651     def draw_header(self, context):
652         md = context.smoke.domain_settings
653
654         self.layout.prop(md, "show_velocity", text="")
655
656     def draw(self, context):
657         layout = self.layout
658         layout.use_property_split = True
659         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
660
661         domain = context.smoke.domain_settings
662
663         col = flow.column()
664         col.enabled = domain.show_velocity
665         col.prop(domain, "vector_display_type", text="Display As")
666         col.prop(domain, "vector_scale")
667
668
669 classes = (
670     PHYSICS_PT_smoke,
671     PHYSICS_PT_smoke_settings,
672     PHYSICS_PT_smoke_settings_initial_velocity,
673     PHYSICS_PT_smoke_settings_particle_size,
674     PHYSICS_PT_smoke_behavior,
675     PHYSICS_PT_smoke_behavior_dissolve,
676     PHYSICS_PT_smoke_adaptive_domain,
677     PHYSICS_PT_smoke_cache,
678     PHYSICS_PT_smoke_field_weights,
679     PHYSICS_PT_smoke_fire,
680     PHYSICS_PT_smoke_flow_texture,
681     PHYSICS_PT_smoke_collections,
682     PHYSICS_PT_smoke_highres,
683     PHYSICS_PT_smoke_viewport_display,
684     PHYSICS_PT_smoke_viewport_display_color,
685     PHYSICS_PT_smoke_viewport_display_debug,
686 )
687
688
689 if __name__ == "__main__":  # only for live edit.
690     from bpy.utils import register_class
691     for cls in classes:
692         register_class(cls)