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 21import bpy 22from bpy.types import Menu, Panel 23from bl_ui.utils import PresetPanel 24from .properties_physics_common import ( 25 effector_weights_ui, 26) 27 28 29class FLUID_PT_presets(PresetPanel, Panel): 30 bl_label = "Fluid Presets" 31 preset_subdir = "fluid" 32 preset_operator = "script.execute_preset" 33 preset_add_operator = "fluid.preset_add" 34 35 36class PhysicButtonsPanel: 37 bl_space_type = 'PROPERTIES' 38 bl_region_type = 'WINDOW' 39 bl_context = "physics" 40 41 @staticmethod 42 def check_domain_has_unbaked_guide(domain): 43 return ( 44 domain.use_guide and not domain.has_cache_baked_guide and 45 ((domain.guide_source == 'EFFECTOR') or 46 (domain.guide_source == 'DOMAIN' and not domain.guide_parent)) 47 ) 48 49 @staticmethod 50 def poll_fluid(context): 51 ob = context.object 52 if not ((ob and ob.type == 'MESH') and (context.fluid)): 53 return False 54 55 md = context.fluid 56 return md and (context.fluid.fluid_type != 'NONE') 57 58 @staticmethod 59 def poll_fluid_domain(context): 60 if not PhysicButtonsPanel.poll_fluid(context): 61 return False 62 63 md = context.fluid 64 return md and (md.fluid_type == 'DOMAIN') 65 66 @staticmethod 67 def poll_gas_domain(context): 68 if not PhysicButtonsPanel.poll_fluid(context): 69 return False 70 71 md = context.fluid 72 if md and (md.fluid_type == 'DOMAIN'): 73 domain = md.domain_settings 74 return domain.domain_type in {'GAS'} 75 return False 76 77 @staticmethod 78 def poll_liquid_domain(context): 79 if not PhysicButtonsPanel.poll_fluid(context): 80 return False 81 82 md = context.fluid 83 if md and (md.fluid_type == 'DOMAIN'): 84 domain = md.domain_settings 85 return domain.domain_type in {'LIQUID'} 86 return False 87 88 @staticmethod 89 def poll_fluid_flow(context): 90 if not PhysicButtonsPanel.poll_fluid(context): 91 return False 92 93 md = context.fluid 94 return md and (md.fluid_type == 'FLOW') 95 96 @staticmethod 97 def poll_fluid_flow_outflow(context): 98 if not PhysicButtonsPanel.poll_fluid_flow(context): 99 return False 100 101 md = context.fluid 102 flow = md.flow_settings 103 if (flow.flow_behavior == 'OUTFLOW'): 104 return True 105 106 @staticmethod 107 def poll_fluid_flow_liquid(context): 108 if not PhysicButtonsPanel.poll_fluid_flow(context): 109 return False 110 111 md = context.fluid 112 flow = md.flow_settings 113 if (flow.flow_type == 'LIQUID'): 114 return True 115 116 117class PHYSICS_PT_fluid(PhysicButtonsPanel, Panel): 118 bl_label = "Fluid" 119 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 120 121 @classmethod 122 def poll(cls, context): 123 ob = context.object 124 return (ob and ob.type == 'MESH') and (context.engine in cls.COMPAT_ENGINES) and (context.fluid) 125 126 def draw(self, context): 127 layout = self.layout 128 layout.use_property_split = True 129 130 if not bpy.app.build_options.fluid: 131 col = layout.column(align=True) 132 col.alignment = 'RIGHT' 133 col.label(text="Built without Fluid modifier") 134 return 135 md = context.fluid 136 137 layout.prop(md, "fluid_type") 138 139 140class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): 141 bl_label = "Settings" 142 bl_parent_id = 'PHYSICS_PT_fluid' 143 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 144 145 @classmethod 146 def poll(cls, context): 147 if not PhysicButtonsPanel.poll_fluid(context): 148 return False 149 150 return (context.engine in cls.COMPAT_ENGINES) 151 152 def draw(self, context): 153 layout = self.layout 154 layout.use_property_split = True 155 156 md = context.fluid 157 ob = context.object 158 scene = context.scene 159 160 if md.fluid_type == 'DOMAIN': 161 domain = md.domain_settings 162 163 is_baking_any = domain.is_cache_baking_any 164 has_baked_data = domain.has_cache_baked_data 165 166 row = layout.row() 167 row.enabled = not is_baking_any and not has_baked_data 168 row.prop(domain, "domain_type", expand=False) 169 170 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 171 flow.enabled = not is_baking_any and not has_baked_data 172 173 col = flow.column() 174 col.enabled = not domain.has_cache_baked_guide 175 col.prop(domain, "resolution_max", text="Resolution Divisions") 176 col.prop(domain, "time_scale", text="Time Scale") 177 col.prop(domain, "cfl_condition", text="CFL Number") 178 179 col = flow.column() 180 col.prop(domain, "use_adaptive_timesteps") 181 sub = col.column(align=True) 182 sub.active = domain.use_adaptive_timesteps 183 sub.prop(domain, "timesteps_max", text="Timesteps Maximum") 184 sub.prop(domain, "timesteps_min", text="Minimum") 185 186 col.separator() 187 188 col = flow.column() 189 if scene.use_gravity: 190 sub = col.column() 191 sub.enabled = False 192 sub.prop(domain, "gravity", text="Using Scene Gravity", icon='SCENE_DATA') 193 else: 194 col.prop(domain, "gravity", text="Gravity") 195 196 col = flow.column() 197 if PhysicButtonsPanel.poll_gas_domain(context): 198 col.prop(domain, "clipping", text="Empty Space") 199 col.prop(domain, "delete_in_obstacle", text="Delete In Obstacle") 200 201 if domain.cache_type == 'MODULAR': 202 col.separator() 203 label = "" 204 205 # Deactivate bake operator if guides are enabled but not baked yet. 206 note_flag = True 207 if self.check_domain_has_unbaked_guide(domain): 208 note_flag = False 209 label = "Unbaked Guides: Bake Guides or disable them" 210 elif not domain.cache_resumable and not label: 211 label = "Non Resumable Cache: Baking " 212 if PhysicButtonsPanel.poll_liquid_domain(context): 213 label += "mesh or particles will not be possible" 214 elif PhysicButtonsPanel.poll_gas_domain(context): 215 label += "noise will not be possible" 216 else: 217 label = "" 218 219 if label: 220 info = layout.split() 221 note = info.row() 222 note.enabled = note_flag 223 note.alignment = 'RIGHT' 224 note.label(icon='INFO', text=label) 225 226 split = layout.split() 227 split.enabled = note_flag and ob.mode == 'OBJECT' 228 229 bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end) 230 if domain.cache_resumable and domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete: 231 col = split.column() 232 col.operator("fluid.bake_data", text="Resume") 233 col = split.column() 234 col.operator("fluid.free_data", text="Free") 235 elif domain.is_cache_baking_data and not domain.has_cache_baked_data: 236 split.enabled = False 237 split.operator("fluid.pause_bake", text="Baking Data - ESC to pause") 238 elif not domain.has_cache_baked_data and not domain.is_cache_baking_data: 239 split.operator("fluid.bake_data", text="Bake Data") 240 else: 241 split.operator("fluid.free_data", text="Free Data") 242 243 elif md.fluid_type == 'FLOW': 244 flow = md.flow_settings 245 246 row = layout.row() 247 row.prop(flow, "flow_type", expand=False) 248 249 grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 250 251 col = grid.column() 252 col.prop(flow, "flow_behavior", expand=False) 253 if flow.flow_behavior in {'INFLOW', 'OUTFLOW'}: 254 col.prop(flow, "use_inflow") 255 256 col.prop(flow, "subframes", text="Sampling Substeps") 257 258 if not flow.flow_behavior == 'OUTFLOW' and flow.flow_type in {'SMOKE', 'BOTH', 'FIRE'}: 259 260 if flow.flow_type in {'SMOKE', 'BOTH'}: 261 col.prop(flow, "smoke_color", text="Smoke Color") 262 263 col = grid.column(align=True) 264 col.prop(flow, "use_absolute", text="Absolute Density") 265 266 if flow.flow_type in {'SMOKE', 'BOTH'}: 267 col.prop(flow, "temperature", text="Initial Temperature") 268 col.prop(flow, "density", text="Density") 269 270 if flow.flow_type in {'FIRE', 'BOTH'}: 271 col.prop(flow, "fuel_amount", text="Fuel") 272 273 col.separator() 274 col.prop_search(flow, "density_vertex_group", ob, "vertex_groups", text="Vertex Group") 275 276 elif md.fluid_type == 'EFFECTOR': 277 effector_settings = md.effector_settings 278 279 row = layout.row() 280 row.prop(effector_settings, "effector_type") 281 282 grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 283 284 col = grid.column() 285 col.prop(effector_settings, "subframes", text="Sampling Substeps") 286 col.prop(effector_settings, "surface_distance", text="Surface Thickness") 287 288 col = grid.column() 289 290 col.prop(effector_settings, "use_effector", text="Use Effector") 291 col.prop(effector_settings, "use_plane_init", text="Is Planar") 292 293 if effector_settings.effector_type == 'GUIDE': 294 col.prop(effector_settings, "velocity_factor", text="Velocity Factor") 295 col.prop(effector_settings, "guide_mode", text="Guide Mode") 296 297 298class PHYSICS_PT_borders(PhysicButtonsPanel, Panel): 299 bl_label = "Border Collisions" 300 bl_parent_id = 'PHYSICS_PT_settings' 301 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 302 303 @classmethod 304 def poll(cls, context): 305 if not PhysicButtonsPanel.poll_fluid_domain(context): 306 return False 307 308 return (context.engine in cls.COMPAT_ENGINES) 309 310 def draw(self, context): 311 layout = self.layout 312 layout.use_property_split = True 313 314 md = context.fluid 315 domain = md.domain_settings 316 317 is_baking_any = domain.is_cache_baking_any 318 has_baked_data = domain.has_cache_baked_data 319 320 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 321 flow.enabled = not is_baking_any and not has_baked_data 322 323 col = flow.column() 324 col.prop(domain, "use_collision_border_front", text="Front") 325 col = flow.column() 326 col.prop(domain, "use_collision_border_back", text="Back") 327 col = flow.column() 328 col.prop(domain, "use_collision_border_right", text="Right") 329 col = flow.column() 330 col.prop(domain, "use_collision_border_left", text="Left") 331 col = flow.column() 332 col.prop(domain, "use_collision_border_top", text="Top") 333 col = flow.column() 334 col.prop(domain, "use_collision_border_bottom", text="Bottom") 335 336 337class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel): 338 bl_label = "Gas" 339 bl_parent_id = 'PHYSICS_PT_fluid' 340 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 341 342 @classmethod 343 def poll(cls, context): 344 if not PhysicButtonsPanel.poll_gas_domain(context): 345 return False 346 347 return (context.engine in cls.COMPAT_ENGINES) 348 349 def draw(self, context): 350 layout = self.layout 351 layout.use_property_split = True 352 353 md = context.fluid 354 domain = md.domain_settings 355 356 is_baking_any = domain.is_cache_baking_any 357 has_baked_data = domain.has_cache_baked_data 358 359 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 360 flow.enabled = not is_baking_any and not has_baked_data 361 362 col = flow.column(align=True) 363 col.prop(domain, "alpha", text="Buoyancy Density") 364 col.prop(domain, "beta", text="Heat") 365 col = flow.column() 366 col.prop(domain, "vorticity") 367 368 369class PHYSICS_PT_smoke_dissolve(PhysicButtonsPanel, Panel): 370 bl_label = "Dissolve" 371 bl_parent_id = 'PHYSICS_PT_smoke' 372 bl_options = {'DEFAULT_CLOSED'} 373 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 374 375 @classmethod 376 def poll(cls, context): 377 if not PhysicButtonsPanel.poll_gas_domain(context): 378 return False 379 380 return (context.engine in cls.COMPAT_ENGINES) 381 382 def draw_header(self, context): 383 md = context.fluid.domain_settings 384 domain = context.fluid.domain_settings 385 386 is_baking_any = domain.is_cache_baking_any 387 388 self.layout.enabled = not is_baking_any 389 self.layout.prop(md, "use_dissolve_smoke", text="") 390 391 def draw(self, context): 392 layout = self.layout 393 layout.use_property_split = True 394 395 md = context.fluid 396 domain = md.domain_settings 397 398 is_baking_any = domain.is_cache_baking_any 399 has_baked_data = domain.has_cache_baked_data 400 401 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 402 flow.enabled = not is_baking_any and not has_baked_data 403 404 layout.active = domain.use_dissolve_smoke 405 406 col = flow.column() 407 col.prop(domain, "dissolve_speed", text="Time") 408 409 col = flow.column() 410 col.prop(domain, "use_dissolve_smoke_log", text="Slow") 411 412 413class PHYSICS_PT_fire(PhysicButtonsPanel, Panel): 414 bl_label = "Fire" 415 bl_parent_id = 'PHYSICS_PT_smoke' 416 bl_options = {'DEFAULT_CLOSED'} 417 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 418 419 @classmethod 420 def poll(cls, context): 421 if not PhysicButtonsPanel.poll_gas_domain(context): 422 return False 423 424 return (context.engine in cls.COMPAT_ENGINES) 425 426 def draw(self, context): 427 layout = self.layout 428 layout.use_property_split = True 429 430 md = context.fluid 431 domain = md.domain_settings 432 433 is_baking_any = domain.is_cache_baking_any 434 has_baked_data = domain.has_cache_baked_data 435 436 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 437 flow.enabled = not is_baking_any and not has_baked_data 438 439 col = flow.column() 440 col.prop(domain, "burning_rate", text="Reaction Speed") 441 row = col.row() 442 sub = row.column(align=True) 443 sub.prop(domain, "flame_smoke", text="Flame Smoke") 444 sub.prop(domain, "flame_vorticity", text="Vorticity") 445 446 col = flow.column(align=True) 447 col.prop(domain, "flame_max_temp", text="Temperature Maximum") 448 col.prop(domain, "flame_ignition", text="Minimum") 449 row = col.row() 450 row.prop(domain, "flame_smoke_color", text="Flame Color") 451 452 453class PHYSICS_PT_liquid(PhysicButtonsPanel, Panel): 454 bl_label = "Liquid" 455 bl_parent_id = 'PHYSICS_PT_fluid' 456 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 457 458 @classmethod 459 def poll(cls, context): 460 if not PhysicButtonsPanel.poll_liquid_domain(context): 461 return False 462 463 return (context.engine in cls.COMPAT_ENGINES) 464 465 def draw_header(self, context): 466 md = context.fluid.domain_settings 467 domain = context.fluid.domain_settings 468 469 is_baking_any = domain.is_cache_baking_any 470 471 self.layout.enabled = not is_baking_any 472 self.layout.prop(md, "use_flip_particles", text="") 473 474 def draw(self, context): 475 layout = self.layout 476 layout.use_property_split = True 477 478 md = context.fluid 479 domain = md.domain_settings 480 481 is_baking_any = domain.is_cache_baking_any 482 has_baked_data = domain.has_cache_baked_data 483 484 layout.enabled = not is_baking_any and not has_baked_data 485 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 486 487 col = flow.column() 488 col.prop(domain, "simulation_method", expand=False) 489 col.prop(domain, "flip_ratio", text="FLIP Ratio") 490 col.prop(domain, "sys_particle_maximum", text="System Maximum") 491 col = col.column(align=True) 492 col.prop(domain, "particle_radius", text="Particle Radius") 493 col.prop(domain, "particle_number", text="Sampling") 494 col.prop(domain, "particle_randomness", text="Randomness") 495 496 col = flow.column() 497 col = col.column(align=True) 498 col.prop(domain, "particle_max", text="Particles Maximum") 499 col.prop(domain, "particle_min", text="Minimum") 500 501 col.separator() 502 503 col = col.column() 504 col.prop(domain, "particle_band_width", text="Narrow Band Width") 505 506 col = col.column() 507 col.prop(domain, "use_fractions", text="Fractional Obstacles") 508 sub = col.column() 509 sub.active = domain.use_fractions 510 sub.prop(domain, "fractions_distance", text="Obstacle Distance") 511 sub.prop(domain, "fractions_threshold", text="Threshold") 512 513 514class PHYSICS_PT_flow_source(PhysicButtonsPanel, Panel): 515 bl_label = "Flow Source" 516 bl_parent_id = 'PHYSICS_PT_settings' 517 bl_options = {'DEFAULT_CLOSED'} 518 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 519 520 @classmethod 521 def poll(cls, context): 522 if not PhysicButtonsPanel.poll_fluid_flow(context): 523 return False 524 525 return (context.engine in cls.COMPAT_ENGINES) 526 527 def draw(self, context): 528 layout = self.layout 529 layout.use_property_split = True 530 531 ob = context.object 532 flow = context.fluid.flow_settings 533 534 col = layout.column() 535 col.prop(flow, "flow_source", expand=False, text="Flow Source") 536 if flow.flow_source == 'PARTICLES': 537 col.prop_search(flow, "particle_system", ob, "particle_systems", text="Particle System") 538 539 grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 540 541 col = grid.column() 542 if flow.flow_source == 'MESH': 543 col.prop(flow, "use_plane_init", text="Is Planar") 544 col.prop(flow, "surface_distance", text="Surface Emission") 545 if flow.flow_type in {'SMOKE', 'BOTH', 'FIRE'}: 546 col = grid.column() 547 col.prop(flow, "volume_density", text="Volume Emission") 548 549 if flow.flow_source == 'PARTICLES': 550 col.prop(flow, "use_particle_size", text="Set Size") 551 sub = col.column() 552 sub.active = flow.use_particle_size 553 sub.prop(flow, "particle_size") 554 555 556class PHYSICS_PT_flow_initial_velocity(PhysicButtonsPanel, Panel): 557 bl_label = "Initial Velocity" 558 bl_parent_id = 'PHYSICS_PT_settings' 559 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 560 561 @classmethod 562 def poll(cls, context): 563 if not PhysicButtonsPanel.poll_fluid_flow(context): 564 return False 565 566 if PhysicButtonsPanel.poll_fluid_flow_outflow(context): 567 return False 568 569 return (context.engine in cls.COMPAT_ENGINES) 570 571 def draw_header(self, context): 572 md = context.fluid 573 flow_smoke = md.flow_settings 574 575 self.layout.prop(flow_smoke, "use_initial_velocity", text="") 576 577 def draw(self, context): 578 layout = self.layout 579 layout.use_property_split = True 580 flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True) 581 582 md = context.fluid 583 flow_smoke = md.flow_settings 584 585 flow.active = flow_smoke.use_initial_velocity 586 587 col = flow.column() 588 col.prop(flow_smoke, "velocity_factor") 589 590 if flow_smoke.flow_source == 'MESH': 591 col.prop(flow_smoke, "velocity_normal") 592 # col.prop(flow_smoke, "velocity_random") 593 col = flow.column() 594 col.prop(flow_smoke, "velocity_coord") 595 596 597class PHYSICS_PT_flow_texture(PhysicButtonsPanel, Panel): 598 bl_label = "Texture" 599 bl_parent_id = 'PHYSICS_PT_settings' 600 bl_options = {'DEFAULT_CLOSED'} 601 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 602 603 @classmethod 604 def poll(cls, context): 605 if not PhysicButtonsPanel.poll_fluid_flow(context): 606 return False 607 608 if PhysicButtonsPanel.poll_fluid_flow_outflow(context): 609 return False 610 611 if PhysicButtonsPanel.poll_fluid_flow_liquid(context): 612 return False 613 614 return (context.engine in cls.COMPAT_ENGINES) 615 616 def draw_header(self, context): 617 md = context.fluid 618 flow_smoke = md.flow_settings 619 620 self.layout.prop(flow_smoke, "use_texture", text="") 621 622 def draw(self, context): 623 layout = self.layout 624 layout.use_property_split = True 625 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 626 627 ob = context.object 628 flow_smoke = context.fluid.flow_settings 629 630 sub = flow.column() 631 sub.active = flow_smoke.use_texture 632 sub.prop(flow_smoke, "noise_texture") 633 sub.prop(flow_smoke, "texture_map_type", text="Mapping") 634 635 col = flow.column() 636 sub = col.column() 637 sub.active = flow_smoke.use_texture 638 639 if flow_smoke.texture_map_type == 'UV': 640 sub.prop_search(flow_smoke, "uv_layer", ob.data, "uv_layers") 641 642 if flow_smoke.texture_map_type == 'AUTO': 643 sub.prop(flow_smoke, "texture_size") 644 645 sub.prop(flow_smoke, "texture_offset") 646 647 648class PHYSICS_PT_adaptive_domain(PhysicButtonsPanel, Panel): 649 bl_label = "Adaptive Domain" 650 bl_parent_id = 'PHYSICS_PT_settings' 651 bl_options = {'DEFAULT_CLOSED'} 652 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 653 654 @classmethod 655 def poll(cls, context): 656 if not PhysicButtonsPanel.poll_gas_domain(context): 657 return False 658 659 md = context.fluid 660 domain = md.domain_settings 661 # Effector guides require a fixed domain size 662 if domain.use_guide and domain.guide_source == 'EFFECTOR': 663 return False 664 665 return (context.engine in cls.COMPAT_ENGINES) 666 667 def draw_header(self, context): 668 md = context.fluid.domain_settings 669 domain = context.fluid.domain_settings 670 671 is_baking_any = domain.is_cache_baking_any 672 has_baked_any = domain.has_cache_baked_any 673 674 self.layout.enabled = not is_baking_any and not has_baked_any 675 self.layout.prop(md, "use_adaptive_domain", text="") 676 677 def draw(self, context): 678 layout = self.layout 679 layout.use_property_split = True 680 681 domain = context.fluid.domain_settings 682 layout.active = domain.use_adaptive_domain 683 684 is_baking_any = domain.is_cache_baking_any 685 has_baked_any = domain.has_cache_baked_any 686 687 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) 688 flow.enabled = not is_baking_any and not has_baked_any 689 690 col = flow.column() 691 col.prop(domain, "additional_res", text="Add Resolution") 692 col.prop(domain, "adapt_margin") 693 694 col.separator() 695 696 col = flow.column() 697 col.prop(domain, "adapt_threshold", text="Threshold") 698 699 700class PHYSICS_PT_noise(PhysicButtonsPanel, Panel): 701 bl_label = "Noise" 702 bl_parent_id = 'PHYSICS_PT_smoke' 703 bl_options = {'DEFAULT_CLOSED'} 704 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 705 706 @classmethod 707 def poll(cls, context): 708 if not PhysicButtonsPanel.poll_gas_domain(context): 709 return False 710 711 return (context.engine in cls.COMPAT_ENGINES) 712 713 def draw_header(self, context): 714 md = context.fluid.domain_settings 715 domain = context.fluid.domain_settings 716 is_baking_any = domain.is_cache_baking_any 717 self.layout.enabled = not is_baking_any 718 self.layout.prop(md, "use_noise", text="") 719 720 def draw(self, context): 721 layout = self.layout 722 layout.use_property_split = True 723 724 ob = context.object 725 domain = context.fluid.domain_settings 726 layout.active = domain.use_noise 727 728 is_baking_any = domain.is_cache_baking_any 729 has_baked_noise = domain.has_cache_baked_noise 730 731 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 732 flow.enabled = not is_baking_any and not has_baked_noise 733 734 col = flow.column() 735 col.prop(domain, "noise_scale", text="Upres Factor") 736 # TODO (sebbas): Mantaflow only supports wavelet noise. Maybe get rid of noise type field. 737 col.prop(domain, "noise_type", text="Noise Method") 738 739 col = flow.column() 740 col.prop(domain, "noise_strength", text="Strength") 741 col.prop(domain, "noise_pos_scale", text="Scale") 742 col.prop(domain, "noise_time_anim", text="Time") 743 744 if domain.cache_type == 'MODULAR': 745 col.separator() 746 747 # Deactivate bake operator if data has not been baked yet. 748 note_flag = True 749 if domain.use_noise: 750 label = "" 751 if not domain.cache_resumable: 752 label = "Non Resumable Cache: Enable resumable option first" 753 elif not domain.has_cache_baked_data: 754 label = "Unbaked Data: Bake Data first" 755 756 if label: 757 info = layout.split() 758 note = info.row() 759 note_flag = False 760 note.enabled = note_flag 761 note.alignment = 'RIGHT' 762 note.label(icon='INFO', text=label) 763 764 split = layout.split() 765 split.enabled = domain.has_cache_baked_data and note_flag and ob.mode == 'OBJECT' 766 767 bake_incomplete = (domain.cache_frame_pause_noise < domain.cache_frame_end) 768 if domain.has_cache_baked_noise and not domain.is_cache_baking_noise and bake_incomplete: 769 col = split.column() 770 col.operator("fluid.bake_noise", text="Resume") 771 col = split.column() 772 col.operator("fluid.free_noise", text="Free") 773 elif not domain.has_cache_baked_noise and domain.is_cache_baking_noise: 774 split.enabled = False 775 split.operator("fluid.pause_bake", text="Baking Noise - ESC to pause") 776 elif not domain.has_cache_baked_noise and not domain.is_cache_baking_noise: 777 split.operator("fluid.bake_noise", text="Bake Noise") 778 else: 779 split.operator("fluid.free_noise", text="Free Noise") 780 781 782class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel): 783 bl_label = "Mesh" 784 bl_parent_id = 'PHYSICS_PT_liquid' 785 bl_options = {'DEFAULT_CLOSED'} 786 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 787 788 @classmethod 789 def poll(cls, context): 790 if not PhysicButtonsPanel.poll_liquid_domain(context): 791 return False 792 793 return (context.engine in cls.COMPAT_ENGINES) 794 795 def draw_header(self, context): 796 md = context.fluid.domain_settings 797 domain = context.fluid.domain_settings 798 is_baking_any = domain.is_cache_baking_any 799 self.layout.enabled = not is_baking_any 800 self.layout.prop(md, "use_mesh", text="") 801 802 def draw(self, context): 803 layout = self.layout 804 layout.use_property_split = True 805 806 ob = context.object 807 domain = context.fluid.domain_settings 808 layout.active = domain.use_mesh 809 810 is_baking_any = domain.is_cache_baking_any 811 has_baked_mesh = domain.has_cache_baked_mesh 812 813 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 814 flow.enabled = not is_baking_any and not has_baked_mesh 815 816 col = flow.column() 817 818 col.prop(domain, "mesh_scale", text="Upres Factor") 819 col.prop(domain, "mesh_particle_radius", text="Particle Radius") 820 821 col = flow.column() 822 col.prop(domain, "use_speed_vectors", text="Use Speed Vectors") 823 824 col.separator() 825 col.prop(domain, "mesh_generator", text="Mesh Generator") 826 827 if domain.mesh_generator in {'IMPROVED'}: 828 col = flow.column(align=True) 829 col.prop(domain, "mesh_smoothen_pos", text="Smoothing Positive") 830 col.prop(domain, "mesh_smoothen_neg", text="Negative") 831 832 col = flow.column(align=True) 833 col.prop(domain, "mesh_concave_upper", text="Concavity Upper") 834 col.prop(domain, "mesh_concave_lower", text="Lower") 835 836 # TODO (sebbas): for now just interpolate any upres grids, ie not sampling highres grids 837 #col.prop(domain, "highres_sampling", text="Flow Sampling:") 838 839 if domain.cache_type == 'MODULAR': 840 col.separator() 841 842 # Deactivate bake operator if data has not been baked yet. 843 note_flag = True 844 if domain.use_mesh: 845 label = "" 846 if not domain.cache_resumable: 847 label = "Non Resumable Cache: Enable resumable option first" 848 elif not domain.has_cache_baked_data: 849 label = "Unbaked Data: Bake Data first" 850 851 if label: 852 info = layout.split() 853 note = info.row() 854 note_flag = False 855 note.enabled = note_flag 856 note.alignment = 'RIGHT' 857 note.label(icon='INFO', text=label) 858 859 split = layout.split() 860 split.enabled = domain.has_cache_baked_data and note_flag and ob.mode == 'OBJECT' 861 862 bake_incomplete = (domain.cache_frame_pause_mesh < domain.cache_frame_end) 863 if domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh and bake_incomplete: 864 col = split.column() 865 col.operator("fluid.bake_mesh", text="Resume") 866 col = split.column() 867 col.operator("fluid.free_mesh", text="Free") 868 elif not domain.has_cache_baked_mesh and domain.is_cache_baking_mesh: 869 split.enabled = False 870 split.operator("fluid.pause_bake", text="Baking Mesh - ESC to pause") 871 elif not domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh: 872 split.operator("fluid.bake_mesh", text="Bake Mesh") 873 else: 874 split.operator("fluid.free_mesh", text="Free Mesh") 875 876 877class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): 878 bl_label = "Particles" 879 bl_parent_id = 'PHYSICS_PT_liquid' 880 bl_options = {'DEFAULT_CLOSED'} 881 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 882 883 @classmethod 884 def poll(cls, context): 885 if not PhysicButtonsPanel.poll_liquid_domain(context): 886 return False 887 888 return (context.engine in cls.COMPAT_ENGINES) 889 890 def draw(self, context): 891 layout = self.layout 892 layout.use_property_split = True 893 894 ob = context.object 895 domain = context.fluid.domain_settings 896 897 is_baking_any = domain.is_cache_baking_any 898 has_baked_particles = domain.has_cache_baked_particles 899 using_particles = domain.use_spray_particles or domain.use_foam_particles or domain.use_bubble_particles 900 901 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 902 flow.enabled = not is_baking_any 903 904 sndparticle_combined_export = domain.sndparticle_combined_export 905 col = flow.column() 906 row = col.row() 907 row.enabled = sndparticle_combined_export in {'OFF', 'FOAM + BUBBLES'} 908 row.prop(domain, "use_spray_particles", text="Spray") 909 row.prop(domain, "use_foam_particles", text="Foam") 910 row.prop(domain, "use_bubble_particles", text="Bubbles") 911 912 col.separator() 913 914 col.prop(domain, "sndparticle_combined_export") 915 916 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 917 flow.enabled = not is_baking_any and not has_baked_particles 918 flow.active = using_particles 919 920 col = flow.column() 921 col.prop(domain, "particle_scale", text="Upres Factor") 922 col.separator() 923 924 col = flow.column(align=True) 925 col.prop(domain, "sndparticle_potential_max_wavecrest", text="Wave Crest Potential Maximum") 926 col.prop(domain, "sndparticle_potential_min_wavecrest", text="Minimum") 927 col.separator() 928 929 col = flow.column(align=True) 930 col.prop(domain, "sndparticle_potential_max_trappedair", text="Trapped Air Potential Maximum") 931 col.prop(domain, "sndparticle_potential_min_trappedair", text="Minimum") 932 col.separator() 933 934 col = flow.column(align=True) 935 col.prop(domain, "sndparticle_potential_max_energy", text="Kinetic Energy Potential Maximum") 936 col.prop(domain, "sndparticle_potential_min_energy", text="Minimum") 937 col.separator() 938 939 col = flow.column(align=True) 940 col.prop(domain, "sndparticle_potential_radius", text="Potential Radius") 941 col.prop(domain, "sndparticle_update_radius", text="Particle Update Radius") 942 col.separator() 943 944 col = flow.column(align=True) 945 col.prop(domain, "sndparticle_sampling_wavecrest", text="Wave Crest Particle Sampling") 946 col.prop(domain, "sndparticle_sampling_trappedair", text="Trapped Air Particle Sampling") 947 col.separator() 948 949 col = flow.column(align=True) 950 col.prop(domain, "sndparticle_life_max", text="Particle Life Maximum") 951 col.prop(domain, "sndparticle_life_min", text="Minimum") 952 col.separator() 953 954 col = flow.column(align=True) 955 col.prop(domain, "sndparticle_bubble_buoyancy", text="Bubble Buoyancy") 956 col.prop(domain, "sndparticle_bubble_drag", text="Bubble Drag") 957 col.separator() 958 959 col = flow.column() 960 col.prop(domain, "sndparticle_boundary", text="Particles in Boundary:") 961 962 if domain.cache_type == 'MODULAR': 963 col.separator() 964 965 # Deactivate bake operator if data has not been baked yet. 966 note_flag = True 967 if using_particles: 968 label = "" 969 if not domain.cache_resumable: 970 label = "Non Resumable Cache: Enable resumable option first" 971 elif not domain.has_cache_baked_data: 972 label = "Unbaked Data: Bake Data first" 973 974 if label: 975 info = layout.split() 976 note = info.row() 977 note_flag = False 978 note.enabled = note_flag 979 note.alignment = 'RIGHT' 980 note.label(icon='INFO', text=label) 981 982 split = layout.split() 983 split.enabled = ( 984 note_flag and 985 ob.mode == 'OBJECT' and 986 domain.has_cache_baked_data and 987 (domain.use_spray_particles or 988 domain.use_bubble_particles or 989 domain.use_foam_particles or 990 domain.use_tracer_particles) 991 ) 992 993 bake_incomplete = (domain.cache_frame_pause_particles < domain.cache_frame_end) 994 if domain.has_cache_baked_particles and not domain.is_cache_baking_particles and bake_incomplete: 995 col = split.column() 996 col.operator("fluid.bake_particles", text="Resume") 997 col = split.column() 998 col.operator("fluid.free_particles", text="Free") 999 elif not domain.has_cache_baked_particles and domain.is_cache_baking_particles: 1000 split.enabled = False 1001 split.operator("fluid.pause_bake", text="Baking Particles - ESC to pause") 1002 elif not domain.has_cache_baked_particles and not domain.is_cache_baking_particles: 1003 split.operator("fluid.bake_particles", text="Bake Particles") 1004 else: 1005 split.operator("fluid.free_particles", text="Free Particles") 1006 1007 1008class PHYSICS_PT_diffusion(PhysicButtonsPanel, Panel): 1009 bl_label = "Diffusion" 1010 bl_parent_id = 'PHYSICS_PT_liquid' 1011 bl_options = {'DEFAULT_CLOSED'} 1012 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 1013 1014 @classmethod 1015 def poll(cls, context): 1016 # Fluid diffusion only enabled for liquids (surface tension and viscosity not relevant for smoke) 1017 if not PhysicButtonsPanel.poll_liquid_domain(context): 1018 return False 1019 1020 return (context.engine in cls.COMPAT_ENGINES) 1021 1022 def draw_header(self, context): 1023 md = context.fluid.domain_settings 1024 domain = context.fluid.domain_settings 1025 is_baking_any = domain.is_cache_baking_any 1026 has_baked_any = domain.has_cache_baked_any 1027 self.layout.enabled = not is_baking_any and not has_baked_any 1028 self.layout.prop(md, "use_diffusion", text="") 1029 1030 def draw_header_preset(self, _context): 1031 FLUID_PT_presets.draw_panel_header(self.layout) 1032 1033 def draw(self, context): 1034 layout = self.layout 1035 layout.use_property_split = True 1036 1037 domain = context.fluid.domain_settings 1038 layout.active = domain.use_diffusion 1039 1040 is_baking_any = domain.is_cache_baking_any 1041 has_baked_any = domain.has_cache_baked_any 1042 has_baked_data = domain.has_cache_baked_data 1043 1044 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 1045 flow.enabled = not is_baking_any and not has_baked_any and not has_baked_data 1046 1047 col = flow.column(align=True) 1048 col.prop(domain, "viscosity_base", text="Base") 1049 col.prop(domain, "viscosity_exponent", text="Exponent", slider=True) 1050 1051 col = flow.column() 1052 col.prop(domain, "surface_tension", text="Surface Tension") 1053 1054 1055class PHYSICS_PT_guide(PhysicButtonsPanel, Panel): 1056 bl_label = "Guides" 1057 bl_parent_id = 'PHYSICS_PT_fluid' 1058 bl_options = {'DEFAULT_CLOSED'} 1059 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 1060 1061 @classmethod 1062 def poll(cls, context): 1063 if not PhysicButtonsPanel.poll_fluid_domain(context): 1064 return False 1065 1066 return (context.engine in cls.COMPAT_ENGINES) 1067 1068 def draw_header(self, context): 1069 md = context.fluid.domain_settings 1070 domain = context.fluid.domain_settings 1071 1072 is_baking_any = domain.is_cache_baking_any 1073 1074 self.layout.enabled = not is_baking_any 1075 self.layout.prop(md, "use_guide", text="") 1076 1077 def draw(self, context): 1078 layout = self.layout 1079 layout.use_property_split = True 1080 1081 domain = context.fluid.domain_settings 1082 1083 layout.active = domain.use_guide 1084 1085 is_baking_any = domain.is_cache_baking_any 1086 has_baked_data = domain.has_cache_baked_data 1087 1088 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 1089 flow.enabled = not is_baking_any and not has_baked_data 1090 1091 col = flow.column() 1092 col.prop(domain, "guide_alpha", text="Weight") 1093 col.prop(domain, "guide_beta", text="Size") 1094 col.prop(domain, "guide_vel_factor", text="Velocity Factor") 1095 1096 col = flow.column() 1097 col.prop(domain, "guide_source", text="Velocity Source") 1098 if domain.guide_source == 'DOMAIN': 1099 col.prop(domain, "guide_parent", text="Guide Parent") 1100 1101 if domain.cache_type == 'MODULAR': 1102 col.separator() 1103 1104 if domain.guide_source == 'EFFECTOR': 1105 split = layout.split() 1106 bake_incomplete = (domain.cache_frame_pause_guide < domain.cache_frame_end) 1107 if domain.has_cache_baked_guide and not domain.is_cache_baking_guide and bake_incomplete: 1108 col = split.column() 1109 col.operator("fluid.bake_guides", text="Resume") 1110 col = split.column() 1111 col.operator("fluid.free_guides", text="Free") 1112 elif not domain.has_cache_baked_guide and domain.is_cache_baking_guide: 1113 split.enabled = False 1114 split.operator("fluid.pause_bake", text="Baking Guides - ESC to pause") 1115 elif not domain.has_cache_baked_guide and not domain.is_cache_baking_guide: 1116 split.operator("fluid.bake_guides", text="Bake Guides") 1117 else: 1118 split.operator("fluid.free_guides", text="Free Guides") 1119 1120 1121class PHYSICS_PT_collections(PhysicButtonsPanel, Panel): 1122 bl_label = "Collections" 1123 bl_parent_id = 'PHYSICS_PT_fluid' 1124 bl_options = {'DEFAULT_CLOSED'} 1125 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 1126 1127 @classmethod 1128 def poll(cls, context): 1129 if not PhysicButtonsPanel.poll_fluid_domain(context): 1130 return False 1131 1132 return (context.engine in cls.COMPAT_ENGINES) 1133 1134 def draw(self, context): 1135 layout = self.layout 1136 layout.use_property_split = True 1137 1138 domain = context.fluid.domain_settings 1139 1140 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 1141 1142 col = flow.column() 1143 col.prop(domain, "fluid_group", text="Flow") 1144 1145 # col.prop(domain, "effector_group", text="Forces") 1146 col.prop(domain, "effector_group", text="Effector") 1147 1148 1149class PHYSICS_PT_cache(PhysicButtonsPanel, Panel): 1150 bl_label = "Cache" 1151 bl_parent_id = 'PHYSICS_PT_fluid' 1152 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 1153 1154 @classmethod 1155 def poll(cls, context): 1156 if not PhysicButtonsPanel.poll_fluid_domain(context): 1157 return False 1158 1159 return (context.engine in cls.COMPAT_ENGINES) 1160 1161 def draw(self, context): 1162 layout = self.layout 1163 1164 ob = context.object 1165 md = context.fluid 1166 domain = context.fluid.domain_settings 1167 1168 is_baking_any = domain.is_cache_baking_any 1169 has_baked_data = domain.has_cache_baked_data 1170 has_baked_mesh = domain.has_cache_baked_mesh 1171 1172 col = layout.column() 1173 col.prop(domain, "cache_directory", text="") 1174 col.enabled = not is_baking_any 1175 1176 layout.use_property_split = True 1177 1178 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 1179 1180 col = flow.column() 1181 row = col.row() 1182 row = row.column(align=True) 1183 row.prop(domain, "cache_frame_start", text="Frame Start") 1184 row.prop(domain, "cache_frame_end", text="End") 1185 row = col.row() 1186 row.enabled = domain.cache_type in {'MODULAR', 'ALL'} 1187 row.prop(domain, "cache_frame_offset", text="Offset") 1188 1189 col.separator() 1190 1191 col = flow.column() 1192 col.prop(domain, "cache_type", expand=False) 1193 1194 row = col.row() 1195 row.enabled = not is_baking_any and not has_baked_data 1196 row.prop(domain, "cache_resumable", text="Is Resumable") 1197 1198 row = col.row() 1199 row.enabled = not is_baking_any and not has_baked_data 1200 row.prop(domain, "cache_data_format", text="Format Volumes") 1201 1202 if md.domain_settings.domain_type in {'LIQUID'} and domain.use_mesh: 1203 row = col.row() 1204 row.enabled = not is_baking_any and not has_baked_mesh 1205 row.prop(domain, "cache_mesh_format", text="Meshes") 1206 1207 if domain.cache_type == 'ALL': 1208 col.separator() 1209 split = layout.split() 1210 split.enabled = ob.mode == 'OBJECT' 1211 1212 bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end) 1213 if domain.cache_resumable and domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete: 1214 col = split.column() 1215 col.operator("fluid.bake_all", text="Resume") 1216 col = split.column() 1217 col.operator("fluid.free_all", text="Free") 1218 elif domain.is_cache_baking_data and not domain.has_cache_baked_data: 1219 split.enabled = False 1220 split.operator("fluid.pause_bake", text="Baking All - ESC to pause") 1221 elif not domain.has_cache_baked_data and not domain.is_cache_baking_data: 1222 split.operator("fluid.bake_all", text="Bake All") 1223 else: 1224 split.operator("fluid.free_all", text="Free All") 1225 1226 1227class PHYSICS_PT_export(PhysicButtonsPanel, Panel): 1228 bl_label = "Advanced" 1229 bl_parent_id = 'PHYSICS_PT_cache' 1230 bl_options = {'DEFAULT_CLOSED'} 1231 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 1232 1233 @classmethod 1234 def poll(cls, context): 1235 domain = context.fluid.domain_settings 1236 if ( 1237 not PhysicButtonsPanel.poll_fluid_domain(context) or 1238 (domain.cache_data_format != 'OPENVDB' and bpy.app.debug_value != 3001) 1239 ): 1240 return False 1241 1242 return (context.engine in cls.COMPAT_ENGINES) 1243 1244 def draw(self, context): 1245 layout = self.layout 1246 layout.use_property_split = True 1247 1248 domain = context.fluid.domain_settings 1249 1250 is_baking_any = domain.is_cache_baking_any 1251 has_baked_any = domain.has_cache_baked_any 1252 has_baked_data = domain.has_cache_baked_data 1253 1254 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 1255 flow.enabled = not is_baking_any and not has_baked_any 1256 1257 col = flow.column() 1258 1259 if domain.cache_data_format == 'OPENVDB': 1260 col.enabled = not is_baking_any and not has_baked_data 1261 col.prop(domain, "openvdb_cache_compress_type", text="Compression Volumes") 1262 1263 col = flow.column() 1264 col.prop(domain, "openvdb_data_depth", text="Precision Volumes") 1265 1266 # Only show the advanced panel to advanced users who know Mantaflow's birthday :) 1267 if bpy.app.debug_value == 3001: 1268 col = flow.column() 1269 col.prop(domain, "export_manta_script", text="Export Mantaflow Script") 1270 1271 1272class PHYSICS_PT_field_weights(PhysicButtonsPanel, Panel): 1273 bl_label = "Field Weights" 1274 bl_parent_id = 'PHYSICS_PT_fluid' 1275 bl_options = {'DEFAULT_CLOSED'} 1276 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 1277 1278 @classmethod 1279 def poll(cls, context): 1280 if not PhysicButtonsPanel.poll_fluid_domain(context): 1281 return False 1282 1283 return (context.engine in cls.COMPAT_ENGINES) 1284 1285 def draw(self, context): 1286 domain = context.fluid.domain_settings 1287 effector_weights_ui(self, domain.effector_weights, 'SMOKE') 1288 1289 1290class PHYSICS_PT_viewport_display(PhysicButtonsPanel, Panel): 1291 bl_label = "Viewport Display" 1292 bl_parent_id = 'PHYSICS_PT_fluid' 1293 bl_options = {'DEFAULT_CLOSED'} 1294 1295 @classmethod 1296 def poll(cls, context): 1297 return (PhysicButtonsPanel.poll_fluid_domain(context)) 1298 1299 def draw(self, context): 1300 layout = self.layout 1301 layout.use_property_split = True 1302 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) 1303 1304 domain = context.fluid.domain_settings 1305 1306 col = flow.column(align=False) 1307 col.prop(domain, "display_thickness") 1308 1309 sub = col.column() 1310 sub.prop(domain, "display_interpolation") 1311 1312 if domain.use_color_ramp and domain.color_ramp_field == 'FLAGS': 1313 sub.enabled = False 1314 1315 col = col.column() 1316 col.active = not domain.use_slice 1317 col.prop(domain, "slice_per_voxel") 1318 1319 1320class PHYSICS_PT_viewport_display_slicing(PhysicButtonsPanel, Panel): 1321 bl_label = "Slice" 1322 bl_parent_id = 'PHYSICS_PT_viewport_display' 1323 bl_options = {'DEFAULT_CLOSED'} 1324 1325 @classmethod 1326 def poll(cls, context): 1327 return (PhysicButtonsPanel.poll_fluid_domain(context)) 1328 1329 def draw_header(self, context): 1330 md = context.fluid.domain_settings 1331 1332 self.layout.prop(md, "use_slice", text="") 1333 1334 def draw(self, context): 1335 layout = self.layout 1336 layout.use_property_split = True 1337 1338 domain = context.fluid.domain_settings 1339 1340 layout.active = domain.use_slice 1341 1342 col = layout.column() 1343 col.prop(domain, "slice_axis") 1344 col.prop(domain, "slice_depth") 1345 1346 sub = col.column() 1347 sub.prop(domain, "show_gridlines") 1348 1349 sub.active = domain.display_interpolation == 'CLOSEST' or domain.color_ramp_field == 'FLAGS' 1350 1351 1352class PHYSICS_PT_viewport_display_color(PhysicButtonsPanel, Panel): 1353 bl_label = "Grid Display" 1354 bl_parent_id = 'PHYSICS_PT_viewport_display' 1355 bl_options = {'DEFAULT_CLOSED'} 1356 1357 @classmethod 1358 def poll(cls, context): 1359 return (PhysicButtonsPanel.poll_fluid_domain(context)) 1360 1361 def draw_header(self, context): 1362 md = context.fluid.domain_settings 1363 1364 self.layout.prop(md, "use_color_ramp", text="") 1365 1366 def draw(self, context): 1367 layout = self.layout 1368 layout.use_property_split = True 1369 1370 domain = context.fluid.domain_settings 1371 col = layout.column() 1372 col.active = domain.use_color_ramp 1373 col.prop(domain, "color_ramp_field") 1374 1375 if not domain.color_ramp_field == 'FLAGS': 1376 col.prop(domain, "color_ramp_field_scale") 1377 1378 col.use_property_split = False 1379 1380 if domain.color_ramp_field[:3] != 'PHI' and domain.color_ramp_field not in {'FLAGS', 'PRESSURE'}: 1381 col = col.column() 1382 col.template_color_ramp(domain, "color_ramp", expand=True) 1383 1384 1385class PHYSICS_PT_viewport_display_debug(PhysicButtonsPanel, Panel): 1386 bl_label = "Vector Display" 1387 bl_parent_id = 'PHYSICS_PT_viewport_display' 1388 bl_options = {'DEFAULT_CLOSED'} 1389 1390 @classmethod 1391 def poll(cls, context): 1392 return (PhysicButtonsPanel.poll_fluid_domain(context)) 1393 1394 def draw_header(self, context): 1395 md = context.fluid.domain_settings 1396 1397 self.layout.prop(md, "show_velocity", text="") 1398 1399 def draw(self, context): 1400 layout = self.layout 1401 layout.use_property_split = True 1402 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) 1403 1404 domain = context.fluid.domain_settings 1405 1406 col = flow.column() 1407 col.active = domain.show_velocity 1408 col.prop(domain, "vector_display_type", text="Display As") 1409 1410 if not domain.use_guide and domain.vector_field == 'GUIDE_VELOCITY': 1411 note = layout.split() 1412 note.label(icon='INFO', text="Enable Guides first! Defaulting to Fluid Velocity") 1413 1414 if domain.vector_display_type == 'MAC': 1415 sub = col.column(heading="MAC Grid") 1416 sub.prop(domain, "vector_show_mac_x") 1417 sub.prop(domain, "vector_show_mac_y") 1418 sub.prop(domain, "vector_show_mac_z") 1419 else: 1420 col.prop(domain, "vector_scale_with_magnitude") 1421 1422 col.prop(domain, "vector_field") 1423 col.prop(domain, "vector_scale") 1424 1425 1426class PHYSICS_PT_viewport_display_advanced(PhysicButtonsPanel, Panel): 1427 bl_label = "Advanced" 1428 bl_parent_id = 'PHYSICS_PT_viewport_display' 1429 bl_options = {'DEFAULT_CLOSED'} 1430 1431 @classmethod 1432 def poll(cls, context): 1433 domain = context.fluid.domain_settings 1434 return PhysicButtonsPanel.poll_fluid_domain(context) and domain.use_slice and domain.show_gridlines 1435 1436 def draw(self, context): 1437 layout = self.layout 1438 layout.use_property_split = True 1439 1440 domain = context.fluid.domain_settings 1441 1442 layout.active = domain.display_interpolation == 'CLOSEST' or domain.color_ramp_field == 'FLAGS' 1443 1444 col = layout.column() 1445 col.prop(domain, "gridlines_color_field", text="Color Gridlines") 1446 1447 if domain.gridlines_color_field == 'RANGE': 1448 if domain.use_color_ramp and domain.color_ramp_field != 'FLAGS': 1449 col.prop(domain, "gridlines_lower_bound") 1450 col.prop(domain, "gridlines_upper_bound") 1451 col.prop(domain, "gridlines_range_color") 1452 col.prop(domain, "gridlines_cell_filter") 1453 else: 1454 note = layout.split() 1455 if not domain.use_color_ramp: 1456 note.label(icon='INFO', text="Enable Grid Display to use range highlighting!") 1457 else: 1458 note.label(icon='INFO', text="Range highlighting for flags is not available!") 1459 1460 1461classes = ( 1462 FLUID_PT_presets, 1463 PHYSICS_PT_fluid, 1464 PHYSICS_PT_settings, 1465 PHYSICS_PT_borders, 1466 PHYSICS_PT_adaptive_domain, 1467 PHYSICS_PT_smoke, 1468 PHYSICS_PT_smoke_dissolve, 1469 PHYSICS_PT_noise, 1470 PHYSICS_PT_fire, 1471 PHYSICS_PT_liquid, 1472 PHYSICS_PT_diffusion, 1473 PHYSICS_PT_particles, 1474 PHYSICS_PT_mesh, 1475 PHYSICS_PT_guide, 1476 PHYSICS_PT_collections, 1477 PHYSICS_PT_cache, 1478 PHYSICS_PT_export, 1479 PHYSICS_PT_field_weights, 1480 PHYSICS_PT_flow_source, 1481 PHYSICS_PT_flow_initial_velocity, 1482 PHYSICS_PT_flow_texture, 1483 PHYSICS_PT_viewport_display, 1484 PHYSICS_PT_viewport_display_slicing, 1485 PHYSICS_PT_viewport_display_color, 1486 PHYSICS_PT_viewport_display_debug, 1487 PHYSICS_PT_viewport_display_advanced, 1488) 1489 1490 1491if __name__ == "__main__": # only for live edit. 1492 from bpy.utils import register_class 1493 for cls in classes: 1494 register_class(cls) 1495