2025-12-01
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
reloadable_modules = [
|
||||
'none_ui',
|
||||
'fluid_ui',
|
||||
'obstacle_ui',
|
||||
'inflow_ui',
|
||||
'outflow_ui',
|
||||
'force_field_ui',
|
||||
'domain_ui',
|
||||
'cache_object_ui',
|
||||
'helper_ui',
|
||||
'flip_fluids_addon_disabled_ui',
|
||||
]
|
||||
for module_name in reloadable_modules:
|
||||
if module_name in locals():
|
||||
importlib.reload(locals()[module_name])
|
||||
|
||||
import bpy
|
||||
|
||||
from . import(
|
||||
none_ui,
|
||||
fluid_ui,
|
||||
obstacle_ui,
|
||||
inflow_ui,
|
||||
outflow_ui,
|
||||
force_field_ui,
|
||||
domain_ui,
|
||||
cache_object_ui,
|
||||
helper_ui,
|
||||
flip_fluids_addon_disabled_ui,
|
||||
)
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
from ..utils import installation_utils
|
||||
from ..utils import api_workaround_utils as api_utils
|
||||
|
||||
|
||||
def append_to_PHYSICS_PT_add_panel(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
if not (obj.type == 'MESH' or obj.type == 'EMPTY', obj.type == 'CURVE'):
|
||||
return
|
||||
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
|
||||
column = self.layout.column(align=True)
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column()
|
||||
column_right = split.column()
|
||||
|
||||
if obj.flip_fluid.is_active:
|
||||
row = column_right.row(align=True)
|
||||
row.operator(
|
||||
"flip_fluid_operators.flip_fluid_remove",
|
||||
text="FLIP Fluid",
|
||||
icon='X'
|
||||
)
|
||||
|
||||
if obj.flip_fluid.is_domain():
|
||||
row.enabled = not is_addon_disabled
|
||||
row.prop(context.scene.flip_fluid, "show_viewport", icon="RESTRICT_VIEW_OFF", text="")
|
||||
row.prop(context.scene.flip_fluid, "show_render", icon="RESTRICT_RENDER_OFF", text="")
|
||||
|
||||
# Experimental Build Warning
|
||||
addon_prefs = vcu.get_addon_preferences(context)
|
||||
if installation_utils.is_experimental_build() and addon_prefs.enable_experimental_build_warning:
|
||||
box = self.layout.box()
|
||||
column = box.column(align=True)
|
||||
column.label(text="This is an experimental build of the FLIP Fluids addon", icon='ERROR')
|
||||
column.label(text="Not for production. Use at your own risk.", icon='ERROR')
|
||||
column.label(text="Please read before using:", icon='ERROR')
|
||||
column.operator(
|
||||
"wm.url_open",
|
||||
text="Experimental Builds",
|
||||
icon="WORLD"
|
||||
).url = "https://github.com/rlguy/Blender-FLIP-Fluids/wiki/Experimental-Builds"
|
||||
|
||||
is_saved = bool(bpy.data.filepath)
|
||||
if not is_saved and obj.flip_fluid.is_domain() and not is_addon_disabled:
|
||||
hprops = context.scene.flip_fluid_helper
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(hprops, "unsaved_blend_file_tooltip", icon="ERROR", emboss=False, text="")
|
||||
row = row.row(align=True)
|
||||
row.alert = True
|
||||
row.label(text="Unsaved File")
|
||||
row = row.row(align=True)
|
||||
row.alert = False
|
||||
row.operator("flip_fluid_operators.helper_save_blend_file", icon='FILE_TICK', text="Save")
|
||||
|
||||
|
||||
else:
|
||||
if not installation_utils.is_installation_complete():
|
||||
column_right.operator(
|
||||
"flip_fluid_operators.flip_fluid_add",
|
||||
text="FLIP Fluid",
|
||||
icon='ERROR'
|
||||
)
|
||||
else:
|
||||
use_custom_icon = True
|
||||
icon = context.scene.flip_fluid.get_logo_icon()
|
||||
if use_custom_icon and icon is not None:
|
||||
column_right.operator(
|
||||
"flip_fluid_operators.flip_fluid_add",
|
||||
text="FLIP Fluid",
|
||||
icon_value=context.scene.flip_fluid.get_logo_icon().icon_id
|
||||
)
|
||||
else:
|
||||
column_right.operator(
|
||||
"flip_fluid_operators.flip_fluid_add",
|
||||
text="FLIP Fluid",
|
||||
icon='MOD_FLUIDSIM'
|
||||
)
|
||||
|
||||
if not installation_utils.is_installation_complete():
|
||||
box = self.layout.box()
|
||||
box.label(text="IMPORTANT: Please Complete Installation", icon="ERROR")
|
||||
box.label(text="Click here to complete installation of the FLIP Fluids Addon:")
|
||||
box.operator("flip_fluid_operators.complete_installation", icon='MOD_FLUIDSIM')
|
||||
|
||||
addon_prefs = vcu.get_addon_preferences(context)
|
||||
flip_fluids_installations = installation_utils.get_enabled_flip_fluids_addon_installations()
|
||||
if len(flip_fluids_installations) > 1:
|
||||
box = self.layout.box()
|
||||
box.label(text="Installation Error Detected", icon="ERROR")
|
||||
box.label(text="Multiple version of the FLIP Fluids addon enabled:", icon="ERROR")
|
||||
for install in flip_fluids_installations:
|
||||
box.label(text=" "*10 + install['addon_name'] + " (" + install['module_name'] + ")")
|
||||
box.label(text="Only 1 version of the add-on can be enabled", icon="ERROR")
|
||||
box.label(text="Disable all other versions in the Blender addon preferences", icon="ERROR")
|
||||
box.label(text="Restart Blender", icon="ERROR")
|
||||
|
||||
is_installation_complete = installation_utils.is_installation_complete()
|
||||
feature_dict = api_utils.get_enabled_features_affected_by_T88811()
|
||||
if feature_dict is not None and not addon_prefs.dismiss_T88811_crash_warning and is_installation_complete:
|
||||
box = self.layout.box()
|
||||
api_utils.draw_T88811_ui_warning(box, addon_prefs, feature_dict)
|
||||
|
||||
is_persistent_data_enabled = api_utils.is_persistent_data_issue_relevant()
|
||||
if is_persistent_data_enabled and not addon_prefs.dismiss_persistent_data_render_warning and is_installation_complete:
|
||||
box = self.layout.box()
|
||||
api_utils.draw_persistent_data_warning(box, addon_prefs)
|
||||
|
||||
|
||||
def register():
|
||||
none_ui.register()
|
||||
fluid_ui.register()
|
||||
obstacle_ui.register()
|
||||
inflow_ui.register()
|
||||
outflow_ui.register()
|
||||
force_field_ui.register()
|
||||
domain_ui.register()
|
||||
cache_object_ui.register()
|
||||
helper_ui.register()
|
||||
flip_fluids_addon_disabled_ui.register()
|
||||
|
||||
bpy.types.PHYSICS_PT_add.append(append_to_PHYSICS_PT_add_panel)
|
||||
|
||||
|
||||
def unregister():
|
||||
none_ui.unregister()
|
||||
fluid_ui.unregister()
|
||||
obstacle_ui.unregister()
|
||||
inflow_ui.unregister()
|
||||
outflow_ui.unregister()
|
||||
force_field_ui.unregister()
|
||||
domain_ui.unregister()
|
||||
cache_object_ui.unregister()
|
||||
helper_ui.unregister()
|
||||
flip_fluids_addon_disabled_ui.unregister()
|
||||
|
||||
bpy.types.PHYSICS_PT_add.remove(append_to_PHYSICS_PT_add_panel)
|
||||
@@ -0,0 +1,342 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from . import domain_display_ui
|
||||
from ..operators import helper_operators
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_CacheObjectTypePanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Mesh Display"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
dprops = context.scene.flip_fluid.get_domain_properties()
|
||||
if dprops is None:
|
||||
return False
|
||||
if not context.scene.flip_fluid.is_domain_in_active_scene():
|
||||
return False
|
||||
obj = vcu.get_active_object(context)
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return dprops.mesh_cache.is_cache_object(obj) and not is_addon_disabled
|
||||
|
||||
|
||||
def get_domain_properties(self):
|
||||
return bpy.context.scene.flip_fluid.get_domain_properties()
|
||||
|
||||
|
||||
def draw_surface_viewport_render_display(self):
|
||||
dprops = self.get_domain_properties()
|
||||
rprops = dprops.render
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column()
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column()
|
||||
column_left.label(text="Render Display Mode:")
|
||||
column_left.prop(rprops, "render_display", expand=True)
|
||||
|
||||
column_right = split.column()
|
||||
column_right.label(text="Viewport Display Mode:")
|
||||
column_right.prop(rprops, "viewport_display", expand=True)
|
||||
|
||||
|
||||
def draw_fluid_particle_viewport_render_display(self):
|
||||
dprops = self.get_domain_properties()
|
||||
rprops = dprops.render
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column()
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column()
|
||||
column_left.label(text="Render Display Mode:")
|
||||
column_left.prop(rprops, "fluid_particle_render_display", expand=True)
|
||||
|
||||
column_right = split.column()
|
||||
column_right.label(text="Viewport Display Mode:")
|
||||
column_right.prop(rprops, "fluid_particle_viewport_display", expand=True)
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column(align=True)
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column(align=True)
|
||||
column_left.label(text="Final Display Settings:")
|
||||
column_left.prop(rprops, "render_fluid_particle_surface_pct", slider=True)
|
||||
column_left.prop(rprops, "render_fluid_particle_boundary_pct", slider=True)
|
||||
column_left.prop(rprops, "render_fluid_particle_interior_pct", slider=True)
|
||||
|
||||
column_right = split.column(align=True)
|
||||
column_right.label(text="Preview Display Settings:")
|
||||
column_right.prop(rprops, "viewport_fluid_particle_surface_pct", slider=True)
|
||||
column_right.prop(rprops, "viewport_fluid_particle_boundary_pct", slider=True)
|
||||
column_right.prop(rprops, "viewport_fluid_particle_interior_pct", slider=True)
|
||||
|
||||
bl_fluid_particles_mesh_cache = dprops.mesh_cache.particles.get_cache_object()
|
||||
point_cloud_detected = helper_operators.is_geometry_node_point_cloud_detected(bl_fluid_particles_mesh_cache)
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column(align=True)
|
||||
column.label(text="Particle Object Settings:")
|
||||
column.separator()
|
||||
|
||||
bl_mod = domain_display_ui.get_motion_blur_geometry_node_modifier(bl_fluid_particles_mesh_cache)
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.label(text="Fluid Particles:")
|
||||
domain_display_ui.draw_fluid_particles_motion_blur_geometry_node_properties(row, bl_mod)
|
||||
|
||||
|
||||
def draw_whitewater_viewport_render_display(self):
|
||||
dprops = self.get_domain_properties()
|
||||
rprops = dprops.render
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column()
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column()
|
||||
column_left.label(text="Render Display Mode:")
|
||||
column_left.prop(rprops, "whitewater_render_display", expand=True)
|
||||
|
||||
column_right = split.column()
|
||||
column_right.label(text="Viewport Display Mode:")
|
||||
column_right.prop(rprops, "whitewater_viewport_display", expand=True)
|
||||
|
||||
|
||||
def draw_whitewater_viewport_render_settings(self, render_pct_prop, viewport_pct_prop):
|
||||
dprops = self.get_domain_properties()
|
||||
rprops = dprops.render
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column(align=True)
|
||||
split = column.split()
|
||||
column = split.column(align = True)
|
||||
column.label(text="Final Display Amount:")
|
||||
column.prop(rprops, render_pct_prop, slider = True)
|
||||
|
||||
column = split.column(align = True)
|
||||
column.label(text="Preview Display Amount:")
|
||||
column.prop(rprops, viewport_pct_prop, slider = True)
|
||||
|
||||
|
||||
def draw_whitewater_particle_object_settings(self, prop_str, material_prop):
|
||||
dprops = self.get_domain_properties()
|
||||
rprops = dprops.render
|
||||
|
||||
bl_object = vcu.get_active_object()
|
||||
|
||||
box = self.layout.box()
|
||||
box.label(text="Particle Display Settings:")
|
||||
column = box.column(align=True)
|
||||
column.separator()
|
||||
split = vcu.ui_split(column, factor=0.1)
|
||||
column1 = split.column(align=True)
|
||||
column2 = split.column(align=True)
|
||||
|
||||
cache_props = dprops.mesh_cache.get_mesh_cache_from_blender_object(bl_object)
|
||||
if cache_props is not None:
|
||||
if cache_props.cache_object_type == 'CACHE_OBJECT_TYPE_FOAM':
|
||||
whitewater_label = "Foam:"
|
||||
elif cache_props.cache_object_type == 'CACHE_OBJECT_TYPE_BUBBLE':
|
||||
whitewater_label = "Bubble:"
|
||||
elif cache_props.cache_object_type == 'CACHE_OBJECT_TYPE_SPRAY':
|
||||
whitewater_label = "Spray:"
|
||||
elif cache_props.cache_object_type == 'CACHE_OBJECT_TYPE_DUST':
|
||||
whitewater_label = "Dust:"
|
||||
|
||||
bl_mod = domain_display_ui.get_motion_blur_geometry_node_modifier(bl_object)
|
||||
row = column1.row(align=True)
|
||||
row.label(text=whitewater_label)
|
||||
row = column2.row(align=True)
|
||||
domain_display_ui.draw_whitewater_particles_motion_blur_geometry_node_properties(row, bl_mod)
|
||||
|
||||
dprops = self.get_domain_properties()
|
||||
|
||||
self.layout.separator()
|
||||
box = self.layout.box()
|
||||
box.label(text="Material Library")
|
||||
box.prop(dprops.materials, material_prop, text=prop_str)
|
||||
box.separator()
|
||||
|
||||
|
||||
def draw_surface(self, cache_props, domain_props):
|
||||
dprops = self.get_domain_properties()
|
||||
|
||||
column = self.layout.column()
|
||||
column.label(text="Fluid Surface")
|
||||
column.separator()
|
||||
|
||||
self.draw_surface_viewport_render_display()
|
||||
|
||||
column = self.layout.column()
|
||||
column.separator()
|
||||
split = column.split()
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
|
||||
column_left.label(text="Surface Material")
|
||||
column_right.prop(dprops.materials, "surface_material", text="")
|
||||
|
||||
self.layout.separator()
|
||||
column = self.layout.column()
|
||||
column.operator("flip_fluid_operators.helper_delete_surface_objects", icon="X")
|
||||
|
||||
|
||||
def draw_fluid_particles(self, cache_props, domain_props):
|
||||
dprops = self.get_domain_properties()
|
||||
|
||||
column = self.layout.column()
|
||||
column.label(text="Fluid Particles")
|
||||
column.separator()
|
||||
|
||||
self.draw_fluid_particle_viewport_render_display()
|
||||
|
||||
self.layout.separator()
|
||||
box = self.layout.box()
|
||||
box.label(text="Material Library")
|
||||
box.prop(dprops.materials, "fluid_particles_material", text="Fluid Particles")
|
||||
box.separator()
|
||||
|
||||
self.layout.separator()
|
||||
column = self.layout.column()
|
||||
column.operator("flip_fluid_operators.helper_delete_particle_objects", icon="X")
|
||||
|
||||
|
||||
def draw_foam(self, cache_props, domain_props):
|
||||
dprops = self.get_domain_properties()
|
||||
rprops = dprops.render
|
||||
|
||||
column = self.layout.column()
|
||||
column.label(text="Whitewater Foam")
|
||||
column.separator()
|
||||
|
||||
self.draw_whitewater_viewport_render_display()
|
||||
self.draw_whitewater_viewport_render_settings('render_foam_pct', 'viewport_foam_pct')
|
||||
self.draw_whitewater_particle_object_settings("Foam", 'whitewater_foam_material')
|
||||
|
||||
self.layout.separator()
|
||||
column = self.layout.column()
|
||||
column.operator("flip_fluid_operators.helper_delete_whitewater_objects",
|
||||
text="Delete Whitewater Foam Mesh Objects",
|
||||
icon="X").whitewater_type = 'TYPE_FOAM'
|
||||
|
||||
column.operator("flip_fluid_operators.helper_delete_whitewater_objects",
|
||||
text="Delete All Whitewater Mesh Objects",
|
||||
icon="X").whitewater_type = 'TYPE_ALL'
|
||||
|
||||
|
||||
def draw_bubble(self, cache_props, domain_props):
|
||||
dprops = self.get_domain_properties()
|
||||
rprops = dprops.render
|
||||
|
||||
column = self.layout.column()
|
||||
column.label(text="Whitewater Bubble")
|
||||
column.separator()
|
||||
|
||||
self.draw_whitewater_viewport_render_display()
|
||||
self.draw_whitewater_viewport_render_settings('render_bubble_pct', 'viewport_bubble_pct')
|
||||
self.draw_whitewater_particle_object_settings("Bubble", 'whitewater_bubble_material')
|
||||
|
||||
self.layout.separator()
|
||||
column = self.layout.column()
|
||||
column.operator("flip_fluid_operators.helper_delete_whitewater_objects",
|
||||
text="Delete Whitewater Bubble Mesh Objects",
|
||||
icon="X").whitewater_type = 'TYPE_BUBBLE'
|
||||
|
||||
column.operator("flip_fluid_operators.helper_delete_whitewater_objects",
|
||||
text="Delete All Whitewater Mesh Objects",
|
||||
icon="X").whitewater_type = 'TYPE_ALL'
|
||||
|
||||
|
||||
def draw_spray(self, cache_props, domain_props):
|
||||
dprops = self.get_domain_properties()
|
||||
rprops = dprops.render
|
||||
|
||||
column = self.layout.column()
|
||||
column.label(text="Whitewater Spray")
|
||||
column.separator()
|
||||
|
||||
self.draw_whitewater_viewport_render_display()
|
||||
self.draw_whitewater_viewport_render_settings('render_spray_pct', 'viewport_spray_pct')
|
||||
self.draw_whitewater_particle_object_settings("Spray", 'whitewater_spray_material')
|
||||
|
||||
self.layout.separator()
|
||||
column = self.layout.column()
|
||||
column.operator("flip_fluid_operators.helper_delete_whitewater_objects",
|
||||
text="Delete Whitewater Spray Mesh Objects",
|
||||
icon="X").whitewater_type = 'TYPE_SPRAY'
|
||||
|
||||
column.operator("flip_fluid_operators.helper_delete_whitewater_objects",
|
||||
text="Delete All Whitewater Mesh Objects",
|
||||
icon="X").whitewater_type = 'TYPE_ALL'
|
||||
|
||||
|
||||
def draw_dust(self, cache_props, domain_props):
|
||||
dprops = self.get_domain_properties()
|
||||
rprops = dprops.render
|
||||
|
||||
column = self.layout.column()
|
||||
column.label(text="Whitewater Dust")
|
||||
column.separator()
|
||||
|
||||
self.draw_whitewater_viewport_render_display()
|
||||
self.draw_whitewater_viewport_render_settings('render_dust_pct', 'viewport_dust_pct')
|
||||
self.draw_whitewater_particle_object_settings("Dust", 'whitewater_dust_material')
|
||||
|
||||
self.layout.separator()
|
||||
column = self.layout.column()
|
||||
column.operator("flip_fluid_operators.helper_delete_whitewater_objects",
|
||||
text="Delete Whitewater Dust Mesh Objects",
|
||||
icon="X").whitewater_type = 'TYPE_DUST'
|
||||
|
||||
column.operator("flip_fluid_operators.helper_delete_whitewater_objects",
|
||||
text="Delete All Whitewater Mesh Objects",
|
||||
icon="X").whitewater_type = 'TYPE_ALL'
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
dprops = self.get_domain_properties()
|
||||
|
||||
obj = vcu.get_active_object(context)
|
||||
cache_props = dprops.mesh_cache.get_mesh_cache_from_blender_object(obj)
|
||||
if cache_props is None:
|
||||
return
|
||||
|
||||
if cache_props.cache_object_type == 'CACHE_OBJECT_TYPE_SURFACE':
|
||||
self.draw_surface(cache_props, dprops)
|
||||
if cache_props.cache_object_type == 'CACHE_OBJECT_TYPE_FLUID_PARTICLES':
|
||||
self.draw_fluid_particles(cache_props, dprops)
|
||||
elif cache_props.cache_object_type == 'CACHE_OBJECT_TYPE_FOAM':
|
||||
self.draw_foam(cache_props, dprops)
|
||||
elif cache_props.cache_object_type == 'CACHE_OBJECT_TYPE_BUBBLE':
|
||||
self.draw_bubble(cache_props, dprops)
|
||||
elif cache_props.cache_object_type == 'CACHE_OBJECT_TYPE_SPRAY':
|
||||
self.draw_spray(cache_props, dprops)
|
||||
elif cache_props.cache_object_type == 'CACHE_OBJECT_TYPE_DUST':
|
||||
self.draw_dust(cache_props, dprops)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_CacheObjectTypePanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_CacheObjectTypePanel)
|
||||
@@ -0,0 +1,192 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypeAdvancedPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Advanced Settings"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
aprops = obj.flip_fluid.domain.advanced
|
||||
wprops = obj.flip_fluid.domain.world
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column(align=True)
|
||||
split = vcu.ui_split(column, factor=0.45)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
|
||||
row = column_left.row(align=True)
|
||||
row.prop(aprops, "frame_substeps_expanded",
|
||||
icon="TRIA_DOWN" if aprops.frame_substeps_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Frame Substeps:")
|
||||
|
||||
if not aprops.frame_substeps_expanded:
|
||||
row = column_right.row(align=True)
|
||||
row.prop(aprops.min_max_time_steps_per_frame, "value_min", text="Min")
|
||||
row.prop(aprops.min_max_time_steps_per_frame, "value_max", text="Max")
|
||||
|
||||
if aprops.frame_substeps_expanded:
|
||||
column = box.column(align=True)
|
||||
if wprops.enable_surface_tension and aprops.min_max_time_steps_per_frame.value_max < wprops.minimum_surface_tension_substeps:
|
||||
row = column.row(align=True)
|
||||
row.alert = True
|
||||
row.prop(aprops, "surface_tension_substeps_exceeded_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text=" Warning: Not Enough Max Substeps")
|
||||
|
||||
row = column.row(align=True)
|
||||
row.prop(aprops.min_max_time_steps_per_frame, "value_min", text="Min")
|
||||
row.prop(aprops.min_max_time_steps_per_frame, "value_max", text="Max")
|
||||
column.prop(aprops, "CFL_condition_number")
|
||||
column.prop(aprops, "enable_adaptive_obstacle_time_stepping")
|
||||
column.prop(aprops, "enable_adaptive_force_field_time_stepping")
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column(align=True)
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
|
||||
row = column_left.row(align=True)
|
||||
row.prop(aprops, "simulation_method_expanded",
|
||||
icon="TRIA_DOWN" if aprops.simulation_method_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Simulation Method:")
|
||||
|
||||
if not aprops.simulation_method_expanded:
|
||||
row = column_right.row(align=True)
|
||||
row.prop(aprops, "velocity_transfer_method", expand=True)
|
||||
|
||||
if aprops.simulation_method_expanded:
|
||||
column = box.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.prop(aprops, "velocity_transfer_method", expand=True)
|
||||
if aprops.velocity_transfer_method == 'VELOCITY_TRANSFER_METHOD_FLIP':
|
||||
column.prop(aprops, "PICFLIP_ratio", slider=True)
|
||||
else:
|
||||
column.label(text="")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(aprops, "simulation_stability_expanded",
|
||||
icon="TRIA_DOWN" if aprops.simulation_stability_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Simulation and Particle Stability:")
|
||||
|
||||
if aprops.simulation_stability_expanded:
|
||||
column = box.column()
|
||||
|
||||
row = column.row(align=True)
|
||||
row.prop(aprops, "particle_jitter_factor", slider=True)
|
||||
row.prop(aprops, "jitter_surface_particles")
|
||||
column.prop(aprops, "enable_extreme_velocity_removal")
|
||||
column.separator()
|
||||
column = box.column(align=True)
|
||||
column.prop(aprops, "pressure_solver_max_iterations")
|
||||
column.prop(aprops, "viscosity_solver_max_iterations")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(aprops, "multithreading_expanded",
|
||||
icon="TRIA_DOWN" if aprops.multithreading_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Multithreading and Performance:")
|
||||
|
||||
if not aprops.multithreading_expanded:
|
||||
info_text = ""
|
||||
if aprops.threading_mode == 'THREADING_MODE_AUTO_DETECT':
|
||||
info_text = "Auto-detect " + str(aprops.num_threads_auto_detect) + " threads"
|
||||
elif aprops.threading_mode == 'THREADING_MODE_FIXED':
|
||||
info_text = "Fixed " + str(aprops.num_threads_fixed) + " threads"
|
||||
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text=info_text)
|
||||
|
||||
if aprops.multithreading_expanded:
|
||||
column = box.column()
|
||||
split = column.split(align=True)
|
||||
|
||||
column_left = split.column(align=True)
|
||||
row = column_left.row(align=True)
|
||||
row.prop(aprops, "threading_mode", expand=True)
|
||||
row = column_left.row(align=True)
|
||||
if aprops.threading_mode == 'THREADING_MODE_AUTO_DETECT':
|
||||
row.enabled = False
|
||||
row.prop(aprops, "num_threads_auto_detect")
|
||||
elif aprops.threading_mode == 'THREADING_MODE_FIXED':
|
||||
row.prop(aprops, "num_threads_fixed")
|
||||
|
||||
column = box.column()
|
||||
column.prop(aprops, "enable_fracture_optimization")
|
||||
|
||||
# Performance and optimization settings are hidden from the UI.
|
||||
# These should always be enabled for performance.
|
||||
"""
|
||||
column = self.layout.column(align=True)
|
||||
column.separator()
|
||||
column.label(text="Performance and Optimization:")
|
||||
column.prop(aprops, "enable_asynchronous_meshing")
|
||||
column.prop(aprops, "precompute_static_obstacles")
|
||||
column.prop(aprops, "reserve_temporary_grids")
|
||||
"""
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(aprops, "warnings_and_errors_expanded",
|
||||
icon="TRIA_DOWN" if aprops.warnings_and_errors_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Warnings and Errors:")
|
||||
|
||||
if aprops.warnings_and_errors_expanded:
|
||||
column = box.column(align=True)
|
||||
column.prop(aprops, "disable_changing_topology_warning")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypeAdvancedPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypeAdvancedPanel)
|
||||
@@ -0,0 +1,166 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy, math
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
def format_bytes(self, num):
|
||||
# Method adapted from: http://stackoverflow.com/a/10171475
|
||||
unit_list = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB']
|
||||
decimal_list = [0, 0, 1, 2, 2, 2]
|
||||
|
||||
if num > 1:
|
||||
exponent = min(int(math.log(num, 1024)), len(unit_list) - 1)
|
||||
quotient = float(num) / 1024**exponent
|
||||
unit, num_decimals = unit_list[exponent], decimal_list[exponent]
|
||||
format_string = '{:.%sf} {}' % (num_decimals)
|
||||
return format_string.format(quotient, unit)
|
||||
if num == 0:
|
||||
return '0 bytes'
|
||||
if num == 1:
|
||||
return '1 byte'
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypeCachePanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Cache"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
domain_object = vcu.get_active_object(context)
|
||||
dprops = domain_object.flip_fluid.domain
|
||||
cprops = dprops.cache
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(cprops, "cache_directory_expanded",
|
||||
icon="TRIA_DOWN" if cprops.cache_directory_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Cache Directory:")
|
||||
|
||||
if not cprops.cache_directory_expanded:
|
||||
row = row.row()
|
||||
row.prop(cprops, "cache_directory")
|
||||
|
||||
if cprops.cache_directory_expanded:
|
||||
column = box.column(align=True)
|
||||
subcolumn = column.column(align=True)
|
||||
subcolumn.enabled = not dprops.bake.is_simulation_running
|
||||
row = subcolumn.row(align=True)
|
||||
row.prop(cprops, "cache_directory")
|
||||
row.operator("flip_fluid_operators.increase_decrease_cache_directory", text="", icon="REMOVE").increment_mode = "DECREASE"
|
||||
row.operator("flip_fluid_operators.increase_decrease_cache_directory", text="", icon="ADD").increment_mode = "INCREASE"
|
||||
|
||||
row = column.row(align=True)
|
||||
row.operator("flip_fluid_operators.relative_cache_directory")
|
||||
row.operator("flip_fluid_operators.absolute_cache_directory")
|
||||
row.operator("flip_fluid_operators.match_filename_cache_directory")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(cprops, "link_exported_geometry_expanded",
|
||||
icon="TRIA_DOWN" if cprops.link_exported_geometry_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Link existing exported geometry:")
|
||||
|
||||
if cprops.link_exported_geometry_expanded:
|
||||
column = box.column(align=True)
|
||||
subcolumn = column.column(align=True)
|
||||
subcolumn.enabled = not dprops.bake.is_simulation_running
|
||||
subcolumn.prop(cprops, "linked_geometry_directory")
|
||||
row = column.row(align=True)
|
||||
row.operator("flip_fluid_operators.relative_linked_geometry_directory")
|
||||
row.operator("flip_fluid_operators.absolute_linked_geometry_directory")
|
||||
column = column.column(align=True)
|
||||
column.operator("flip_fluid_operators.clear_linked_geometry_directory")
|
||||
column.separator()
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(cprops, "cache_operators_expanded",
|
||||
icon="TRIA_DOWN" if cprops.cache_operators_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Cache Operators:")
|
||||
|
||||
if not cprops.cache_operators_expanded:
|
||||
if dprops.stats.is_cache_info_available:
|
||||
free_text = "Free (" + format_bytes(self, dprops.stats.cache_bytes.get()) + ")"
|
||||
else:
|
||||
free_text = "Free"
|
||||
row.operator("flip_fluid_operators.free_cache", text=free_text)
|
||||
|
||||
|
||||
if cprops.cache_operators_expanded:
|
||||
column = box.column(align=True)
|
||||
|
||||
# The move, rename, and copy cache operations should not be performed
|
||||
# in Blender and are removed from the UI. There is a potential for Blender
|
||||
# to crash, which could lead to loss of data. It is best to perform these
|
||||
# operations through the OS filesystem which is cabable of handling failures.
|
||||
"""
|
||||
row = column.row(align=True)
|
||||
row.operator("flip_fluid_operators.move_cache", text="Move")
|
||||
row.prop(cprops, "move_cache_directory")
|
||||
|
||||
row = column.row(align=True)
|
||||
row.operator("flip_fluid_operators.rename_cache", text="Rename")
|
||||
row.prop(cprops, "rename_cache_directory")
|
||||
|
||||
row = column.row(align=True)
|
||||
row.operator("flip_fluid_operators.copy_cache", text="Copy")
|
||||
row.prop(cprops, "copy_cache_directory")
|
||||
"""
|
||||
|
||||
if dprops.stats.is_cache_info_available:
|
||||
free_text = "Free (" + format_bytes(self, dprops.stats.cache_bytes.get()) + ")"
|
||||
else:
|
||||
free_text = "Free"
|
||||
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_left.operator("flip_fluid_operators.free_cache", text=free_text)
|
||||
column_right.prop(cprops, "clear_cache_directory_logs", text="Free log files")
|
||||
column_right.prop(cprops, "clear_cache_directory_export", text="Free export files")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypeCachePanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypeCachePanel)
|
||||
@@ -0,0 +1,204 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypeDebugPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Debug"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
gprops = obj.flip_fluid.domain.debug
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(gprops, "grid_display_settings_expanded",
|
||||
icon="TRIA_DOWN" if gprops.grid_display_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
if not gprops.grid_display_settings_expanded:
|
||||
row.prop(gprops, "display_simulation_grid", text="")
|
||||
row.label(text="Grid Visualization:")
|
||||
|
||||
if gprops.grid_display_settings_expanded:
|
||||
split = vcu.ui_split(box, align=True, factor=0.3)
|
||||
column = split.column(align=True)
|
||||
column.prop(gprops, "display_simulation_grid", text="Display Grid")
|
||||
|
||||
column = split.column(align=True)
|
||||
column.enabled = gprops.display_simulation_grid
|
||||
split = column.split(align=True)
|
||||
column = split.column(align=True)
|
||||
column.prop(gprops, "grid_display_mode", text="")
|
||||
column = split.column(align=True)
|
||||
column.prop(gprops, "grid_display_scale", text="Draw Scale")
|
||||
|
||||
split = vcu.ui_split(box, align=True, factor=0.3)
|
||||
column = split.column(align=True)
|
||||
column.enabled = gprops.display_simulation_grid
|
||||
column.label(text="Enabled Grids:")
|
||||
column.label(text="Grid Colors:")
|
||||
column.label(text="Grid Offsets:")
|
||||
column = split.column(align=True)
|
||||
column.enabled = gprops.display_simulation_grid
|
||||
row = column.row(align=True)
|
||||
row.prop(gprops, "enabled_debug_grids", text="", toggle=True)
|
||||
row = column.row(align=True)
|
||||
row.prop(gprops, "x_grid_color", text="")
|
||||
row.prop(gprops, "y_grid_color", text="")
|
||||
row.prop(gprops, "z_grid_color", text="")
|
||||
row = column.row(align=True)
|
||||
row.prop(gprops, "debug_grid_offsets", text="", slider=True)
|
||||
column.prop(gprops, "snap_offsets_to_grid")
|
||||
|
||||
column = box.column(align=True)
|
||||
split = vcu.ui_split(column, align=True, factor=0.3)
|
||||
column = split.column(align=True)
|
||||
column.prop(gprops, "display_domain_bounds")
|
||||
|
||||
column = split.column(align=True)
|
||||
column.enabled = gprops.display_simulation_grid or gprops.display_domain_bounds
|
||||
column.prop(gprops, "domain_bounds_color", text="")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(gprops, "particle_debug_settings_expanded",
|
||||
icon="TRIA_DOWN" if gprops.particle_debug_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
if not gprops.particle_debug_settings_expanded:
|
||||
row.prop(gprops, "enable_fluid_particle_debug_output", text="")
|
||||
row.label(text="Fluid Particle Debugging:")
|
||||
|
||||
next_row = row.row()
|
||||
next_row.alignment = 'RIGHT'
|
||||
next_row.prop(gprops, "fluid_particles_visibility",
|
||||
text="",
|
||||
icon=vcu.get_hide_off_icon() if gprops.fluid_particles_visibility else vcu.get_hide_on_icon(),
|
||||
emboss=False
|
||||
)
|
||||
|
||||
|
||||
if gprops.particle_debug_settings_expanded:
|
||||
box.prop(gprops, "enable_fluid_particle_debug_output")
|
||||
column = box.column(align=True)
|
||||
column.enabled = gprops.enable_fluid_particle_debug_output
|
||||
column.label(text="Particle Display Settings:")
|
||||
row = column.row(align=True)
|
||||
row.prop(gprops, "min_gradient_speed")
|
||||
row.prop(gprops, "max_gradient_speed")
|
||||
row = column.row(align=True)
|
||||
row.prop(gprops, "low_speed_particle_color", text="")
|
||||
row.prop(gprops, "high_speed_particle_color", text="")
|
||||
row = column.row(align=True)
|
||||
row.prop(gprops, "fluid_particle_gradient_mode", expand=True)
|
||||
|
||||
column = box.column()
|
||||
column.enabled = gprops.enable_fluid_particle_debug_output
|
||||
split = vcu.ui_split(column, factor=0.33)
|
||||
column = split.column()
|
||||
column.label(text="Particle Size:")
|
||||
column.label(text="Draw Bounds:")
|
||||
column = split.column()
|
||||
column.prop(gprops, "particle_size", text="")
|
||||
column.prop_search(gprops, "particle_draw_aabb", bpy.data, "objects", text="")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(gprops, "force_field_debug_settings_expanded",
|
||||
icon="TRIA_DOWN" if gprops.force_field_debug_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
if not gprops.force_field_debug_settings_expanded:
|
||||
row.prop(gprops, "export_force_field", text="")
|
||||
row.label(text="Force Field Debugging:")
|
||||
|
||||
next_row = row.row()
|
||||
next_row.alignment = 'RIGHT'
|
||||
next_row.prop(gprops, "force_field_visibility",
|
||||
text="",
|
||||
icon=vcu.get_hide_off_icon() if gprops.force_field_visibility else vcu.get_hide_on_icon(),
|
||||
emboss=False
|
||||
)
|
||||
|
||||
if gprops.force_field_debug_settings_expanded:
|
||||
box.prop(gprops, "export_force_field")
|
||||
column = box.column(align=True)
|
||||
column.enabled = gprops.export_force_field
|
||||
column.label(text="Force Field Display Settings:")
|
||||
|
||||
row = column.row(align=True)
|
||||
row.prop(gprops, "min_gradient_force")
|
||||
row.prop(gprops, "max_gradient_force")
|
||||
row = column.row(align=True)
|
||||
row.prop(gprops, "low_force_field_color", text="")
|
||||
row.prop(gprops, "high_force_field_color", text="")
|
||||
row = column.row(align=True)
|
||||
row.prop(gprops, "force_field_gradient_mode", expand=True)
|
||||
|
||||
column = box.column()
|
||||
column.enabled = gprops.export_force_field
|
||||
split = vcu.ui_split(column, factor=0.33)
|
||||
column = split.column()
|
||||
column.label(text="Display Amount:")
|
||||
column.label(text="Line Size:")
|
||||
column = split.column()
|
||||
|
||||
column.prop(gprops, "force_field_display_amount", text="")
|
||||
column.prop(gprops, "force_field_line_size", text="")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(gprops, "export_internal_obstacle_mesh")
|
||||
next_row = row.row()
|
||||
next_row.alignment = 'RIGHT'
|
||||
next_row.prop(gprops, "internal_obstacle_mesh_visibility",
|
||||
text="",
|
||||
icon=vcu.get_hide_off_icon() if gprops.internal_obstacle_mesh_visibility else vcu.get_hide_on_icon(),
|
||||
emboss=False
|
||||
)
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column(align=True)
|
||||
column.prop(gprops, "display_render_passes_console_output")
|
||||
column.prop(gprops, "display_console_output")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypeDebugPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypeDebugPanel)
|
||||
@@ -0,0 +1,464 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..operators import helper_operators
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
def draw_simulation_display_settings(self, context):
|
||||
domain_object = vcu.get_active_object(context)
|
||||
rprops = domain_object.flip_fluid.domain.render
|
||||
scene_props = context.scene.flip_fluid
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column()
|
||||
|
||||
row = column.row(align=True)
|
||||
row.prop(rprops, "simulation_display_settings_expanded",
|
||||
icon="TRIA_DOWN" if rprops.simulation_display_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
|
||||
row.label(text="Simulation Visibility:")
|
||||
if not scene_props.show_viewport or not scene_props.show_render:
|
||||
visibility_text = ""
|
||||
if not scene_props.show_viewport and not scene_props.show_render:
|
||||
visibility_text += "Disabled in Viewport + Render"
|
||||
elif not scene_props.show_viewport:
|
||||
visibility_text += "Disabled in Viewport"
|
||||
elif not scene_props.show_render:
|
||||
visibility_text += "Disabled in Render"
|
||||
|
||||
row = row.row(align=True)
|
||||
row.alert = True
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text=visibility_text, icon="CANCEL")
|
||||
|
||||
if scene_props.show_viewport and scene_props.show_render:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Visibility Enabled", icon="CHECKMARK")
|
||||
|
||||
if not rprops.simulation_display_settings_expanded:
|
||||
return
|
||||
|
||||
split = vcu.ui_split(column)
|
||||
column_left = split.column()
|
||||
column_left.prop(scene_props, "show_render", text="Show In Render", icon="RESTRICT_RENDER_OFF")
|
||||
|
||||
column_right = split.column()
|
||||
column_right.prop(scene_props, "show_viewport", text="Show In Viewport", icon="RESTRICT_VIEW_OFF")
|
||||
|
||||
|
||||
def draw_surface_display_settings(self, context, menu_expand_prop_group=None):
|
||||
domain_object = vcu.get_active_object(context)
|
||||
rprops = domain_object.flip_fluid.domain.render
|
||||
mprops = domain_object.flip_fluid.domain.materials
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column()
|
||||
|
||||
row = column.row(align=True)
|
||||
row.prop(menu_expand_prop_group, "surface_display_settings_expanded",
|
||||
icon="TRIA_DOWN" if getattr(menu_expand_prop_group, "surface_display_settings_expanded") else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Surface Display and Render:")
|
||||
|
||||
if not getattr(menu_expand_prop_group, "surface_display_settings_expanded"):
|
||||
info_text = ""
|
||||
if rprops.render_display == 'DISPLAY_FINAL':
|
||||
info_text += "Render Final"
|
||||
elif rprops.render_display == 'DISPLAY_PREVIEW':
|
||||
info_text += "Render Preview"
|
||||
elif rprops.render_display == 'DISPLAY_NONE':
|
||||
info_text += "Render None"
|
||||
info_text += " / "
|
||||
if rprops.viewport_display == 'DISPLAY_FINAL':
|
||||
info_text += "View Final"
|
||||
elif rprops.viewport_display == 'DISPLAY_PREVIEW':
|
||||
info_text += "View Preview"
|
||||
elif rprops.viewport_display == 'DISPLAY_NONE':
|
||||
info_text += "View None"
|
||||
row = row.row(align=True)
|
||||
row.alignment='RIGHT'
|
||||
row.label(text=info_text)
|
||||
return
|
||||
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column()
|
||||
column_left.label(text="Render Display Mode:")
|
||||
column_left.prop(rprops, "render_display", expand=True)
|
||||
|
||||
column_right = split.column()
|
||||
column_right.label(text="Viewport Display Mode:")
|
||||
column_right.prop(rprops, "viewport_display", expand=True)
|
||||
|
||||
column_left.label(text="Surface Material:")
|
||||
column_right.prop(mprops, "surface_material", text="")
|
||||
|
||||
|
||||
def draw_fluid_particle_display_settings(self, context, menu_expand_prop_group=None):
|
||||
domain_object = vcu.get_active_object(context)
|
||||
dprops = domain_object.flip_fluid.domain
|
||||
rprops = domain_object.flip_fluid.domain.render
|
||||
mprops = domain_object.flip_fluid.domain.materials
|
||||
is_fluid_particles_enabled = domain_object.flip_fluid.domain.particles.enable_fluid_particle_output
|
||||
|
||||
box = self.layout.box()
|
||||
column = box.column()
|
||||
|
||||
if is_fluid_particles_enabled:
|
||||
row = column.row(align=True)
|
||||
row.prop(menu_expand_prop_group, "fluid_particle_display_settings_expanded",
|
||||
icon="TRIA_DOWN" if getattr(menu_expand_prop_group, "fluid_particle_display_settings_expanded") else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Fluid Particle Display and Render:")
|
||||
else:
|
||||
split = column.split()
|
||||
left_column = split.column()
|
||||
row = left_column.row(align=True)
|
||||
row.prop(menu_expand_prop_group, "fluid_particle_display_settings_expanded",
|
||||
icon="TRIA_DOWN" if getattr(menu_expand_prop_group, "fluid_particle_display_settings_expanded") else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Fluid Particle Display and Render:")
|
||||
|
||||
right_column = split.column()
|
||||
row = right_column.row()
|
||||
row.alignment = 'RIGHT'
|
||||
c = row.row(align=True)
|
||||
c.alignment = 'RIGHT'
|
||||
c.enabled = False
|
||||
c.label(text="Enable in 'Particles' panel")
|
||||
row.operator("flip_fluid_operators.display_enable_fluid_particles_tooltip",
|
||||
text="", icon="QUESTION", emboss=False)
|
||||
|
||||
if not getattr(menu_expand_prop_group, "fluid_particle_display_settings_expanded"):
|
||||
if is_fluid_particles_enabled:
|
||||
info_text = ""
|
||||
if rprops.fluid_particle_render_display == 'DISPLAY_FINAL':
|
||||
info_text += "Render Final"
|
||||
elif rprops.fluid_particle_render_display == 'DISPLAY_PREVIEW':
|
||||
info_text += "Render Preview"
|
||||
elif rprops.fluid_particle_render_display == 'DISPLAY_NONE':
|
||||
info_text += "Render None"
|
||||
info_text += " / "
|
||||
if rprops.fluid_particle_viewport_display == 'DISPLAY_FINAL':
|
||||
info_text += "View Final"
|
||||
elif rprops.fluid_particle_viewport_display == 'DISPLAY_PREVIEW':
|
||||
info_text += "View Preview"
|
||||
elif rprops.fluid_particle_viewport_display == 'DISPLAY_NONE':
|
||||
info_text += "View None"
|
||||
row = row.row(align=True)
|
||||
row.alignment='RIGHT'
|
||||
row.label(text=info_text)
|
||||
return
|
||||
|
||||
if getattr(menu_expand_prop_group, "fluid_particle_display_settings_expanded"):
|
||||
subbox = box.box()
|
||||
column = subbox.column(align=True)
|
||||
column.enabled = is_fluid_particles_enabled
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column()
|
||||
column_left.label(text="Render Display Mode:")
|
||||
column_left.prop(rprops, "fluid_particle_render_display", expand=True)
|
||||
|
||||
column_right = split.column()
|
||||
column_right.label(text="Viewport Display Mode:")
|
||||
column_right.prop(rprops, "fluid_particle_viewport_display", expand=True)
|
||||
|
||||
subbox = box.box()
|
||||
column = subbox.column(align=True)
|
||||
column.enabled = is_fluid_particles_enabled
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column(align=True)
|
||||
column_left.label(text="Final Display Amount:")
|
||||
column_left.prop(rprops, "render_fluid_particle_surface_pct", slider=True)
|
||||
column_left.prop(rprops, "render_fluid_particle_boundary_pct", slider=True)
|
||||
column_left.prop(rprops, "render_fluid_particle_interior_pct", slider=True)
|
||||
|
||||
column_right = split.column(align=True)
|
||||
column_right.label(text="Preview Display Amount:")
|
||||
column_right.prop(rprops, "viewport_fluid_particle_surface_pct", slider=True)
|
||||
column_right.prop(rprops, "viewport_fluid_particle_boundary_pct", slider=True)
|
||||
column_right.prop(rprops, "viewport_fluid_particle_interior_pct", slider=True)
|
||||
|
||||
bl_fluid_particles_mesh_cache = dprops.mesh_cache.particles.get_cache_object()
|
||||
|
||||
subbox = box.box()
|
||||
subbox.enabled = is_fluid_particles_enabled
|
||||
column = subbox.column(align=True)
|
||||
column.label(text="Particle Display Settings:")
|
||||
column.separator()
|
||||
|
||||
bl_mod = get_motion_blur_geometry_node_modifier(bl_fluid_particles_mesh_cache)
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.label(text="Fluid Particles:")
|
||||
if is_fluid_particles_enabled:
|
||||
draw_fluid_particles_motion_blur_geometry_node_properties(row, bl_mod)
|
||||
else:
|
||||
row.label(text="Enable Fluid Particle feature to view full particle settings", icon='INFO')
|
||||
|
||||
subbox = box.box()
|
||||
column = subbox.column(align=True)
|
||||
column.enabled = is_fluid_particles_enabled
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column()
|
||||
column_right = split.column()
|
||||
column_left.label(text="Fluid Particle Material:")
|
||||
column_right.prop(mprops, "fluid_particles_material", text="")
|
||||
|
||||
|
||||
def get_motion_blur_geometry_node_modifier(bl_object):
|
||||
if bl_object is None:
|
||||
return None
|
||||
for mod in bl_object.modifiers:
|
||||
if mod.type == "NODES" and mod.node_group and mod.node_group.name.startswith("FF_GeometryNodes"):
|
||||
return mod
|
||||
|
||||
|
||||
def draw_whitewater_particles_motion_blur_geometry_node_properties(ui_row, bl_mod):
|
||||
if bl_mod is None:
|
||||
ui_row.alert = True
|
||||
ui_row.operator(
|
||||
"flip_fluid_operators.helper_initialize_cache_objects",
|
||||
text="Initialize Geometry Nodes - Missing FF_GeometryNodesWhitewater modifier",
|
||||
icon="ERROR"
|
||||
).cache_object_type = 'CACHE_OBJECT_TYPE_WHITEWATER_PARTICLES'
|
||||
return
|
||||
|
||||
ui_row.alignment = 'LEFT'
|
||||
if "Input_6" in bl_mod:
|
||||
ui_row.prop(bl_mod, '["Input_6"]', text="Scale")
|
||||
if "Input_4" in bl_mod:
|
||||
ui_row.prop(bl_mod, '["Input_4"]', text="Blur Scale")
|
||||
if "Input_8" in bl_mod:
|
||||
ui_row.prop(bl_mod, '["Input_8"]', text="Motion Blur")
|
||||
if "Input_9" in bl_mod:
|
||||
ui_row.prop(bl_mod, '["Input_9"]', text="Point Cloud")
|
||||
|
||||
|
||||
def draw_fluid_particles_motion_blur_geometry_node_properties(ui_row, bl_mod):
|
||||
if bl_mod is None:
|
||||
ui_row.alert = True
|
||||
ui_row.operator(
|
||||
"flip_fluid_operators.helper_initialize_cache_objects",
|
||||
text="Initialize Geometry Nodes - Missing FF_GeometryNodesFluidParticles modifier",
|
||||
icon="ERROR"
|
||||
).cache_object_type = 'CACHE_OBJECT_TYPE_FLUID_PARTICLES'
|
||||
return
|
||||
|
||||
ui_row.alignment = 'LEFT'
|
||||
if "Input_6" in bl_mod:
|
||||
ui_row.prop(bl_mod, '["Input_6"]', text="Scale")
|
||||
if "Input_4" in bl_mod:
|
||||
ui_row.prop(bl_mod, '["Input_4"]', text="Blur Scale")
|
||||
if "Input_8" in bl_mod:
|
||||
ui_row.prop(bl_mod, '["Input_8"]', text="Motion Blur")
|
||||
if "Input_9" in bl_mod:
|
||||
ui_row.prop(bl_mod, '["Input_9"]', text="Point Cloud")
|
||||
|
||||
|
||||
def draw_whitewater_display_settings(self, context, menu_expand_prop_group=None):
|
||||
obj = vcu.get_active_object(context)
|
||||
dprops = obj.flip_fluid.domain
|
||||
rprops = dprops.render
|
||||
is_whitewater_enabled = dprops.whitewater.enable_whitewater_simulation
|
||||
|
||||
master_box = self.layout.box()
|
||||
column = master_box.column()
|
||||
|
||||
if is_whitewater_enabled:
|
||||
row = column.row(align=True)
|
||||
row.prop(menu_expand_prop_group, "whitewater_display_settings_expanded",
|
||||
icon="TRIA_DOWN" if getattr(menu_expand_prop_group, "whitewater_display_settings_expanded") else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Whitewater Display and Render:")
|
||||
else:
|
||||
split = column.split()
|
||||
left_column = split.column()
|
||||
row = left_column.row(align=True)
|
||||
row.prop(menu_expand_prop_group, "whitewater_display_settings_expanded",
|
||||
icon="TRIA_DOWN" if getattr(menu_expand_prop_group, "whitewater_display_settings_expanded") else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Whitewater Display and Render:")
|
||||
|
||||
right_column = split.column()
|
||||
row = right_column.row()
|
||||
row.alignment = 'RIGHT'
|
||||
c = row.row(align=True)
|
||||
c.alignment = 'RIGHT'
|
||||
c.enabled = False
|
||||
c.label(text="Enable in 'Whitewater' panel")
|
||||
row.operator("flip_fluid_operators.display_enable_whitewater_tooltip",
|
||||
text="", icon="QUESTION", emboss=False)
|
||||
|
||||
if not getattr(menu_expand_prop_group, "whitewater_display_settings_expanded"):
|
||||
if is_whitewater_enabled:
|
||||
info_text = ""
|
||||
if rprops.whitewater_render_display == 'DISPLAY_FINAL':
|
||||
info_text += "Render Final"
|
||||
elif rprops.whitewater_render_display == 'DISPLAY_PREVIEW':
|
||||
info_text += "Render Preview"
|
||||
elif rprops.whitewater_render_display == 'DISPLAY_NONE':
|
||||
info_text += "Render None"
|
||||
info_text += " / "
|
||||
if rprops.whitewater_viewport_display == 'DISPLAY_FINAL':
|
||||
info_text += "View Final"
|
||||
elif rprops.whitewater_viewport_display == 'DISPLAY_PREVIEW':
|
||||
info_text += "View Preview"
|
||||
elif rprops.whitewater_viewport_display == 'DISPLAY_NONE':
|
||||
info_text += "View None"
|
||||
row = row.row(align=True)
|
||||
row.alignment='RIGHT'
|
||||
row.label(text=info_text)
|
||||
return
|
||||
|
||||
if getattr(menu_expand_prop_group, "whitewater_display_settings_expanded"):
|
||||
box = master_box.box()
|
||||
box.enabled = is_whitewater_enabled
|
||||
|
||||
column = box.column(align=True)
|
||||
split = column.split()
|
||||
column = split.column(align=True)
|
||||
column.label(text="Render Display Mode:")
|
||||
column.prop(rprops, "whitewater_render_display", expand=True)
|
||||
|
||||
column = split.column(align=True)
|
||||
column.label(text="Viewport Display Mode:")
|
||||
column.prop(rprops, "whitewater_viewport_display", expand=True)
|
||||
|
||||
# Whitewater motion blur rendering is currently too resource intensive
|
||||
# for Blender Cycles
|
||||
"""
|
||||
column = box.column()
|
||||
column.label(text="Motion Blur:")
|
||||
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column()
|
||||
column_left.prop(rprops, "render_whitewater_motion_blur")
|
||||
|
||||
column_right = split.column()
|
||||
column_right.prop(rprops, "whitewater_motion_blur_scale")
|
||||
"""
|
||||
|
||||
box = master_box.box()
|
||||
box.enabled = is_whitewater_enabled
|
||||
|
||||
column = box.column(align=True)
|
||||
split = column.split()
|
||||
column = split.column(align=True)
|
||||
column.label(text="Final Display Amount:")
|
||||
column.prop(rprops, "render_foam_pct", slider=True)
|
||||
column.prop(rprops, "render_bubble_pct", slider=True)
|
||||
column.prop(rprops, "render_spray_pct", slider=True)
|
||||
column.prop(rprops, "render_dust_pct", slider=True)
|
||||
|
||||
column = split.column(align=True)
|
||||
column.label(text="Preview Display Amount:")
|
||||
column.prop(rprops, "viewport_foam_pct", slider=True)
|
||||
column.prop(rprops, "viewport_bubble_pct", slider=True)
|
||||
column.prop(rprops, "viewport_spray_pct", slider=True)
|
||||
column.prop(rprops, "viewport_dust_pct", slider=True)
|
||||
|
||||
box = master_box.box()
|
||||
box.enabled = is_whitewater_enabled
|
||||
|
||||
column = box.column(align=True)
|
||||
column.label(text="Particle Display Settings:")
|
||||
|
||||
column.separator()
|
||||
split = vcu.ui_split(column, factor=0.1)
|
||||
column1 = split.column(align=True)
|
||||
column2 = split.column(align=True)
|
||||
|
||||
whitewater_labels = ["Foam:", "Bubble:", "Spray:", "Dust:"]
|
||||
mesh_cache_objects = [
|
||||
dprops.mesh_cache.foam.get_cache_object(),
|
||||
dprops.mesh_cache.bubble.get_cache_object(),
|
||||
dprops.mesh_cache.spray.get_cache_object(),
|
||||
dprops.mesh_cache.dust.get_cache_object()
|
||||
]
|
||||
|
||||
for idx, bl_object in enumerate(mesh_cache_objects):
|
||||
bl_mod = get_motion_blur_geometry_node_modifier(bl_object)
|
||||
row = column1.row(align=True)
|
||||
row.label(text=whitewater_labels[idx])
|
||||
row = column2.row(align=True)
|
||||
if is_whitewater_enabled:
|
||||
draw_whitewater_particles_motion_blur_geometry_node_properties(row, bl_mod)
|
||||
else:
|
||||
row.label(text="Enable Whitewater feature to view full particle settings", icon='INFO')
|
||||
|
||||
box = master_box.box()
|
||||
box.enabled = is_whitewater_enabled
|
||||
|
||||
mprops = dprops.materials
|
||||
column = box.column(align=True)
|
||||
column.label(text="Particle Materials:")
|
||||
column.prop(mprops, "whitewater_foam_material", text="Foam")
|
||||
column.prop(mprops, "whitewater_bubble_material", text="Bubble")
|
||||
column.prop(mprops, "whitewater_spray_material", text="Spray")
|
||||
column.prop(mprops, "whitewater_dust_material", text="Dust")
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypeDisplayPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Display and Render Settings"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
domain_object = vcu.get_active_object(context)
|
||||
rprops = domain_object.flip_fluid.domain.render
|
||||
|
||||
draw_simulation_display_settings(self, context)
|
||||
draw_surface_display_settings(self, context, rprops)
|
||||
draw_fluid_particle_display_settings(self, context, rprops)
|
||||
draw_whitewater_display_settings(self, context, rprops)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypeDisplayPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypeDisplayPanel)
|
||||
@@ -0,0 +1,69 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..materials import material_library
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypeMaterialsPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Materials"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
def draw(self, context):
|
||||
if not material_library.is_material_library_available():
|
||||
self.layout.label(text="This feature is missing data and will be disabled.")
|
||||
self.layout.label(text="Please contact the developers if you think this is an error.")
|
||||
return
|
||||
|
||||
obj = vcu.get_active_object(context)
|
||||
mprops = obj.flip_fluid.domain.materials
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(mprops, "surface_material", text="Surface")
|
||||
column.prop(mprops, "fluid_particles_material", text="Fluid Particles")
|
||||
column.prop(mprops, "whitewater_foam_material", text="Foam")
|
||||
column.prop(mprops, "whitewater_bubble_material", text="Bubble")
|
||||
column.prop(mprops, "whitewater_spray_material", text="Spray")
|
||||
column.prop(mprops, "whitewater_dust_material", text="Dust")
|
||||
|
||||
self.layout.row().separator()
|
||||
row = self.layout.row(align = True)
|
||||
row.prop(mprops, "material_import", text = "")
|
||||
column = row.column(align=True)
|
||||
column.operator("flip_fluid_operators.import_material")
|
||||
column.operator("flip_fluid_operators.import_material_copy")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypeMaterialsPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypeMaterialsPanel)
|
||||
@@ -0,0 +1,275 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..operators import helper_operators
|
||||
from ..ui import domain_display_ui
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
from ..utils import installation_utils
|
||||
|
||||
|
||||
def _draw_fluid_particle_display_settings(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
dprops = obj.flip_fluid.domain
|
||||
domain_display_ui.draw_fluid_particle_display_settings(self, context, dprops.particles)
|
||||
|
||||
|
||||
def _draw_geometry_attributes_menu(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
pprops = obj.flip_fluid.domain.particles
|
||||
sprops = obj.flip_fluid.domain.surface
|
||||
|
||||
#
|
||||
# Geometry Attributes
|
||||
#
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(pprops, "geometry_attributes_expanded",
|
||||
icon="TRIA_DOWN" if pprops.geometry_attributes_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Fluid Particle Attributes:")
|
||||
|
||||
if pprops.geometry_attributes_expanded:
|
||||
if not vcu.is_blender_31():
|
||||
column = box.column(align=True)
|
||||
column.enabled = False
|
||||
column.label(text="Geometry attribute features for fluid particles are only available in", icon='ERROR')
|
||||
column.label(text="Blender 3.1 or later", icon='ERROR')
|
||||
return
|
||||
|
||||
#
|
||||
# Velocity Attributes
|
||||
#
|
||||
subbox = box.box()
|
||||
row = subbox.row(align=True)
|
||||
row.prop(pprops, "velocity_attributes_expanded",
|
||||
icon="TRIA_DOWN" if pprops.velocity_attributes_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Velocity Based Attributes:")
|
||||
|
||||
if pprops.velocity_attributes_expanded:
|
||||
column = subbox.column(align=True)
|
||||
column.prop(pprops, "enable_fluid_particle_velocity_vector_attribute", text="Velocity Attributes")
|
||||
column.prop(pprops, "enable_fluid_particle_speed_attribute", text="Speed Attributes")
|
||||
column.prop(pprops, "enable_fluid_particle_vorticity_vector_attribute", text="Vorticity Attributes")
|
||||
column.operator("flip_fluid_operators.helper_initialize_motion_blur")
|
||||
else:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(pprops, "enable_fluid_particle_velocity_vector_attribute", text="Velocity")
|
||||
row.prop(pprops, "enable_fluid_particle_speed_attribute", text="Speed")
|
||||
row.prop(pprops, "enable_fluid_particle_vorticity_vector_attribute", text="Vorticity")
|
||||
|
||||
#
|
||||
# Color Attributes
|
||||
#
|
||||
subbox = box.box()
|
||||
row = subbox.row(align=True)
|
||||
row.prop(pprops, "color_attributes_expanded",
|
||||
icon="TRIA_DOWN" if pprops.color_attributes_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Color and Mixing Attributes:")
|
||||
|
||||
if pprops.color_attributes_expanded:
|
||||
column = subbox.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_left.prop(pprops, "enable_fluid_particle_color_attribute", text="Color Attributes")
|
||||
|
||||
column = subbox.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_left.enabled = pprops.enable_fluid_particle_color_attribute
|
||||
column_left.prop(sprops, "enable_color_attribute_mixing", text="Enable Mixing")
|
||||
column_right.enabled = pprops.enable_fluid_particle_color_attribute and sprops.enable_color_attribute_mixing
|
||||
column_right.prop(sprops, "color_attribute_mixing_rate", text="Mix Rate", slider=True)
|
||||
|
||||
column = subbox.column(align=True)
|
||||
column.enabled = pprops.enable_fluid_particle_color_attribute and sprops.enable_color_attribute_mixing
|
||||
column.label(text="Mixing Mode:")
|
||||
row = column.row(align=True)
|
||||
row.enabled = pprops.enable_fluid_particle_color_attribute
|
||||
row.prop(sprops, "color_attribute_mixing_mode", expand=True)
|
||||
|
||||
if sprops.color_attribute_mixing_mode == 'COLOR_MIXING_MODE_MIXBOX':
|
||||
if not installation_utils.is_mixbox_supported():
|
||||
column.label(text="Mixbox feature is not supported", icon="ERROR")
|
||||
column.label(text="in this version of the FLIP Fluids Addon", icon="ERROR")
|
||||
|
||||
if installation_utils.is_mixbox_supported():
|
||||
if installation_utils.is_mixbox_installation_complete():
|
||||
column.label(text="Mixbox Plugin Status: Installed", icon="CHECKMARK")
|
||||
else:
|
||||
column.label(text="Install the Mixbox plugin in the", icon="INFO")
|
||||
column.label(text="FLIP Fluids Addon preferences", icon="INFO")
|
||||
column.operator(
|
||||
"flip_fluid_operators.open_preferences",
|
||||
text="Open Preferences", icon="PREFERENCES"
|
||||
).view_mode = 'PREFERENCES_MENU_VIEW_MIXBOX'
|
||||
else:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(pprops, "enable_fluid_particle_color_attribute", text="Color")
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.enabled = pprops.enable_fluid_particle_color_attribute
|
||||
row.prop(sprops, "enable_color_attribute_mixing", text="Mixing")
|
||||
|
||||
#
|
||||
# Other Attributes
|
||||
#
|
||||
subbox = box.box()
|
||||
row = subbox.row(align=True)
|
||||
row.prop(pprops, "other_attributes_expanded",
|
||||
icon="TRIA_DOWN" if pprops.other_attributes_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Other Attributes:")
|
||||
|
||||
if pprops.other_attributes_expanded:
|
||||
column = subbox.column(align=True)
|
||||
column.prop(pprops, "enable_fluid_particle_age_attribute", text="Age Attributes")
|
||||
row = column.row(align=True)
|
||||
row.prop(pprops, "enable_fluid_particle_lifetime_attribute", text="Lifetime Attributes")
|
||||
row.prop(sprops, "lifetime_attribute_death_time")
|
||||
column.prop(pprops, "enable_fluid_particle_whitewater_proximity_attribute", text="Whitewater Proximity Attributes")
|
||||
column.prop(pprops, "enable_fluid_particle_source_id_attribute", text="Source ID Attributes")
|
||||
row = column.row(align=True)
|
||||
row.prop(pprops, "enable_fluid_particle_uid_attribute", text="UID Attributes")
|
||||
row.prop(pprops, "enable_fluid_particle_uid_attribute_reuse", text="Reuse UIDs")
|
||||
else:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(pprops, "enable_fluid_particle_age_attribute", text="Age")
|
||||
row.prop(pprops, "enable_fluid_particle_lifetime_attribute", text="Life")
|
||||
row.prop(pprops, "enable_fluid_particle_whitewater_proximity_attribute", text="WW Prox.")
|
||||
row.prop(pprops, "enable_fluid_particle_source_id_attribute", text="Source ID")
|
||||
row.prop(pprops, "enable_fluid_particle_uid_attribute", text="UID")
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypeFluidParticlesPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Particles"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
dprops = obj.flip_fluid.domain
|
||||
pprops = obj.flip_fluid.domain.particles
|
||||
sprops = obj.flip_fluid.domain.surface
|
||||
|
||||
prefs = vcu.get_addon_preferences()
|
||||
if not prefs.is_extra_features_enabled():
|
||||
warn_box = self.layout.box()
|
||||
warn_column = warn_box.column(align=True)
|
||||
warn_column.enabled = True
|
||||
warn_column.label(text=" This feature is affected by a current bug in Blender.", icon='ERROR')
|
||||
warn_column.label(text=" The Extra Features option must be enabled in preferences")
|
||||
warn_column.label(text=" to use this feature.")
|
||||
warn_column.separator()
|
||||
warn_column.prop(prefs, "enable_extra_features", text="Enable Extra Features in Preferences")
|
||||
warn_column.separator()
|
||||
warn_column.operator(
|
||||
"wm.url_open",
|
||||
text="Important Info and Limitations",
|
||||
icon="WORLD"
|
||||
).url = "https://github.com/rlguy/Blender-FLIP-Fluids/wiki/Preferences-Menu-Settings#developer-tools"
|
||||
return
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(pprops, "fluid_particles_expanded",
|
||||
icon="TRIA_DOWN" if pprops.fluid_particles_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Fluid Particle Export:")
|
||||
|
||||
if not pprops.fluid_particles_expanded:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(pprops, "enable_fluid_particle_output")
|
||||
|
||||
if pprops.fluid_particles_expanded:
|
||||
column = box.column(align=True)
|
||||
column.prop(pprops, "enable_fluid_particle_output")
|
||||
subbox = column.box()
|
||||
subbox.enabled = pprops.enable_fluid_particle_output
|
||||
subcolumn = subbox.column(align=True)
|
||||
subcolumn.prop(pprops, "fluid_particle_output_amount", slider=True)
|
||||
subcolumn.prop(pprops, "enable_fluid_particle_surface_output")
|
||||
subcolumn.prop(pprops, "enable_fluid_particle_boundary_output")
|
||||
subcolumn.prop(pprops, "enable_fluid_particle_interior_output")
|
||||
subcolumn.separator()
|
||||
subcolumn.prop(pprops, "fluid_particle_source_id_blacklist", text="Skip Particles With Source ID Value")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(pprops, "fluid_particle_generation_expanded",
|
||||
icon="TRIA_DOWN" if pprops.fluid_particle_generation_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Fluid Particle Generation:")
|
||||
|
||||
aprops = dprops.advanced
|
||||
if not pprops.fluid_particle_generation_expanded:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(aprops, "jitter_surface_particles")
|
||||
|
||||
if pprops.fluid_particle_generation_expanded:
|
||||
column = box.column()
|
||||
row = column.row(align=True)
|
||||
row.prop(aprops, "particle_jitter_factor", slider=True)
|
||||
row.prop(aprops, "jitter_surface_particles")
|
||||
|
||||
_draw_fluid_particle_display_settings(self, context)
|
||||
_draw_geometry_attributes_menu(self, context)
|
||||
|
||||
self.layout.separator()
|
||||
column = self.layout.column(align=True)
|
||||
column.operator("flip_fluid_operators.helper_delete_particle_objects", icon="X")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypeFluidParticlesPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypeFluidParticlesPanel)
|
||||
@@ -0,0 +1,282 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..presets import preset_library
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypePresetsPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Presets"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN"
|
||||
|
||||
|
||||
def draw_preset_selector(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
dprops = obj.flip_fluid.domain
|
||||
preprops = dprops.presets
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(preprops, "enable_presets")
|
||||
|
||||
column = self.layout.column()
|
||||
column.enabled = preprops.enable_presets
|
||||
column.label(text="Preset Package:")
|
||||
column.prop(preprops, "current_package", text="")
|
||||
|
||||
current_package_info = \
|
||||
preset_library.package_identifier_to_info(preprops.current_package)
|
||||
|
||||
if current_package_info["use_custom_icons"]:
|
||||
column.label(text="Preset:")
|
||||
row = column.row()
|
||||
row.prop(preprops, "current_preset", text="")
|
||||
if preprops.current_preset != "PRESET_NONE":
|
||||
row.operator(
|
||||
"flip_fluid_operators.preset_display_info",
|
||||
text="",
|
||||
icon="INFO",
|
||||
)
|
||||
|
||||
row = column.row()
|
||||
subcol = row.column()
|
||||
subcol.scale_y = 6
|
||||
subcol.operator(
|
||||
"flip_fluid_operators.preset_select_previous",
|
||||
text="",
|
||||
icon="TRIA_LEFT"
|
||||
)
|
||||
|
||||
subcol = row.column()
|
||||
subcol.template_icon_view(preprops, "current_preset", show_labels=True)
|
||||
|
||||
subcol = row.column()
|
||||
subcol.scale_y = 6
|
||||
subcol.operator(
|
||||
"flip_fluid_operators.preset_select_next",
|
||||
text="",
|
||||
icon="TRIA_RIGHT"
|
||||
)
|
||||
|
||||
split = column.split()
|
||||
column_left = split.column()
|
||||
column_right = split.column()
|
||||
row_left = column_left.row()
|
||||
row_right = column_right.row()
|
||||
|
||||
row_left.alignment = 'LEFT'
|
||||
row_left.prop(preprops, "preview_preset", text="Auto Assign Preset")
|
||||
|
||||
if preprops.current_preset != "PRESET_NONE":
|
||||
is_on_stack = preprops.preset_stack.is_preset_in_stack(preprops.current_preset)
|
||||
op_text = "Added to Stack" if is_on_stack else "Add to Stack"
|
||||
|
||||
row_right.enabled = not is_on_stack
|
||||
row_right.alignment = 'RIGHT'
|
||||
row_right.operator(
|
||||
"flip_fluid_operators.preset_add_to_stack",
|
||||
text=op_text,
|
||||
)
|
||||
else:
|
||||
column.label(text="Preset:")
|
||||
row = column.row()
|
||||
row.prop(preprops, "current_preset", text="")
|
||||
if preprops.current_preset != "PRESET_NONE":
|
||||
row.operator(
|
||||
"flip_fluid_operators.preset_display_info",
|
||||
text="",
|
||||
icon="INFO",
|
||||
)
|
||||
|
||||
split = column.split()
|
||||
column_left = split.column()
|
||||
column_right = split.column()
|
||||
row_left = column_left.row()
|
||||
row_right = column_right.row()
|
||||
|
||||
row_left.alignment = 'LEFT'
|
||||
row_left.prop(preprops, "preview_preset", text="Auto Assign Preset")
|
||||
|
||||
if preprops.current_preset != "PRESET_NONE":
|
||||
is_on_stack = preprops.preset_stack.is_preset_in_stack(preprops.current_preset)
|
||||
op_text = "Added to Stack" if is_on_stack else "Add to Stack"
|
||||
|
||||
row_right.enabled = not is_on_stack
|
||||
row_right.alignment = 'RIGHT'
|
||||
row_right.operator(
|
||||
"flip_fluid_operators.preset_add_to_stack",
|
||||
text=op_text,
|
||||
)
|
||||
|
||||
|
||||
def draw_preset_stack(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
preprops = obj.flip_fluid.domain.presets
|
||||
|
||||
column = self.layout.column()
|
||||
column.enabled = preprops.enable_presets
|
||||
column.separator()
|
||||
column.separator()
|
||||
box = column.box()
|
||||
box.label(text="Preset Stack:")
|
||||
column = box.column(align=True)
|
||||
preset_icons = preset_library.get_custom_icons()
|
||||
if len(preprops.preset_stack.preset_stack) == 0:
|
||||
column.label(text="No presets loaded...")
|
||||
for pidx,p in enumerate(preprops.preset_stack.preset_stack):
|
||||
info = preset_library.preset_identifier_to_info(p.identifier)
|
||||
subbox = column.box()
|
||||
split = vcu.ui_split(subbox, factor=0.5, align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
row_left = column_left.row(align=True)
|
||||
row_right = column_right.row()
|
||||
|
||||
if "icon" in info and info['icon'] in preset_icons:
|
||||
row_left.label(text=info['name'], icon_value=preset_icons.get(info['icon']).icon_id)
|
||||
else:
|
||||
row_left.label(text=" "*5 + info['name'])
|
||||
|
||||
row_right.alignment='RIGHT'
|
||||
row_right.operator(
|
||||
"flip_fluid_operators.preset_display_info",
|
||||
text="",
|
||||
icon="INFO",
|
||||
emboss=False,
|
||||
).identifier = p.identifier
|
||||
row_right.operator(
|
||||
"flip_fluid_operators.preset_apply_remove_from_stack",
|
||||
).stack_index=pidx
|
||||
row_right.prop(p, "is_enabled", text="", icon='RESTRICT_VIEW_OFF')
|
||||
row_right = row_right.row(align=True)
|
||||
row_right.operator(
|
||||
"flip_fluid_operators.preset_move_up_in_stack",
|
||||
text="",
|
||||
icon="TRIA_UP",
|
||||
).stack_index=pidx
|
||||
row_right.operator(
|
||||
"flip_fluid_operators.preset_move_down_in_stack",
|
||||
text="",
|
||||
icon="TRIA_DOWN",
|
||||
).stack_index=pidx
|
||||
row_right = row_right.row()
|
||||
row_right.operator(
|
||||
"flip_fluid_operators.preset_remove_from_stack",
|
||||
text="",
|
||||
icon="X",
|
||||
emboss=False,
|
||||
).stack_index=pidx
|
||||
|
||||
|
||||
def draw_preset_manager(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
preprops = obj.flip_fluid.domain.presets
|
||||
|
||||
self.layout.separator()
|
||||
box = self.layout.box()
|
||||
row = box.row()
|
||||
row.prop(preprops, "preset_manager_expanded",
|
||||
icon="TRIA_DOWN" if preprops.preset_manager_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Preset Manager")
|
||||
if preprops.preset_manager_expanded:
|
||||
column = box.column(align=True)
|
||||
column.label(text="Package Operators:")
|
||||
column.operator("flip_fluid_operators.preset_create_new_package")
|
||||
column.operator("flip_fluid_operators.preset_delete_package")
|
||||
column.separator()
|
||||
|
||||
# These operators need to be reworked to support both 2.79 and 2.80
|
||||
"""
|
||||
split = column.split(align=True)
|
||||
split_column = split.column(align=True)
|
||||
split_column.enabled = bool(preprops.import_package_settings.package_filepath)
|
||||
split_column.operator("flip_fluid_operators.preset_import_package")
|
||||
split_column = split.column(align=True)
|
||||
row = split_column.row(align=True)
|
||||
row.prop(preprops.import_package_settings, "package_filepath")
|
||||
row.operator("flip_fluid_operators.select_package_zipfile", text="", icon=vcu.get_file_folder_icon())
|
||||
|
||||
row = column.row(align=True)
|
||||
row.operator("flip_fluid_operators.preset_export_package")
|
||||
row.prop(preprops.export_package_settings, "export_directory")
|
||||
"""
|
||||
|
||||
column = box.column(align=True)
|
||||
column.label(text="Preset Operators:")
|
||||
column.operator("flip_fluid_operators.preset_create_new_preset")
|
||||
column.operator("flip_fluid_operators.preset_delete_preset")
|
||||
column.operator("flip_fluid_operators.preset_edit_preset")
|
||||
|
||||
|
||||
def draw_default_settings_operators(self, context):
|
||||
self.layout.separator()
|
||||
box = self.layout.box()
|
||||
column = box.column(align=True)
|
||||
column.label(text="Default Settings:")
|
||||
split = vcu.ui_split(column, align=True, factor=0.66)
|
||||
column = split.column(align=True)
|
||||
column.operator(
|
||||
"flip_fluid_operators.preset_save_user_default_settings",
|
||||
text="Save",
|
||||
icon='FILE_TICK'
|
||||
)
|
||||
column = split.column(align=True)
|
||||
column.operator(
|
||||
"flip_fluid_operators.preset_restore_system_default_settings",
|
||||
text="Restore",
|
||||
)
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
if not preset_library.get_user_package_info_list():
|
||||
self.layout.label(text="This feature is missing data and will be disabled.")
|
||||
self.layout.label(text="Please contact the developers if you think this is an error.")
|
||||
return
|
||||
|
||||
self.draw_preset_selector(context)
|
||||
self.draw_preset_stack(context)
|
||||
|
||||
self.draw_preset_manager(context)
|
||||
self.draw_default_settings_operators(context)
|
||||
|
||||
|
||||
def register():
|
||||
preferences = vcu.get_addon_preferences()
|
||||
if preferences.enable_presets:
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypePresetsPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
try:
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypePresetsPanel)
|
||||
except:
|
||||
pass
|
||||
@@ -0,0 +1,714 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy, math
|
||||
|
||||
from ..utils import export_utils
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
DRAW_OBJECT_FLIP_TYPE_PROPERTY = True
|
||||
|
||||
|
||||
def draw_bake_operator_UI_element(context, ui_box):
|
||||
dprops = context.scene.flip_fluid.get_domain_properties()
|
||||
if dprops is None:
|
||||
return
|
||||
|
||||
bakeprops = dprops.bake
|
||||
simprops = dprops.simulation
|
||||
if not bakeprops.is_simulation_running:
|
||||
if bakeprops.is_autosave_available:
|
||||
frame_str = str(bakeprops.autosave_frame + 1)
|
||||
if simprops.enable_savestates:
|
||||
frame_str = str(int(simprops.selected_savestate) + 1)
|
||||
|
||||
operator_text = "Resume Baking (from frame " + frame_str + ")"
|
||||
else:
|
||||
operator_text = "Bake"
|
||||
elif bakeprops.is_export_operator_running:
|
||||
progress = bakeprops.export_progress
|
||||
stage = bakeprops.export_stage
|
||||
pct_string = str(round(progress * 100, 1)) + "%"
|
||||
|
||||
if stage == 'STATIC':
|
||||
operator_text = "Exporting static data... " + pct_string
|
||||
elif stage == 'KEYFRAMED':
|
||||
operator_text = "Exporting keyframe data... " + pct_string
|
||||
elif stage == 'ANIMATED':
|
||||
operator_text = "Exporting animated data... " + pct_string
|
||||
else:
|
||||
operator_text = "Exporting data... "
|
||||
|
||||
elif not bakeprops.is_bake_initialized:
|
||||
operator_text = "Baking in progress... initializing"
|
||||
elif bakeprops.is_bake_cancelled:
|
||||
if bakeprops.is_safe_to_exit:
|
||||
safety_str = "Safe to quit Blender"
|
||||
else:
|
||||
safety_str = "Do NOT quit Blender"
|
||||
operator_text = "Cancelling... " + safety_str
|
||||
else:
|
||||
num_frames = simprops.frame_end - simprops.frame_start + 1
|
||||
frame_progress_string = str(bakeprops.num_baked_frames) + " / " + str(num_frames)
|
||||
pct_string = str(round((bakeprops.num_baked_frames / num_frames) * 100, 1)) + "%"
|
||||
frames_string = pct_string + " (" + frame_progress_string + ")"
|
||||
frames_string += " (" + str(simprops.frame_start + bakeprops.num_baked_frames) + ")"
|
||||
operator_text = "Baking in progress... " + frames_string
|
||||
|
||||
column = ui_box.column(align=True)
|
||||
|
||||
if not bakeprops.is_simulation_running and bakeprops.is_autosave_available:
|
||||
split = vcu.ui_split(column, factor=0.75, align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.operator("flip_fluid_operators.bake_fluid_simulation",
|
||||
text=operator_text)
|
||||
|
||||
column_right = split.column(align=True)
|
||||
column_right.alert = True
|
||||
column_right.operator("flip_fluid_operators.reset_bake",
|
||||
text="Reset")
|
||||
|
||||
if simprops.enable_savestates:
|
||||
if simprops.get_num_savestate_enums() < 100:
|
||||
column_left.prop(simprops, "selected_savestate", text="")
|
||||
else:
|
||||
column_right.alert = False
|
||||
row = column_left.row(align=True)
|
||||
row.prop(simprops, "selected_savestate_int", text="Resume from frame")
|
||||
column_right.label(text=simprops.selected_savestate_int_label)
|
||||
|
||||
else:
|
||||
column.operator("flip_fluid_operators.bake_fluid_simulation",
|
||||
text=operator_text)
|
||||
if bakeprops.is_simulation_running:
|
||||
column.operator("flip_fluid_operators.cancel_bake_fluid_simulation",
|
||||
text="Stop / Pause")
|
||||
|
||||
if dprops.bake.is_simulation_running:
|
||||
row = column.row()
|
||||
row.alignment = "RIGHT"
|
||||
if dprops.stats.is_estimated_time_remaining_available:
|
||||
row.label(text="Estimated Time Remaining: " + dprops.stats.get_time_remaining_string(context))
|
||||
else:
|
||||
row.label(text="Calculating time remaining...")
|
||||
|
||||
|
||||
def draw_bake_operator(self, context, box):
|
||||
box.label(text="Bake Simulation:")
|
||||
draw_bake_operator_UI_element(context, box)
|
||||
|
||||
|
||||
def draw_more_bake_settings(self, context, box):
|
||||
obj = vcu.get_active_object(context)
|
||||
dprops = obj.flip_fluid.domain
|
||||
sprops = dprops.simulation
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(sprops, "more_bake_settings_expanded",
|
||||
icon="TRIA_DOWN" if sprops.more_bake_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="More Bake Settings")
|
||||
|
||||
if not sprops.more_bake_settings_expanded:
|
||||
return
|
||||
|
||||
subbox = box.box()
|
||||
column = subbox.column(align=True)
|
||||
column.label(text="Frame Range:")
|
||||
row = column.row()
|
||||
row.prop(sprops, "frame_range_mode", expand=True)
|
||||
row = column.row(align=True)
|
||||
if sprops.frame_range_mode == 'FRAME_RANGE_TIMELINE':
|
||||
row_left = row.row(align=True)
|
||||
row_right = row.row(align=True)
|
||||
if dprops.bake.is_autosave_available:
|
||||
row_left.enabled = False
|
||||
row_left.prop(dprops.bake, "original_frame_start")
|
||||
else:
|
||||
row_left.prop(context.scene, "frame_start")
|
||||
row_right.prop(context.scene, "frame_end")
|
||||
else:
|
||||
row_left = row.row(align=True)
|
||||
row_right = row.row(align=True)
|
||||
if dprops.bake.is_autosave_available:
|
||||
row_left.enabled = False
|
||||
row_left.prop(dprops.bake, "original_frame_start")
|
||||
else:
|
||||
row_left.prop(sprops.frame_range_custom, "value_min")
|
||||
row_right.prop(sprops.frame_range_custom, "value_max")
|
||||
|
||||
subbox = box.box()
|
||||
column = subbox.column(align=True)
|
||||
column.label(text="Settings and Mesh Export:")
|
||||
column.prop(sprops, "update_settings_on_resume")
|
||||
|
||||
indent_str = 5 * " "
|
||||
|
||||
subbox = subbox.box()
|
||||
row = subbox.row(align=True)
|
||||
row.prop(sprops, "skip_mesh_reexport_expanded",
|
||||
icon="TRIA_DOWN" if sprops.skip_mesh_reexport_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Skip Mesh Re-Export:")
|
||||
|
||||
if sprops.skip_mesh_reexport_expanded:
|
||||
flip_props = context.scene.flip_fluid
|
||||
flip_objects = (flip_props.get_obstacle_objects() +
|
||||
flip_props.get_fluid_objects() +
|
||||
flip_props.get_inflow_objects() +
|
||||
flip_props.get_outflow_objects() +
|
||||
flip_props.get_force_field_objects())
|
||||
|
||||
num_flip_objects = len(flip_objects)
|
||||
flip_object_count_limit = 128
|
||||
if num_flip_objects > flip_object_count_limit:
|
||||
column = subbox.column()
|
||||
column.alert = True
|
||||
column.label(text="Menu Unavailable", icon="ERROR")
|
||||
column.label(text="This menu is only available in scenes containing " + str(flip_object_count_limit) + " FLIP objects or fewer", icon="ERROR")
|
||||
column.label(text="Current number of FLIP objects: " + str(num_flip_objects), icon="ERROR")
|
||||
else:
|
||||
column = subbox.column()
|
||||
column.label(text="Object Motion Type:")
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "mesh_reexport_type_filter", expand=True)
|
||||
|
||||
flip_objects.sort(key=lambda x: x.name)
|
||||
|
||||
is_all_filter_selected = sprops.mesh_reexport_type_filter == 'MOTION_FILTER_TYPE_ALL'
|
||||
if sprops.mesh_reexport_type_filter == 'MOTION_FILTER_TYPE_ALL':
|
||||
filtered_objects = flip_objects
|
||||
motion_type_string = "simulation"
|
||||
elif sprops.mesh_reexport_type_filter == 'MOTION_FILTER_TYPE_STATIC':
|
||||
filtered_objects = [x for x in flip_objects if _get_object_motion_type(self, x) == 'STATIC']
|
||||
motion_type_string = "static"
|
||||
elif sprops.mesh_reexport_type_filter == 'MOTION_FILTER_TYPE_KEYFRAMED':
|
||||
filtered_objects = [x for x in flip_objects if _get_object_motion_type(self, x) == 'KEYFRAMED']
|
||||
motion_type_string = "keyframed"
|
||||
elif sprops.mesh_reexport_type_filter == 'MOTION_FILTER_TYPE_ANIMATED':
|
||||
filtered_objects = [x for x in flip_objects if _get_object_motion_type(self, x) == 'ANIMATED']
|
||||
motion_type_string = "animated"
|
||||
|
||||
if len(filtered_objects) == 0:
|
||||
column.label(text=indent_str + "No " + motion_type_string + " objects found...")
|
||||
else:
|
||||
split = column.split()
|
||||
column_left = split.column(align=True)
|
||||
if is_all_filter_selected:
|
||||
column_animated = split.column(align=True)
|
||||
|
||||
column_middle = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
|
||||
column_left.label(text="")
|
||||
column_left.label(text="Object")
|
||||
op_box = column_left.box()
|
||||
op_box.label(text="")
|
||||
|
||||
if is_all_filter_selected:
|
||||
column_animated.label(text="")
|
||||
column_animated.label(text="Export Animated")
|
||||
op_box = column_animated.box()
|
||||
row = op_box.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.operator("flip_fluid_operators.helper_batch_export_animated_mesh", icon='CHECKBOX_HLT', text="").enable_state = True
|
||||
row.operator("flip_fluid_operators.helper_batch_export_animated_mesh", icon='CHECKBOX_DEHLT', text="").enable_state = False
|
||||
row.label(text="All")
|
||||
|
||||
column_middle.label(text="")
|
||||
column_middle.label(text="Skip Re-Export")
|
||||
op_box = column_middle.box()
|
||||
row = op_box.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.operator("flip_fluid_operators.helper_batch_skip_reexport", icon='CHECKBOX_HLT', text="").enable_state = True
|
||||
row.operator("flip_fluid_operators.helper_batch_skip_reexport", icon='CHECKBOX_DEHLT', text="").enable_state = False
|
||||
row.label(text="All")
|
||||
|
||||
column_right.label(text="Force Export")
|
||||
column_right.label(text="On Next Bake")
|
||||
op_box = column_right.box()
|
||||
row = op_box.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.operator("flip_fluid_operators.helper_batch_force_reexport", icon='CHECKBOX_HLT', text="").enable_state = True
|
||||
row.operator("flip_fluid_operators.helper_batch_force_reexport", icon='CHECKBOX_DEHLT', text="").enable_state = False
|
||||
row.label(text="All")
|
||||
|
||||
is_export_hint_enabled = not vcu.get_addon_preferences().dismiss_export_animated_mesh_parented_relation_hint
|
||||
for ob in filtered_objects:
|
||||
pgroup = ob.flip_fluid.get_property_group()
|
||||
column_left_row = column_left.row(align=True)
|
||||
column_left_row.alignment = 'LEFT'
|
||||
column_left_row.label(text=ob.name, icon="OBJECT_DATA")
|
||||
|
||||
is_child_object = ob.parent is not None
|
||||
if is_export_hint_enabled and not pgroup.export_animated_mesh and is_child_object:
|
||||
column_left_row.prop(context.scene.flip_fluid_helper, "export_animated_mesh_parent_tooltip",
|
||||
icon="INFO", emboss=False, text=""
|
||||
)
|
||||
|
||||
if is_all_filter_selected:
|
||||
column_animated.prop(pgroup, "export_animated_mesh", text="animated", toggle=True)
|
||||
column_middle.prop(pgroup, "skip_reexport", text="skip", toggle=True)
|
||||
column_right.prop(pgroup, "force_reexport_on_next_bake", text="force", toggle=True)
|
||||
|
||||
subbox = box.box()
|
||||
column = subbox.column(align=True)
|
||||
column.label(text="Savestates:")
|
||||
column.prop(sprops, "enable_savestates")
|
||||
column = subbox.column(align=True)
|
||||
column.enabled = sprops.enable_savestates
|
||||
split = column.split()
|
||||
column = split.column()
|
||||
row = column.row()
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Generate savestate every")
|
||||
column = split.column()
|
||||
split = column.split()
|
||||
column = split.column()
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "savestate_interval", text="")
|
||||
row.label(text="frames")
|
||||
column = subbox.column(align=True)
|
||||
column.enabled = sprops.enable_savestates
|
||||
column.prop(sprops, "delete_outdated_savestates")
|
||||
column.prop(sprops, "delete_outdated_meshes")
|
||||
|
||||
|
||||
def draw_resolution_settings(self, context, master_column):
|
||||
obj = vcu.get_active_object(context)
|
||||
dprops = obj.flip_fluid.domain
|
||||
sprops = dprops.simulation
|
||||
wprops = dprops.world
|
||||
aprops = dprops.advanced
|
||||
|
||||
box = master_column.box()
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
row = column_left.row(align=True)
|
||||
row.prop(sprops, "simulation_resolution_expanded",
|
||||
icon="TRIA_DOWN" if sprops.simulation_resolution_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Grid Resolution:")
|
||||
|
||||
if not sprops.simulation_resolution_expanded:
|
||||
row = column_right.row(align=True)
|
||||
row.prop(sprops, "resolution", text="Resolution")
|
||||
else:
|
||||
column = box.column(align=True)
|
||||
split = vcu.ui_split(column, factor=0.5, align=True)
|
||||
|
||||
column_left = split.column(align=True)
|
||||
column_left.enabled = not sprops.lock_cell_size
|
||||
column_left.prop(sprops, "resolution")
|
||||
|
||||
column_right = split.column(align=True)
|
||||
column_right.enabled = not sprops.auto_preview_resolution
|
||||
column_right.prop(sprops, "preview_resolution")
|
||||
|
||||
column = box.column(align=True)
|
||||
split = vcu.ui_split(column, factor=0.5)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_left.prop(sprops, "lock_cell_size")
|
||||
|
||||
column_right.prop(sprops, "auto_preview_resolution", text="Use Recommended")
|
||||
|
||||
if not dprops.bake.is_simulation_running and sprops.is_current_grid_upscaled():
|
||||
old_resolution = max(sprops.savestate_isize, sprops.savestate_jsize, sprops.savestate_ksize)
|
||||
|
||||
indent = 7
|
||||
subbox = box.box()
|
||||
column = subbox.column(align=True)
|
||||
column.label(text="Increased resolution detected")
|
||||
row = column.row()
|
||||
row.prop(sprops, "upscale_resolution_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text="Simulation will be upscaled on resume.")
|
||||
column.label(text=indent*" " + " Cache resolution: " + str(old_resolution))
|
||||
column.label(text=indent*" " + " Current resolution: " + str(sprops.resolution))
|
||||
|
||||
box = master_column.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(sprops, "grid_info_expanded",
|
||||
icon="TRIA_DOWN" if sprops.grid_info_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Grid Info:")
|
||||
row = row.row()
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(dprops.debug, "grid_display_mode", text="")
|
||||
row.prop(dprops.debug, "display_simulation_grid", text="Visualize Grid")
|
||||
|
||||
if sprops.grid_info_expanded:
|
||||
column = box.column(align=True)
|
||||
|
||||
split = vcu.ui_split(column, factor=0.05, align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
split = vcu.ui_split(column_right, factor=0.4, align=True)
|
||||
column_middle = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
|
||||
if dprops.debug.grid_display_mode == 'GRID_DISPLAY_SIMULATION':
|
||||
column_left.prop(sprops, "grid_voxels_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
column_left.prop(sprops, "grid_dimensions_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
column_left.prop(sprops, "grid_voxel_size_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
column_left.prop(sprops, "grid_voxel_count_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
|
||||
row = column_middle.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Voxels 3D =")
|
||||
|
||||
row = column_middle.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Dimensions 3D =")
|
||||
|
||||
row = column_middle.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Voxel Size =")
|
||||
|
||||
row = column_middle.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Voxel Count =")
|
||||
|
||||
isize, jsize, ksize, dx = sprops.get_simulation_grid_dimensions()
|
||||
if dprops.debug.grid_display_mode == 'GRID_DISPLAY_PREVIEW':
|
||||
isize, jsize, ksize, dx = sprops.get_simulation_grid_dimensions(resolution=sprops.preview_resolution)
|
||||
|
||||
if dprops.debug.grid_display_mode == 'GRID_DISPLAY_MESH':
|
||||
subdivision_multiplier = 1 + dprops.surface.subdivisions
|
||||
isize *= subdivision_multiplier
|
||||
jsize *= subdivision_multiplier
|
||||
ksize *= subdivision_multiplier
|
||||
dx /= subdivision_multiplier
|
||||
elif dprops.debug.grid_display_mode == 'GRID_DISPLAY_FORCE_FIELD':
|
||||
reduction_multiplier = 1
|
||||
if dprops.world.force_field_resolution == 'FORCE_FIELD_RESOLUTION_HIGH':
|
||||
reduction_multiplier = 2
|
||||
elif dprops.world.force_field_resolution == 'FORCE_FIELD_RESOLUTION_NORMAL':
|
||||
reduction_multiplier = 3
|
||||
elif dprops.world.force_field_resolution == 'FORCE_FIELD_RESOLUTION_LOW':
|
||||
reduction_multiplier = 4
|
||||
isize = math.ceil(isize / reduction_multiplier)
|
||||
jsize = math.ceil(jsize / reduction_multiplier)
|
||||
ksize = math.ceil(ksize / reduction_multiplier)
|
||||
dx *= reduction_multiplier
|
||||
|
||||
voxel_str = str(isize) + " x " + str(jsize) + " x " + str(ksize)
|
||||
|
||||
xdims, ydims, zdims = wprops.get_simulation_dimensions(context)
|
||||
xdims_str = '{:.2f}'.format(round(xdims, 2))
|
||||
ydims_str = '{:.2f}'.format(round(ydims, 2))
|
||||
zdims_str = '{:.2f}'.format(round(zdims, 2))
|
||||
xdims_str = xdims_str.rstrip("0").rstrip(".") + "m"
|
||||
ydims_str = ydims_str.rstrip("0").rstrip(".") + "m"
|
||||
zdims_str = zdims_str.rstrip("0").rstrip(".") + "m"
|
||||
dimensions_str = str(xdims_str) + " x " + str(ydims_str) + " x " + str(zdims_str)
|
||||
|
||||
display_dx = dx
|
||||
suffix = "m"
|
||||
if int(display_dx) == 0:
|
||||
display_dx *= 100
|
||||
suffix = "cm"
|
||||
if int(display_dx) == 0:
|
||||
display_dx *= 10
|
||||
suffix = "mm"
|
||||
voxel_size_str = '{:.3f}'.format(round(display_dx, 3)) + " " + suffix
|
||||
|
||||
voxel_count_str = '{:,}'.format(isize * jsize * ksize).replace(',', ' ')
|
||||
|
||||
column_right.label(text=voxel_str)
|
||||
column_right.label(text=dimensions_str)
|
||||
column_right.label(text=voxel_size_str)
|
||||
column_right.label(text=voxel_count_str)
|
||||
|
||||
box = master_column.box()
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
row = column_left.row(align=True)
|
||||
row.prop(sprops, "simulation_method_expanded",
|
||||
icon="TRIA_DOWN" if sprops.simulation_method_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Simulation Method:")
|
||||
|
||||
if not sprops.simulation_method_expanded:
|
||||
row = column_right.row()
|
||||
row.prop(aprops, "velocity_transfer_method", expand=True)
|
||||
pass
|
||||
else:
|
||||
column = box.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.prop(aprops, "velocity_transfer_method", expand=True)
|
||||
row = column.row(align=True)
|
||||
if aprops.velocity_transfer_method == 'VELOCITY_TRANSFER_METHOD_FLIP':
|
||||
row.prop(aprops, "PICFLIP_ratio", slider=True)
|
||||
else:
|
||||
row.label(text="")
|
||||
|
||||
box = master_column.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(sprops, "world_scale_expanded",
|
||||
icon="TRIA_DOWN" if sprops.world_scale_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
|
||||
xdims, ydims, zdims = wprops.get_simulation_dimensions(context)
|
||||
xdims_str = '{:.2f}'.format(round(xdims, 2))
|
||||
ydims_str = '{:.2f}'.format(round(ydims, 2))
|
||||
zdims_str = '{:.2f}'.format(round(zdims, 2))
|
||||
xdims_str = xdims_str.rstrip("0").rstrip(".") + "m"
|
||||
ydims_str = ydims_str.rstrip("0").rstrip(".") + "m"
|
||||
zdims_str = zdims_str.rstrip("0").rstrip(".") + "m"
|
||||
dimensions_str = str(xdims_str) + " x " + str(ydims_str) + " x " + str(zdims_str)
|
||||
row.label(text="World Scale:")
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.label(text=dimensions_str)
|
||||
|
||||
if not sprops.world_scale_expanded:
|
||||
pass
|
||||
else:
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "world_scale_mode", expand=True)
|
||||
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column()
|
||||
column_right = split.column()
|
||||
|
||||
if wprops.world_scale_mode == 'WORLD_SCALE_MODE_RELATIVE':
|
||||
row = column_left.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="1 Blender Unit = ")
|
||||
column_right.prop(wprops, "world_scale_relative")
|
||||
else:
|
||||
row = column_left.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Domain Length = ")
|
||||
column_right.prop(wprops, "world_scale_absolute")
|
||||
|
||||
box = master_column.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(sprops, "boundary_collisions_expanded",
|
||||
icon="TRIA_DOWN" if sprops.boundary_collisions_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Domain Boundary Collisions:")
|
||||
|
||||
if all(sprops.fluid_boundary_collisions):
|
||||
boundary_collision_status_str = "Closed"
|
||||
elif not any(sprops.fluid_boundary_collisions):
|
||||
boundary_collision_status_str = "Open"
|
||||
else:
|
||||
boundary_collision_status_str = "Mixed"
|
||||
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text=boundary_collision_status_str)
|
||||
|
||||
if not sprops.boundary_collisions_expanded:
|
||||
pass
|
||||
else:
|
||||
column = box.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=0, text="X –")
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=1, text="X+")
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=2, text="Y –")
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=3, text="Y+")
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=4, text="Z –")
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=5, text="Z+")
|
||||
column.prop(sprops, "fluid_open_boundary_width", slider=True)
|
||||
|
||||
|
||||
def _get_object_motion_type(self, obj):
|
||||
props = obj.flip_fluid.get_property_group()
|
||||
if hasattr(props, 'export_animated_mesh') and props.export_animated_mesh:
|
||||
return 'ANIMATED'
|
||||
if export_utils.is_object_keyframe_animated(obj):
|
||||
return 'KEYFRAMED'
|
||||
return 'STATIC'
|
||||
|
||||
|
||||
def draw_time_settings(self, context, box):
|
||||
obj = vcu.get_active_object(context)
|
||||
sprops = obj.flip_fluid.domain.simulation
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(sprops, "frame_rate_and_time_scale_expanded",
|
||||
icon="TRIA_DOWN" if sprops.frame_rate_and_time_scale_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Frame Rate and Time Scale:")
|
||||
|
||||
if not sprops.frame_rate_and_time_scale_expanded:
|
||||
fps_str = "{:.2f}".format(sprops.get_frame_rate()) + " FPS"
|
||||
row = row.row(align =True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text=fps_str)
|
||||
return
|
||||
|
||||
column = box.column(align=True)
|
||||
column.label(text="Frame Rate:")
|
||||
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "frame_rate_mode", expand=True)
|
||||
column = column.column(align=True)
|
||||
column.enabled = sprops.frame_rate_mode == 'FRAME_RATE_MODE_CUSTOM'
|
||||
if sprops.frame_rate_mode == 'FRAME_RATE_MODE_SCENE':
|
||||
column.prop(context.scene.render, "fps", text="Scene Frame Rate")
|
||||
else:
|
||||
column.prop(sprops, "frame_rate_custom", text="Custom Frame Rate")
|
||||
|
||||
column = box.column(align=True)
|
||||
column.label(text="Time Scale:")
|
||||
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "time_scale_mode", expand=True)
|
||||
column = column.column(align=True)
|
||||
|
||||
if sprops.time_scale_mode == 'TIME_SCALE_MODE_CUSTOM':
|
||||
column.prop(sprops, "time_scale", text="Custom Time Scale")
|
||||
elif sprops.time_scale_mode == 'TIME_SCALE_MODE_RIGID_BODY':
|
||||
if bpy.context.scene.rigidbody_world is not None:
|
||||
column.prop(bpy.context.scene.rigidbody_world, "time_scale", text="Rigid Body Time Scale")
|
||||
else:
|
||||
row = column.row(align=True)
|
||||
row.label(text="No Rigid Body World: ")
|
||||
row.operator("rigidbody.world_add")
|
||||
|
||||
elif sprops.time_scale_mode == 'TIME_SCALE_MODE_SOFT_BODY':
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_left.label(text="Soft Body Object:")
|
||||
column_right.prop(sprops, "time_scale_object_soft_body", text="")
|
||||
|
||||
row = column.row(align=True)
|
||||
soft_body_modifier = sprops.get_selected_time_scale_object_soft_body_modifier()
|
||||
if soft_body_modifier is not None:
|
||||
row.prop(soft_body_modifier.settings, "speed", text="Soft Body Time Scale")
|
||||
else:
|
||||
if sprops.time_scale_object_soft_body is not None:
|
||||
row.label(text="No soft body simulation found on object")
|
||||
else:
|
||||
row.label(text="No soft body object selected")
|
||||
|
||||
elif sprops.time_scale_mode == 'TIME_SCALE_MODE_CLOTH':
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_left.label(text="Cloth Object:")
|
||||
column_right.prop(sprops, "time_scale_object_cloth", text="")
|
||||
|
||||
row = column.row(align=True)
|
||||
cloth_modifier = sprops.get_selected_time_scale_object_cloth_modifier()
|
||||
if cloth_modifier is not None:
|
||||
row.prop(cloth_modifier.settings, "time_scale", text="Cloth Time Scale")
|
||||
else:
|
||||
if sprops.time_scale_object_cloth is not None:
|
||||
row.label(text="No cloth simulation found on object")
|
||||
else:
|
||||
row.label(text="No cloth object selected")
|
||||
|
||||
elif sprops.time_scale_mode == 'TIME_SCALE_MODE_FLUID':
|
||||
if vcu.is_blender_282():
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_left.label(text="Fluid Domain Object:")
|
||||
column_right.prop(sprops, "time_scale_object_fluid", text="")
|
||||
|
||||
row = column.row(align=True)
|
||||
fluid_modifier = sprops.get_selected_time_scale_object_fluid_modifier()
|
||||
if fluid_modifier is not None:
|
||||
row.prop(fluid_modifier.domain_settings, "time_scale", text="Fluid Time Scale")
|
||||
else:
|
||||
if sprops.time_scale_object_fluid is not None:
|
||||
row.label(text="No Mantaflow domain found on object")
|
||||
else:
|
||||
row.label(text="No Mantaflow domain object selected")
|
||||
else:
|
||||
column.label(text="Mantaflow fluid simulation time scale mode")
|
||||
column.label(text="only supported in Blender 2.82 or later")
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypePanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Simulation"
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
|
||||
dprops = context.scene.flip_fluid.get_domain_properties()
|
||||
if dprops is None:
|
||||
return
|
||||
is_simulation_running = dprops.bake.is_simulation_running
|
||||
|
||||
global DRAW_OBJECT_FLIP_TYPE_PROPERTY
|
||||
if DRAW_OBJECT_FLIP_TYPE_PROPERTY:
|
||||
column = self.layout.column()
|
||||
column.enabled = not is_simulation_running
|
||||
column.prop(obj_props, "object_type")
|
||||
|
||||
box = self.layout.box()
|
||||
draw_bake_operator(self, context, box)
|
||||
draw_more_bake_settings(self, context, box)
|
||||
|
||||
column = self.layout.column()
|
||||
draw_resolution_settings(self, context, column)
|
||||
|
||||
box = self.layout.box()
|
||||
draw_time_settings(self, context, box)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypePanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypePanel)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,398 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..ui import domain_display_ui
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
from ..utils import installation_utils
|
||||
|
||||
|
||||
def _draw_fluid_surface_display_settings(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
dprops = obj.flip_fluid.domain
|
||||
domain_display_ui.draw_surface_display_settings(self, context, dprops.surface)
|
||||
|
||||
|
||||
def _draw_geometry_attributes_menu(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
sprops = obj.flip_fluid.domain.surface
|
||||
rprops = obj.flip_fluid.domain.render
|
||||
|
||||
#
|
||||
# Geometry Attributes
|
||||
#
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.alert = not sprops.enable_surface_mesh_generation
|
||||
row.prop(sprops, "geometry_attributes_expanded",
|
||||
icon="TRIA_DOWN" if sprops.geometry_attributes_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Surface Attributes:")
|
||||
|
||||
if sprops.geometry_attributes_expanded:
|
||||
prefs = vcu.get_addon_preferences()
|
||||
if not prefs.is_extra_features_enabled():
|
||||
warn_box = box.box()
|
||||
warn_column = warn_box.column(align=True)
|
||||
warn_column.enabled = True
|
||||
warn_column.label(text=" This feature is affected by a current bug in Blender.", icon='ERROR')
|
||||
warn_column.label(text=" The Extra Features option must be enabled in preferences")
|
||||
warn_column.label(text=" to use this feature.")
|
||||
warn_column.separator()
|
||||
warn_column.prop(prefs, "enable_extra_features", text="Enable Extra Features in Preferences")
|
||||
warn_column.separator()
|
||||
warn_column.operator(
|
||||
"wm.url_open",
|
||||
text="Important Info and Limitations",
|
||||
icon="WORLD"
|
||||
).url = "https://github.com/rlguy/Blender-FLIP-Fluids/wiki/Preferences-Menu-Settings#developer-tools"
|
||||
return
|
||||
|
||||
if not vcu.is_blender_293():
|
||||
column = box.column(align=True)
|
||||
column.enabled = False
|
||||
column.label(text="Geometry attribute features for the fluid surface are only available in", icon='ERROR')
|
||||
column.label(text="Blender 2.93 or later. Blender 3.1 or later recommended.", icon='ERROR')
|
||||
return
|
||||
|
||||
is_preview_mode_enabled = rprops.viewport_display == 'DISPLAY_PREVIEW'
|
||||
is_attributes_enabled = (
|
||||
sprops.enable_velocity_vector_attribute or
|
||||
sprops.enable_speed_attribute or
|
||||
sprops.enable_vorticity_vector_attribute or
|
||||
sprops.enable_color_attribute or
|
||||
sprops.enable_age_attribute or
|
||||
sprops.enable_lifetime_attribute or
|
||||
sprops.enable_whitewater_proximity_attribute or
|
||||
sprops.enable_source_id_attribute or
|
||||
sprops.enable_viscosity_attribute
|
||||
)
|
||||
|
||||
if is_preview_mode_enabled and is_attributes_enabled:
|
||||
row = box.row(align=True)
|
||||
row.alert = True
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(sprops, "preview_mode_attributes_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text="Warning: Surface attributes will not be loaded in mesh Preview Mode")
|
||||
|
||||
#
|
||||
# Velocity Attributes
|
||||
#
|
||||
subbox = box.box()
|
||||
row = subbox.row(align=True)
|
||||
row.prop(sprops, "velocity_attributes_expanded",
|
||||
icon="TRIA_DOWN" if sprops.velocity_attributes_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Velocity Based Attributes:")
|
||||
|
||||
if sprops.velocity_attributes_expanded:
|
||||
column = subbox.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_right.enabled = sprops.enable_velocity_vector_attribute or sprops.enable_speed_attribute or sprops.enable_vorticity_vector_attribute
|
||||
column_left.prop(sprops, "enable_velocity_vector_attribute", text="Velocity Attributes")
|
||||
|
||||
# This option should always be on. Hiding option from UI, and always enabling this in the simulator.
|
||||
#column_right.prop(sprops, "enable_velocity_vector_attribute_against_obstacles", text="Generate Against Obstacles")
|
||||
|
||||
column.prop(sprops, "enable_speed_attribute", text="Speed Attributes")
|
||||
column.prop(sprops, "enable_vorticity_vector_attribute", text="Vorticity Attributes")
|
||||
column.separator()
|
||||
column.operator("flip_fluid_operators.helper_initialize_motion_blur")
|
||||
else:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(sprops, "enable_velocity_vector_attribute", text="Velocity")
|
||||
row.prop(sprops, "enable_speed_attribute", text="Speed")
|
||||
row.prop(sprops, "enable_vorticity_vector_attribute", text="Vorticity")
|
||||
|
||||
#
|
||||
# Color Attributes
|
||||
#
|
||||
subbox = box.box()
|
||||
row = subbox.row(align=True)
|
||||
row.prop(sprops, "color_attributes_expanded",
|
||||
icon="TRIA_DOWN" if sprops.color_attributes_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Color and Mixing Attributes:")
|
||||
|
||||
if sprops.color_attributes_expanded:
|
||||
column = subbox.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_left.prop(sprops, "enable_color_attribute", text="Color Attributes")
|
||||
if sprops.show_smoothing_radius_in_ui:
|
||||
column_right.prop(sprops, "color_attribute_radius", text="Smoothing", slider=True)
|
||||
|
||||
column = subbox.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_left.enabled = sprops.enable_color_attribute
|
||||
column_left.prop(sprops, "enable_color_attribute_mixing", text="Enable Mixing")
|
||||
column_right.enabled = sprops.enable_color_attribute and sprops.enable_color_attribute_mixing
|
||||
column_right.prop(sprops, "color_attribute_mixing_rate", text="Mix Rate", slider=True)
|
||||
|
||||
column = subbox.column(align=True)
|
||||
column.enabled = sprops.enable_color_attribute and sprops.enable_color_attribute_mixing
|
||||
column.label(text="Mixing Mode:")
|
||||
row = column.row(align=True)
|
||||
row.enabled = sprops.enable_color_attribute
|
||||
row.prop(sprops, "color_attribute_mixing_mode", expand=True)
|
||||
|
||||
if sprops.color_attribute_mixing_mode == 'COLOR_MIXING_MODE_MIXBOX':
|
||||
if not installation_utils.is_mixbox_supported():
|
||||
column.label(text="Mixbox feature is not supported", icon="ERROR")
|
||||
column.label(text="in this version of the FLIP Fluids Addon", icon="ERROR")
|
||||
|
||||
if installation_utils.is_mixbox_supported():
|
||||
if installation_utils.is_mixbox_installation_complete():
|
||||
column.label(text="Mixbox Plugin Status: Installed", icon="CHECKMARK")
|
||||
else:
|
||||
column.label(text="Install the Mixbox plugin in the", icon="INFO")
|
||||
column.label(text="FLIP Fluids Addon preferences", icon="INFO")
|
||||
column.operator(
|
||||
"flip_fluid_operators.open_preferences",
|
||||
text="Open Preferences", icon="PREFERENCES"
|
||||
).view_mode = 'PREFERENCES_MENU_VIEW_MIXBOX'
|
||||
else:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(sprops, "enable_color_attribute", text="Color")
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.enabled = sprops.enable_color_attribute
|
||||
row.prop(sprops, "enable_color_attribute_mixing", text="Mixing")
|
||||
|
||||
#
|
||||
# Other Attributes
|
||||
#
|
||||
subbox = box.box()
|
||||
row = subbox.row(align=True)
|
||||
row.prop(sprops, "other_attributes_expanded",
|
||||
icon="TRIA_DOWN" if sprops.other_attributes_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Other Attributes:")
|
||||
|
||||
if sprops.other_attributes_expanded:
|
||||
column = subbox.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "enable_age_attribute", text="Age Attributes")
|
||||
if sprops.show_smoothing_radius_in_ui:
|
||||
row.prop(sprops, "age_attribute_radius", text="Smoothing", slider=True)
|
||||
else:
|
||||
row.label(text="")
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "enable_lifetime_attribute", text="Lifetime Attributes")
|
||||
row.prop(sprops, "lifetime_attribute_death_time")
|
||||
if sprops.show_smoothing_radius_in_ui:
|
||||
row.prop(sprops, "lifetime_attribute_radius", text="Smoothing", slider=True)
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "enable_whitewater_proximity_attribute", text="Whitewater Proximity Attributes")
|
||||
if sprops.show_smoothing_radius_in_ui:
|
||||
row.prop(sprops, "whitewater_proximity_attribute_radius", text="Smoothing", slider=True)
|
||||
column.prop(sprops, "enable_source_id_attribute", text="Source ID Attributes")
|
||||
else:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(sprops, "enable_age_attribute", text="Age")
|
||||
row.prop(sprops, "enable_lifetime_attribute", text="Life")
|
||||
row.prop(sprops, "enable_whitewater_proximity_attribute", text="WW Prox.")
|
||||
row.prop(sprops, "enable_source_id_attribute", text="Source ID")
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypeFluidSurfacePanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Surface"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
sprops = obj.flip_fluid.domain.surface
|
||||
|
||||
column = self.layout.column(align=True)
|
||||
column.prop(sprops, "enable_surface_mesh_generation")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.alert = not sprops.enable_surface_mesh_generation
|
||||
row.prop(sprops, "surface_mesh_expanded",
|
||||
icon="TRIA_DOWN" if sprops.surface_mesh_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Surface Mesh:")
|
||||
|
||||
if not sprops.surface_mesh_expanded:
|
||||
info_text = "Subdivisions " + str(sprops.subdivisions) + " / "
|
||||
info_text += "Scale " + "{:.2f}".format(sprops.particle_scale)
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
if sprops.particle_scale < 0.999:
|
||||
row.alert = True
|
||||
row.label(text=info_text)
|
||||
|
||||
if sprops.surface_mesh_expanded:
|
||||
column = box.column(align=True)
|
||||
column.prop(sprops, "subdivisions")
|
||||
row = column.row(align=True)
|
||||
if sprops.particle_scale < 0.999:
|
||||
row.alert = True
|
||||
row.prop(sprops, "particle_scale")
|
||||
|
||||
object_collection = vcu.get_scene_collection()
|
||||
if vcu.is_blender_28():
|
||||
search_group = "all_objects"
|
||||
else:
|
||||
search_group = "objects"
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.alert = not sprops.enable_surface_mesh_generation
|
||||
row.prop(sprops, "meshing_volume_expanded",
|
||||
icon="TRIA_DOWN" if sprops.meshing_volume_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Meshing Volume:")
|
||||
|
||||
if not sprops.meshing_volume_expanded:
|
||||
info_text = ""
|
||||
if sprops.meshing_volume_mode == "MESHING_VOLUME_MODE_DOMAIN":
|
||||
info_text = "Domain Volume"
|
||||
elif sprops.meshing_volume_mode == "MESHING_VOLUME_MODE_OBJECT":
|
||||
info_text = "Object Volume"
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text=info_text)
|
||||
|
||||
if sprops.meshing_volume_expanded:
|
||||
row = box.row(align=True)
|
||||
row.prop(sprops, "meshing_volume_mode", expand=True)
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_right.enabled = sprops.meshing_volume_mode == "MESHING_VOLUME_MODE_OBJECT"
|
||||
column_right.prop_search(sprops, "meshing_volume_object", object_collection, search_group, text="Object")
|
||||
column_right.prop(sprops, "export_animated_meshing_volume_object")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.alert = not sprops.enable_surface_mesh_generation
|
||||
row.prop(sprops, "meshing_against_boundary_expanded",
|
||||
icon="TRIA_DOWN" if sprops.meshing_against_boundary_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Meshing Against Boundary:")
|
||||
|
||||
if not sprops.meshing_against_boundary_expanded:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(sprops, "remove_mesh_near_domain", text="Remove")
|
||||
|
||||
if sprops.meshing_against_boundary_expanded:
|
||||
column = box.column(align=True)
|
||||
column.prop(sprops, "remove_mesh_near_domain")
|
||||
|
||||
column = box.column(align=True)
|
||||
column.enabled = sprops.remove_mesh_near_domain
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "remove_mesh_near_domain_sides", index=0, text="X –")
|
||||
row.prop(sprops, "remove_mesh_near_domain_sides", index=1, text="X+")
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "remove_mesh_near_domain_sides", index=2, text="Y –")
|
||||
row.prop(sprops, "remove_mesh_near_domain_sides", index=3, text="Y+")
|
||||
row = column.row(align=True)
|
||||
row.prop(sprops, "remove_mesh_near_domain_sides", index=4, text="Z –")
|
||||
row.prop(sprops, "remove_mesh_near_domain_sides", index=5, text="Z+")
|
||||
column.prop(sprops, "remove_mesh_near_domain_distance")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.alert = not sprops.enable_surface_mesh_generation
|
||||
row.prop(sprops, "meshing_against_obstacles_expanded",
|
||||
icon="TRIA_DOWN" if sprops.meshing_against_obstacles_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Meshing Against Obstacles:")
|
||||
|
||||
if not sprops.meshing_against_obstacles_expanded:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(sprops, "enable_meshing_offset", text="Enable ")
|
||||
|
||||
if sprops.meshing_against_obstacles_expanded:
|
||||
column = box.column(align=True)
|
||||
column.prop(sprops, "enable_meshing_offset")
|
||||
row = box.row(align=True)
|
||||
row.enabled = sprops.enable_meshing_offset
|
||||
row.prop(sprops, "obstacle_meshing_mode", expand=True)
|
||||
|
||||
# Removed surface smoothing options. These are better set
|
||||
# using a Blender smooth modifier.
|
||||
"""
|
||||
box = self.layout.box()
|
||||
box.label(text="Smoothing:")
|
||||
row = box.row(align=True)
|
||||
row.prop(sprops, "smoothing_value")
|
||||
row.prop(sprops, "smoothing_iterations")
|
||||
"""
|
||||
|
||||
# Motion Blur is no longer supported
|
||||
#column = self.layout.column(align=True)
|
||||
#column.separator()
|
||||
#column.prop(sprops, "generate_motion_blur_data")
|
||||
|
||||
_draw_fluid_surface_display_settings(self, context)
|
||||
_draw_geometry_attributes_menu(self, context)
|
||||
|
||||
self.layout.separator()
|
||||
column = self.layout.column(align=True)
|
||||
column.operator("flip_fluid_operators.helper_delete_surface_objects", icon="X")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypeFluidSurfacePanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypeFluidSurfacePanel)
|
||||
@@ -0,0 +1,127 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
from . import domain_simulation_ui
|
||||
from . import domain_cache_ui
|
||||
from . import domain_display_ui
|
||||
from . import domain_particles_ui
|
||||
from . import domain_surface_ui
|
||||
from . import domain_whitewater_ui
|
||||
from . import domain_world_ui
|
||||
from . import domain_materials_ui
|
||||
from . import domain_advanced_ui
|
||||
from . import domain_debug_ui
|
||||
from . import domain_stats_ui
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypeTabbedPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Domain"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
|
||||
dprops = context.scene.flip_fluid.get_domain_properties()
|
||||
if dprops is None:
|
||||
return
|
||||
is_simulation_running = dprops.bake.is_simulation_running
|
||||
|
||||
column = self.layout.column()
|
||||
column.enabled = not is_simulation_running
|
||||
column.prop(obj_props, "object_type")
|
||||
column.separator()
|
||||
|
||||
column = self.layout.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_SIMULATION')
|
||||
row = column.row(align=True)
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_CACHE')
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_DISPLAY')
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_SURFACE')
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_PARTICLES')
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_WHITEWATER')
|
||||
row = column.row(align=True)
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_WORLD')
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_MATERIALS')
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_ADVANCED')
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_DEBUG')
|
||||
row.prop_enum(dprops, "domain_settings_tabbed_panel_view", 'DOMAIN_SETTINGS_PANEL_STATS')
|
||||
column.separator()
|
||||
|
||||
selected_panel = dprops.domain_settings_tabbed_panel_view
|
||||
if selected_panel == 'DOMAIN_SETTINGS_PANEL_SIMULATION':
|
||||
column.label(text="FLIP Fluid Simulation")
|
||||
domain_simulation_ui.DRAW_OBJECT_FLIP_TYPE_PROPERTY = False
|
||||
try:
|
||||
domain_simulation_ui.FLIPFLUID_PT_DomainTypePanel.draw(self, context)
|
||||
except Exception as e:
|
||||
domain_simulation_ui.DRAW_OBJECT_FLIP_TYPE_PROPERTY = True
|
||||
raise Exception(e)
|
||||
domain_simulation_ui.DRAW_OBJECT_FLIP_TYPE_PROPERTY = True
|
||||
elif selected_panel == 'DOMAIN_SETTINGS_PANEL_CACHE':
|
||||
column.label(text="FLIP Fluid Cache")
|
||||
domain_cache_ui.FLIPFLUID_PT_DomainTypeCachePanel.draw(self, context)
|
||||
elif selected_panel == 'DOMAIN_SETTINGS_PANEL_DISPLAY':
|
||||
column.label(text="FLIP Fluid Display Settings")
|
||||
domain_display_ui.FLIPFLUID_PT_DomainTypeDisplayPanel.draw(self, context)
|
||||
elif selected_panel == 'DOMAIN_SETTINGS_PANEL_PARTICLES':
|
||||
column.label(text="FLIP Fluid Particles")
|
||||
domain_particles_ui.FLIPFLUID_PT_DomainTypeFluidParticlesPanel.draw(self, context)
|
||||
elif selected_panel == 'DOMAIN_SETTINGS_PANEL_SURFACE':
|
||||
column.label(text="FLIP Fluid Surface")
|
||||
domain_surface_ui.FLIPFLUID_PT_DomainTypeFluidSurfacePanel.draw(self, context)
|
||||
elif selected_panel == 'DOMAIN_SETTINGS_PANEL_WHITEWATER':
|
||||
column.label(text="FLIP Fluid Whitewater")
|
||||
domain_whitewater_ui.FLIPFLUID_PT_DomainTypeWhitewaterPanel.draw(self, context)
|
||||
elif selected_panel == 'DOMAIN_SETTINGS_PANEL_WORLD':
|
||||
column.label(text="FLIP Fluid World")
|
||||
domain_world_ui.FLIPFLUID_PT_DomainTypeFluidWorldPanel.draw(self, context)
|
||||
elif selected_panel == 'DOMAIN_SETTINGS_PANEL_MATERIALS':
|
||||
column.label(text="FLIP Fluid Materials")
|
||||
domain_materials_ui.FLIPFLUID_PT_DomainTypeMaterialsPanel.draw(self, context)
|
||||
elif selected_panel == 'DOMAIN_SETTINGS_PANEL_ADVANCED':
|
||||
column.label(text="FLIP Fluid Advanced Settings")
|
||||
domain_advanced_ui.FLIPFLUID_PT_DomainTypeAdvancedPanel.draw(self, context)
|
||||
elif selected_panel == 'DOMAIN_SETTINGS_PANEL_DEBUG':
|
||||
column.label(text="FLIP Fluid Debug")
|
||||
domain_debug_ui.FLIPFLUID_PT_DomainTypeDebugPanel.draw(self, context)
|
||||
elif selected_panel == 'DOMAIN_SETTINGS_PANEL_STATS':
|
||||
column.label(text="FLIP Fluid Stats")
|
||||
domain_stats_ui.FLIPFLUID_PT_DomainTypeStatsPanel.draw(self, context)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypeTabbedPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypeTabbedPanel)
|
||||
@@ -0,0 +1,86 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
reloadable_modules = [
|
||||
'domain_simulation_ui',
|
||||
'domain_cache_ui',
|
||||
'domain_display_ui',
|
||||
'domain_surface_ui',
|
||||
'domain_particles_ui',
|
||||
'domain_whitewater_ui',
|
||||
'domain_world_ui',
|
||||
'domain_presets_ui',
|
||||
'domain_materials_ui',
|
||||
'domain_advanced_ui',
|
||||
'domain_debug_ui',
|
||||
'domain_stats_ui',
|
||||
'domain_tabbed_ui',
|
||||
]
|
||||
for module_name in reloadable_modules:
|
||||
if module_name in locals():
|
||||
importlib.reload(locals()[module_name])
|
||||
|
||||
import bpy
|
||||
|
||||
from . import(
|
||||
domain_simulation_ui,
|
||||
domain_cache_ui,
|
||||
domain_display_ui,
|
||||
domain_surface_ui,
|
||||
domain_particles_ui,
|
||||
domain_whitewater_ui,
|
||||
domain_world_ui,
|
||||
domain_presets_ui,
|
||||
domain_materials_ui,
|
||||
domain_advanced_ui,
|
||||
domain_debug_ui,
|
||||
domain_stats_ui,
|
||||
domain_tabbed_ui,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
domain_simulation_ui.register()
|
||||
domain_cache_ui.register()
|
||||
domain_display_ui.register()
|
||||
domain_surface_ui.register()
|
||||
domain_particles_ui.register()
|
||||
domain_whitewater_ui.register()
|
||||
domain_world_ui.register()
|
||||
domain_presets_ui.register()
|
||||
domain_materials_ui.register()
|
||||
domain_advanced_ui.register()
|
||||
domain_debug_ui.register()
|
||||
domain_stats_ui.register()
|
||||
domain_tabbed_ui.register()
|
||||
|
||||
|
||||
def unregister():
|
||||
domain_simulation_ui.unregister()
|
||||
domain_cache_ui.unregister()
|
||||
domain_display_ui.unregister()
|
||||
domain_surface_ui.unregister()
|
||||
domain_particles_ui.unregister()
|
||||
domain_whitewater_ui.unregister()
|
||||
domain_world_ui.unregister()
|
||||
domain_materials_ui.unregister()
|
||||
domain_presets_ui.unregister()
|
||||
domain_advanced_ui.unregister()
|
||||
domain_debug_ui.unregister()
|
||||
domain_stats_ui.unregister()
|
||||
domain_tabbed_ui.unregister()
|
||||
@@ -0,0 +1,471 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..operators import helper_operators
|
||||
from . import domain_display_ui
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
def _draw_whitewater_display_settings(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
dprops = obj.flip_fluid.domain
|
||||
domain_display_ui.draw_whitewater_display_settings(self, context, dprops.whitewater)
|
||||
|
||||
|
||||
def _draw_geometry_attributes_menu(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
dprops = obj.flip_fluid.domain
|
||||
wprops = dprops.whitewater
|
||||
prefs = vcu.get_addon_preferences()
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "geometry_attributes_expanded",
|
||||
icon="TRIA_DOWN" if wprops.geometry_attributes_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Whitewater Attributes:")
|
||||
|
||||
if wprops.geometry_attributes_expanded:
|
||||
if not prefs.is_extra_features_enabled():
|
||||
warn_box = box.box()
|
||||
warn_column = warn_box.column(align=True)
|
||||
warn_column.enabled = True
|
||||
warn_column.label(text=" This feature is affected by a current bug in Blender.", icon='ERROR')
|
||||
warn_column.label(text=" The Extra Features option must be enabled in preferences")
|
||||
warn_column.label(text=" to use this feature.")
|
||||
warn_column.separator()
|
||||
warn_column.prop(prefs, "enable_extra_features", text="Enable Extra Features in Preferences")
|
||||
warn_column.separator()
|
||||
warn_column.operator(
|
||||
"wm.url_open",
|
||||
text="Important Info and Limitations",
|
||||
icon="WORLD"
|
||||
).url = "https://github.com/rlguy/Blender-FLIP-Fluids/wiki/Preferences-Menu-Settings#developer-tools"
|
||||
return
|
||||
|
||||
column = box.column(align=True)
|
||||
if not vcu.is_blender_31():
|
||||
column.enabled = False
|
||||
column.label(text="Geometry attribute features for whitewater are only available in", icon='ERROR')
|
||||
column.label(text="Blender 3.1 or later", icon='ERROR')
|
||||
return
|
||||
|
||||
column = box.column(align=True)
|
||||
column.prop(wprops, "enable_velocity_vector_attribute")
|
||||
column.prop(wprops, "enable_id_attribute")
|
||||
column.prop(wprops, "enable_lifetime_attribute")
|
||||
column.separator()
|
||||
column.operator("flip_fluid_operators.helper_initialize_motion_blur")
|
||||
else:
|
||||
if not vcu.is_blender_31():
|
||||
row = row.row(align=True)
|
||||
row.enabled = False
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="(Blender 3.1 or later required)")
|
||||
return
|
||||
if not prefs.is_extra_features_enabled():
|
||||
return
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(wprops, "enable_velocity_vector_attribute", text="Velocity")
|
||||
row.prop(wprops, "enable_id_attribute", text="ID")
|
||||
row.prop(wprops, "enable_lifetime_attribute", text="Lifetime")
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypeWhitewaterPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid Whitewater"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
dprops = obj.flip_fluid.domain
|
||||
wprops = dprops.whitewater
|
||||
is_whitewater_enabled = wprops.enable_whitewater_simulation
|
||||
show_advanced_whitewater = (wprops.whitewater_ui_mode == 'WHITEWATER_UI_MODE_ADVANCED')
|
||||
highlight_advanced = wprops.highlight_advanced_settings
|
||||
|
||||
column = self.layout.column(align=True)
|
||||
column.prop(wprops, "enable_whitewater_simulation")
|
||||
column.separator()
|
||||
|
||||
box = self.layout.box()
|
||||
box.enabled = is_whitewater_enabled
|
||||
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
|
||||
row = column_left.row(align=True)
|
||||
row.prop(wprops, "settings_view_mode_expanded",
|
||||
icon="TRIA_DOWN" if wprops.settings_view_mode_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Settings View Mode:")
|
||||
|
||||
if not wprops.settings_view_mode_expanded:
|
||||
row = column_right.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(wprops, "whitewater_ui_mode", expand=True)
|
||||
|
||||
if wprops.settings_view_mode_expanded:
|
||||
column = box.column(align=True)
|
||||
row = column.row()
|
||||
row.prop(wprops, "whitewater_ui_mode", expand=True)
|
||||
|
||||
split = column.split()
|
||||
split.column()
|
||||
column_right = split.column()
|
||||
column_right.enabled = show_advanced_whitewater
|
||||
column_right.prop(wprops, "highlight_advanced_settings")
|
||||
|
||||
box = self.layout.box()
|
||||
box.enabled = is_whitewater_enabled
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "whitewater_simulation_particles_expanded",
|
||||
icon="TRIA_DOWN" if wprops.whitewater_simulation_particles_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Whitewater Particles:")
|
||||
|
||||
if not wprops.whitewater_simulation_particles_expanded:
|
||||
info_text = ""
|
||||
enabled_particles = []
|
||||
if wprops.enable_foam:
|
||||
enabled_particles.append("Foam")
|
||||
if wprops.enable_bubbles:
|
||||
enabled_particles.append("Bubble")
|
||||
if wprops.enable_spray:
|
||||
enabled_particles.append("Spray")
|
||||
if wprops.enable_dust:
|
||||
enabled_particles.append("Dust")
|
||||
|
||||
if enabled_particles:
|
||||
for ptype in enabled_particles:
|
||||
info_text += ptype + "/"
|
||||
info_text = info_text.rstrip("/")
|
||||
else:
|
||||
info_text = "None"
|
||||
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text=info_text)
|
||||
|
||||
if wprops.whitewater_simulation_particles_expanded:
|
||||
column = box.column(align=True)
|
||||
column.enabled = is_whitewater_enabled
|
||||
|
||||
row = column.row()
|
||||
row.prop(wprops, "enable_foam")
|
||||
row.prop(wprops, "enable_bubbles")
|
||||
row.prop(wprops, "enable_spray")
|
||||
row.prop(wprops, "enable_dust")
|
||||
|
||||
box = self.layout.box()
|
||||
box.enabled = is_whitewater_enabled
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "emitter_settings_expanded",
|
||||
icon="TRIA_DOWN" if wprops.emitter_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Emitter Settings:")
|
||||
|
||||
if wprops.emitter_settings_expanded:
|
||||
column = box.column(align=True)
|
||||
|
||||
column.prop(wprops, "enable_whitewater_emission")
|
||||
|
||||
if show_advanced_whitewater:
|
||||
column = box.column(align=True)
|
||||
column.alert = highlight_advanced
|
||||
column.prop(wprops, "whitewater_emitter_generation_rate")
|
||||
|
||||
column = box.column(align=True)
|
||||
column.prop(wprops, "wavecrest_emission_rate")
|
||||
column.prop(wprops, "turbulence_emission_rate")
|
||||
column = column.column(align=True)
|
||||
column.enabled = wprops.enable_dust
|
||||
column.prop(wprops, "dust_emission_rate")
|
||||
|
||||
column = box.column(align=True)
|
||||
column.prop(wprops, "spray_emission_speed", slider=True)
|
||||
|
||||
if show_advanced_whitewater:
|
||||
column = box.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.prop(wprops.min_max_whitewater_energy_speed, "value_min")
|
||||
row.prop(wprops.min_max_whitewater_energy_speed, "value_max")
|
||||
|
||||
row = column.row(align=True)
|
||||
row.alert = highlight_advanced
|
||||
row.prop(wprops.min_max_whitewater_wavecrest_curvature, "value_min")
|
||||
row.prop(wprops.min_max_whitewater_wavecrest_curvature, "value_max")
|
||||
|
||||
row = column.row(align=True)
|
||||
row.alert = highlight_advanced
|
||||
row.prop(wprops.min_max_whitewater_turbulence, "value_min")
|
||||
row.prop(wprops.min_max_whitewater_turbulence, "value_max")
|
||||
else:
|
||||
column = box.column()
|
||||
row = column.row(align=True)
|
||||
row.prop(wprops.min_max_whitewater_energy_speed, "value_min")
|
||||
row.prop(wprops.min_max_whitewater_energy_speed, "value_max")
|
||||
|
||||
column = box.column(align=True)
|
||||
column.prop(wprops, "max_whitewater_particles")
|
||||
|
||||
if show_advanced_whitewater:
|
||||
column = box.column(align=True)
|
||||
column.alert = highlight_advanced
|
||||
column.prop(wprops, "enable_whitewater_emission_near_boundary")
|
||||
|
||||
column = box.column(align=True)
|
||||
column.enabled = wprops.enable_dust
|
||||
column.prop(wprops, "enable_dust_emission_near_boundary", text="Enable dust emission near domain floor")
|
||||
|
||||
box = self.layout.box()
|
||||
box.enabled = is_whitewater_enabled
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "particle_settings_expanded",
|
||||
icon="TRIA_DOWN" if wprops.particle_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Particle Behavior Settings:")
|
||||
|
||||
if wprops.particle_settings_expanded:
|
||||
column = box.column()
|
||||
column.label(text="Foam:")
|
||||
|
||||
row = column.row()
|
||||
row.prop(wprops, "foam_advection_strength", text="Advection Strength", slider=True)
|
||||
|
||||
if show_advanced_whitewater:
|
||||
row = column.row()
|
||||
row.alert = highlight_advanced
|
||||
row.prop(wprops, "foam_layer_depth", text="Depth", slider=True)
|
||||
|
||||
row = column.row()
|
||||
row.alert = highlight_advanced
|
||||
row.prop(wprops, "foam_layer_offset", text="Offset", slider=True)
|
||||
|
||||
column = box.column(align=True)
|
||||
column.label(text="Bubble:")
|
||||
column.prop(wprops, "bubble_drag_coefficient", text="Drag Coefficient", slider=True)
|
||||
column.prop(wprops, "bubble_bouyancy_coefficient", text="Buoyancy Coefficient")
|
||||
|
||||
column = box.column(align=True)
|
||||
column.label(text="Spray:")
|
||||
column.prop(wprops, "spray_drag_coefficient", text="Drag Coefficient", slider=True)
|
||||
|
||||
column = box.column(align=True)
|
||||
column.enabled = wprops.enable_dust
|
||||
column.label(text="Dust:")
|
||||
column.prop(wprops, "dust_drag_coefficient", text="Drag Coefficient", slider=True)
|
||||
column.prop(wprops, "dust_bouyancy_coefficient", text="Buoyancy Coefficient")
|
||||
|
||||
column = box.column(align=True)
|
||||
split = column.split()
|
||||
column = split.column(align=True)
|
||||
column.label(text="Lifespan:")
|
||||
column.prop(wprops.min_max_whitewater_lifespan, "value_min", text="Min")
|
||||
column.prop(wprops.min_max_whitewater_lifespan, "value_max", text="Max")
|
||||
column.prop(wprops, "whitewater_lifespan_variance", text="Variance")
|
||||
|
||||
column = split.column(align=True)
|
||||
column.label(text="Lifespan Modifiers:")
|
||||
column.prop(wprops, "foam_lifespan_modifier", text="Foam")
|
||||
column.prop(wprops, "bubble_lifespan_modifier", text="Bubble")
|
||||
column.prop(wprops, "spray_lifespan_modifier", text="Spray")
|
||||
column = column.column(align=True)
|
||||
column.enabled = wprops.enable_dust
|
||||
column.prop(wprops, "dust_lifespan_modifier", text="Dust")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "boundary_behaviour_settings_expanded",
|
||||
icon="TRIA_DOWN" if wprops.boundary_behaviour_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Domain Boundary Collisions:")
|
||||
|
||||
if not wprops.boundary_behaviour_settings_expanded:
|
||||
info_text = ""
|
||||
if wprops.whitewater_boundary_collisions_mode == 'BOUNDARY_COLLISIONS_MODE_INHERIT':
|
||||
info_text = "Inherit"
|
||||
elif wprops.whitewater_boundary_collisions_mode == 'BOUNDARY_COLLISIONS_MODE_CUSTOM':
|
||||
info_text = "Custom"
|
||||
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text=info_text)
|
||||
|
||||
if wprops.boundary_behaviour_settings_expanded:
|
||||
column = box.column()
|
||||
row = column.row(align=True)
|
||||
row.prop(wprops, "whitewater_boundary_collisions_mode", expand=True)
|
||||
|
||||
if wprops.whitewater_boundary_collisions_mode == 'BOUNDARY_COLLISIONS_MODE_INHERIT':
|
||||
sprops = dprops.simulation
|
||||
column = box.column()
|
||||
column.enabled = False
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=0, text="X –")
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=1, text="X+")
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=2, text="Y –")
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=3, text="Y+")
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=4, text="Z –")
|
||||
row.prop(sprops, "fluid_boundary_collisions", index=5, text="Z+")
|
||||
else:
|
||||
split = column.split(align=True)
|
||||
column1 = split.column(align=True)
|
||||
column2 = split.column(align=True)
|
||||
column3 = split.column(align=True)
|
||||
column4 = split.column(align=True)
|
||||
|
||||
column1.label(text="Foam:")
|
||||
row = column1.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "foam_boundary_collisions", index=0, text="X –")
|
||||
row.prop(wprops, "foam_boundary_collisions", index=1, text="X+")
|
||||
row = column1.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "foam_boundary_collisions", index=2, text="Y –")
|
||||
row.prop(wprops, "foam_boundary_collisions", index=3, text="Y+")
|
||||
row = column1.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "foam_boundary_collisions", index=4, text="Z –")
|
||||
row.prop(wprops, "foam_boundary_collisions", index=5, text="Z+")
|
||||
|
||||
column2.label(text="Bubble:")
|
||||
row = column2.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "bubble_boundary_collisions", index=0, text="X –")
|
||||
row.prop(wprops, "bubble_boundary_collisions", index=1, text="X+")
|
||||
row = column2.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "bubble_boundary_collisions", index=2, text="Y –")
|
||||
row.prop(wprops, "bubble_boundary_collisions", index=3, text="Y+")
|
||||
row = column2.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "bubble_boundary_collisions", index=4, text="Z –")
|
||||
row.prop(wprops, "bubble_boundary_collisions", index=5, text="Z+")
|
||||
|
||||
column3.label(text="Spray:")
|
||||
row = column3.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "spray_boundary_collisions", index=0, text="X –")
|
||||
row.prop(wprops, "spray_boundary_collisions", index=1, text="X+")
|
||||
row = column3.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "spray_boundary_collisions", index=2, text="Y –")
|
||||
row.prop(wprops, "spray_boundary_collisions", index=3, text="Y+")
|
||||
row = column3.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "spray_boundary_collisions", index=4, text="Z –")
|
||||
row.prop(wprops, "spray_boundary_collisions", index=5, text="Z+")
|
||||
|
||||
column4.label(text="Dust:")
|
||||
row = column4.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "dust_boundary_collisions", index=0, text="X –")
|
||||
row.prop(wprops, "dust_boundary_collisions", index=1, text="X+")
|
||||
row = column4.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "dust_boundary_collisions", index=2, text="Y –")
|
||||
row.prop(wprops, "dust_boundary_collisions", index=3, text="Y+")
|
||||
row = column4.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(wprops, "dust_boundary_collisions", index=4, text="Z –")
|
||||
row.prop(wprops, "dust_boundary_collisions", index=5, text="Z+")
|
||||
|
||||
box = self.layout.box()
|
||||
box.enabled = is_whitewater_enabled
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "obstacle_settings_expanded",
|
||||
icon="TRIA_DOWN" if wprops.obstacle_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Obstacle Influence Settings:")
|
||||
|
||||
if wprops.obstacle_settings_expanded:
|
||||
column = box.column(align=True)
|
||||
|
||||
# The following properties are probably set at reasonable values and
|
||||
# are not needed by the user
|
||||
"""
|
||||
column.prop(wprops, "obstacle_influence_base_level", text="Base Level")
|
||||
column.prop(wprops, "obstacle_influence_decay_rate", text="Decay Rate")
|
||||
"""
|
||||
|
||||
obstacle_objects = context.scene.flip_fluid.get_obstacle_objects()
|
||||
indent_str = 5 * " "
|
||||
column.label(text="Obstacle Object Influence:")
|
||||
if len(obstacle_objects) == 0:
|
||||
column.label(text=indent_str + "No obstacle objects found...")
|
||||
else:
|
||||
split = vcu.ui_split(column, factor=0.25, align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
for ob in obstacle_objects:
|
||||
pgroup = ob.flip_fluid.get_property_group()
|
||||
column_left.label(text=ob.name, icon="OBJECT_DATA")
|
||||
row = column_right.row()
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(pgroup, "whitewater_influence", text="influence")
|
||||
row.prop(pgroup, "dust_emission_strength", text="dust emission")
|
||||
|
||||
_draw_whitewater_display_settings(self, context)
|
||||
_draw_geometry_attributes_menu(self, context)
|
||||
|
||||
self.layout.separator()
|
||||
column = self.layout.column(align=True)
|
||||
column.operator("flip_fluid_operators.helper_delete_whitewater_objects", icon="X").whitewater_type = 'TYPE_ALL'
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypeWhitewaterPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypeWhitewaterPanel)
|
||||
@@ -0,0 +1,385 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy, math
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
def format_number_precision(self, value):
|
||||
value_str = '{:.9f}'.format(value)
|
||||
return value_str
|
||||
|
||||
|
||||
class FLIPFLUID_PT_DomainTypeFluidWorldPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid World"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if vcu.get_addon_preferences(context).enable_tabbed_domain_settings_view:
|
||||
return False
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_DOMAIN" and not is_addon_disabled
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
sprops = obj.flip_fluid.domain.simulation
|
||||
attrprops = obj.flip_fluid.domain.surface
|
||||
wprops = obj.flip_fluid.domain.world
|
||||
aprops = obj.flip_fluid.domain.advanced
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "world_scale_settings_expanded",
|
||||
icon="TRIA_DOWN" if wprops.world_scale_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="World Scale:")
|
||||
|
||||
if not wprops.world_scale_settings_expanded:
|
||||
xdims, ydims, zdims = wprops.get_simulation_dimensions(context)
|
||||
xdims_str = '{:.2f}'.format(round(xdims, 2)) + " m"
|
||||
ydims_str = '{:.2f}'.format(round(ydims, 2)) + " m"
|
||||
zdims_str = '{:.2f}'.format(round(zdims, 2)) + " m"
|
||||
|
||||
info_text = xdims_str + " x " + ydims_str + " x " + zdims_str
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text=info_text)
|
||||
|
||||
if wprops.world_scale_settings_expanded:
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "world_scale_mode", expand=True)
|
||||
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column()
|
||||
column_right = split.column()
|
||||
|
||||
if wprops.world_scale_mode == 'WORLD_SCALE_MODE_RELATIVE':
|
||||
row = column_left.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="1 Blender Unit = ")
|
||||
column_right.prop(wprops, "world_scale_relative")
|
||||
else:
|
||||
row = column_left.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Domain Length = ")
|
||||
column_right.prop(wprops, "world_scale_absolute")
|
||||
|
||||
row = column_left.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Simulation dimensions: X = ")
|
||||
row = column_left.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Y = ")
|
||||
row = column_left.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Z = ")
|
||||
|
||||
xdims, ydims, zdims = wprops.get_simulation_dimensions(context)
|
||||
xdims_str = '{:.2f}'.format(round(xdims, 2)) + " m"
|
||||
ydims_str = '{:.2f}'.format(round(ydims, 2)) + " m"
|
||||
zdims_str = '{:.2f}'.format(round(zdims, 2)) + " m"
|
||||
|
||||
column_right.label(text=xdims_str)
|
||||
column_right.label(text=ydims_str)
|
||||
column_right.label(text=zdims_str)
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "force_field_settings_expanded",
|
||||
icon="TRIA_DOWN" if wprops.force_field_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Gravity and Force Fields:")
|
||||
|
||||
if wprops.force_field_settings_expanded:
|
||||
|
||||
subbox = box.box()
|
||||
subbox.label(text="Gravity:")
|
||||
row = subbox.row(align=True)
|
||||
row.prop(wprops, "gravity_type", expand=True)
|
||||
|
||||
column = subbox.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column()
|
||||
column_right = split.column()
|
||||
|
||||
gvector = wprops.get_gravity_vector()
|
||||
magnitude = (gvector[0] * gvector[0] + gvector[1] * gvector[1] + gvector[2] * gvector[2])**(1.0/2.0)
|
||||
gforce = magnitude / 9.81
|
||||
mag_str = '{:.2f}'.format(round(magnitude, 2))
|
||||
gforce_str = '{:.2f}'.format(round(gforce, 2))
|
||||
|
||||
if wprops.gravity_type == 'GRAVITY_TYPE_SCENE':
|
||||
column_left.label(text="")
|
||||
row = column_left.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="magnitude = " + mag_str)
|
||||
row = column_left.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="g-force = " + gforce_str)
|
||||
|
||||
column_right.enabled = not (wprops.gravity_type == 'GRAVITY_TYPE_SCENE')
|
||||
|
||||
if wprops.gravity_type == 'GRAVITY_TYPE_SCENE':
|
||||
column_right.prop(context.scene, "use_gravity", text="Gravity Enabled")
|
||||
column_right.prop(context.scene, "gravity", text="")
|
||||
elif wprops.gravity_type == 'GRAVITY_TYPE_CUSTOM':
|
||||
column_right.prop(wprops, "gravity", text="")
|
||||
|
||||
column = subbox.column(align=True)
|
||||
column.operator("flip_fluid_operators.make_zero_gravity")
|
||||
|
||||
subbox = box.box()
|
||||
subbox.label(text="Force Field Resolution:")
|
||||
column = subbox.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.prop(wprops, "force_field_resolution", expand=True)
|
||||
|
||||
column = subbox.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
|
||||
field_resolution = sprops.resolution
|
||||
if wprops.force_field_resolution == 'FORCE_FIELD_RESOLUTION_LOW':
|
||||
field_resolution = int(math.ceil(field_resolution / 4))
|
||||
elif wprops.force_field_resolution == 'FORCE_FIELD_RESOLUTION_NORMAL':
|
||||
field_resolution = int(math.ceil(field_resolution / 3))
|
||||
elif wprops.force_field_resolution == 'FORCE_FIELD_RESOLUTION_HIGH':
|
||||
field_resolution = int(math.ceil(field_resolution / 2))
|
||||
|
||||
row = column_left.row()
|
||||
row.prop(wprops, "force_field_resolution_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text="Grid resolution: ")
|
||||
column_right.label(text=str(field_resolution))
|
||||
|
||||
subbox = box.box()
|
||||
subbox.label(text="Force Field Weights:")
|
||||
column = subbox.column(align=True)
|
||||
column.prop(wprops, "force_field_weight_fluid_particles", slider=True)
|
||||
column.prop(wprops, "force_field_weight_whitewater_foam", slider=True)
|
||||
column.prop(wprops, "force_field_weight_whitewater_bubble", slider=True)
|
||||
column.prop(wprops, "force_field_weight_whitewater_spray", slider=True)
|
||||
column.prop(wprops, "force_field_weight_whitewater_dust", slider=True)
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "viscosity_settings_expanded",
|
||||
icon="TRIA_DOWN" if wprops.viscosity_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
if not wprops.viscosity_settings_expanded:
|
||||
row.prop(wprops, "enable_viscosity", text="")
|
||||
row.label(text="Viscosity:")
|
||||
|
||||
is_variable_viscosity_enabled = attrprops.enable_viscosity_attribute
|
||||
if not wprops.viscosity_settings_expanded:
|
||||
if not is_variable_viscosity_enabled:
|
||||
total_viscosity = wprops.viscosity * (10**(-wprops.viscosity_exponent))
|
||||
total_viscosity_str = format_number_precision(self, total_viscosity)
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.enabled = wprops.enable_viscosity
|
||||
row.label(text=total_viscosity_str)
|
||||
|
||||
if wprops.viscosity_settings_expanded:
|
||||
column = box.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.prop(wprops, "enable_viscosity")
|
||||
|
||||
if vcu.get_addon_preferences().is_extra_features_enabled():
|
||||
row = row.row(align=True)
|
||||
row.enabled = wprops.enable_viscosity
|
||||
row.prop(attrprops, "enable_viscosity_attribute", text="Variable Viscosity")
|
||||
|
||||
column = box.column(align=True)
|
||||
column.enabled = wprops.enable_viscosity
|
||||
|
||||
if is_variable_viscosity_enabled:
|
||||
column.label(text="Variable viscosity values can be set in the", icon='INFO')
|
||||
column.label(text="Fluid or Inflow physics properties menu", icon='INFO')
|
||||
else:
|
||||
column.prop(wprops, "viscosity", text="Base")
|
||||
column.prop(wprops, "viscosity_exponent", text="Exponent")
|
||||
|
||||
column.prop(wprops, "viscosity_solver_error_tolerance", text="Solver Accuracy", slider=True)
|
||||
|
||||
if is_variable_viscosity_enabled:
|
||||
column.label(text="")
|
||||
else:
|
||||
total_viscosity = wprops.viscosity * (10**(-wprops.viscosity_exponent))
|
||||
total_viscosity_str = "Total viscosity = " + format_number_precision(self, total_viscosity)
|
||||
column.label(text=total_viscosity_str)
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "surface_tension_settings_expanded",
|
||||
icon="TRIA_DOWN" if wprops.surface_tension_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
if not wprops.surface_tension_settings_expanded:
|
||||
row.prop(wprops, "enable_surface_tension", text="")
|
||||
row.label(text="Surface Tension:")
|
||||
|
||||
if not wprops.surface_tension_settings_expanded:
|
||||
total_surface_tension = wprops.get_surface_tension_value()
|
||||
surface_tension_str = format_number_precision(self, total_surface_tension)
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.enabled = wprops.enable_surface_tension
|
||||
row.alert = wprops.enable_surface_tension and wprops.minimum_surface_tension_substeps > aprops.min_max_time_steps_per_frame.value_max
|
||||
row.label(text=surface_tension_str)
|
||||
|
||||
if wprops.surface_tension_settings_expanded:
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.prop(wprops, "enable_surface_tension")
|
||||
column_left.label(text="")
|
||||
column_left.label(text="")
|
||||
column_left = column_left.column(align=True)
|
||||
column_left.enabled = wprops.enable_surface_tension
|
||||
row = column_left.row(align=True)
|
||||
row.enabled = wprops.enable_surface_tension
|
||||
row.alignment='RIGHT'
|
||||
row.label(text="Total Surface Tension =")
|
||||
row = column_left.row(align=True)
|
||||
row.enabled = wprops.enable_surface_tension
|
||||
row.alignment='RIGHT'
|
||||
row.prop(wprops, "surface_tension_substeps_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text="Estimated substeps =")
|
||||
|
||||
total_surface_tension = wprops.get_surface_tension_value()
|
||||
surface_tension_str = format_number_precision(self, total_surface_tension)
|
||||
|
||||
column_right = split.column(align=True)
|
||||
column_right.enabled = wprops.enable_surface_tension
|
||||
column_right.prop(wprops, "surface_tension", text="Base")
|
||||
column_right.prop(wprops, "surface_tension_exponent", text="Exponent")
|
||||
column_right.prop(wprops, "surface_tension_accuracy", text="Solver Accuracy")
|
||||
column_right.label(text=surface_tension_str)
|
||||
column_right.label(text=str(wprops.minimum_surface_tension_substeps))
|
||||
|
||||
if wprops.enable_surface_tension and wprops.minimum_surface_tension_substeps > aprops.min_max_time_steps_per_frame.value_max:
|
||||
row = column.row(align=True)
|
||||
row.alert = True
|
||||
row.prop(wprops, "surface_tension_substeps_exceeded_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text=" Warning: Too Many Substeps")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "sheeting_settings_expanded",
|
||||
icon="TRIA_DOWN" if wprops.sheeting_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
if not wprops.sheeting_settings_expanded:
|
||||
row.prop(wprops, "enable_sheet_seeding", text="")
|
||||
row.label(text="Sheeting Effects:")
|
||||
|
||||
if wprops.sheeting_settings_expanded:
|
||||
box.label(text="Sheeting Effects:")
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.prop(wprops, "enable_sheet_seeding")
|
||||
column_right = split.column(align=True)
|
||||
column_right.enabled = wprops.enable_sheet_seeding
|
||||
column_right.prop(wprops, "sheet_fill_rate")
|
||||
column_right.prop(wprops, "sheet_fill_threshold")
|
||||
|
||||
obstacle_objects = context.scene.flip_fluid.get_obstacle_objects()
|
||||
indent_str = 5 * " "
|
||||
column.label(text="Obstacle Sheeting:")
|
||||
if len(obstacle_objects) == 0:
|
||||
column.label(text=indent_str + "No obstacle objects found...")
|
||||
else:
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
for ob in obstacle_objects:
|
||||
pgroup = ob.flip_fluid.get_property_group()
|
||||
column_left.label(text=ob.name, icon="OBJECT_DATA")
|
||||
column_right.prop(pgroup, "sheeting_strength", text="Strength Scale")
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "friction_settings_expanded",
|
||||
icon="TRIA_DOWN" if wprops.friction_settings_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Friction:")
|
||||
|
||||
if not wprops.friction_settings_expanded:
|
||||
row = row.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.label(text="Boundary Friction ")
|
||||
row.prop(wprops, "boundary_friction", text="")
|
||||
|
||||
if wprops.friction_settings_expanded:
|
||||
column = box.column()
|
||||
split = column.split(align=True)
|
||||
column_left = split.column()
|
||||
column_left.label(text="Boundary Friction:")
|
||||
column_right = split.column()
|
||||
column_right.prop(wprops, "boundary_friction", text="")
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(wprops, "obstacle_friction_expanded",
|
||||
icon="TRIA_DOWN" if wprops.obstacle_friction_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False
|
||||
)
|
||||
row.label(text="Obstacle Friction:")
|
||||
|
||||
if wprops.obstacle_friction_expanded:
|
||||
obstacle_objects = context.scene.flip_fluid.get_obstacle_objects()
|
||||
|
||||
column = box.column(align=True)
|
||||
indent_str = 5 * " "
|
||||
if len(obstacle_objects) == 0:
|
||||
column.label(text=indent_str + "No obstacle objects found...")
|
||||
else:
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
for ob in obstacle_objects:
|
||||
pgroup = ob.flip_fluid.get_property_group()
|
||||
column_left.label(text=ob.name, icon="OBJECT_DATA")
|
||||
column_right.prop(pgroup, "friction")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_DomainTypeFluidWorldPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_DomainTypeFluidWorldPanel)
|
||||
@@ -0,0 +1,56 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_FLIPFluidsAddonDisabledPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluids Addon Disabled"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return is_addon_disabled
|
||||
|
||||
def draw(self, context):
|
||||
column = self.layout.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.alert = True
|
||||
row.label(text="FLIP Fluids Addon has been disabled in this Blend file", icon="INFO")
|
||||
row = column.row(align=True)
|
||||
row.alert = True
|
||||
row.label(text="Click to re-enable and use the addon", icon="INFO")
|
||||
|
||||
operator_name = "flip_fluid_operators.enable_addon_in_blend_file"
|
||||
icon = context.scene.flip_fluid.get_logo_icon()
|
||||
if icon is not None:
|
||||
column.operator(operator_name, icon_value=icon.icon_id)
|
||||
else:
|
||||
column.operator(operator_name, icon='X')
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_FLIPFluidsAddonDisabledPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_FLIPFluidsAddonDisabledPanel)
|
||||
@@ -0,0 +1,247 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_FluidTypePanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid"
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == 'TYPE_FLUID' and not is_addon_disabled
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
obj_props = obj.flip_fluid
|
||||
fluid_props = obj_props.fluid
|
||||
dprops = context.scene.flip_fluid.get_domain_properties()
|
||||
|
||||
show_disabled_in_viewport_warning = True
|
||||
if show_disabled_in_viewport_warning and obj.hide_viewport:
|
||||
box = self.layout.box()
|
||||
box.alert = True
|
||||
column = box.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(fluid_props, "disabled_in_viewport_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text="Object is currently disabled in the viewport")
|
||||
row.label(text="", icon="RESTRICT_VIEW_ON")
|
||||
column.label(text="This object will not be included within the simulation")
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(obj_props, "object_type")
|
||||
column.separator()
|
||||
|
||||
column.label(text="Trigger:")
|
||||
row = column.row(align= True)
|
||||
row.prop(fluid_props, "frame_offset_type", text = "")
|
||||
if fluid_props.frame_offset_type == 'OFFSET_TYPE_FRAME':
|
||||
row.prop(fluid_props, "frame_offset")
|
||||
elif fluid_props.frame_offset_type == 'OFFSET_TYPE_TIMELINE':
|
||||
row.prop(fluid_props, "timeline_offset")
|
||||
self.layout.separator()
|
||||
column.prop(fluid_props, "priority", text="Priority Level")
|
||||
|
||||
box = self.layout.box()
|
||||
box.label(text="Fluid Velocity Mode:")
|
||||
row = box.row(align=True)
|
||||
row.prop(fluid_props, "fluid_velocity_mode", expand=True)
|
||||
|
||||
if fluid_props.fluid_velocity_mode == 'FLUID_VELOCITY_MANUAL':
|
||||
column = box.column(align=True)
|
||||
column.label(text="Fluid Velocity:")
|
||||
row = column.row(align=True)
|
||||
row.prop(fluid_props, "initial_velocity", text="")
|
||||
row = column.row(align=True)
|
||||
row.label(text="")
|
||||
elif fluid_props.fluid_velocity_mode == 'FLUID_VELOCITY_AXIS':
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.label(text="Fluid Speed:")
|
||||
column_left.prop(fluid_props, "initial_speed")
|
||||
|
||||
column_right = split.column(align=True)
|
||||
column_right.label(text="Local Axis:")
|
||||
row = column_right.row(align=True)
|
||||
row.prop_enum(fluid_props, "fluid_axis_mode", 'LOCAL_AXIS_POS_X')
|
||||
row.prop_enum(fluid_props, "fluid_axis_mode", 'LOCAL_AXIS_POS_Y')
|
||||
row.prop_enum(fluid_props, "fluid_axis_mode", 'LOCAL_AXIS_POS_Z')
|
||||
row = column_right.row(align=True)
|
||||
row.prop_enum(fluid_props, "fluid_axis_mode", 'LOCAL_AXIS_NEG_X')
|
||||
row.prop_enum(fluid_props, "fluid_axis_mode", 'LOCAL_AXIS_NEG_Y')
|
||||
row.prop_enum(fluid_props, "fluid_axis_mode", 'LOCAL_AXIS_NEG_Z')
|
||||
|
||||
elif fluid_props.fluid_velocity_mode == 'FLUID_VELOCITY_TARGET':
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.label(text="Fluid Speed:")
|
||||
column_left.prop(fluid_props, "initial_speed")
|
||||
|
||||
target_collection = vcu.get_scene_collection()
|
||||
if vcu.is_blender_28():
|
||||
search_group = "all_objects"
|
||||
else:
|
||||
search_group = "objects"
|
||||
|
||||
column_right = split.column(align=True)
|
||||
column_right.label(text="Target Object:")
|
||||
column_right.prop_search(fluid_props, "target_object", target_collection, search_group, text="")
|
||||
|
||||
target_object = fluid_props.get_target_object()
|
||||
if target_object is not None:
|
||||
is_target_domain = target_object.flip_fluid.is_domain()
|
||||
target_props = target_object.flip_fluid.get_property_group()
|
||||
if target_props is not None and not is_target_domain:
|
||||
column_right.prop(target_props, "export_animated_mesh", text="Export Animated Target")
|
||||
else:
|
||||
column_right.prop(fluid_props, "export_animated_target")
|
||||
else:
|
||||
column_right.prop(fluid_props, "export_animated_target")
|
||||
|
||||
|
||||
column = box.column(align=True)
|
||||
split = vcu.ui_split(column, factor=0.66)
|
||||
column = split.column()
|
||||
column.prop(fluid_props, "append_object_velocity")
|
||||
column = split.column()
|
||||
column.enabled = fluid_props.append_object_velocity
|
||||
column.prop(fluid_props, "append_object_velocity_influence")
|
||||
|
||||
if vcu.get_addon_preferences().is_extra_features_enabled():
|
||||
box = self.layout.box()
|
||||
box.label(text="Geometry Attributes:")
|
||||
column = box.column(align=True)
|
||||
if vcu.is_blender_293():
|
||||
is_color_attribute_enabled = dprops is not None and (dprops.surface.enable_color_attribute or
|
||||
dprops.particles.enable_fluid_particle_color_attribute)
|
||||
show_color = dprops is not None and is_color_attribute_enabled
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.enabled = show_color
|
||||
column_left.prop(fluid_props, "color")
|
||||
column_right = split.column(align=True)
|
||||
column_right.label(text="")
|
||||
row = column_right.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
if dprops is not None and not show_color:
|
||||
row.operator("flip_fluid_operators.enable_color_attribute_tooltip",
|
||||
text="Enable Color Attribute", icon="PLUS", emboss=False)
|
||||
if dprops is None:
|
||||
row.label(text="Domain required for this option")
|
||||
column.separator()
|
||||
|
||||
show_viscosity = dprops is not None and dprops.surface.enable_viscosity_attribute
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.enabled = show_viscosity
|
||||
column_left.prop(fluid_props, "viscosity")
|
||||
column_right = split.column(align=True)
|
||||
row = column_right.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
if dprops is not None and not show_viscosity:
|
||||
row.operator("flip_fluid_operators.enable_viscosity_attribute_tooltip",
|
||||
text="Enable Viscosity Attribute", icon="PLUS", emboss=False)
|
||||
if dprops is None:
|
||||
row.label(text="Domain required for this option")
|
||||
column.separator()
|
||||
|
||||
is_lifetime_attribute_enabled = dprops is not None and (dprops.surface.enable_lifetime_attribute or
|
||||
dprops.particles.enable_fluid_particle_lifetime_attribute)
|
||||
show_lifetime = dprops is not None and is_lifetime_attribute_enabled
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.enabled = show_lifetime
|
||||
column_left.prop(fluid_props, "lifetime")
|
||||
column_right = split.column(align=True)
|
||||
row = column_right.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
if dprops is not None and not show_lifetime:
|
||||
row.operator("flip_fluid_operators.enable_lifetime_attribute_tooltip",
|
||||
text="Enable Lifetime Attribute", icon="PLUS", emboss=False)
|
||||
elif dprops is not None:
|
||||
row.alignment = 'EXPAND'
|
||||
row.prop(fluid_props, "lifetime_variance", text="Variance")
|
||||
if dprops is None:
|
||||
row.label(text="Domain required for this option")
|
||||
column.separator()
|
||||
|
||||
is_source_id_attribute_enabled = dprops is not None and (dprops.surface.enable_source_id_attribute or
|
||||
dprops.particles.enable_fluid_particle_source_id_attribute)
|
||||
show_source_id = dprops is not None and is_source_id_attribute_enabled
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.enabled = show_source_id
|
||||
column_left.prop(fluid_props, "source_id")
|
||||
column_right = split.column(align=True)
|
||||
row = column_right.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
if dprops is not None and not show_source_id:
|
||||
row.operator("flip_fluid_operators.enable_source_id_attribute_tooltip",
|
||||
text="Enable Source ID Attribute", icon="PLUS", emboss=False)
|
||||
if dprops is None:
|
||||
row.label(text="Domain required for this option")
|
||||
column.separator()
|
||||
else:
|
||||
column.enabled = False
|
||||
column.label(text="Geometry attribute features are only available in", icon='ERROR')
|
||||
column.label(text="Blender 2.93 or later", icon='ERROR')
|
||||
|
||||
box = self.layout.box()
|
||||
box.label(text="Mesh Data Export:")
|
||||
column = box.column(align=True)
|
||||
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(fluid_props, "export_animated_mesh")
|
||||
|
||||
is_child_object = obj.parent is not None
|
||||
is_hint_enabled = not vcu.get_addon_preferences().dismiss_export_animated_mesh_parented_relation_hint
|
||||
if is_hint_enabled and not fluid_props.export_animated_mesh and is_child_object:
|
||||
row.prop(context.scene.flip_fluid_helper, "export_animated_mesh_parent_tooltip",
|
||||
icon="QUESTION", emboss=False, text=""
|
||||
)
|
||||
row.label(text="←Hint: export option may be required")
|
||||
|
||||
column.prop(fluid_props, "skip_reexport")
|
||||
column.separator()
|
||||
column = box.column(align=True)
|
||||
column.enabled = fluid_props.skip_reexport
|
||||
column.prop(fluid_props, "force_reexport_on_next_bake", toggle=True)\
|
||||
|
||||
column = self.layout.column(align=True)
|
||||
column.separator()
|
||||
column.operator("flip_fluid_operators.copy_setting_to_selected", icon='COPYDOWN')
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_FluidTypePanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_FluidTypePanel)
|
||||
@@ -0,0 +1,243 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy, math
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_ForceFieldTypePanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_FORCE_FIELD" and not is_addon_disabled
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
obj_props = obj.flip_fluid
|
||||
force_field_props = obj_props.force_field
|
||||
|
||||
show_disabled_in_viewport_warning = True
|
||||
if show_disabled_in_viewport_warning and obj.hide_viewport:
|
||||
box = self.layout.box()
|
||||
box.alert = True
|
||||
column = box.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(force_field_props, "disabled_in_viewport_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text="Object is currently disabled in the viewport")
|
||||
row.label(text="", icon="RESTRICT_VIEW_ON")
|
||||
column.label(text="This object will not be included within the simulation")
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(obj_props, "object_type")
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(force_field_props, "force_field_type", text="Mode")
|
||||
|
||||
if obj.type == 'CURVE' and force_field_props.force_field_type != 'FORCE_FIELD_TYPE_CURVE':
|
||||
column = self.layout.column()
|
||||
column.label(text="Curve objects can only be set as a Curve Guide Force Field")
|
||||
return
|
||||
|
||||
if obj.type == 'EMPTY' and force_field_props.force_field_type != 'FORCE_FIELD_TYPE_POINT':
|
||||
column = self.layout.column()
|
||||
column.label(text="Empty objects can only be set as a Point Force Field")
|
||||
return
|
||||
|
||||
if obj.type == 'MESH' and force_field_props.force_field_type == 'FORCE_FIELD_TYPE_CURVE':
|
||||
column = self.layout.column()
|
||||
column.label(text="Mesh objects cannot be used as a Curve Guide Force Field")
|
||||
column.label(text="Curve Guide Force Fields only support Curve objects")
|
||||
return
|
||||
|
||||
elif force_field_props.force_field_type == 'FORCE_FIELD_TYPE_VORTEX':
|
||||
column = self.layout.column()
|
||||
column.label(text="Vortex force is not yet available")
|
||||
column.label(text="Feature implementation in progress")
|
||||
return
|
||||
elif force_field_props.force_field_type == 'FORCE_FIELD_TYPE_TURBULENCE':
|
||||
column = self.layout.column()
|
||||
column.label(text="Turbulence guided force is not yet available")
|
||||
column.label(text="Feature implementation in progress")
|
||||
return
|
||||
elif force_field_props.force_field_type == 'FORCE_FIELD_TYPE_PROGRAMMABLE':
|
||||
column = self.layout.column()
|
||||
column.label(text="Programmable guided force is not yet available")
|
||||
column.label(text="Feature implementation in progress")
|
||||
return
|
||||
elif force_field_props.force_field_type == 'FORCE_FIELD_TYPE_OTHER':
|
||||
column = self.layout.column()
|
||||
column.label(text="More force field modes are in development")
|
||||
column.label(text="Try out our experimental builds for the latest features!")
|
||||
column.operator(
|
||||
"wm.url_open",
|
||||
text="Experimental Builds",
|
||||
icon="WORLD"
|
||||
).url = "https://github.com/rlguy/Blender-FLIP-Fluids/wiki/Experimental-Builds"
|
||||
return
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(force_field_props, "is_enabled")
|
||||
|
||||
strength_text = "Strength"
|
||||
if force_field_props.force_field_type == 'FORCE_FIELD_TYPE_CURVE':
|
||||
strength_text = "Attraction Strength"
|
||||
|
||||
box = self.layout.box()
|
||||
box.label(text="Field Strength and Falloff:")
|
||||
column = box.column(align=True)
|
||||
column.prop(force_field_props, "strength", text=strength_text)
|
||||
|
||||
if force_field_props.force_field_type == 'FORCE_FIELD_TYPE_CURVE':
|
||||
column.prop(force_field_props, "flow_strength")
|
||||
column.prop(force_field_props, "spin_strength")
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'RIGHT'
|
||||
row.prop(force_field_props, "enable_endcaps")
|
||||
|
||||
column = box.column(align=True)
|
||||
if force_field_props.force_field_type == 'FORCE_FIELD_TYPE_POINT':
|
||||
split = column.split()
|
||||
column_left = split.column(align=True)
|
||||
column_right = split.column(align=True)
|
||||
column_left.prop(force_field_props, "falloff_power")
|
||||
column_right.enabled=False
|
||||
|
||||
# Todo: implement point shapes
|
||||
#column_right.prop(force_field_props, "falloff_shape", text="Shape")
|
||||
else:
|
||||
column.prop(force_field_props, "falloff_power")
|
||||
|
||||
split = column.split()
|
||||
col = split.column()
|
||||
row = col.row(align=True)
|
||||
row.prop(force_field_props, "enable_min_distance", text="")
|
||||
sub = row.row(align=True)
|
||||
sub.active = force_field_props.enable_min_distance
|
||||
sub.prop(force_field_props.min_max_distance, "value_min")
|
||||
|
||||
col = split.column()
|
||||
row = col.row(align=True)
|
||||
row.prop(force_field_props, "enable_max_distance", text="")
|
||||
sub = row.row(align=True)
|
||||
sub.active = force_field_props.enable_max_distance
|
||||
sub.prop(force_field_props.min_max_distance, "value_max")
|
||||
|
||||
eps = 1e-12
|
||||
power = force_field_props.falloff_power
|
||||
distance = eps
|
||||
if force_field_props.enable_min_distance:
|
||||
distance = max(force_field_props.min_max_distance.value_min, eps)
|
||||
denominator = math.pow(distance, force_field_props.falloff_power)
|
||||
|
||||
if denominator == 0.0:
|
||||
max_strength_str = "infinite"
|
||||
else:
|
||||
strength = abs(force_field_props.strength)
|
||||
max_strength = strength * (1.0 / denominator)
|
||||
limit_factor = force_field_props.maximum_force_limit_factor
|
||||
max_strength = min(max_strength, limit_factor * strength)
|
||||
max_strength_str = self._format_strength_value(max_strength)
|
||||
|
||||
column.separator()
|
||||
column.separator()
|
||||
column.prop(force_field_props, "maximum_force_limit_factor")
|
||||
|
||||
split = column.split()
|
||||
column = split.column()
|
||||
row = column.row()
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(force_field_props, "maximum_strength_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text="Max Force:")
|
||||
column = split.column()
|
||||
row = column.row()
|
||||
row.label(text=max_strength_str)
|
||||
|
||||
if force_field_props.force_field_type == 'FORCE_FIELD_TYPE_SURFACE':
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.label(text="Enabled Sides:")
|
||||
row.prop(force_field_props, "enable_frontfacing")
|
||||
row.prop(force_field_props, "enable_backfacing")
|
||||
row.prop(force_field_props, "enable_edgefacing")
|
||||
|
||||
self.layout.separator()
|
||||
box = self.layout.box()
|
||||
box.label(text="Antigravity")
|
||||
column = box.column(align=True)
|
||||
|
||||
if force_field_props.force_field_type == 'FORCE_FIELD_TYPE_POINT':
|
||||
column.prop(force_field_props, "gravity_scale_point", slider=True)
|
||||
column.prop(force_field_props, "gravity_scale_width_point", text="Width", slider=True)
|
||||
elif force_field_props.force_field_type == 'FORCE_FIELD_TYPE_SURFACE':
|
||||
column.prop(force_field_props, "gravity_scale_surface", slider=True)
|
||||
column.prop(force_field_props, "gravity_scale_width_surface", text="Width", slider=True)
|
||||
elif force_field_props.force_field_type == 'FORCE_FIELD_TYPE_VOLUME':
|
||||
column.prop(force_field_props, "gravity_scale_volume", slider=True)
|
||||
column.prop(force_field_props, "gravity_scale_width_volume", text="Width", slider=True)
|
||||
elif force_field_props.force_field_type == 'FORCE_FIELD_TYPE_CURVE':
|
||||
column.prop(force_field_props, "gravity_scale_curve", slider=True)
|
||||
column.prop(force_field_props, "gravity_scale_width_curve", text="Width", slider=True)
|
||||
|
||||
box = self.layout.box()
|
||||
box.label(text="Mesh Data Export:")
|
||||
column = box.column(align=True)
|
||||
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(force_field_props, "export_animated_mesh")
|
||||
|
||||
is_child_object = obj.parent is not None
|
||||
is_hint_enabled = not vcu.get_addon_preferences().dismiss_export_animated_mesh_parented_relation_hint
|
||||
if is_hint_enabled and not force_field_props.export_animated_mesh and is_child_object:
|
||||
row.prop(context.scene.flip_fluid_helper, "export_animated_mesh_parent_tooltip",
|
||||
icon="QUESTION", emboss=False, text=""
|
||||
)
|
||||
row.label(text="←Hint: export option may be required")
|
||||
|
||||
column.prop(force_field_props, "skip_reexport")
|
||||
column.separator()
|
||||
column = box.column(align=True)
|
||||
column.enabled = force_field_props.skip_reexport
|
||||
column.prop(force_field_props, "force_reexport_on_next_bake", toggle=True)
|
||||
|
||||
column = self.layout.column(align=True)
|
||||
column.separator()
|
||||
column.operator("flip_fluid_operators.copy_setting_to_selected", icon='COPYDOWN')
|
||||
|
||||
|
||||
def _format_strength_value(self, s):
|
||||
if s > 10:
|
||||
return '{0:.1f}'.format(s)
|
||||
if s > 1:
|
||||
return '{0:.2f}'.format(s)
|
||||
return '{0:.3f}'.format(s)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_ForceFieldTypePanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_ForceFieldTypePanel)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,242 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_InflowTypePanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == 'TYPE_INFLOW' and not is_addon_disabled
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
obj_props = obj.flip_fluid
|
||||
inflow_props = obj_props.inflow
|
||||
dprops = context.scene.flip_fluid.get_domain_properties()
|
||||
|
||||
show_disabled_in_viewport_warning = True
|
||||
if show_disabled_in_viewport_warning and obj.hide_viewport:
|
||||
box = self.layout.box()
|
||||
box.alert = True
|
||||
column = box.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(inflow_props, "disabled_in_viewport_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text="Object is currently disabled in the viewport")
|
||||
row.label(text="", icon="RESTRICT_VIEW_ON")
|
||||
column.label(text="This object will not be included within the simulation")
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(obj_props, "object_type")
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(inflow_props, "is_enabled")
|
||||
column.prop(inflow_props, "substep_emissions")
|
||||
column.prop(inflow_props, "priority", text="Priority Level")
|
||||
|
||||
column.separator()
|
||||
box = self.layout.box()
|
||||
box.label(text="Inflow Velocity Mode:")
|
||||
row = box.row(align=True)
|
||||
row.prop(inflow_props, "inflow_velocity_mode", expand=True)
|
||||
|
||||
if inflow_props.inflow_velocity_mode == 'INFLOW_VELOCITY_MANUAL':
|
||||
column = box.column(align=True)
|
||||
column.label(text="Inflow Velocity:")
|
||||
row = column.row(align=True)
|
||||
row.prop(inflow_props, "inflow_velocity", text="")
|
||||
row = column.row(align=True)
|
||||
row.label(text="")
|
||||
elif inflow_props.inflow_velocity_mode == 'INFLOW_VELOCITY_AXIS':
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.label(text="Inflow Speed:")
|
||||
column_left.prop(inflow_props, "inflow_speed")
|
||||
|
||||
column_right = split.column(align=True)
|
||||
column_right.label(text="Local Axis:")
|
||||
row = column_right.row(align=True)
|
||||
row.prop_enum(inflow_props, "inflow_axis_mode", 'LOCAL_AXIS_POS_X')
|
||||
row.prop_enum(inflow_props, "inflow_axis_mode", 'LOCAL_AXIS_POS_Y')
|
||||
row.prop_enum(inflow_props, "inflow_axis_mode", 'LOCAL_AXIS_POS_Z')
|
||||
row = column_right.row(align=True)
|
||||
row.prop_enum(inflow_props, "inflow_axis_mode", 'LOCAL_AXIS_NEG_X')
|
||||
row.prop_enum(inflow_props, "inflow_axis_mode", 'LOCAL_AXIS_NEG_Y')
|
||||
row.prop_enum(inflow_props, "inflow_axis_mode", 'LOCAL_AXIS_NEG_Z')
|
||||
elif inflow_props.inflow_velocity_mode == 'INFLOW_VELOCITY_TARGET':
|
||||
column = box.column(align=True)
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.label(text="Inflow Speed:")
|
||||
column_left.prop(inflow_props, "inflow_speed")
|
||||
|
||||
target_collection = vcu.get_scene_collection()
|
||||
if vcu.is_blender_28():
|
||||
search_group = "all_objects"
|
||||
else:
|
||||
search_group = "objects"
|
||||
|
||||
column_right = split.column(align=True)
|
||||
column_right.label(text="Target Object:")
|
||||
column_right.prop_search(inflow_props, "target_object", target_collection, search_group, text="")
|
||||
|
||||
target_object = inflow_props.get_target_object()
|
||||
if target_object is not None:
|
||||
is_target_domain = target_object.flip_fluid.is_domain()
|
||||
target_props = target_object.flip_fluid.get_property_group()
|
||||
if target_props is not None and not is_target_domain:
|
||||
column_right.prop(target_props, "export_animated_mesh", text="Export Animated Target")
|
||||
else:
|
||||
column_right.prop(inflow_props, "export_animated_target")
|
||||
else:
|
||||
column_right.prop(inflow_props, "export_animated_target")
|
||||
|
||||
box.separator()
|
||||
column = box.column(align=True)
|
||||
split = vcu.ui_split(column, factor=0.60)
|
||||
column = split.column()
|
||||
column.prop(inflow_props, "append_object_velocity")
|
||||
column = split.column(align=True)
|
||||
column.enabled = inflow_props.append_object_velocity
|
||||
column.prop(inflow_props, "append_object_velocity_influence")
|
||||
column = box.column(align=True)
|
||||
column.prop(inflow_props, "constrain_fluid_velocity")
|
||||
|
||||
if vcu.get_addon_preferences().is_extra_features_enabled():
|
||||
box = self.layout.box()
|
||||
box.label(text="Geometry Attributes:")
|
||||
column = box.column(align=True)
|
||||
if vcu.is_blender_293():
|
||||
is_color_attribute_enabled = dprops is not None and (dprops.surface.enable_color_attribute or
|
||||
dprops.particles.enable_fluid_particle_color_attribute)
|
||||
show_color = dprops is not None and is_color_attribute_enabled
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.enabled = show_color
|
||||
column_left.prop(inflow_props, "color")
|
||||
column_right = split.column(align=True)
|
||||
column_right.label(text="")
|
||||
row = column_right.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
if dprops is not None and not show_color:
|
||||
row.operator("flip_fluid_operators.enable_color_attribute_tooltip",
|
||||
text="Enable Color Attribute", icon="PLUS", emboss=False)
|
||||
if dprops is None:
|
||||
row.label(text="Domain required for this option")
|
||||
column.separator()
|
||||
|
||||
show_viscosity = dprops is not None and dprops.surface.enable_viscosity_attribute
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.enabled = show_viscosity
|
||||
column_left.prop(inflow_props, "viscosity")
|
||||
column_right = split.column(align=True)
|
||||
row = column_right.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
if dprops is not None and not show_viscosity:
|
||||
row.operator("flip_fluid_operators.enable_viscosity_attribute_tooltip",
|
||||
text="Enable Viscosity Attribute", icon="PLUS", emboss=False)
|
||||
if dprops is None:
|
||||
row.label(text="Domain required for this option")
|
||||
column.separator()
|
||||
|
||||
is_lifetime_attribute_enabled = dprops is not None and (dprops.surface.enable_lifetime_attribute or
|
||||
dprops.particles.enable_fluid_particle_lifetime_attribute)
|
||||
show_lifetime = dprops is not None and is_lifetime_attribute_enabled
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.enabled = show_lifetime
|
||||
column_left.prop(inflow_props, "lifetime")
|
||||
column_right = split.column(align=True)
|
||||
row = column_right.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
if dprops is not None and not show_lifetime:
|
||||
row.operator("flip_fluid_operators.enable_lifetime_attribute_tooltip",
|
||||
text="Enable Lifetime Attribute", icon="PLUS", emboss=False)
|
||||
elif dprops is not None:
|
||||
row.alignment = 'EXPAND'
|
||||
row.prop(inflow_props, "lifetime_variance", text="Variance")
|
||||
if dprops is None:
|
||||
row.label(text="Domain required for this option")
|
||||
column.separator()
|
||||
|
||||
is_source_id_attribute_enabled = dprops is not None and (dprops.surface.enable_source_id_attribute or
|
||||
dprops.particles.enable_fluid_particle_source_id_attribute)
|
||||
show_source_id = dprops is not None and is_source_id_attribute_enabled
|
||||
split = column.split(align=True)
|
||||
column_left = split.column(align=True)
|
||||
column_left.enabled = show_source_id
|
||||
column_left.prop(inflow_props, "source_id")
|
||||
column_right = split.column(align=True)
|
||||
row = column_right.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
if dprops is not None and not show_source_id:
|
||||
row.operator("flip_fluid_operators.enable_source_id_attribute_tooltip",
|
||||
text="Enable Source ID Attribute", icon="PLUS", emboss=False)
|
||||
if dprops is None:
|
||||
row.label(text="Domain required for this option")
|
||||
column.separator()
|
||||
else:
|
||||
column.enabled = False
|
||||
column.label(text="Geometry attribute features are only available in", icon='ERROR')
|
||||
column.label(text="Blender 2.93 or later", icon='ERROR')
|
||||
|
||||
box = self.layout.box()
|
||||
box.label(text="Mesh Data Export:")
|
||||
column = box.column(align=True)
|
||||
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(inflow_props, "export_animated_mesh")
|
||||
|
||||
is_child_object = obj.parent is not None
|
||||
is_hint_enabled = not vcu.get_addon_preferences().dismiss_export_animated_mesh_parented_relation_hint
|
||||
if is_hint_enabled and not inflow_props.export_animated_mesh and is_child_object:
|
||||
row.prop(context.scene.flip_fluid_helper, "export_animated_mesh_parent_tooltip",
|
||||
icon="QUESTION", emboss=False, text=""
|
||||
)
|
||||
row.label(text="←Hint: export option may be required")
|
||||
|
||||
column.prop(inflow_props, "skip_reexport")
|
||||
column.separator()
|
||||
column = box.column(align=True)
|
||||
column.enabled = inflow_props.skip_reexport
|
||||
column.prop(inflow_props, "force_reexport_on_next_bake", toggle=True)
|
||||
|
||||
column = self.layout.column(align=True)
|
||||
column.separator()
|
||||
column.operator("flip_fluid_operators.copy_setting_to_selected", icon='COPYDOWN')
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_InflowTypePanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_InflowTypePanel)
|
||||
@@ -0,0 +1,49 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_NoneTypePanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid"
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
return obj_props.is_active and obj_props.object_type == 'TYPE_NONE'
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
obj_props = obj.flip_fluid
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(obj_props, "object_type")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_NoneTypePanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_NoneTypePanel)
|
||||
@@ -0,0 +1,120 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_ObstacleTypePanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid"
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_OBSTACLE" and not is_addon_disabled
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
obj_props = obj.flip_fluid
|
||||
obstacle_props = obj_props.obstacle
|
||||
preferences = vcu.get_addon_preferences(context)
|
||||
|
||||
show_disabled_in_viewport_warning = True
|
||||
if show_disabled_in_viewport_warning and obj.hide_viewport:
|
||||
box = self.layout.box()
|
||||
box.alert = True
|
||||
column = box.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(obstacle_props, "disabled_in_viewport_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text="Object is currently disabled in the viewport")
|
||||
row.label(text="", icon="RESTRICT_VIEW_ON")
|
||||
column.label(text="This object will not be included within the simulation")
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(obj_props, "object_type")
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(obstacle_props, "is_enabled")
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(obstacle_props, "is_inversed")
|
||||
|
||||
box = self.layout.box()
|
||||
box.label(text="Obstacle Properties")
|
||||
|
||||
column = box.column()
|
||||
column.prop(obstacle_props, "friction", slider=True)
|
||||
|
||||
column = box.column()
|
||||
column.prop(obstacle_props, "velocity_scale")
|
||||
|
||||
column = box.column()
|
||||
column.prop(obstacle_props, "whitewater_influence")
|
||||
|
||||
column = box.column()
|
||||
column.prop(obstacle_props, "dust_emission_strength")
|
||||
|
||||
column = box.column()
|
||||
column.prop(obstacle_props, "sheeting_strength")
|
||||
|
||||
column = box.column()
|
||||
alert_threshold = 0.05 + 1e-5
|
||||
if abs(obstacle_props.mesh_expansion) > alert_threshold:
|
||||
column.alert = True
|
||||
column.prop(obstacle_props, "mesh_expansion")
|
||||
|
||||
box = self.layout.box()
|
||||
box.label(text="Mesh Data Export:")
|
||||
column = box.column(align=True)
|
||||
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(obstacle_props, "export_animated_mesh")
|
||||
|
||||
is_child_object = obj.parent is not None
|
||||
is_hint_enabled = not vcu.get_addon_preferences().dismiss_export_animated_mesh_parented_relation_hint
|
||||
if is_hint_enabled and not obstacle_props.export_animated_mesh and is_child_object:
|
||||
row.prop(context.scene.flip_fluid_helper, "export_animated_mesh_parent_tooltip",
|
||||
icon="QUESTION", emboss=False, text=""
|
||||
)
|
||||
row.label(text="←Hint: export option may be required")
|
||||
|
||||
column.prop(obstacle_props, "skip_reexport")
|
||||
column.separator()
|
||||
column = box.column(align=True)
|
||||
column.enabled = obstacle_props.skip_reexport
|
||||
column.prop(obstacle_props, "force_reexport_on_next_bake", toggle=True)
|
||||
|
||||
column = self.layout.column(align=True)
|
||||
column.separator()
|
||||
column.operator("flip_fluid_operators.copy_setting_to_selected", icon='COPYDOWN')
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_ObstacleTypePanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_ObstacleTypePanel)
|
||||
@@ -0,0 +1,101 @@
|
||||
# Blender FLIP Fluids Add-on
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from ..utils import version_compatibility_utils as vcu
|
||||
|
||||
|
||||
class FLIPFLUID_PT_OutflowTypePanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_category = "FLIP Fluid"
|
||||
bl_label = "FLIP Fluid"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj_props = vcu.get_active_object(context).flip_fluid
|
||||
is_addon_disabled = context.scene.flip_fluid.is_addon_disabled_in_blend_file()
|
||||
return obj_props.is_active and obj_props.object_type == "TYPE_OUTFLOW" and not is_addon_disabled
|
||||
|
||||
def draw(self, context):
|
||||
obj = vcu.get_active_object(context)
|
||||
obj_props = obj.flip_fluid
|
||||
outflow_props = obj_props.outflow
|
||||
|
||||
show_disabled_in_viewport_warning = True
|
||||
if show_disabled_in_viewport_warning and obj.hide_viewport:
|
||||
box = self.layout.box()
|
||||
box.alert = True
|
||||
column = box.column(align=True)
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(outflow_props, "disabled_in_viewport_tooltip", icon="QUESTION", emboss=False, text="")
|
||||
row.label(text="Object is currently disabled in the viewport")
|
||||
row.label(text="", icon="RESTRICT_VIEW_ON")
|
||||
column.label(text="This object will not be included within the simulation")
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(obj_props, "object_type")
|
||||
|
||||
column = self.layout.column()
|
||||
column.prop(outflow_props, "is_enabled")
|
||||
|
||||
column = self.layout.column()
|
||||
split = column.split()
|
||||
column = split.column()
|
||||
column.prop(outflow_props, "remove_fluid")
|
||||
column = split.column()
|
||||
column.prop(outflow_props, "remove_whitewater")
|
||||
|
||||
self.layout.separator()
|
||||
column = self.layout.column()
|
||||
column.prop(outflow_props, "is_inversed")
|
||||
|
||||
box = self.layout.box()
|
||||
box.label(text="Mesh Data Export:")
|
||||
column = box.column(align=True)
|
||||
|
||||
row = column.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.prop(outflow_props, "export_animated_mesh")
|
||||
|
||||
is_child_object = obj.parent is not None
|
||||
is_hint_enabled = not vcu.get_addon_preferences().dismiss_export_animated_mesh_parented_relation_hint
|
||||
if is_hint_enabled and not outflow_props.export_animated_mesh and is_child_object:
|
||||
row.prop(context.scene.flip_fluid_helper, "export_animated_mesh_parent_tooltip",
|
||||
icon="QUESTION", emboss=False, text=""
|
||||
)
|
||||
row.label(text="←Hint: export option may be required")
|
||||
|
||||
column.prop(outflow_props, "skip_reexport")
|
||||
column.separator()
|
||||
column = box.column(align=True)
|
||||
column.enabled = outflow_props.skip_reexport
|
||||
column.prop(outflow_props, "force_reexport_on_next_bake", toggle=True)
|
||||
|
||||
column = self.layout.column(align=True)
|
||||
column.separator()
|
||||
column.operator("flip_fluid_operators.copy_setting_to_selected", icon='COPYDOWN')
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(FLIPFLUID_PT_OutflowTypePanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(FLIPFLUID_PT_OutflowTypePanel)
|
||||
Reference in New Issue
Block a user