blender/scripts/startup/bl_ui/properties_physics_fluid.py
Aaron Carlisle 67b21ce54d UI: Clarify Liquid Diffusion/Viscosity Properties
The viscosity panel was confusing for users because viscosity
is defined by the base and exponent properties in the Diffusion panel.

What this setting actually does is use a special solver algorithm that is designed for high viscosity liquids.
See 635694c0ff8fc5c9828bf920ecb81bb9bf792a82 for details.

The below changes are designed to better represent this to the user.

- Move the "Viscosity" Panel to a sub panel of "Diffusion"
- Rename "Viscosity" to "High Viscosity Solver" to better represent what this property does
- Update `use_viscosity` tooltip to better explain that this uses a different solver algorithm.

Pull Request: https://projects.blender.org/blender/blender/pulls/116118
2024-01-12 16:55:05 +01:00

1653 lines
56 KiB
Python

# SPDX-FileCopyrightText: 2009-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Panel
from bl_ui.utils import PresetPanel
from bl_ui.properties_physics_common import (
effector_weights_ui,
)
class FLUID_PT_presets(PresetPanel, Panel):
bl_label = "Fluid Presets"
preset_subdir = "fluid"
preset_operator = "script.execute_preset"
preset_add_operator = "fluid.preset_add"
class PhysicButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "physics"
@staticmethod
def check_domain_has_unbaked_guide(domain):
return (
domain.use_guide and not domain.has_cache_baked_guide and
((domain.guide_source == 'EFFECTOR') or
(domain.guide_source == 'DOMAIN' and not domain.guide_parent))
)
@staticmethod
def poll_fluid(context):
ob = context.object
if not ((ob and ob.type == 'MESH') and (context.fluid)):
return False
md = context.fluid
return md and (context.fluid.fluid_type != 'NONE')
@staticmethod
def poll_fluid_domain(context):
if not PhysicButtonsPanel.poll_fluid(context):
return False
md = context.fluid
return md and (md.fluid_type == 'DOMAIN')
@staticmethod
def poll_gas_domain(context):
if not PhysicButtonsPanel.poll_fluid(context):
return False
md = context.fluid
if md and (md.fluid_type == 'DOMAIN'):
domain = md.domain_settings
return domain.domain_type == 'GAS'
return False
@staticmethod
def poll_liquid_domain(context):
if not PhysicButtonsPanel.poll_fluid(context):
return False
md = context.fluid
if md and (md.fluid_type == 'DOMAIN'):
domain = md.domain_settings
return domain.domain_type == 'LIQUID'
return False
@staticmethod
def poll_fluid_flow(context):
if not PhysicButtonsPanel.poll_fluid(context):
return False
md = context.fluid
return md and (md.fluid_type == 'FLOW')
@staticmethod
def poll_fluid_flow_outflow(context):
if not PhysicButtonsPanel.poll_fluid_flow(context):
return False
md = context.fluid
flow = md.flow_settings
return (flow.flow_behavior == 'OUTFLOW')
@staticmethod
def poll_fluid_flow_liquid(context):
if not PhysicButtonsPanel.poll_fluid_flow(context):
return False
md = context.fluid
flow = md.flow_settings
return (flow.flow_type == 'LIQUID')
class PHYSICS_PT_fluid(PhysicButtonsPanel, Panel):
bl_label = "Fluid"
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
ob = context.object
return (ob and ob.type == 'MESH') and (context.engine in cls.COMPAT_ENGINES) and (context.fluid)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
if not bpy.app.build_options.fluid:
col = layout.column(align=True)
col.alignment = 'RIGHT'
col.label(text="Built without Fluid modifier")
return
md = context.fluid
layout.prop(md, "fluid_type")
class PHYSICS_PT_settings(PhysicButtonsPanel, Panel):
bl_label = "Settings"
bl_parent_id = "PHYSICS_PT_fluid"
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_fluid(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
md = context.fluid
ob = context.object
scene = context.scene
if md.fluid_type == 'DOMAIN':
domain = md.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_data = domain.has_cache_baked_data
row = layout.row()
row.enabled = not is_baking_any and not has_baked_data
row.prop(domain, "domain_type", expand=False)
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_data
col = flow.column()
col.enabled = not domain.has_cache_baked_guide
col.prop(domain, "resolution_max", text="Resolution Divisions")
col.prop(domain, "time_scale", text="Time Scale")
col.prop(domain, "cfl_condition", text="CFL Number")
col = flow.column()
col.prop(domain, "use_adaptive_timesteps")
sub = col.column(align=True)
sub.active = domain.use_adaptive_timesteps
sub.prop(domain, "timesteps_max", text="Timesteps Maximum")
sub.prop(domain, "timesteps_min", text="Minimum")
col.separator()
col = flow.column()
if scene.use_gravity:
sub = col.column()
sub.enabled = False
sub.prop(domain, "gravity", text="Using Scene Gravity", icon='SCENE_DATA')
else:
col.prop(domain, "gravity", text="Gravity")
col = flow.column()
if PhysicButtonsPanel.poll_gas_domain(context):
col.prop(domain, "clipping", text="Empty Space")
col.prop(domain, "delete_in_obstacle", text="Delete in Obstacle")
if domain.cache_type == 'MODULAR':
col.separator()
label = ""
# Deactivate bake operator if guides are enabled but not baked yet.
note_flag = True
if self.check_domain_has_unbaked_guide(domain):
note_flag = False
label = "Unbaked Guides: Bake Guides or disable them"
elif not domain.cache_resumable and not label:
label = "Non Resumable Cache: Baking "
if PhysicButtonsPanel.poll_liquid_domain(context):
label += "mesh or particles will not be possible"
elif PhysicButtonsPanel.poll_gas_domain(context):
label += "noise will not be possible"
else:
label = ""
if label:
info = layout.split()
note = info.row()
note.enabled = note_flag
note.alignment = 'RIGHT'
note.label(icon='INFO', text=label)
split = layout.split()
split.enabled = note_flag and ob.mode == 'OBJECT'
bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end)
if (
domain.cache_resumable and
domain.has_cache_baked_data and
not domain.is_cache_baking_data and
bake_incomplete
):
col = split.column()
col.operator("fluid.bake_data", text="Resume")
col = split.column()
col.operator("fluid.free_data", text="Free")
elif domain.is_cache_baking_data and not domain.has_cache_baked_data:
split.enabled = False
split.operator("fluid.pause_bake", text="Baking Data - ESC to pause")
elif not domain.has_cache_baked_data and not domain.is_cache_baking_data:
split.operator("fluid.bake_data", text="Bake Data")
else:
split.operator("fluid.free_data", text="Free Data")
elif md.fluid_type == 'FLOW':
flow = md.flow_settings
row = layout.row()
row.prop(flow, "flow_type", expand=False)
grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
col = grid.column()
col.prop(flow, "flow_behavior", expand=False)
if flow.flow_behavior in {'INFLOW', 'OUTFLOW'}:
col.prop(flow, "use_inflow")
col.prop(flow, "subframes", text="Sampling Substeps")
if not flow.flow_behavior == 'OUTFLOW' and flow.flow_type in {'SMOKE', 'BOTH', 'FIRE'}:
if flow.flow_type in {'SMOKE', 'BOTH'}:
col.prop(flow, "smoke_color", text="Smoke Color")
col = grid.column(align=True)
col.prop(flow, "use_absolute", text="Absolute Density")
if flow.flow_type in {'SMOKE', 'BOTH'}:
col.prop(flow, "temperature", text="Initial Temperature")
col.prop(flow, "density", text="Density")
if flow.flow_type in {'FIRE', 'BOTH'}:
col.prop(flow, "fuel_amount", text="Fuel")
col.separator()
col.prop_search(flow, "density_vertex_group", ob, "vertex_groups", text="Vertex Group")
elif md.fluid_type == 'EFFECTOR':
effector_settings = md.effector_settings
row = layout.row()
row.prop(effector_settings, "effector_type")
grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
col = grid.column()
col.prop(effector_settings, "subframes", text="Sampling Substeps")
col.prop(effector_settings, "surface_distance", text="Surface Thickness")
col = grid.column()
col.prop(effector_settings, "use_effector", text="Use Effector")
col.prop(effector_settings, "use_plane_init", text="Is Planar")
if effector_settings.effector_type == 'GUIDE':
col.prop(effector_settings, "velocity_factor", text="Velocity Factor")
col.prop(effector_settings, "guide_mode", text="Guide Mode")
class PHYSICS_PT_borders(PhysicButtonsPanel, Panel):
bl_label = "Border Collisions"
bl_parent_id = "PHYSICS_PT_settings"
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_fluid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
md = context.fluid
domain = md.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_data = domain.has_cache_baked_data
col = layout.column(align=True)
col.enabled = not is_baking_any and not has_baked_data
col.prop(domain, "use_collision_border_front")
col.prop(domain, "use_collision_border_back")
col.prop(domain, "use_collision_border_right")
col.prop(domain, "use_collision_border_left")
col.prop(domain, "use_collision_border_top")
col.prop(domain, "use_collision_border_bottom")
class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
bl_label = "Gas"
bl_parent_id = "PHYSICS_PT_fluid"
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_gas_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
md = context.fluid
domain = md.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_data = domain.has_cache_baked_data
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_data
col = flow.column(align=True)
col.prop(domain, "alpha", text="Buoyancy Density")
col.prop(domain, "beta", text="Heat")
col = flow.column()
col.prop(domain, "vorticity")
class PHYSICS_PT_smoke_dissolve(PhysicButtonsPanel, Panel):
bl_label = "Dissolve"
bl_parent_id = "PHYSICS_PT_smoke"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_gas_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.fluid.domain_settings
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
self.layout.enabled = not is_baking_any
self.layout.prop(md, "use_dissolve_smoke", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
md = context.fluid
domain = md.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_data = domain.has_cache_baked_data
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_data
layout.active = domain.use_dissolve_smoke
col = flow.column()
col.prop(domain, "dissolve_speed", text="Time")
col = flow.column()
col.prop(domain, "use_dissolve_smoke_log", text="Slow")
class PHYSICS_PT_fire(PhysicButtonsPanel, Panel):
bl_label = "Fire"
bl_parent_id = "PHYSICS_PT_smoke"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_gas_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
md = context.fluid
domain = md.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_data = domain.has_cache_baked_data
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_data
col = flow.column()
col.prop(domain, "burning_rate", text="Reaction Speed")
row = col.row()
sub = row.column(align=True)
sub.prop(domain, "flame_smoke", text="Flame Smoke")
sub.prop(domain, "flame_vorticity", text="Vorticity")
col = flow.column(align=True)
col.prop(domain, "flame_max_temp", text="Temperature Maximum")
col.prop(domain, "flame_ignition", text="Minimum")
row = col.row()
row.prop(domain, "flame_smoke_color", text="Smoke Color")
class PHYSICS_PT_liquid(PhysicButtonsPanel, Panel):
bl_label = "Liquid"
bl_parent_id = "PHYSICS_PT_fluid"
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_liquid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.fluid.domain_settings
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
self.layout.enabled = not is_baking_any
self.layout.prop(md, "use_flip_particles", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
md = context.fluid
domain = md.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_data = domain.has_cache_baked_data
layout.enabled = not is_baking_any and not has_baked_data
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
col = flow.column()
col.prop(domain, "simulation_method", expand=False)
if domain.simulation_method == 'FLIP':
col.prop(domain, "flip_ratio", text="FLIP Ratio")
col.prop(domain, "sys_particle_maximum", text="System Maximum")
col = col.column(align=True)
col.prop(domain, "particle_radius", text="Particle Radius")
col.prop(domain, "particle_number", text="Sampling")
col.prop(domain, "particle_randomness", text="Randomness")
col = flow.column()
col = col.column(align=True)
col.prop(domain, "particle_max", text="Particles Maximum")
col.prop(domain, "particle_min", text="Minimum")
col.separator()
col = col.column()
col.prop(domain, "particle_band_width", text="Narrow Band Width")
col = col.column()
col.prop(domain, "use_fractions", text="Fractional Obstacles")
sub = col.column()
sub.active = domain.use_fractions
sub.prop(domain, "fractions_distance", text="Obstacle Distance")
sub.prop(domain, "fractions_threshold", text="Threshold")
class PHYSICS_PT_flow_source(PhysicButtonsPanel, Panel):
bl_label = "Flow Source"
bl_parent_id = "PHYSICS_PT_settings"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_fluid_flow(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
ob = context.object
flow = context.fluid.flow_settings
col = layout.column()
col.prop(flow, "flow_source", expand=False, text="Flow Source")
if flow.flow_source == 'PARTICLES':
col.prop_search(flow, "particle_system", ob, "particle_systems", text="Particle System")
grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
col = grid.column()
if flow.flow_source == 'MESH':
col.prop(flow, "use_plane_init", text="Is Planar")
col.prop(flow, "surface_distance", text="Surface Emission")
if flow.flow_type in {'SMOKE', 'BOTH', 'FIRE'}:
col = grid.column()
col.prop(flow, "volume_density", text="Volume Emission")
if flow.flow_source == 'PARTICLES':
col.prop(flow, "use_particle_size", text="Set Size")
sub = col.column()
sub.active = flow.use_particle_size
sub.prop(flow, "particle_size")
class PHYSICS_PT_flow_initial_velocity(PhysicButtonsPanel, Panel):
bl_label = "Initial Velocity"
bl_parent_id = "PHYSICS_PT_settings"
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_fluid_flow(context):
return False
if PhysicButtonsPanel.poll_fluid_flow_outflow(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.fluid
flow_smoke = md.flow_settings
self.layout.prop(flow_smoke, "use_initial_velocity", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
md = context.fluid
flow_smoke = md.flow_settings
flow.active = flow_smoke.use_initial_velocity
col = flow.column()
col.prop(flow_smoke, "velocity_factor")
if flow_smoke.flow_source == 'MESH':
col.prop(flow_smoke, "velocity_normal")
# col.prop(flow_smoke, "velocity_random")
col = flow.column()
col.prop(flow_smoke, "velocity_coord")
class PHYSICS_PT_flow_texture(PhysicButtonsPanel, Panel):
bl_label = "Texture"
bl_parent_id = "PHYSICS_PT_settings"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_fluid_flow(context):
return False
if PhysicButtonsPanel.poll_fluid_flow_outflow(context):
return False
if PhysicButtonsPanel.poll_fluid_flow_liquid(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.fluid
flow_smoke = md.flow_settings
self.layout.prop(flow_smoke, "use_texture", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
ob = context.object
flow_smoke = context.fluid.flow_settings
sub = flow.column()
sub.active = flow_smoke.use_texture
sub.prop(flow_smoke, "noise_texture")
sub.prop(flow_smoke, "texture_map_type", text="Mapping")
col = flow.column()
sub = col.column()
sub.active = flow_smoke.use_texture
if flow_smoke.texture_map_type == 'UV':
sub.prop_search(flow_smoke, "uv_layer", ob.data, "uv_layers")
if flow_smoke.texture_map_type == 'AUTO':
sub.prop(flow_smoke, "texture_size")
sub.prop(flow_smoke, "texture_offset")
class PHYSICS_PT_adaptive_domain(PhysicButtonsPanel, Panel):
bl_label = "Adaptive Domain"
bl_parent_id = "PHYSICS_PT_settings"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_gas_domain(context):
return False
md = context.fluid
domain = md.domain_settings
# Effector guides require a fixed domain size
if domain.use_guide and domain.guide_source == 'EFFECTOR':
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.fluid.domain_settings
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_any = domain.has_cache_baked_any
self.layout.enabled = not is_baking_any and not has_baked_any
self.layout.prop(md, "use_adaptive_domain", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
domain = context.fluid.domain_settings
layout.active = domain.use_adaptive_domain
is_baking_any = domain.is_cache_baking_any
has_baked_any = domain.has_cache_baked_any
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
flow.enabled = not is_baking_any and not has_baked_any
col = flow.column()
col.prop(domain, "additional_res", text="Add Resolution")
col.prop(domain, "adapt_margin")
col.separator()
col = flow.column()
col.prop(domain, "adapt_threshold", text="Threshold")
class PHYSICS_PT_noise(PhysicButtonsPanel, Panel):
bl_label = "Noise"
bl_parent_id = "PHYSICS_PT_smoke"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_gas_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.fluid.domain_settings
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
self.layout.enabled = not is_baking_any
self.layout.prop(md, "use_noise", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
ob = context.object
domain = context.fluid.domain_settings
layout.active = domain.use_noise
is_baking_any = domain.is_cache_baking_any
has_baked_noise = domain.has_cache_baked_noise
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_noise
col = flow.column()
col.prop(domain, "noise_scale", text="Upres Factor")
col.prop(domain, "noise_strength", text="Strength")
col = flow.column()
col.prop(domain, "noise_pos_scale", text="Scale")
col.prop(domain, "noise_time_anim", text="Time")
if domain.cache_type == 'MODULAR':
col.separator()
# Deactivate bake operator if data has not been baked yet.
note_flag = True
if domain.use_noise:
label = ""
if not domain.cache_resumable:
label = "Non Resumable Cache: Enable resumable option first"
elif not domain.has_cache_baked_data:
label = "Unbaked Data: Bake Data first"
if label:
info = layout.split()
note = info.row()
note_flag = False
note.enabled = note_flag
note.alignment = 'RIGHT'
note.label(icon='INFO', text=label)
split = layout.split()
split.enabled = domain.has_cache_baked_data and note_flag and ob.mode == 'OBJECT'
bake_incomplete = (domain.cache_frame_pause_noise < domain.cache_frame_end)
if domain.has_cache_baked_noise and not domain.is_cache_baking_noise and bake_incomplete:
col = split.column()
col.operator("fluid.bake_noise", text="Resume")
col = split.column()
col.operator("fluid.free_noise", text="Free")
elif not domain.has_cache_baked_noise and domain.is_cache_baking_noise:
split.enabled = False
split.operator("fluid.pause_bake", text="Baking Noise - ESC to pause")
elif not domain.has_cache_baked_noise and not domain.is_cache_baking_noise:
split.operator("fluid.bake_noise", text="Bake Noise")
else:
split.operator("fluid.free_noise", text="Free Noise")
class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel):
bl_label = "Mesh"
bl_parent_id = "PHYSICS_PT_liquid"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_liquid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.fluid.domain_settings
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
self.layout.enabled = not is_baking_any
self.layout.prop(md, "use_mesh", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
ob = context.object
domain = context.fluid.domain_settings
layout.active = domain.use_mesh
is_baking_any = domain.is_cache_baking_any
has_baked_mesh = domain.has_cache_baked_mesh
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_mesh
col = flow.column()
col.prop(domain, "mesh_scale", text="Upres Factor")
col.prop(domain, "mesh_particle_radius", text="Particle Radius")
col = flow.column()
col.prop(domain, "use_speed_vectors", text="Use Speed Vectors")
col.separator()
col.prop(domain, "mesh_generator", text="Mesh Generator")
if domain.mesh_generator == 'IMPROVED':
col = flow.column(align=True)
col.prop(domain, "mesh_smoothen_pos", text="Smoothing Positive")
col.prop(domain, "mesh_smoothen_neg", text="Negative")
col = flow.column(align=True)
col.prop(domain, "mesh_concave_upper", text="Concavity Upper")
col.prop(domain, "mesh_concave_lower", text="Lower")
# TODO(@sebbas): for now just interpolate any up-resolution grids, ie not sampling high-resolution grids
# col.prop(domain, "highres_sampling", text="Flow Sampling:")
if domain.cache_type == 'MODULAR':
col.separator()
# Deactivate bake operator if data has not been baked yet.
note_flag = True
if domain.use_mesh:
label = ""
if not domain.cache_resumable:
label = "Non Resumable Cache: Enable resumable option first"
elif not domain.has_cache_baked_data:
label = "Unbaked Data: Bake Data first"
if label:
info = layout.split()
note = info.row()
note_flag = False
note.enabled = note_flag
note.alignment = 'RIGHT'
note.label(icon='INFO', text=label)
split = layout.split()
split.enabled = domain.has_cache_baked_data and note_flag and ob.mode == 'OBJECT'
bake_incomplete = (domain.cache_frame_pause_mesh < domain.cache_frame_end)
if domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh and bake_incomplete:
col = split.column()
col.operator("fluid.bake_mesh", text="Resume")
col = split.column()
col.operator("fluid.free_mesh", text="Free")
elif not domain.has_cache_baked_mesh and domain.is_cache_baking_mesh:
split.enabled = False
split.operator("fluid.pause_bake", text="Baking Mesh - ESC to pause")
elif not domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh:
split.operator("fluid.bake_mesh", text="Bake Mesh")
else:
split.operator("fluid.free_mesh", text="Free Mesh")
class PHYSICS_PT_particles(PhysicButtonsPanel, Panel):
bl_label = "Particles"
bl_parent_id = "PHYSICS_PT_liquid"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_liquid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
ob = context.object
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_particles = domain.has_cache_baked_particles
using_particles = domain.use_spray_particles or domain.use_foam_particles or domain.use_bubble_particles
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any
sndparticle_combined_export = domain.sndparticle_combined_export
col = flow.column()
row = col.row()
row.enabled = sndparticle_combined_export in {'OFF', 'FOAM + BUBBLES'}
row.prop(domain, "use_spray_particles", text="Spray")
row.prop(domain, "use_foam_particles", text="Foam")
row.prop(domain, "use_bubble_particles", text="Bubbles")
col.separator()
col.prop(domain, "sndparticle_combined_export")
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_particles
flow.active = using_particles
col = flow.column()
col.prop(domain, "particle_scale", text="Upres Factor")
col.separator()
col = flow.column(align=True)
col.prop(domain, "sndparticle_potential_max_wavecrest", text="Wave Crest Potential Maximum")
col.prop(domain, "sndparticle_potential_min_wavecrest", text="Minimum")
col.separator()
col = flow.column(align=True)
col.prop(domain, "sndparticle_potential_max_trappedair", text="Trapped Air Potential Maximum")
col.prop(domain, "sndparticle_potential_min_trappedair", text="Minimum")
col.separator()
col = flow.column(align=True)
col.prop(domain, "sndparticle_potential_max_energy", text="Kinetic Energy Potential Maximum")
col.prop(domain, "sndparticle_potential_min_energy", text="Minimum")
col.separator()
col = flow.column(align=True)
col.prop(domain, "sndparticle_potential_radius", text="Potential Radius")
col.prop(domain, "sndparticle_update_radius", text="Particle Update Radius")
col.separator()
col = flow.column(align=True)
col.prop(domain, "sndparticle_sampling_wavecrest", text="Wave Crest Particle Sampling")
col.prop(domain, "sndparticle_sampling_trappedair", text="Trapped Air Particle Sampling")
col.separator()
col = flow.column(align=True)
col.prop(domain, "sndparticle_life_max", text="Particle Life Maximum")
col.prop(domain, "sndparticle_life_min", text="Minimum")
col.separator()
col = flow.column(align=True)
col.prop(domain, "sndparticle_bubble_buoyancy", text="Bubble Buoyancy")
col.prop(domain, "sndparticle_bubble_drag", text="Bubble Drag")
col.separator()
col = flow.column()
col.prop(domain, "sndparticle_boundary", text="Particles in Boundary")
if domain.cache_type == 'MODULAR':
col.separator()
# Deactivate bake operator if data has not been baked yet.
note_flag = True
if using_particles:
label = ""
if not domain.cache_resumable:
label = "Non Resumable Cache: Enable resumable option first"
elif not domain.has_cache_baked_data:
label = "Unbaked Data: Bake Data first"
if label:
info = layout.split()
note = info.row()
note_flag = False
note.enabled = note_flag
note.alignment = 'RIGHT'
note.label(icon='INFO', text=label)
split = layout.split()
split.enabled = (
note_flag and
ob.mode == 'OBJECT' and
domain.has_cache_baked_data and
(domain.use_spray_particles or
domain.use_bubble_particles or
domain.use_foam_particles or
domain.use_tracer_particles)
)
bake_incomplete = (domain.cache_frame_pause_particles < domain.cache_frame_end)
if domain.has_cache_baked_particles and not domain.is_cache_baking_particles and bake_incomplete:
col = split.column()
col.operator("fluid.bake_particles", text="Resume")
col = split.column()
col.operator("fluid.free_particles", text="Free")
elif not domain.has_cache_baked_particles and domain.is_cache_baking_particles:
split.enabled = False
split.operator("fluid.pause_bake", text="Baking Particles - ESC to pause")
elif not domain.has_cache_baked_particles and not domain.is_cache_baking_particles:
split.operator("fluid.bake_particles", text="Bake Particles")
else:
split.operator("fluid.free_particles", text="Free Particles")
class PHYSICS_PT_diffusion(PhysicButtonsPanel, Panel):
bl_label = "Diffusion"
bl_parent_id = "PHYSICS_PT_liquid"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
# Fluid diffusion only enabled for liquids (surface tension and viscosity not relevant for smoke)
if not PhysicButtonsPanel.poll_liquid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.fluid.domain_settings
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_any = domain.has_cache_baked_any
self.layout.enabled = not is_baking_any and not has_baked_any
self.layout.prop(md, "use_diffusion", text="")
def draw_header_preset(self, _context):
FLUID_PT_presets.draw_panel_header(self.layout)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
domain = context.fluid.domain_settings
layout.active = domain.use_diffusion
is_baking_any = domain.is_cache_baking_any
has_baked_any = domain.has_cache_baked_any
has_baked_data = domain.has_cache_baked_data
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_any and not has_baked_data
col = flow.column(align=True)
col.prop(domain, "viscosity_base", text="Base")
col.prop(domain, "viscosity_exponent", text="Exponent", slider=True)
col = flow.column()
col.prop(domain, "surface_tension", text="Surface Tension")
class PHYSICS_PT_viscosity(PhysicButtonsPanel, Panel):
bl_label = "High Viscosity Solver"
bl_parent_id = "PHYSICS_PT_diffusion"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
# Fluid viscosity only enabled for liquids
if not PhysicButtonsPanel.poll_liquid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.fluid.domain_settings
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_any = domain.has_cache_baked_any
self.layout.enabled = not is_baking_any and not has_baked_any
self.layout.prop(md, "use_viscosity", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
domain = context.fluid.domain_settings
layout.active = domain.use_viscosity
is_baking_any = domain.is_cache_baking_any
has_baked_any = domain.has_cache_baked_any
has_baked_data = domain.has_cache_baked_data
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_any and not has_baked_data
col = flow.column(align=True)
col.prop(domain, "viscosity_value", text="Strength")
class PHYSICS_PT_guide(PhysicButtonsPanel, Panel):
bl_label = "Guides"
bl_parent_id = "PHYSICS_PT_fluid"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_fluid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.fluid.domain_settings
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
self.layout.enabled = not is_baking_any
self.layout.prop(md, "use_guide", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
domain = context.fluid.domain_settings
layout.active = domain.use_guide
is_baking_any = domain.is_cache_baking_any
has_baked_data = domain.has_cache_baked_data
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_data
col = flow.column()
col.prop(domain, "guide_alpha", text="Weight")
col.prop(domain, "guide_beta", text="Size")
col.prop(domain, "guide_vel_factor", text="Velocity Factor")
col = flow.column()
col.prop(domain, "guide_source", text="Velocity Source")
if domain.guide_source == 'DOMAIN':
col.prop(domain, "guide_parent", text="Guide Parent")
if domain.cache_type == 'MODULAR':
col.separator()
if domain.guide_source == 'EFFECTOR':
split = layout.split()
bake_incomplete = (domain.cache_frame_pause_guide < domain.cache_frame_end)
if domain.has_cache_baked_guide and not domain.is_cache_baking_guide and bake_incomplete:
col = split.column()
col.operator("fluid.bake_guides", text="Resume")
col = split.column()
col.operator("fluid.free_guides", text="Free")
elif not domain.has_cache_baked_guide and domain.is_cache_baking_guide:
split.enabled = False
split.operator("fluid.pause_bake", text="Baking Guides - ESC to pause")
elif not domain.has_cache_baked_guide and not domain.is_cache_baking_guide:
split.operator("fluid.bake_guides", text="Bake Guides")
else:
split.operator("fluid.free_guides", text="Free Guides")
class PHYSICS_PT_collections(PhysicButtonsPanel, Panel):
bl_label = "Collections"
bl_parent_id = "PHYSICS_PT_fluid"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_fluid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
domain = context.fluid.domain_settings
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
col = flow.column()
col.prop(domain, "fluid_group", text="Flow")
# col.prop(domain, "effector_group", text="Forces")
col.prop(domain, "effector_group", text="Effector")
class PHYSICS_PT_cache(PhysicButtonsPanel, Panel):
bl_label = "Cache"
bl_parent_id = "PHYSICS_PT_fluid"
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_fluid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
ob = context.object
md = context.fluid
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_data = domain.has_cache_baked_data
has_baked_mesh = domain.has_cache_baked_mesh
col = layout.column()
col.prop(domain, "cache_directory", text="")
col.enabled = not is_baking_any
layout.use_property_split = True
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
col = flow.column()
row = col.row()
row = row.column(align=True)
row.prop(domain, "cache_frame_start", text="Frame Start")
row.prop(domain, "cache_frame_end", text="End")
row = col.row()
row.enabled = domain.cache_type in {'MODULAR', 'ALL'}
row.prop(domain, "cache_frame_offset", text="Offset")
col.separator()
col = flow.column()
col.prop(domain, "cache_type", expand=False)
row = col.row()
row.enabled = not is_baking_any and not has_baked_data
row.prop(domain, "cache_resumable", text="Is Resumable")
row = col.row()
row.enabled = not is_baking_any and not has_baked_data
row.prop(domain, "cache_data_format", text="Format Volumes")
if md.domain_settings.domain_type == 'LIQUID' and domain.use_mesh:
row = col.row()
row.enabled = not is_baking_any and not has_baked_mesh
row.prop(domain, "cache_mesh_format", text="Meshes")
if domain.cache_type == 'ALL':
col.separator()
split = layout.split()
split.enabled = ob.mode == 'OBJECT'
bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end)
if (
domain.cache_resumable and
domain.has_cache_baked_data and
not domain.is_cache_baking_data and
bake_incomplete
):
col = split.column()
col.operator("fluid.bake_all", text="Resume")
col = split.column()
col.operator("fluid.free_all", text="Free")
elif domain.is_cache_baking_data and not domain.has_cache_baked_data:
split.enabled = False
split.operator("fluid.pause_bake", text="Baking All - ESC to pause")
elif not domain.has_cache_baked_data and not domain.is_cache_baking_data:
split.operator("fluid.bake_all", text="Bake All")
else:
split.operator("fluid.free_all", text="Free All")
class PHYSICS_PT_export(PhysicButtonsPanel, Panel):
bl_label = "Advanced"
bl_parent_id = "PHYSICS_PT_cache"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
domain = context.fluid.domain_settings
if (
not PhysicButtonsPanel.poll_fluid_domain(context) or
(domain.cache_data_format != 'OPENVDB' and bpy.app.debug_value != 3001)
):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
domain = context.fluid.domain_settings
is_baking_any = domain.is_cache_baking_any
has_baked_any = domain.has_cache_baked_any
has_baked_data = domain.has_cache_baked_data
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
flow.enabled = not is_baking_any and not has_baked_any
col = flow.column()
if domain.cache_data_format == 'OPENVDB':
col.enabled = not is_baking_any and not has_baked_data
col.prop(domain, "openvdb_cache_compress_type", text="Compression Volumes")
col = flow.column()
col.prop(domain, "openvdb_data_depth", text="Precision Volumes")
# Only show the advanced panel to advanced users who know Mantaflow's birthday :)
if bpy.app.debug_value == 3001:
col = flow.column()
col.prop(domain, "export_manta_script", text="Export Mantaflow Script")
class PHYSICS_PT_field_weights(PhysicButtonsPanel, Panel):
bl_label = "Field Weights"
bl_parent_id = "PHYSICS_PT_fluid"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_fluid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
domain = context.fluid.domain_settings
effector_weights_ui(self, domain.effector_weights, 'SMOKE')
class PHYSICS_PT_viewport_display(PhysicButtonsPanel, Panel):
bl_label = "Viewport Display"
bl_parent_id = "PHYSICS_PT_fluid"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return (PhysicButtonsPanel.poll_fluid_domain(context))
def draw(self, context):
layout = self.layout
layout.use_property_split = True
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
domain = context.fluid.domain_settings
col = flow.column(align=False)
col.prop(domain, "display_thickness")
sub = col.column()
sub.prop(domain, "display_interpolation")
if domain.use_color_ramp and domain.color_ramp_field == 'FLAGS':
sub.enabled = False
col = col.column()
col.active = not domain.use_slice
col.prop(domain, "slice_per_voxel")
class PHYSICS_PT_viewport_display_slicing(PhysicButtonsPanel, Panel):
bl_label = "Slice"
bl_parent_id = "PHYSICS_PT_viewport_display"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return (PhysicButtonsPanel.poll_fluid_domain(context))
def draw_header(self, context):
md = context.fluid.domain_settings
self.layout.prop(md, "use_slice", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
domain = context.fluid.domain_settings
layout.active = domain.use_slice
col = layout.column()
col.prop(domain, "slice_axis")
col.prop(domain, "slice_depth")
sub = col.column()
sub.prop(domain, "show_gridlines")
sub.active = domain.display_interpolation == 'CLOSEST' or domain.color_ramp_field == 'FLAGS'
class PHYSICS_PT_viewport_display_color(PhysicButtonsPanel, Panel):
bl_label = "Grid Display"
bl_parent_id = "PHYSICS_PT_viewport_display"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return (PhysicButtonsPanel.poll_fluid_domain(context))
def draw_header(self, context):
md = context.fluid.domain_settings
self.layout.prop(md, "use_color_ramp", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
domain = context.fluid.domain_settings
col = layout.column()
col.active = domain.use_color_ramp
col.prop(domain, "color_ramp_field")
if not domain.color_ramp_field == 'FLAGS':
col.prop(domain, "color_ramp_field_scale")
col.use_property_split = False
if domain.color_ramp_field[:3] != 'PHI' and domain.color_ramp_field not in {'FLAGS', 'PRESSURE'}:
col = col.column()
col.template_color_ramp(domain, "color_ramp", expand=True)
class PHYSICS_PT_viewport_display_debug(PhysicButtonsPanel, Panel):
bl_label = "Vector Display"
bl_parent_id = "PHYSICS_PT_viewport_display"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return (PhysicButtonsPanel.poll_fluid_domain(context))
def draw_header(self, context):
md = context.fluid.domain_settings
self.layout.prop(md, "show_velocity", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
domain = context.fluid.domain_settings
col = flow.column()
col.active = domain.show_velocity
col.prop(domain, "vector_display_type", text="Display As")
if not domain.use_guide and domain.vector_field == 'GUIDE_VELOCITY':
note = layout.split()
note.label(icon='INFO', text="Enable Guides first! Defaulting to Fluid Velocity")
if domain.vector_display_type == 'MAC':
sub = col.column(heading="MAC Grid")
sub.prop(domain, "vector_show_mac_x")
sub.prop(domain, "vector_show_mac_y")
sub.prop(domain, "vector_show_mac_z")
else:
col.prop(domain, "vector_scale_with_magnitude")
col.prop(domain, "vector_field")
col.prop(domain, "vector_scale")
class PHYSICS_PT_viewport_display_advanced(PhysicButtonsPanel, Panel):
bl_label = "Advanced"
bl_parent_id = "PHYSICS_PT_viewport_display"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
domain = context.fluid.domain_settings
return PhysicButtonsPanel.poll_fluid_domain(context) and domain.use_slice and domain.show_gridlines
def draw(self, context):
layout = self.layout
layout.use_property_split = True
domain = context.fluid.domain_settings
layout.active = domain.display_interpolation == 'CLOSEST' or domain.color_ramp_field == 'FLAGS'
col = layout.column()
col.prop(domain, "gridlines_color_field", text="Color Gridlines")
if domain.gridlines_color_field == 'RANGE':
if domain.use_color_ramp and domain.color_ramp_field != 'FLAGS':
col.prop(domain, "gridlines_lower_bound")
col.prop(domain, "gridlines_upper_bound")
col.prop(domain, "gridlines_range_color")
col.prop(domain, "gridlines_cell_filter")
else:
note = layout.split()
if not domain.use_color_ramp:
note.label(icon='INFO', text="Enable Grid Display to use range highlighting!")
else:
note.label(icon='INFO', text="Range highlighting for flags is not available!")
class PHYSICS_PT_fluid_domain_render(PhysicButtonsPanel, Panel):
bl_label = "Render"
bl_parent_id = "PHYSICS_PT_fluid"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {
'BLENDER_RENDER',
'BLENDER_EEVEE',
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
@classmethod
def poll(cls, context):
if not PhysicButtonsPanel.poll_gas_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
domain = context.fluid.domain_settings
layout.prop(domain, "velocity_scale")
classes = (
FLUID_PT_presets,
PHYSICS_PT_fluid,
PHYSICS_PT_settings,
PHYSICS_PT_borders,
PHYSICS_PT_adaptive_domain,
PHYSICS_PT_smoke,
PHYSICS_PT_smoke_dissolve,
PHYSICS_PT_noise,
PHYSICS_PT_fire,
PHYSICS_PT_liquid,
PHYSICS_PT_diffusion,
PHYSICS_PT_viscosity,
PHYSICS_PT_particles,
PHYSICS_PT_mesh,
PHYSICS_PT_guide,
PHYSICS_PT_collections,
PHYSICS_PT_cache,
PHYSICS_PT_export,
PHYSICS_PT_field_weights,
PHYSICS_PT_flow_source,
PHYSICS_PT_flow_initial_velocity,
PHYSICS_PT_flow_texture,
PHYSICS_PT_viewport_display,
PHYSICS_PT_viewport_display_slicing,
PHYSICS_PT_viewport_display_color,
PHYSICS_PT_viewport_display_debug,
PHYSICS_PT_viewport_display_advanced,
PHYSICS_PT_fluid_domain_render,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)