1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
22 from bpy.types import (
25 from .properties_physics_common import (
31 class PhysicButtonsPanel:
32 bl_space_type = 'PROPERTIES'
33 bl_region_type = 'WINDOW'
34 bl_context = "physics"
36 def poll_smoke(context):
38 if not ((ob and ob.type == 'MESH') and (context.smoke)):
42 return md and (context.smoke.smoke_type != 'NONE') and (bpy.app.build_options.mod_smoke)
44 def poll_smoke_domain(context):
45 if not PhysicButtonsPanel.poll_smoke(context):
49 return md and (md.smoke_type == 'DOMAIN')
52 class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
54 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
57 def poll(cls, context):
59 return (ob and ob.type == 'MESH') and (context.engine in cls.COMPAT_ENGINES) and (context.smoke)
61 def draw(self, context):
63 layout.use_property_split = True
65 if not bpy.app.build_options.mod_smoke:
66 col = layout.column(align=True)
67 col.alignment = 'RIGHT'
68 col.label(text="Built without Smoke modifier")
73 layout.prop(md, "smoke_type")
76 class PHYSICS_PT_smoke_settings(PhysicButtonsPanel, Panel):
78 bl_parent_id = 'PHYSICS_PT_smoke'
79 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
82 def poll(cls, context):
83 if not PhysicButtonsPanel.poll_smoke(context):
86 return (context.engine in cls.COMPAT_ENGINES)
88 def draw(self, context):
90 layout.use_property_split = True
95 if md.smoke_type == 'DOMAIN':
96 domain = md.domain_settings
98 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
101 col.enabled = (not domain.point_cache.is_baked)
102 col.prop(domain, "resolution_max", text="Resolution Divisions")
103 col.prop(domain, "time_scale", text="Time Scale")
109 sub.enabled = (not domain.point_cache.is_baked)
110 sub.prop(domain, "collision_extents", text="Border Collisions")
112 # This can be tweaked after baking, for render.
113 col.prop(domain, "clipping", text="Empty Space")
115 elif md.smoke_type == 'FLOW':
116 flow_smoke = md.flow_settings
118 col = layout.column()
119 col.prop(flow_smoke, "smoke_flow_type", expand=False)
123 flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
126 if flow_smoke.smoke_flow_type != 'OUTFLOW':
127 col.prop(flow_smoke, "smoke_flow_source", expand=False, text="Flow Source")
129 if flow_smoke.smoke_flow_source == 'PARTICLES':
131 flow_smoke, "particle_system", ob, "particle_systems",
132 text="Particle System"
135 col.prop(flow_smoke, "surface_distance")
136 col.prop(flow_smoke, "volume_density")
139 col.prop(flow_smoke, "use_absolute")
141 if flow_smoke.smoke_flow_type in {'SMOKE', 'BOTH'}:
142 col.prop(flow_smoke, "density")
143 col.prop(flow_smoke, "temperature", text="Temperature Diff.")
148 col.prop(flow_smoke, "smoke_color")
150 if flow_smoke.smoke_flow_type in {'FIRE', 'BOTH'}:
151 col.prop(flow_smoke, "fuel_amount")
153 col.prop(flow_smoke, "subframes", text="Sampling Subframes")
157 col.prop_search(flow_smoke, "density_vertex_group", ob, "vertex_groups", text="Vertex Group")
159 elif md.smoke_type == 'COLLISION':
160 coll = md.coll_settings
162 col = layout.column()
163 col.prop(coll, "collision_type")
166 class PHYSICS_PT_smoke_settings_initial_velocity(PhysicButtonsPanel, Panel):
167 bl_label = "Initial Velocity"
168 bl_parent_id = 'PHYSICS_PT_smoke_settings'
169 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
172 def poll(cls, context):
173 if not PhysicButtonsPanel.poll_smoke(context):
177 return (md and (md.smoke_type == 'FLOW')
178 and md.flow_settings and md.flow_settings.smoke_flow_type != 'OUTFLOW'
179 and context.engine in cls.COMPAT_ENGINES)
181 def draw_header(self, context):
183 flow_smoke = md.flow_settings
185 self.layout.prop(flow_smoke, "use_initial_velocity", text="")
187 def draw(self, context):
189 layout.use_property_split = True
190 flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
193 flow_smoke = md.flow_settings
195 flow.active = flow_smoke.use_initial_velocity
197 col = flow.column(align=True)
198 col.prop(flow_smoke, "velocity_factor")
200 if flow_smoke.smoke_flow_source == 'MESH':
202 col.prop(flow_smoke, "velocity_normal")
203 # sub.prop(flow_smoke, "velocity_random")
206 class PHYSICS_PT_smoke_settings_particle_size(PhysicButtonsPanel, Panel):
207 bl_label = "Particle Size"
208 bl_parent_id = 'PHYSICS_PT_smoke_settings'
209 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
212 def poll(cls, context):
213 if not PhysicButtonsPanel.poll_smoke(context):
217 return (md and (md.smoke_type == 'FLOW')
218 and md.flow_settings and md.flow_settings.smoke_flow_type != 'OUTFLOW'
219 and md.flow_settings.smoke_flow_source == 'PARTICLES'
220 and context.engine in cls.COMPAT_ENGINES)
222 def draw_header(self, context):
224 flow_smoke = md.flow_settings
226 self.layout.prop(flow_smoke, "use_particle_size", text="")
228 def draw(self, context):
230 layout.use_property_split = True
233 flow_smoke = md.flow_settings
235 layout.active = flow_smoke.use_particle_size
237 layout.prop(flow_smoke, "particle_size")
240 class PHYSICS_PT_smoke_behavior(PhysicButtonsPanel, Panel):
241 bl_label = "Behavior"
242 bl_parent_id = 'PHYSICS_PT_smoke_settings'
243 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
246 def poll(cls, context):
247 if not PhysicButtonsPanel.poll_smoke_domain(context):
250 return (context.engine in cls.COMPAT_ENGINES)
252 def draw(self, context):
254 layout.use_property_split = True
257 domain = md.domain_settings
259 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
260 flow.enabled = (not domain.point_cache.is_baked)
263 col.prop(domain, "alpha")
264 col.prop(domain, "beta", text="Temperature Diff.")
266 col.prop(domain, "vorticity")
269 class PHYSICS_PT_smoke_behavior_dissolve(PhysicButtonsPanel, Panel):
270 bl_label = "Dissolve"
271 bl_parent_id = 'PHYSICS_PT_smoke_behavior'
272 bl_options = {'DEFAULT_CLOSED'}
273 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
276 def poll(cls, context):
277 if not PhysicButtonsPanel.poll_smoke_domain(context):
280 return (context.engine in cls.COMPAT_ENGINES)
282 def draw_header(self, context):
284 domain = md.domain_settings
286 self.layout.prop(domain, "use_dissolve_smoke", text="")
288 def draw(self, context):
290 layout.use_property_split = True
293 domain = md.domain_settings
295 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
296 flow.enabled = (not domain.point_cache.is_baked)
298 layout.active = domain.use_dissolve_smoke
301 col.prop(domain, "dissolve_speed", text="Time")
304 col.prop(domain, "use_dissolve_smoke_log", text="Slow")
307 class PHYSICS_PT_smoke_flow_texture(PhysicButtonsPanel, Panel):
309 bl_parent_id = 'PHYSICS_PT_smoke'
310 bl_options = {'DEFAULT_CLOSED'}
311 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
314 def poll(cls, context):
315 if not PhysicButtonsPanel.poll_smoke(context):
319 return (md and (md.smoke_type == 'FLOW')
320 and (md.flow_settings.smoke_flow_source == 'MESH')
321 and (context.engine in cls.COMPAT_ENGINES))
323 def draw_header(self, context):
325 flow_smoke = md.flow_settings
327 self.layout.prop(flow_smoke, "use_texture", text="")
329 def draw(self, context):
331 layout.use_property_split = True
332 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
335 flow_smoke = context.smoke.flow_settings
338 sub.active = flow_smoke.use_texture
339 sub.prop(flow_smoke, "noise_texture")
340 sub.prop(flow_smoke, "texture_map_type", text="Mapping")
344 sub.active = flow_smoke.use_texture
346 if flow_smoke.texture_map_type == 'UV':
347 sub.prop_search(flow_smoke, "uv_layer", ob.data, "uv_layers")
349 if flow_smoke.texture_map_type == 'AUTO':
350 sub.prop(flow_smoke, "texture_size")
352 sub.prop(flow_smoke, "texture_offset")
355 class PHYSICS_PT_smoke_fire(PhysicButtonsPanel, Panel):
357 bl_parent_id = 'PHYSICS_PT_smoke'
358 bl_options = {'DEFAULT_CLOSED'}
359 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
362 def poll(cls, context):
363 if not PhysicButtonsPanel.poll_smoke_domain(context):
366 return (context.engine in cls.COMPAT_ENGINES)
368 def draw(self, context):
370 layout.use_property_split = True
372 domain = context.smoke.domain_settings
374 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
375 flow.enabled = (not domain.point_cache.is_baked)
378 col.prop(domain, "burning_rate", text="Reaction Speed")
379 col.prop(domain, "flame_smoke")
380 col.prop(domain, "flame_vorticity")
384 col = flow.column(align=True)
385 col.prop(domain, "flame_ignition", text="Temperature Ignition")
386 col.prop(domain, "flame_max_temp")
391 sub.prop(domain, "flame_smoke_color")
394 class PHYSICS_PT_smoke_adaptive_domain(PhysicButtonsPanel, Panel):
395 bl_label = "Adaptive Domain"
396 bl_parent_id = 'PHYSICS_PT_smoke'
397 bl_options = {'DEFAULT_CLOSED'}
398 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
401 def poll(cls, context):
402 if not PhysicButtonsPanel.poll_smoke_domain(context):
405 return (context.engine in cls.COMPAT_ENGINES)
407 def draw_header(self, context):
408 md = context.smoke.domain_settings
410 self.layout.prop(md, "use_adaptive_domain", text="")
412 def draw(self, context):
414 layout.use_property_split = True
416 domain = context.smoke.domain_settings
417 layout.active = domain.use_adaptive_domain
419 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
420 flow.enabled = (not domain.point_cache.is_baked)
423 col.prop(domain, "additional_res", text="Add Resolution")
424 col.prop(domain, "adapt_margin")
429 col.prop(domain, "adapt_threshold", text="Threshold")
432 class PHYSICS_PT_smoke_highres(PhysicButtonsPanel, Panel):
433 bl_label = "High Resolution"
434 bl_parent_id = 'PHYSICS_PT_smoke'
435 bl_options = {'DEFAULT_CLOSED'}
436 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
439 def poll(cls, context):
440 if not PhysicButtonsPanel.poll_smoke_domain(context):
443 return (context.engine in cls.COMPAT_ENGINES)
445 def draw_header(self, context):
446 md = context.smoke.domain_settings
448 self.layout.prop(md, "use_high_resolution", text="")
450 def draw(self, context):
452 layout.use_property_split = True
454 md = context.smoke.domain_settings
455 layout.active = md.use_high_resolution
457 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
460 col.enabled = not md.point_cache.is_baked
461 col.prop(md, "amplify", text="Resolution Divisions")
462 col.prop(md, "highres_sampling", text="Flow Sampling")
467 col.enabled = not md.point_cache.is_baked
468 col.prop(md, "noise_type", text="Noise Method")
469 col.prop(md, "strength")
471 layout.prop(md, "show_high_resolution")
474 class PHYSICS_PT_smoke_collections(PhysicButtonsPanel, Panel):
475 bl_label = "Collections"
476 bl_parent_id = 'PHYSICS_PT_smoke'
477 bl_options = {'DEFAULT_CLOSED'}
478 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
481 def poll(cls, context):
482 if not PhysicButtonsPanel.poll_smoke_domain(context):
485 return (context.engine in cls.COMPAT_ENGINES)
487 def draw(self, context):
489 layout.use_property_split = True
491 domain = context.smoke.domain_settings
493 col = layout.column()
494 col.prop(domain, "fluid_collection", text="Flow")
496 # col = layout.column()
497 # col.prop(domain, "effector_collection", text="Effector")
498 col.prop(domain, "collision_collection", text="Collision")
501 class PHYSICS_PT_smoke_cache(PhysicButtonsPanel, Panel):
503 bl_parent_id = 'PHYSICS_PT_smoke'
504 bl_options = {'DEFAULT_CLOSED'}
505 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
508 def poll(cls, context):
509 if not PhysicButtonsPanel.poll_smoke_domain(context):
512 return (context.engine in cls.COMPAT_ENGINES)
514 def draw(self, context):
516 layout.use_property_split = True
517 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
519 domain = context.smoke.domain_settings
520 cache_file_format = domain.cache_file_format
523 col.prop(domain, "cache_file_format")
525 if cache_file_format == 'POINTCACHE':
527 col.prop(domain, "point_cache_compress_type", text="Compression")
530 elif cache_file_format == 'OPENVDB':
531 if not bpy.app.build_options.openvdb:
532 row = layout.row(align=True)
533 row.alignment = 'RIGHT'
534 row.label(text="Built without OpenVDB support")
538 col.prop(domain, "openvdb_cache_compress_type", text="Compression")
539 col.prop(domain, "data_depth", text="Data Depth")
542 cache = domain.point_cache
543 point_cache_ui(self, context, cache, (cache.is_baked is False), 'SMOKE')
546 class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel):
547 bl_label = "Field Weights"
548 bl_parent_id = 'PHYSICS_PT_smoke'
549 bl_options = {'DEFAULT_CLOSED'}
550 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
553 def poll(cls, context):
554 if not PhysicButtonsPanel.poll_smoke_domain(context):
557 return (context.engine in cls.COMPAT_ENGINES)
559 def draw(self, context):
560 domain = context.smoke.domain_settings
561 effector_weights_ui(self, context, domain.effector_weights, 'SMOKE')
564 class PHYSICS_PT_smoke_viewport_display(PhysicButtonsPanel, Panel):
565 bl_label = "Viewport Display"
566 bl_parent_id = 'PHYSICS_PT_smoke'
567 bl_options = {'DEFAULT_CLOSED'}
570 def poll(cls, context):
571 return (PhysicButtonsPanel.poll_smoke_domain(context))
573 def draw(self, context):
575 layout.use_property_split = True
576 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
578 domain = context.smoke.domain_settings
581 col.prop(domain, "display_thickness")
585 col.prop(domain, "slice_method", text="Slicing")
587 slice_method = domain.slice_method
588 axis_slice_method = domain.axis_slice_method
590 do_axis_slicing = (slice_method == 'AXIS_ALIGNED')
591 do_full_slicing = (axis_slice_method == 'FULL')
594 col.enabled = do_axis_slicing
595 col.prop(domain, "axis_slice_method")
599 sub.enabled = not do_full_slicing and do_axis_slicing
600 sub.prop(domain, "slice_axis")
601 sub.prop(domain, "slice_depth")
604 row.enabled = do_full_slicing or not do_axis_slicing
605 row.prop(domain, "slice_per_voxel")
607 col.prop(domain, "display_interpolation")
610 class PHYSICS_PT_smoke_viewport_display_color(PhysicButtonsPanel, Panel):
611 bl_label = "Color Mapping"
612 bl_parent_id = 'PHYSICS_PT_smoke_viewport_display'
613 bl_options = {'DEFAULT_CLOSED'}
616 def poll(cls, context):
617 return (PhysicButtonsPanel.poll_smoke_domain(context))
619 def draw_header(self, context):
620 md = context.smoke.domain_settings
622 self.layout.prop(md, "use_color_ramp", text="")
624 def draw(self, context):
626 layout.use_property_split = True
628 domain = context.smoke.domain_settings
629 col = layout.column()
630 col.enabled = domain.use_color_ramp
632 col.prop(domain, "coba_field")
634 col.use_property_split = False
637 col.template_color_ramp(domain, "color_ramp", expand=True)
640 class PHYSICS_PT_smoke_viewport_display_debug(PhysicButtonsPanel, Panel):
641 bl_label = "Debug Velocity"
642 bl_parent_id = 'PHYSICS_PT_smoke_viewport_display'
643 bl_options = {'DEFAULT_CLOSED'}
646 def poll(cls, context):
647 return (PhysicButtonsPanel.poll_smoke_domain(context))
649 def draw_header(self, context):
650 md = context.smoke.domain_settings
652 self.layout.prop(md, "display_velocity", text="")
654 def draw(self, context):
656 layout.use_property_split = True
657 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
659 domain = context.smoke.domain_settings
662 col.enabled = domain.display_velocity
663 col.prop(domain, "vector_display_type", text="Display As")
664 col.prop(domain, "vector_scale")
669 PHYSICS_PT_smoke_settings,
670 PHYSICS_PT_smoke_settings_initial_velocity,
671 PHYSICS_PT_smoke_settings_particle_size,
672 PHYSICS_PT_smoke_behavior,
673 PHYSICS_PT_smoke_behavior_dissolve,
674 PHYSICS_PT_smoke_adaptive_domain,
675 PHYSICS_PT_smoke_cache,
676 PHYSICS_PT_smoke_field_weights,
677 PHYSICS_PT_smoke_fire,
678 PHYSICS_PT_smoke_flow_texture,
679 PHYSICS_PT_smoke_collections,
680 PHYSICS_PT_smoke_highres,
681 PHYSICS_PT_smoke_viewport_display,
682 PHYSICS_PT_smoke_viewport_display_color,
683 PHYSICS_PT_smoke_viewport_display_debug,
687 if __name__ == "__main__": # only for live edit.
688 from bpy.utils import register_class