2026-02-16

This commit is contained in:
2026-03-17 15:25:32 -06:00
parent d5dd373de0
commit 60100fbab2
560 changed files with 33397 additions and 20776 deletions
@@ -205,7 +205,12 @@ class FlipFluidAddQuickLiquid(bpy.types.Operator):
# Domain Settings
domain_properties = bpy.context.scene.flip_fluid.get_domain_properties()
domain_properties.materials.surface_material = 'FF Water (ocean volumetric)'
try:
# Materials may not be available depending on addon version
domain_properties.materials.surface_material = 'FF Water (ocean volumetric)'
except:
pass
# Surface Settings
domain_properties = bpy.context.scene.flip_fluid.get_domain_properties()
@@ -282,9 +287,14 @@ class FlipFluidAddThickViscousLiquid(bpy.types.Operator):
domain_props.surface.subdivisions = 2
domain_props.world.enable_viscosity = True
domain_props.world.viscosity = 15
domain_props.materials.surface_material = 'FF Caramel'
domain_props.advanced.jitter_surface_particles = True
try:
# Materials may not be available depending on addon version
domain_props.materials.surface_material = 'FF Caramel'
except:
pass
# Create Inflow
bpy.ops.mesh.primitive_cylinder_add(radius=0.25, location=(-0.125, -0.125, 1.25 + z_offset), scale=(0.4, 0.4, 0.25))
bl_cylinder = bpy.context.active_object
@@ -402,9 +412,14 @@ class FlipFluidAddThinViscousLiquid(bpy.types.Operator):
domain_props.world.surface_tension_settings_expaned = True
domain_props.world.enable_surface_tension = True
domain_props.world.surface_tension = 0.3
domain_props.materials.surface_material = 'FF Caramel'
domain_props.advanced.jitter_surface_particles = True
try:
# Materials may not be available depending on addon version
domain_props.materials.surface_material = 'FF Caramel'
except:
pass
# Create Inflow
bpy.ops.mesh.primitive_cylinder_add(radius=0.25, location=(-0.5, -0.5, 0.0 + z_offset), scale=(0.4, 0.4, 0.125))
bl_cylinder = bpy.context.active_object
@@ -39,47 +39,42 @@ class FLIPFluidsAlembicImporter(bpy.types.Operator, bpy_extras.io_utils.ImportHe
return bl_object
def apply_default_modifier_settings(self, target_object, gn_modifier):
gn_modifier["Input_2_use_attribute"] = True
gn_modifier["Input_2_attribute_name"] = 'flip_velocity'
gn_modifier["Output_3_attribute_name"] = 'velocity'
def apply_default_modifier_settings(self, target_object, domain_object, surface_object, gn_modifier):
# Apply Simulation Time Scale and Apply Simulation Time Scale should be disabled.
# These modifier features require a FLIP Domain and addon scripting to function. If enabled,
# motion blur velocity will be set to (0.0, 0.0, 0.0)
key_value_pairs_surface = [
("Input_6", True), # Enable Motion Blur
("Socket_8", False), # Apply Simulation Time Scale - Disable: Requires FLIP Domain and addon scripting
("Socket_9", False), # Apply Simulation Time Scale - Disable: Requires FLIP Domain and addon scripting
("Socket_7", domain_object), # FLIP Domain Object
]
key_value_pairs_particles = [
("Input_8", True), # Enable Motion Blur
("Socket_47", False), # Apply Simulation Time Scale - Disable: Requires FLIP Domain and addon scripting
("Socket_48", False), # Apply Simulation World Scale - Disable: Requires FLIP Domain and addon scripting
("Socket_46", domain_object), # FLIP Domain Object
("Socket_49", surface_object), # FLIP Surface Object
]
# Depending on FLIP Fluids version, the GN set up may not
# have these inputs.
gn_name = gn_modifier.name
if gn_name.startswith("FF_GeometryNodesSurface"):
# Depending on FLIP Fluids version, the GN set up may not
# have these inputs. Available in FLIP Fluids 1.7.2 or later.
try:
# Enable Motion Blur
gn_modifier["Input_6"] = True
except:
pass
if gn_name.startswith("FF_GeometryNodesAlembicSurface"):
for (key, value) in key_value_pairs_surface:
try:
gn_modifier[key] = value
except:
pass
if gn_name.startswith("FF_GeometryNodesWhitewater") or gn_name.startswith("FF_GeometryNodesFluidParticles"):
# Depending on FLIP Fluids version, the GN set up may not
# have these inputs. Available in FLIP Fluids 1.7.2 or later.
try:
# Material
gn_modifier["Input_5"] = target_object.active_material
except:
pass
try:
# Enable Motion Blur
gn_modifier["Input_8"] = True
except:
pass
try:
# Enable Point Cloud
gn_modifier["Input_9"] = True
except:
pass
try:
# Enable Instancing
gn_modifier["Input_10"] = False
except:
pass
if gn_name.startswith("FF_GeometryNodesAlembicParticles"):
for (key, value) in key_value_pairs_particles:
try:
gn_modifier[key] = value
except:
pass
def add_smooth_modifier(self, target_object):
@@ -89,11 +84,25 @@ class FLIPFluidsAlembicImporter(bpy.types.Operator, bpy_extras.io_utils.ImportHe
return smooth_mod
def toggle_cycles_ray_visibility(self, obj, is_enabled):
# Cycles may not be enabled in the user's preferences
try:
obj.visible_camera = is_enabled
obj.visible_diffuse = is_enabled
obj.visible_glossy = is_enabled
obj.visible_transmission = is_enabled
obj.visible_volume_scatter = is_enabled
obj.visible_shadow = is_enabled
except:
pass
def execute(self, context):
print("FLIP Fluids Alembic Import: <" + self.filepath + ">")
bpy.ops.wm.alembic_import(filepath=self.filepath)
bpy.ops.wm.alembic_import(filepath=self.filepath, always_add_cache_reader=True)
bl_domain = self.find_flip_fluids_mesh(bpy.context.selected_objects, "FLIP_Domain")
bl_fluid_surface = self.find_flip_fluids_mesh(bpy.context.selected_objects, "fluid_surface")
bl_fluid_particles = self.find_flip_fluids_mesh(bpy.context.selected_objects, "fluid_particles")
bl_whitewater_foam = self.find_flip_fluids_mesh(bpy.context.selected_objects, "whitewater_foam")
@@ -107,46 +116,51 @@ class FLIPFluidsAlembicImporter(bpy.types.Operator, bpy_extras.io_utils.ImportHe
found_mesh_cache_list = []
if bl_domain is not None:
bl_domain.hide_render = True
bl_domain.display_type = 'BOUNDS'
self.toggle_cycles_ray_visibility(bl_domain, False)
if bl_fluid_surface is not None:
self.add_smooth_modifier(bl_fluid_surface)
helper_operators.add_geometry_node_modifier(bl_fluid_surface, resource_filepath, "FF_AlembicImportSurface")
gn_modifier = helper_operators.add_geometry_node_modifier(bl_fluid_surface, resource_filepath, "FF_GeometryNodesSurface")
self.apply_default_modifier_settings(bl_fluid_surface, gn_modifier)
gn_modifier = helper_operators.add_geometry_node_modifier(bl_fluid_surface, resource_filepath, "FF_GeometryNodesAlembicSurface")
self.apply_default_modifier_settings(bl_fluid_surface, bl_domain, bl_fluid_surface, gn_modifier)
self.report({'INFO'}, "Found fluid surface cache... Initialized geometry nodes")
found_mesh_cache_list.append("Surface")
if bl_fluid_particles is not None:
helper_operators.add_geometry_node_modifier(bl_fluid_particles, resource_filepath, "FF_AlembicImportFluidParticles")
gn_modifier = helper_operators.add_geometry_node_modifier(bl_fluid_particles, resource_filepath, "FF_GeometryNodesFluidParticles")
self.apply_default_modifier_settings(bl_fluid_particles, gn_modifier)
gn_modifier = helper_operators.add_geometry_node_modifier(bl_fluid_particles, resource_filepath, "FF_GeometryNodesAlembicParticles")
self.apply_default_modifier_settings(bl_fluid_particles, bl_domain, bl_fluid_surface, gn_modifier)
self.report({'INFO'}, "Found fluid particle cache... Initialized geometry nodes")
found_mesh_cache_list.append("FluidParticles")
if bl_whitewater_foam is not None:
helper_operators.add_geometry_node_modifier(bl_whitewater_foam, resource_filepath, "FF_AlembicImportWhitewaterFoam")
gn_modifier = helper_operators.add_geometry_node_modifier(bl_whitewater_foam, resource_filepath, "FF_GeometryNodesWhitewaterFoam")
self.apply_default_modifier_settings(bl_whitewater_foam, gn_modifier)
gn_modifier = helper_operators.add_geometry_node_modifier(bl_whitewater_foam, resource_filepath, "FF_GeometryNodesAlembicParticles")
self.apply_default_modifier_settings(bl_whitewater_foam, bl_domain, bl_fluid_surface, gn_modifier)
self.report({'INFO'}, "Found whitewater foam cache... Initialized geometry nodes")
found_mesh_cache_list.append("Foam")
if bl_whitewater_bubble is not None:
helper_operators.add_geometry_node_modifier(bl_whitewater_bubble, resource_filepath, "FF_AlembicImportWhitewaterBubble")
gn_modifier = helper_operators.add_geometry_node_modifier(bl_whitewater_bubble, resource_filepath, "FF_GeometryNodesWhitewaterBubble")
self.apply_default_modifier_settings(bl_whitewater_bubble, gn_modifier)
gn_modifier = helper_operators.add_geometry_node_modifier(bl_whitewater_bubble, resource_filepath, "FF_GeometryNodesAlembicParticles")
self.apply_default_modifier_settings(bl_whitewater_bubble, bl_domain, bl_fluid_surface, gn_modifier)
self.report({'INFO'}, "Found whitewater bubble cache... Initialized geometry nodes")
found_mesh_cache_list.append("Bubble")
if bl_whitewater_spray is not None:
helper_operators.add_geometry_node_modifier(bl_whitewater_spray, resource_filepath, "FF_AlembicImportWhitewaterSpray")
gn_modifier = helper_operators.add_geometry_node_modifier(bl_whitewater_spray, resource_filepath, "FF_GeometryNodesWhitewaterSpray")
self.apply_default_modifier_settings(bl_whitewater_spray, gn_modifier)
gn_modifier = helper_operators.add_geometry_node_modifier(bl_whitewater_spray, resource_filepath, "FF_GeometryNodesAlembicParticles")
self.apply_default_modifier_settings(bl_whitewater_spray, bl_domain, bl_fluid_surface, gn_modifier)
self.report({'INFO'}, "Found whitewater spray cache... Initialized geometry nodes")
found_mesh_cache_list.append("Spray")
if bl_whitewater_dust is not None:
helper_operators.add_geometry_node_modifier(bl_whitewater_dust, resource_filepath, "FF_AlembicImportWhitewaterDust")
gn_modifier = helper_operators.add_geometry_node_modifier(bl_whitewater_dust, resource_filepath, "FF_GeometryNodesWhitewaterDust")
self.apply_default_modifier_settings(bl_whitewater_dust, gn_modifier)
gn_modifier = helper_operators.add_geometry_node_modifier(bl_whitewater_dust, resource_filepath, "FF_GeometryNodesAlembicParticles")
self.apply_default_modifier_settings(bl_whitewater_dust, bl_domain, bl_fluid_surface, gn_modifier)
self.report({'INFO'}, "Found whitewater dust cache... Initialized geometry nodes")
found_mesh_cache_list.append("Dust")
@@ -177,12 +191,17 @@ class FLIPFluidsAlembicExporter(bpy.types.Operator, bpy_extras.io_utils.ExportHe
return context.scene.flip_fluid.get_domain_object() is not None and bool(bpy.data.filepath)
def draw(self, context):
def draw_alembic_export_engine_blender(self, context):
hprops = context.scene.flip_fluid_helper
self.layout.use_property_split = True
self.layout.use_property_decorate = False
header, body = self.layout.panel("alembic_export_engine", default_closed=False)
header.label(text="Alembic Exporter")
if body:
column = body.column(align=True)
column.prop(hprops, "alembic_export_engine", text="Engine")
header, body = self.layout.panel("alembic_scene", default_closed=False)
header.label(text="Scene")
if body:
@@ -217,16 +236,116 @@ class FLIPFluidsAlembicExporter(bpy.types.Operator, bpy_extras.io_utils.ExportHe
header, body = self.layout.panel("alembic_command", default_closed=True)
header.label(text="Command")
if body:
column = body.column(heading="Attributes", align=True)
hprops = context.scene.flip_fluid_helper
column = body.column(heading="Command", align=True)
column.operator("flip_fluid_operators.helper_cmd_alembic_export_to_clipboard", text="Copy Command to Clipboard", icon='COPYDOWN')
def draw_alembic_export_engine_flip_fluids(self, context):
hprops = context.scene.flip_fluid_helper
self.layout.use_property_split = True
self.layout.use_property_decorate = False
header, body = self.layout.panel("alembic_export_engine", default_closed=False)
header.label(text="Alembic Exporter")
if body:
column = body.column(align=True)
column.prop(hprops, "alembic_export_engine", text="Engine")
header, body = self.layout.panel("alembic_scene", default_closed=False)
header.label(text="Scene")
if body:
column = body.column(align=True)
column.prop(hprops, "alembic_frame_range_mode", text="Frame Range")
if hprops.alembic_frame_range_mode == 'FRAME_RANGE_TIMELINE':
column.prop(context.scene, "frame_start")
column.prop(context.scene, "frame_end")
else:
column.prop(hprops.alembic_frame_range_custom, "value_min")
column.prop(hprops.alembic_frame_range_custom, "value_max")
column.separator()
column.prop(hprops, "alembic_global_scale", text="Scale (TODO)")
header, body = self.layout.panel("alembic_include", default_closed=False)
header.label(text="Include")
if body:
column = body.column(heading="Mesh", align=True)
column.prop(hprops, "alembic_export_surface")
column.prop(hprops, "alembic_export_surface_preview", text="Surface Preview (TODO)")
column.prop(hprops, "alembic_export_fluid_particles", text="Fluid Particles (TODO)")
column.prop(hprops, "alembic_export_foam", text="Foam (TODO)")
column.prop(hprops, "alembic_export_bubble", text="Bubble (TODO)")
column.prop(hprops, "alembic_export_spray", text="Spray (TODO)")
column.prop(hprops, "alembic_export_dust", text="Dust (TODO)")
column = body.column(heading="Attributes", align=True)
column.prop(hprops, "alembic_export_velocity", text="Velocity (TODO)")
column.prop(hprops, "alembic_export_color", text="Color (TODO)")
header, body = self.layout.panel("alembic_command", default_closed=True)
header.label(text="Command")
if body:
hprops = context.scene.flip_fluid_helper
column = body.column(heading="Command", align=True)
column.enabled = False
column.operator("flip_fluid_operators.cmd_custom_alembic_export_to_clipboard", text="Copy Command to Clipboard (TODO)", icon='COPYDOWN')
def draw(self, context):
hprops = context.scene.flip_fluid_helper
if hprops.alembic_export_engine == 'ALEMBIC_EXPORT_ENGINE_FLIP_FLUIDS':
self.draw_alembic_export_engine_flip_fluids(context)
elif hprops.alembic_export_engine == 'ALEMBIC_EXPORT_ENGINE_BLENDER':
self.draw_alembic_export_engine_blender(context)
def alembic_export_engine_flip_fluids(self):
hprops = bpy.context.scene.flip_fluid_helper
hprops.alembic_output_filepath = self.filepath
bpy.ops.flip_fluid_operators.helper_cmd_custom_alembic_export('INVOKE_DEFAULT')
def alembic_export_engine_blender(self):
hprops = bpy.context.scene.flip_fluid_helper
hprops.alembic_output_filepath = self.filepath
bpy.ops.flip_fluid_operators.helper_command_line_alembic_export('INVOKE_DEFAULT')
def check_cache_exists(self, context):
dprops = context.scene.flip_fluid.get_domain_properties()
cache_directory = dprops.cache.get_cache_abspath()
bakefiles_directory = os.path.join(cache_directory, "bakefiles")
file_count = 0
cache_exists = False
if os.path.isdir(bakefiles_directory):
cache_exists = True
file_count = len(os.listdir(bakefiles_directory))
if not cache_exists or file_count == 0:
return False
return True
def execute(self, context):
if not self.check_cache_exists(context):
dprops = context.scene.flip_fluid.get_domain_properties()
cache_directory = dprops.cache.get_cache_abspath()
self.report({'ERROR'}, "No data in simulation cache. Nothing to export in <" + cache_directory + ">")
return {'CANCELLED'}
print("FLIP Fluids Alembic Export: <" + self.filepath + ">")
hprops = context.scene.flip_fluid_helper
hprops.alembic_output_filepath = self.filepath
bpy.ops.flip_fluid_operators.helper_command_line_alembic_export('INVOKE_DEFAULT')
if hprops.alembic_export_engine == 'ALEMBIC_EXPORT_ENGINE_FLIP_FLUIDS':
self.alembic_export_engine_flip_fluids()
elif hprops.alembic_export_engine == 'ALEMBIC_EXPORT_ENGINE_BLENDER':
self.alembic_export_engine_blender()
else:
self.report({'WARNING'}, "Unknown Alembic Export Engine: " + hprops.alembic_export_engine)
return {'CANCELLED'}
return {'FINISHED'}
@@ -429,8 +429,7 @@ class FlipFluidIncreaseDecreaseCacheDirectory(bpy.types.Operator):
bl_description = ("Increase or decrease a numbered suffix on the cache directory." +
" Note: this will not rename an existing cache directory")
increment_mode = StringProperty(default="INCREASE")
exec(vcu.convert_attribute_to_28("increment_mode"))
increment_mode: StringProperty(default="INCREASE")
@classmethod
@@ -487,8 +486,7 @@ class FlipFluidIncreaseDecreaseRenderDirectory(bpy.types.Operator):
bl_description = ("Increase or decrease a numbered suffix on the render output directory." +
" Note: this will not rename an existing render output directory")
increment_mode = StringProperty(default="INCREASE")
exec(vcu.convert_attribute_to_28("increment_mode"))
increment_mode: StringProperty(default="INCREASE")
@classmethod
@@ -565,8 +563,7 @@ class FlipFluidIncreaseDecreaseCacheRenderVersion(bpy.types.Operator):
" directory and render output directory. Note: this will not rename an" +
" existing cache our render output directory")
increment_mode = StringProperty(default="INCREASE")
exec(vcu.convert_attribute_to_28("increment_mode"))
increment_mode: StringProperty(default="INCREASE")
@classmethod
@@ -14,7 +14,7 @@
# 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, os, pathlib, stat, subprocess, platform, random, shlex, shutil, traceback
import bpy, os, pathlib, stat, subprocess, platform, random, shlex, shutil, traceback, ctypes, json
from bpy.props import (
BoolProperty,
)
@@ -470,6 +470,46 @@ def get_command_line_script_filepath(script_filename):
return script_path
def get_flip_fluids_alembic_exporter_filepath():
executable_name = ""
system = platform.system()
if system == "Windows":
executable_name = "ff_alembic_exporter_windows.exe"
elif system == "Darwin":
executable_name = "ff_alembic_exporter_macos"
elif system == "Linux":
executable_name = "ff_alembic_exporter_linux"
executable_path = os.path.dirname(os.path.realpath(__file__))
executable_path = os.path.dirname(executable_path)
executable_path = os.path.join(executable_path, "ffengine", "lib", executable_name)
if not os.path.isfile(executable_path):
errmsg = "Unable to locate executable <" + executable_path + ">. Please contact the developers with this error."
raise Exception(errmsg)
return executable_path
def get_flip_fluids_alembic_exporter_lib_filepath():
executable_name = ""
system = platform.system()
if system == "Windows":
lib_name = "libffalembicengine.dll"
elif system == "Darwin":
lib_name = "libffalembicengine.dylib"
elif system == "Linux":
lib_name = "libffalembicengine.so"
lib_path = os.path.dirname(os.path.realpath(__file__))
lib_path = os.path.dirname(lib_path)
lib_path = os.path.join(lib_path, "ffengine", "lib", lib_name)
if not os.path.isfile(lib_path):
errmsg = "Unable to locate executable <" + lib_path + ">. Please contact the developers with this error."
raise Exception(errmsg)
return lib_path
def save_blend_file_before_launch(override_preferences=False):
prefs = vcu.get_addon_preferences()
if prefs.cmd_save_blend_file_before_launch or override_preferences:
@@ -539,11 +579,15 @@ def write_scripts_directory_readme():
f.write(readme_text)
def launch_command_universal_os(command_text, script_prefix_string, keep_window_open=True, skip_launch=False):
def launch_command_universal_os(command_text, script_prefix_string, keep_window_open=True, skip_launch=False, chcp=None):
system = platform.system()
if system == "Windows":
code_page = 65001
if chcp:
code_page = chcp
script_extension = ".bat"
script_header = "echo off\nchcp 65001\n\n"
script_header = "echo off\nchcp " + str(code_page) + "\n\n"
script_footer = ""
if keep_window_open:
script_footer = "\ncmd /k\n"
@@ -624,8 +668,7 @@ class FlipFluidHelperCommandLineBake(bpy.types.Operator):
" The .blend file will need to be saved for before using" +
" this operator for changes to take effect")
skip_launch = BoolProperty(False)
exec(vcu.convert_attribute_to_28("skip_launch"))
skip_launch: BoolProperty(False)
@classmethod
@@ -764,11 +807,9 @@ class FlipFluidHelperCommandLineRender(bpy.types.Operator):
bl_description = ("Launch a new command line window and start rendering the animation." +
" The .blend file will need to be saved before using this operator for changes to take effect")
use_turbo_tools = BoolProperty(False)
exec(vcu.convert_attribute_to_28("use_turbo_tools"))
use_turbo_tools: BoolProperty(False)
skip_launch = BoolProperty(False)
exec(vcu.convert_attribute_to_28("skip_launch"))
skip_launch: BoolProperty(False)
@classmethod
@@ -923,8 +964,7 @@ class FlipFluidHelperCommandLineRenderToClipboard(bpy.types.Operator):
bl_description = ("Copy command for rendering to your system clipboard." +
" The .blend file will need to be saved before running this command for changes to take effect")
use_turbo_tools = BoolProperty(False)
exec(vcu.convert_attribute_to_28("use_turbo_tools"))
use_turbo_tools: BoolProperty(False)
@classmethod
@@ -951,11 +991,9 @@ class FlipFluidHelperCommandLineRenderFrame(bpy.types.Operator):
bl_description = ("Launch a new command line window and start rendering the current timeline frame." +
" The .blend file will need to be saved before using this operator for changes to take effect")
use_turbo_tools = BoolProperty(False)
exec(vcu.convert_attribute_to_28("use_turbo_tools"))
use_turbo_tools: BoolProperty(False)
skip_launch = BoolProperty(False)
exec(vcu.convert_attribute_to_28("skip_launch"))
skip_launch: BoolProperty(False)
@classmethod
@@ -1022,14 +1060,13 @@ class FlipFluidHelperCommandLineRenderFrame(bpy.types.Operator):
class FlipFluidHelperCmdRenderFrameToClipboard(bpy.types.Operator):
class FlipFluidHelperCommandLineRenderFrameToClipboard(bpy.types.Operator):
bl_idname = "flip_fluid_operators.helper_cmd_render_frame_to_clipboard"
bl_label = "Launch Frame Render"
bl_description = ("Copy command for frame rendering to your system clipboard." +
" The .blend file will need to be saved before running this command for changes to take effect")
use_turbo_tools = BoolProperty(False)
exec(vcu.convert_attribute_to_28("use_turbo_tools"))
use_turbo_tools: BoolProperty(False)
@classmethod
@@ -1055,8 +1092,7 @@ class FlipFluidHelperCommandLineAlembicExport(bpy.types.Operator):
bl_description = ("Launch a new command line window and start exporting the simulation meshes to the Alembic (.abc) format." +
" The .blend file will need to be saved before using this operator for changes to take effect")
skip_launch = BoolProperty(False)
exec(vcu.convert_attribute_to_28("skip_launch"))
skip_launch: BoolProperty(False)
@classmethod
@@ -1085,7 +1121,7 @@ class FlipFluidHelperCommandLineAlembicExport(bpy.types.Operator):
return {'FINISHED'}
class FlipFluidHelperCmdAlembicExportToClipboard(bpy.types.Operator):
class FlipFluidHelperCommandLineAlembicExportToClipboard(bpy.types.Operator):
bl_idname = "flip_fluid_operators.helper_cmd_alembic_export_to_clipboard"
bl_label = "Launch Alembic Export"
bl_description = ("Copy command for Alembic export to your system clipboard." +
@@ -1109,6 +1145,75 @@ class FlipFluidHelperCmdAlembicExportToClipboard(bpy.types.Operator):
return {'FINISHED'}
class FlipFluidHelperCommandLineCustomAlembicExport(bpy.types.Operator):
bl_idname = "flip_fluid_operators.helper_cmd_custom_alembic_export"
bl_label = "Launch Alembic Export"
bl_description = ("Launch a new command line window and start exporting the simulation meshes to the Alembic (.abc) format." +
" The .blend file will need to be saved before using this operator for changes to take effect")
skip_launch: BoolProperty(False)
@classmethod
def poll(cls, context):
return context.scene.flip_fluid.get_domain_object() is not None and bool(bpy.data.filepath)
def get_export_frame_range(self):
frame_start = bpy.context.scene.frame_start
frame_end = bpy.context.scene.frame_end
hprops = bpy.context.scene.flip_fluid_helper
if hprops.alembic_frame_range_mode == 'FRAME_RANGE_CUSTOM':
frame_start = hprops.alembic_frame_range_custom.value_min
frame_end = hprops.alembic_frame_range_custom.value_max
return frame_start, frame_end
def execute(self, context):
save_blend_file_before_launch(override_preferences=False)
restore_blender_original_cwd()
script_path = get_command_line_script_filepath("flip_fluids_alembic_export.py")
command_text = "\"" + bpy.app.binary_path + "\" --background \"" + bpy.data.filepath + "\" --python \"" + script_path + "\""
script_filepath = launch_command_universal_os(command_text, "FF_ALEMBIC_EXPORT_", keep_window_open=True, skip_launch=self.skip_launch)
if not self.skip_launch:
info_msg = "Launched command line Alembic export window. If the Alembic export process did not begin,"
info_msg += " this may be caused by a conflict with another addon or a security feature of your OS that restricts"
info_msg += " automatic command execution. You may try running following script file manually:\n\n"
info_msg += script_filepath + "\n\n"
info_msg += "For more information on command line operators, visit our documentation:\n"
info_msg += "https://github.com/rlguy/Blender-FLIP-Fluids/wiki/Helper-Menu-Settings#command-line-alembic-export"
self.report({'INFO'}, info_msg)
return {'FINISHED'}
class FlipFluidHelperCommandLineCustomAlembicExportToClipboard(bpy.types.Operator):
bl_idname = "flip_fluid_operators.cmd_custom_alembic_export_to_clipboard"
bl_label = "Launch Alembic Export"
bl_description = ("Copy command for Alembic export to your system clipboard." +
" The .blend file will need to be saved before running this command for changes to take effect")
@classmethod
def poll(cls, context):
return context.scene.flip_fluid.get_domain_object() is not None and bool(bpy.data.filepath)
def execute(self, context):
bpy.ops.flip_fluid_operators.helper_cmd_custom_alembic_export('INVOKE_DEFAULT', skip_launch=True)
info_msg = "Copied the following Alembic export command to your clipboard:\n\n"
info_msg += bpy.context.window_manager.clipboard + "\n\n"
info_msg += "For more information on command line tools, visit our documentation:\n"
info_msg += "https://github.com/rlguy/Blender-FLIP-Fluids/wiki/Helper-Menu-Settings#command-line-alembic-export"
self.report({'INFO'}, info_msg)
return {'FINISHED'}
def get_render_output_info():
full_path = bpy.path.abspath(bpy.context.scene.render.filepath)
directory_path = full_path
@@ -1346,8 +1451,7 @@ class FlipFluidHelperCommandLineRenderPassAnimation(bpy.types.Operator):
bl_label = "Launch Render Pass Animation"
bl_description = ("Description: todo - launch render pass animation script")
skip_launch = BoolProperty(False)
exec(vcu.convert_attribute_to_28("skip_launch"))
skip_launch: BoolProperty(False)
@classmethod
@@ -1426,7 +1530,7 @@ class FlipFluidHelperCommandLineRenderPassAnimation(bpy.types.Operator):
self.report({'ERROR'}, errmsg)
return {'CANCELLED'}
if context.scene.render.engine == 'BLENDER_EEVEE':
if context.scene.render.engine == 'BLENDER_EEVEE' or context.scene.render.engine == 'BLENDER_EEVEE_NEXT':
self.report({'ERROR'}, "The EEVEE render engine is not supported for this feature. Set the render engine to Cycles, save, and try again.")
return {'CANCELLED'}
if context.scene.render.engine == 'BLENDER_WORKBENCH':
@@ -1510,8 +1614,7 @@ class FlipFluidHelperCommandLineRenderPassFrame(bpy.types.Operator):
bl_label = "Launch Render Pass Frame"
bl_description = ("Description: todo - launch render pass animation script")
skip_launch = BoolProperty(False)
exec(vcu.convert_attribute_to_28("skip_launch"))
skip_launch: BoolProperty(False)
@classmethod
@@ -1566,7 +1669,7 @@ class FlipFluidHelperCommandLineRenderPassFrame(bpy.types.Operator):
self.report({'ERROR'}, errmsg)
return {'CANCELLED'}
if context.scene.render.engine == 'BLENDER_EEVEE':
if context.scene.render.engine == 'BLENDER_EEVEE' or context.scene.render.engine == 'BLENDER_EEVEE_NEXT':
self.report({'ERROR'}, "The EEVEE render engine is not supported for this feature. Set the render engine to Cycles, save, and try again.")
return {'CANCELLED'}
if context.scene.render.engine == 'BLENDER_WORKBENCH':
@@ -1679,9 +1782,11 @@ def register():
bpy.utils.register_class(FlipFluidHelperCommandLineRender)
bpy.utils.register_class(FlipFluidHelperCommandLineRenderToClipboard)
bpy.utils.register_class(FlipFluidHelperCommandLineRenderFrame)
bpy.utils.register_class(FlipFluidHelperCmdRenderFrameToClipboard)
bpy.utils.register_class(FlipFluidHelperCommandLineRenderFrameToClipboard)
bpy.utils.register_class(FlipFluidHelperCommandLineAlembicExport)
bpy.utils.register_class(FlipFluidHelperCmdAlembicExportToClipboard)
bpy.utils.register_class(FlipFluidHelperCommandLineAlembicExportToClipboard)
bpy.utils.register_class(FlipFluidHelperCommandLineCustomAlembicExport)
bpy.utils.register_class(FlipFluidHelperCommandLineCustomAlembicExportToClipboard)
bpy.utils.register_class(FlipFluidHelperOpenRenderOutputFolder)
bpy.utils.register_class(FlipFluidHelperOpenCacheOutputFolder)
bpy.utils.register_class(FlipFluidHelperOpenAlembicOutputFolder)
@@ -1717,9 +1822,11 @@ def unregister():
bpy.utils.unregister_class(FlipFluidHelperCommandLineRender)
bpy.utils.unregister_class(FlipFluidHelperCommandLineRenderToClipboard)
bpy.utils.unregister_class(FlipFluidHelperCommandLineRenderFrame)
bpy.utils.unregister_class(FlipFluidHelperCmdRenderFrameToClipboard)
bpy.utils.unregister_class(FlipFluidHelperCommandLineRenderFrameToClipboard)
bpy.utils.unregister_class(FlipFluidHelperCommandLineAlembicExport)
bpy.utils.unregister_class(FlipFluidHelperCmdAlembicExportToClipboard)
bpy.utils.unregister_class(FlipFluidHelperCommandLineAlembicExportToClipboard)
bpy.utils.unregister_class(FlipFluidHelperCommandLineCustomAlembicExport)
bpy.utils.unregister_class(FlipFluidHelperCommandLineCustomAlembicExportToClipboard)
bpy.utils.unregister_class(FlipFluidHelperOpenRenderOutputFolder)
bpy.utils.unregister_class(FlipFluidHelperOpenCacheOutputFolder)
bpy.utils.unregister_class(FlipFluidHelperOpenAlembicOutputFolder)
@@ -1738,5 +1845,9 @@ def unregister():
# Remove shortcuts
for km, kmi in ADDON_KEYMAPS:
km.keymap_items.remove(kmi)
try:
# Keymap may be unavailable depending on context
km.keymap_items.remove(kmi)
except:
pass
ADDON_KEYMAPS.clear()
@@ -14,7 +14,8 @@
# 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, blf, math, colorsys
import bpy, blf, math, colorsys, gpu
from gpu_extras.batch import batch_for_shader
from bpy.props import (
IntProperty
@@ -25,10 +26,6 @@ from ..utils import ui_utils
from ..utils import version_compatibility_utils as vcu
from .. import render
if vcu.is_blender_28():
import gpu
from gpu_extras.batch import batch_for_shader
particle_vertices = []
particle_vertex_colors = []
@@ -88,51 +85,41 @@ def update_debug_force_field_geometry(context):
color_tuple = (color[0], color[1], color[2], 1.0)
particle_vertex_colors.append(color_tuple)
if vcu.is_blender_28():
global particle_shader
global particle_batch_draw
global particle_shader
global particle_batch_draw
vertex_shader = """
uniform mat4 ModelViewProjectionMatrix;
vertex_shader = """
uniform mat4 ModelViewProjectionMatrix;
in vec3 pos;
in vec4 color;
in vec3 pos;
in vec4 color;
out vec4 finalColor;
out vec4 finalColor;
void main()
{
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
finalColor = color;
}
"""
void main()
{
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
finalColor = color;
}
"""
fragment_shader = """
in vec4 finalColor;
out vec4 fragColor;
fragment_shader = """
in vec4 finalColor;
out vec4 fragColor;
void main()
{
fragColor = finalColor;
}
"""
void main()
{
fragColor = finalColor;
}
"""
if vcu.is_blender_35():
# Needed for support on MacOS Apple Silicon systems in Blender 3.5 or later
# Could possibly be a Blender regression bug why the below method no longer
# works in Blender 3.5 or later for MacOS. Should file a report.
shader_name = '3D_SMOOTH_COLOR'
if vcu.is_blender_40():
shader_name = 'SMOOTH_COLOR'
particle_shader = gpu.shader.from_builtin(shader_name)
else:
particle_shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
shader_name = 'SMOOTH_COLOR'
particle_shader = gpu.shader.from_builtin(shader_name)
particle_batch_draw = batch_for_shader(
particle_shader, 'POINTS',
{"pos": particle_vertices, "color": particle_vertex_colors},
)
particle_batch_draw = batch_for_shader(
particle_shader, 'POINTS',
{"pos": particle_vertices, "color": particle_vertex_colors},
)
def _lerp_rgb(minc, maxc, factor, mode='RGB'):
@@ -185,50 +172,13 @@ class FlipFluidDrawForceField(bpy.types.Operator):
if vcu.get_object_hide_viewport(domain):
return
if not vcu.is_blender_28():
dlayers = [i for i,v in enumerate(domain.layers) if v]
slayers = [i for i,v in enumerate(context.scene.layers) if v]
if not (set(dlayers) & set(slayers)):
return
if vcu.is_blender_28():
global particle_shader
global particle_batch_draw
if vcu.is_blender_35():
# Warnings in Blender 3.5 when using bgl module, which is to
# be deprecated in Blender 3.7. Use gpu module instead.
gpu.state.point_size_set(dprops.debug.force_field_line_size)
else:
# only attempt to import bgl when necessary (older versions of Blender). In Blender >= 3.5,
# importing bgl generates a warning, and possibly an error in Blender >= 4.0.
import bgl
bgl.glPointSize(dprops.debug.force_field_line_size)
if vcu.is_blender_35():
# Can be drawn with depth in Blender 3.5 or later
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
particle_batch_draw.draw(particle_shader)
if vcu.is_blender_35():
gpu.state.depth_mask_set(False)
else:
# only attempt to import bgl when necessary (older versions of Blender). In Blender >= 3.5,
# importing bgl generates a warning, and possibly an error in Blender >= 4.0.
import bgl
bgl.glPointSize(dprops.debug.force_field_line_size)
bgl.glBegin(bgl.GL_POINTS)
current_color = None
for i in range(len(particle_vertices)):
if current_color != particle_vertex_colors[i]:
current_color = particle_vertex_colors[i]
bgl.glColor4f(current_color[0], current_color[1], current_color[2], 1.0)
bgl.glVertex3f(*(particle_vertices[i]))
bgl.glEnd()
bgl.glPointSize(1)
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
global particle_shader
global particle_batch_draw
gpu.state.point_size_set(dprops.debug.force_field_line_size)
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
particle_batch_draw.draw(particle_shader)
gpu.state.depth_mask_set(False)
def modal(self, context, event):
@@ -25,9 +25,8 @@ from ..utils import ui_utils
from ..utils import version_compatibility_utils as vcu
from .. import render
if vcu.is_blender_28():
import gpu
from gpu_extras.batch import batch_for_shader
import gpu
from gpu_extras.batch import batch_for_shader
x_coords = []
@@ -195,33 +194,11 @@ class FlipFluidDrawDebugGrid(bpy.types.Operator):
if not dprops.debug.display_simulation_grid:
return
if not vcu.is_blender_28():
dlayers = [i for i,v in enumerate(domain.layers) if v]
slayers = [i for i,v in enumerate(context.scene.layers) if v]
if not (set(dlayers) & set(slayers)):
return
if dprops.debug.grid_display_mode == 'GRID_DISPLAY_SIMULATION':
isize, jsize, ksize, viewport_dx = dprops.simulation.get_viewport_grid_dimensions()
_, _, _, simulation_dx = dprops.simulation.get_simulation_grid_dimensions()
elif dprops.debug.grid_display_mode == 'GRID_DISPLAY_PREVIEW':
presolution = dprops.simulation.preview_resolution
"""
isize, jsize, ksize, viewport_dx = dprops.simulation.get_viewport_grid_dimensions(resolution=presolution)
_, _, _, simulation_dx = dprops.simulation.get_simulation_grid_dimensions(resolution=presolution)
else:
isize, jsize, ksize, viewport_dx = dprops.simulation.get_viewport_grid_dimensions()
_, _, _, simulation_dx = dprops.simulation.get_simulation_grid_dimensions()
if dprops.debug.grid_display_mode == 'GRID_DISPLAY_MESH':
isize *= (dprops.surface.subdivisions + 1)
jsize *= (dprops.surface.subdivisions + 1)
ksize *= (dprops.surface.subdivisions + 1)
viewport_dx /= (dprops.surface.subdivisions + 1)
"""
isize, jsize, ksize, viewport_dx = dprops.simulation.get_viewport_grid_dimensions(resolution=presolution)
_, _, _, simulation_dx = dprops.simulation.get_simulation_grid_dimensions(resolution=presolution)
elif dprops.debug.grid_display_mode == 'GRID_DISPLAY_MESH':
@@ -249,12 +226,8 @@ class FlipFluidDrawDebugGrid(bpy.types.Operator):
simulation_dx *= reduction
width = context.region.width
if vcu.is_blender_28():
height = 200
xstart = context.region.width - 400
else:
height = context.region.height
xstart = 50
height = 200
xstart = context.region.width - 400
font_id = 0
try:
@@ -360,12 +333,6 @@ class FlipFluidDrawDebugGrid(bpy.types.Operator):
if vcu.get_object_hide_viewport(domain):
return
if not vcu.is_blender_28():
dlayers = [i for i,v in enumerate(domain.layers) if v]
slayers = [i for i,v in enumerate(context.scene.layers) if v]
if not (set(dlayers) & set(slayers)):
return
x_color = dprops.debug.x_grid_color
y_color = dprops.debug.y_grid_color
z_color = dprops.debug.z_grid_color
@@ -373,86 +340,49 @@ class FlipFluidDrawDebugGrid(bpy.types.Operator):
# Draw
display_grid = dprops.debug.display_simulation_grid
if vcu.is_blender_28():
line_draw_mode = '3D_UNIFORM_COLOR'
if vcu.is_blender_36():
# 3D/2D prefix deprecated in recent versions of Blender
line_draw_mode = 'UNIFORM_COLOR'
if display_grid and dprops.debug.enabled_debug_grids[2]:
shader = gpu.shader.from_builtin(line_draw_mode)
batch = batch_for_shader(shader, 'LINES', {"pos": z_coords})
shader.bind()
shader.uniform_float("color", (z_color[0], z_color[1], z_color[2], 1.0))
line_draw_mode = 'UNIFORM_COLOR'
if vcu.is_blender_35():
# Can be drawn with depth in Blender 3.5 or later
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
batch.draw(shader)
if vcu.is_blender_35():
gpu.state.depth_mask_set(False)
if display_grid and dprops.debug.enabled_debug_grids[1]:
shader = gpu.shader.from_builtin(line_draw_mode)
batch = batch_for_shader(shader, 'LINES', {"pos": y_coords})
shader.bind()
shader.uniform_float("color", (y_color[0], y_color[1], y_color[2], 1.0))
if vcu.is_blender_35():
# Can be drawn with depth in Blender 3.5 or later
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
batch.draw(shader)
if vcu.is_blender_35():
gpu.state.depth_mask_set(False)
if display_grid and dprops.debug.enabled_debug_grids[0]:
shader = gpu.shader.from_builtin(line_draw_mode)
batch = batch_for_shader(shader, 'LINES', {"pos": x_coords})
shader.bind()
shader.uniform_float("color", (x_color[0], x_color[1], x_color[2], 1.0))
if vcu.is_blender_35():
# Can be drawn with depth in Blender 3.5 or later
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
batch.draw(shader)
if vcu.is_blender_35():
gpu.state.depth_mask_set(False)
if dprops.debug.display_domain_bounds:
shader = gpu.shader.from_builtin(line_draw_mode)
batch = batch_for_shader(shader, 'LINES', {"pos": bounds_coords})
shader.bind()
shader.uniform_float("color", (bounds_color[0], bounds_color[1], bounds_color[2], 1.0))
if vcu.is_blender_35():
# Can be drawn with depth in Blender 3.5 or later
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
batch.draw(shader)
if vcu.is_blender_35():
gpu.state.depth_mask_set(False)
else:
import bgl
bgl.glLineWidth(1)
bgl.glBegin(bgl.GL_LINES)
if display_grid and dprops.debug.enabled_debug_grids[2]:
shader = gpu.shader.from_builtin(line_draw_mode)
batch = batch_for_shader(shader, 'LINES', {"pos": z_coords})
shader.bind()
shader.uniform_float("color", (z_color[0], z_color[1], z_color[2], 1.0))
if display_grid and dprops.debug.enabled_debug_grids[2]:
bgl.glColor4f(*z_color, 1.0)
for c in z_coords:
bgl.glVertex3f(*c)
if display_grid and dprops.debug.enabled_debug_grids[1]:
bgl.glColor4f(*y_color, 1.0)
for c in y_coords:
bgl.glVertex3f(*c)
if display_grid and dprops.debug.enabled_debug_grids[0]:
bgl.glColor4f(*x_color, 1.0)
for c in x_coords:
bgl.glVertex3f(*c)
if dprops.debug.display_domain_bounds:
bgl.glColor4f(*(bounds_color), 1.0)
for c in bounds_coords:
bgl.glVertex3f(*c)
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
batch.draw(shader)
gpu.state.depth_mask_set(False)
bgl.glEnd()
bgl.glLineWidth(1)
bgl.glEnable(bgl.GL_DEPTH_TEST)
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
if display_grid and dprops.debug.enabled_debug_grids[1]:
shader = gpu.shader.from_builtin(line_draw_mode)
batch = batch_for_shader(shader, 'LINES', {"pos": y_coords})
shader.bind()
shader.uniform_float("color", (y_color[0], y_color[1], y_color[2], 1.0))
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
batch.draw(shader)
gpu.state.depth_mask_set(False)
if display_grid and dprops.debug.enabled_debug_grids[0]:
shader = gpu.shader.from_builtin(line_draw_mode)
batch = batch_for_shader(shader, 'LINES', {"pos": x_coords})
shader.bind()
shader.uniform_float("color", (x_color[0], x_color[1], x_color[2], 1.0))
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
batch.draw(shader)
gpu.state.depth_mask_set(False)
if dprops.debug.display_domain_bounds:
shader = gpu.shader.from_builtin(line_draw_mode)
batch = batch_for_shader(shader, 'LINES', {"pos": bounds_coords})
shader.bind()
shader.uniform_float("color", (bounds_color[0], bounds_color[1], bounds_color[2], 1.0))
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
batch.draw(shader)
gpu.state.depth_mask_set(False)
def modal(self, context, event):
@@ -25,9 +25,8 @@ from ..utils import ui_utils
from ..utils import version_compatibility_utils as vcu
from .. import render
if vcu.is_blender_28():
import gpu
from gpu_extras.batch import batch_for_shader
import gpu
from gpu_extras.batch import batch_for_shader
particle_vertices = []
@@ -87,51 +86,16 @@ def update_debug_particle_geometry(context):
particle_vertices.append(particles[pidx])
particle_vertex_colors.append(color_tuple)
if vcu.is_blender_28():
global particle_shader
global particle_batch_draw
global particle_shader
global particle_batch_draw
vertex_shader = """
uniform mat4 ModelViewProjectionMatrix;
shader_name = 'SMOOTH_COLOR'
particle_shader = gpu.shader.from_builtin(shader_name)
in vec3 pos;
in vec4 color;
out vec4 finalColor;
void main()
{
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
finalColor = color;
}
"""
fragment_shader = """
in vec4 finalColor;
out vec4 fragColor;
void main()
{
fragColor = finalColor;
}
"""
if vcu.is_blender_35():
# Needed for support on MacOS Apple Silicon systems in Blender 3.5 or later
# Could possibly be a Blender regression bug why the below method no longer
# works in Blender 3.5 or later for MacOS. Should file a report.
shader_name = '3D_SMOOTH_COLOR'
if vcu.is_blender_40():
shader_name = 'SMOOTH_COLOR'
particle_shader = gpu.shader.from_builtin(shader_name)
else:
particle_shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
particle_batch_draw = batch_for_shader(
particle_shader, 'POINTS',
{"pos": particle_vertices, "color": particle_vertex_colors},
)
particle_batch_draw = batch_for_shader(
particle_shader, 'POINTS',
{"pos": particle_vertices, "color": particle_vertex_colors},
)
def _get_color_ranges(pdata, num_colors):
@@ -224,50 +188,13 @@ class FlipFluidDrawGLParticles(bpy.types.Operator):
if vcu.get_object_hide_viewport(domain):
return
if not vcu.is_blender_28():
dlayers = [i for i,v in enumerate(domain.layers) if v]
slayers = [i for i,v in enumerate(context.scene.layers) if v]
if not (set(dlayers) & set(slayers)):
return
if vcu.is_blender_28():
global particle_shader
global particle_batch_draw
if vcu.is_blender_35():
# Warnings in Blender 3.5 when using bgl module, which is to
# be deprecated in Blender 3.7. Use gpu module instead.
gpu.state.point_size_set(dprops.debug.particle_size)
else:
# only attempt to import bgl when necessary (older versions of Blender). In Blender >= 3.5,
# importing bgl generates a warning, and possibly an error in Blender >= 4.0.
import bgl
bgl.glPointSize(dprops.debug.particle_size)
if vcu.is_blender_35():
# Can be drawn with depth in Blender 3.5 or later
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
particle_batch_draw.draw(particle_shader)
if vcu.is_blender_35():
gpu.state.depth_mask_set(False)
else:
# only attempt to import bgl when necessary (older versions of Blender). In Blender >= 3.5,
# importing bgl generates a warning, and possibly an error in Blender >= 4.0.
import bgl
bgl.glPointSize(dprops.debug.particle_size)
bgl.glBegin(bgl.GL_POINTS)
current_color = None
for i in range(len(particle_vertices)):
if current_color != particle_vertex_colors[i]:
current_color = particle_vertex_colors[i]
bgl.glColor4f(current_color[0], current_color[1], current_color[2], 1.0)
bgl.glVertex3f(*(particle_vertices[i]))
bgl.glEnd()
bgl.glPointSize(1)
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
global particle_shader
global particle_batch_draw
gpu.state.point_size_set(dprops.debug.particle_size)
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
particle_batch_draw.draw(particle_shader)
gpu.state.depth_mask_set(False)
def modal(self, context, event):
@@ -28,14 +28,9 @@ class FlipFluidDisplayError(bpy.types.Operator):
bl_label = ""
bl_description = ""
error_message = StringProperty()
exec(vcu.convert_attribute_to_28("error_message"))
error_description = StringProperty()
exec(vcu.convert_attribute_to_28("error_description"))
popup_width = IntProperty(default=400)
exec(vcu.convert_attribute_to_28("popup_width"))
error_message: StringProperty()
error_description: StringProperty()
popup_width: IntProperty(default=400)
def draw(self, context):
@@ -55,14 +55,11 @@ class FlipFluidHelperRemesh(bpy.types.Operator):
" of the input geometry. Use the link next to this operator to view documentation and a video guide" +
" for using this feature")
skip_hide_render_objects = BoolProperty(True)
exec(vcu.convert_attribute_to_28("skip_hide_render_objects"))
skip_hide_render_objects: BoolProperty(True)
apply_object_modifiers = BoolProperty(True)
exec(vcu.convert_attribute_to_28("apply_object_modifiers"))
apply_object_modifiers: BoolProperty(True)
convert_objects_to_mesh = BoolProperty(True)
exec(vcu.convert_attribute_to_28("convert_objects_to_mesh"))
convert_objects_to_mesh: BoolProperty(True)
@classmethod
@@ -446,8 +443,7 @@ class FlipFluidHelperSelectObjects(bpy.types.Operator):
bl_label = "Select Objects"
bl_description = "Select all FLIP Fluid objects of this type"
object_type = StringProperty("TYPE_NONE")
exec(vcu.convert_attribute_to_28("object_type"))
object_type: StringProperty("TYPE_NONE")
@classmethod
@@ -802,8 +798,7 @@ class FlipFluidHelperAddObjects(bpy.types.Operator):
bl_label = "Add Objects"
bl_description = "Add selected objects as FLIP Fluid objects"
object_type = StringProperty("TYPE_NONE")
exec(vcu.convert_attribute_to_28("object_type"))
object_type: StringProperty("TYPE_NONE")
@classmethod
def poll(cls, context):
@@ -1014,8 +1009,7 @@ class FlipFluidHelperDeleteWhitewaterObjects(bpy.types.Operator):
" Warning: deleting these objects will also remove any modifications such as added" +
" modifiers and materials")
whitewater_type = StringProperty("TYPE_ALL")
exec(vcu.convert_attribute_to_28("whitewater_type"))
whitewater_type: StringProperty("TYPE_ALL")
@classmethod
@@ -1053,7 +1047,7 @@ class FlipFluidHelperOrganizeOutliner(bpy.types.Operator):
@classmethod
def poll(cls, context):
return vcu.is_blender_28()
return True
def initialize_child_collection(self, context, child_name, parent_collection):
@@ -1159,7 +1153,7 @@ class FlipFluidHelperSeparateFLIPMeshes(bpy.types.Operator):
@classmethod
def poll(cls, context):
dprops = context.scene.flip_fluid.get_domain_properties()
return vcu.is_blender_28() and dprops is not None
return dprops is not None
def initialize_child_collection(self, context, child_name, parent_collection):
@@ -1237,7 +1231,7 @@ class FlipFluidHelperUndoOrganizeOutliner(bpy.types.Operator):
@classmethod
def poll(cls, context):
return vcu.is_blender_28()
return True
def unlink_collection(self, context, collection_name):
@@ -1287,7 +1281,7 @@ class FlipFluidHelperUndoSeparateFLIPMeshes(bpy.types.Operator):
@classmethod
def poll(cls, context):
dprops = context.scene.flip_fluid.get_domain_properties()
return vcu.is_blender_28() and dprops is not None
return dprops is not None
def unlink_collection(self, context, collection_name):
@@ -1331,8 +1325,7 @@ class FlipFluidHelperSetObjectViewportDisplay(bpy.types.Operator):
bl_label = "Object Viewport Display"
bl_description = "Set how selected objects are displayed/rendered in the viewport"
display_mode = StringProperty("TYPE_NONE")
exec(vcu.convert_attribute_to_28("display_mode"))
display_mode: StringProperty("TYPE_NONE")
@classmethod
@@ -1365,8 +1358,7 @@ class FlipFluidHelperSetObjectRenderDisplay(bpy.types.Operator):
bl_label = "Object Render Display"
bl_description = "Set selected objects visiblility in the render"
hide_render = BoolProperty(False)
exec(vcu.convert_attribute_to_28("hide_render"))
hide_render: BoolProperty(False)
@classmethod
@@ -1568,7 +1560,7 @@ class FlipFluidEnableColorAttributeTooltip(bpy.types.Operator):
class FlipFluidEnableViscosityAttribute(bpy.types.Operator):
bl_idname = "flip_fluid_operators.enable_viscosity_attribute"
bl_label = "Enable Viscosity Attribute"
bl_description = "Enable viscosity solver and variable viscosity attribute in the Domain FLIP Fluid World panel"
bl_description = "Enable viscosity solver and variable viscosity attribute in the Domain World panel"
@classmethod
def poll(cls, context):
@@ -1606,10 +1598,50 @@ class FlipFluidEnableViscosityAttributeTooltip(bpy.types.Operator):
return {'FINISHED'}
class FlipFluidEnableDensityAttribute(bpy.types.Operator):
bl_idname = "flip_fluid_operators.enable_density_attribute"
bl_label = "Enable Density Attribute"
bl_description = "Enable variable density solver and attribute in the Domain World panel"
@classmethod
def poll(cls, context):
return context.scene.flip_fluid.get_domain_object() is not None
def execute(self, context):
dprops = context.scene.flip_fluid.get_domain_properties()
dprops.world.enable_density_attribute = True
return {'FINISHED'}
class FlipFluidEnableDensityAttributeMenu(bpy.types.Menu):
bl_label = ""
bl_idname = "FLIP_FLUID_MENUS_MT_enable_density_attribute_menu"
def draw(self, context):
self.layout.operator("flip_fluid_operators.enable_density_attribute")
class FlipFluidEnableDensityAttributeTooltip(bpy.types.Operator):
bl_idname = "flip_fluid_operators.enable_density_attribute_tooltip"
bl_label = "Enable Density Attribute"
bl_description = "Click to enable the variable density solver and attribute in the Domain World panel"
@classmethod
def poll(cls, context):
return context.scene.flip_fluid.get_domain_object() is not None
def execute(self, context):
bpy.ops.wm.call_menu(name="FLIP_FLUID_MENUS_MT_enable_density_attribute_menu")
return {'FINISHED'}
class FlipFluidEnableLifetimeAttribute(bpy.types.Operator):
bl_idname = "flip_fluid_operators.enable_lifetime_attribute"
bl_label = "Enable Lifetime Attribute"
bl_description = "Enable lifetime attribute in the Domain FLIP Fluid Surface and Domain FLIP Fluid Particles panel"
bl_description = "Enable lifetime attribute in the Domain Surface and/or Domain Particles panel"
@classmethod
def poll(cls, context):
@@ -1619,7 +1651,8 @@ class FlipFluidEnableLifetimeAttribute(bpy.types.Operator):
def execute(self, context):
dprops = context.scene.flip_fluid.get_domain_properties()
dprops.surface.enable_lifetime_attribute = True
dprops.particles.enable_lifetime_attribute = True
dprops.particles.enable_fluid_particle_lifetime_attribute = True
dprops.whitewater.enable_lifetime_attribute = True
return {'FINISHED'}
@@ -1634,7 +1667,7 @@ class FlipFluidEnableLifetimeAttributeMenu(bpy.types.Menu):
class FlipFluidEnableLifetimeAttributeTooltip(bpy.types.Operator):
bl_idname = "flip_fluid_operators.enable_lifetime_attribute_tooltip"
bl_label = "Enable Lifetime Attribute"
bl_description = "Click to enable the lifetime attribute in the Domain FLIP Fluid Surface and Domain FLIP Fluid Particles panel"
bl_description = "Click to enable the lifetime attribute in the Domain Surface and/or Domain Particles panel"
@classmethod
@@ -1650,7 +1683,7 @@ class FlipFluidEnableLifetimeAttributeTooltip(bpy.types.Operator):
class FlipFluidEnableSourceIDAttribute(bpy.types.Operator):
bl_idname = "flip_fluid_operators.enable_source_id_attribute"
bl_label = "Enable Source ID Attribute"
bl_description = "Enable source ID attribute in the Domain FLIP Fluid Surface and FLIP Fluid Particles panel"
bl_description = "Enable source ID attribute in the Domain Surface and/or Particles panel"
@classmethod
def poll(cls, context):
@@ -1675,7 +1708,7 @@ class FlipFluidEnableSourceIDAttributeMenu(bpy.types.Menu):
class FlipFluidEnableSourceIDAttributeTooltip(bpy.types.Operator):
bl_idname = "flip_fluid_operators.enable_source_id_attribute_tooltip"
bl_label = "Enable Source ID Attribute"
bl_description = "Click to enable the source ID attribute in the Domain FLIP Fluid Surface and FLIP Fluid Particles panel"
bl_description = "Click to enable the source ID attribute in the Domain Surface and/or Domain Particles panel"
@classmethod
@@ -1689,9 +1722,6 @@ class FlipFluidEnableSourceIDAttributeTooltip(bpy.types.Operator):
def is_geometry_node_point_cloud_detected(bl_mesh_cache_object=None):
if not vcu.is_blender_31():
return False
try:
dprops = bpy.context.scene.flip_fluid.get_domain_properties()
if bl_mesh_cache_object is None:
@@ -1723,7 +1753,7 @@ def is_geometry_node_point_cloud_detected(bl_mesh_cache_object=None):
def update_geometry_node_material(bl_object, resource_name):
if not vcu.is_blender_31() or bl_object is None:
if bl_object is None:
return
gn_modifier = None
@@ -1769,6 +1799,13 @@ def add_geometry_node_modifier(target_object, resource_filepath, resource_name):
return gn_modifier
def get_geometry_node_modifier(target_object, resource_name):
for mod in target_object.modifiers:
if mod.type == 'NODES' and mod.name == resource_name:
return mod
return None
class FlipFluidHelperInitializeMotionBlur(bpy.types.Operator):
bl_idname = "flip_fluid_operators.helper_initialize_motion_blur"
bl_label = "Initialize Motion Blur"
@@ -1776,8 +1813,7 @@ class FlipFluidHelperInitializeMotionBlur(bpy.types.Operator):
" This will be applied to the fluid surface, fluid particles, and whitewater particles if enabled." +
" Node groups can be viewed in the geometry nodes editor and modifier")
resource_prefix = StringProperty(default="FF_GeometryNodes")
exec(vcu.convert_attribute_to_28("resource_prefix"))
resource_prefix: StringProperty(default="FF_GeometryNodes")
@classmethod
@@ -1786,10 +1822,6 @@ class FlipFluidHelperInitializeMotionBlur(bpy.types.Operator):
def apply_modifier_settings(self, target_object, gn_modifier):
gn_modifier["Input_2_use_attribute"] = True
gn_modifier["Input_2_attribute_name"] = 'flip_velocity'
gn_modifier["Output_3_attribute_name"] = 'velocity'
gn_name = gn_modifier.name
if gn_name.startswith("FF_GeometryNodesSurface"):
# Depending on FLIP Fluids version, the GN set up may not
@@ -1815,30 +1847,15 @@ class FlipFluidHelperInitializeMotionBlur(bpy.types.Operator):
except:
pass
try:
# Enable Point Cloud
gn_modifier["Input_9"] = True
except:
pass
try:
# Enable Instancing
gn_modifier["Input_10"] = False
except:
pass
def execute(self, context):
if not vcu.is_blender_31():
self.report({'INFO'}, "Blender 3.1 or later is required for this feature")
return {'CANCELLED'}
if not context.scene.flip_fluid.is_domain_in_active_scene():
self.report({"ERROR"},
"Active scene must contain domain object to use this operator. Select the scene that contains the domain object and try again.")
return {'CANCELLED'}
if context.scene.render.engine != 'CYCLES':
unsupported_render_engines = ['BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH']
if context.scene.render.engine in unsupported_render_engines:
context.scene.render.engine = 'CYCLES'
self.report({'INFO'}, "Setting render engine to Cycles")
if not context.scene.render.use_motion_blur:
@@ -1848,15 +1865,15 @@ class FlipFluidHelperInitializeMotionBlur(bpy.types.Operator):
dprops = context.scene.flip_fluid.get_domain_properties()
if not dprops.surface.enable_velocity_vector_attribute:
dprops.surface.enable_velocity_vector_attribute = True
self.report({'INFO'}, "Enabled generation of fluid surface velocity vector attributes in FLIP Fluid Surface panel (baking required)")
self.report({'INFO'}, "Enabled generation of fluid surface velocity vector attributes in Domain Surface panel (baking required)")
if not dprops.particles.enable_fluid_particle_velocity_vector_attribute:
dprops.particles.enable_fluid_particle_velocity_vector_attribute = True
self.report({'INFO'}, "Enabled generation of fluid particle velocity vector attributes in FLIP Fluid Particles panel (baking required)")
self.report({'INFO'}, "Enabled generation of fluid particle velocity vector attributes in Domain Particles panel (baking required)")
if not dprops.whitewater.enable_velocity_vector_attribute:
dprops.whitewater.enable_velocity_vector_attribute = True
self.report({'INFO'}, "Enabled generation of whitewater velocity vector attributes in FLIP Fluid Whitewater (baking required)")
self.report({'INFO'}, "Enabled generation of whitewater velocity vector attributes in Domain Whitewater (baking required)")
blend_filename = "geometry_nodes_library.blend"
surface_resource = self.resource_prefix + "Surface"
@@ -1943,8 +1960,7 @@ class FlipFluidHelperRemoveMotionBlur(bpy.types.Operator):
" operator will not disable the Domain surface/particles/whitewater velocity attribute settings")
resource_prefix = StringProperty(default="FF_GeometryNodes")
exec(vcu.convert_attribute_to_28("resource_prefix"))
resource_prefix: StringProperty(default="FF_GeometryNodes")
@classmethod
@@ -1953,10 +1969,6 @@ class FlipFluidHelperRemoveMotionBlur(bpy.types.Operator):
def execute(self, context):
if not vcu.is_blender_31():
self.report({'INFO'}, "Blender 3.1 or later is required for this feature")
return {'CANCELLED'}
if not context.scene.flip_fluid.is_domain_in_active_scene():
self.report({"ERROR"},
"Active scene must contain domain object to use this operator. Select the scene that contains the domain object and try again.")
@@ -2019,8 +2031,7 @@ class FlipFluidHelperToggleMotionBlurRendering(bpy.types.Operator):
bl_description = ("Toggle motion blur rendering for the simulation meshes on or off. This operator will enable or" +
" disable the simulations mesh object and geometry node settings for motion blur rendering")
enable_motion_blur_rendering = BoolProperty(default=True)
exec(vcu.convert_attribute_to_28("enable_motion_blur_rendering"))
enable_motion_blur_rendering: BoolProperty(default=True)
@classmethod
@@ -2070,13 +2081,207 @@ class FlipFluidHelperToggleMotionBlurRendering(bpy.types.Operator):
return {'FINISHED'}
class FlipFluidHelperUpdateGeometryNodeModifiers(bpy.types.Operator):
bl_idname = "flip_fluid_operators.helper_update_geometry_node_modifiers"
bl_label = "Update Geometry Node Modifiers"
bl_description = ("Update the fluid surface, particle, and whitewater geometry nodes modifiers to the current addon version and transfer settings." +
" This operator will not delete existing FLIP Fluids modifiers or datablocks. Existing modifiers will be renamed with a" +
" BACKUP prefix and disabled in the modifier stack. This backup can be removed if not wanted")
resource_prefix: StringProperty(default="FF_GeometryNodes")
@classmethod
def poll(cls, context):
return context.scene.flip_fluid.get_domain_object() is not None
def transfer_gn_modifer_settings(self, old_gn_modifier, new_gn_modifier, old_new_key_pairs):
for key in old_new_key_pairs:
old_key = key[0]
new_key = key[1]
if old_key in old_gn_modifier:
try:
new_gn_modifier[new_key] = old_gn_modifier[old_key]
except:
pass
def transfer_gn_modifier_settings_surface(self, old_gn_modifier, new_gn_modifier):
# Old Key, New Key
keys = [
("Input_6", "Input_6"), # Enable Motion Blur
("Input_4", "Input_4"), # Motion Blur Scale
]
self.transfer_gn_modifer_settings(old_gn_modifier, new_gn_modifier, keys)
def transfer_gn_modifier_settings_fluid_particle(self, old_gn_modifier, new_gn_modifier):
# Old Key, New Key
keys = [
("Input_5", "Input_5"), # Material
("Input_8", "Input_8"), # Enable Motion Blur
("Input_4", "Input_4"), # Motion Blur Scale
("Input_6", "Input_6"), # Particle Scale
("Socket_2", "Socket_2"), # Particle Scale Random
("Socket_1", "Socket_1"), # Fading Width
("Socket_0", "Socket_0"), # Fading Strength
("Socket_4", "Socket_4"), # Fading Density
]
self.transfer_gn_modifer_settings(old_gn_modifier, new_gn_modifier, keys)
def transfer_gn_modifier_settings_whitewater(self, old_gn_modifier, new_gn_modifier):
# Old Key, New Key
keys = [
("Input_5", "Input_5"), # Material
("Input_8", "Input_8"), # Enable Motion Blur
("Input_4", "Input_4"), # Motion Blur Scale
("Input_6", "Input_6"), # Particle Scale
("Socket_2", "Socket_2"), # Particle Scale Random
("Socket_1", "Socket_1"), # Fading Width
("Socket_0", "Socket_0"), # Fading Strength
("Socket_4", "Socket_4"), # Fading Density
]
self.transfer_gn_modifer_settings(old_gn_modifier, new_gn_modifier, keys)
def get_ff_geometry_node_modifiers(self, bl_object):
modifiers = []
if bl_object is None:
return modifiers
for mod in bl_object.modifiers:
if mod.type == "NODES" and mod.node_group and mod.node_group.name.startswith("FF_GeometryNodes"):
modifiers.append(mod)
return modifiers
def execute(self, context):
if not context.scene.flip_fluid.is_domain_in_active_scene():
self.report({"ERROR"},
"Active scene must contain domain object to use this operator. Select the scene that contains the domain object and try again.")
return {'CANCELLED'}
dprops = context.scene.flip_fluid.get_domain_properties()
# Gather Blender cache objects
surface_mesh_caches = [dprops.mesh_cache.surface]
surface_cache_objects = []
for m in surface_mesh_caches:
bl_object = m.get_cache_object()
if bl_object is not None:
surface_cache_objects.append(bl_object)
fluid_particle_mesh_caches = [dprops.mesh_cache.particles]
fluid_particle_cache_objects = []
for m in fluid_particle_mesh_caches:
bl_object = m.get_cache_object()
if bl_object is not None:
fluid_particle_cache_objects.append(bl_object)
whitewater_mesh_caches = [
dprops.mesh_cache.foam,
dprops.mesh_cache.bubble,
dprops.mesh_cache.spray,
dprops.mesh_cache.dust
]
whitewater_cache_objects = []
for m in whitewater_mesh_caches:
bl_object = m.get_cache_object()
if bl_object is not None:
whitewater_cache_objects.append(bl_object)
bl_cache_objects = surface_cache_objects + fluid_particle_cache_objects + whitewater_cache_objects
# Search for the last FF_GeometryNodes modifier in the stack for each cache object for transferring settings
surface_gn_modifiers = []
for bl_object in surface_cache_objects:
existing_gn_modifiers = self.get_ff_geometry_node_modifiers(bl_object)
if existing_gn_modifiers:
surface_gn_modifiers.append(existing_gn_modifiers[-1])
else:
surface_gn_modifiers.append(None)
fluid_particle_gn_modifiers = []
for bl_object in fluid_particle_cache_objects:
existing_gn_modifiers = self.get_ff_geometry_node_modifiers(bl_object)
if existing_gn_modifiers:
fluid_particle_gn_modifiers.append(existing_gn_modifiers[-1])
else:
fluid_particle_gn_modifiers.append(None)
whitewater_gn_modifiers = []
for bl_object in whitewater_cache_objects:
existing_gn_modifiers = self.get_ff_geometry_node_modifiers(bl_object)
if existing_gn_modifiers:
whitewater_gn_modifiers.append(existing_gn_modifiers[-1])
else:
whitewater_gn_modifiers.append(None)
# Disable existing FF_GeometryNode modifiers and rename with BACKUP_ prefix
for bl_object in bl_cache_objects:
existing_gn_modifiers = self.get_ff_geometry_node_modifiers(bl_object)
for mod in existing_gn_modifiers:
mod.show_viewport = False
mod.show_render = False
mod.show_expanded = False
mod.name = "BACKUP_" + mod.name
mod.node_group.name = "BACKUP_" + mod.node_group.name
# Initialize current FF_GeometryNode modifiers
geometry_nodes_library = "geometry_nodes_library.blend"
parent_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
resource_filepath = os.path.join(parent_path, "resources", "geometry_nodes", geometry_nodes_library)
surface_resource = self.resource_prefix + "Surface"
fluid_particle_resource = self.resource_prefix + "FluidParticles"
whitewater_foam_resource = self.resource_prefix + "WhitewaterFoam"
whitewater_bubble_resource = self.resource_prefix + "WhitewaterBubble"
whitewater_spray_resource = self.resource_prefix + "WhitewaterSpray"
whitewater_dust_resource = self.resource_prefix + "WhitewaterDust"
for idx, bl_surface in enumerate(surface_cache_objects):
old_gn_modifier = surface_gn_modifiers[idx]
new_gn_modifier = add_geometry_node_modifier(bl_surface, resource_filepath, surface_resource)
if old_gn_modifier and new_gn_modifier:
self.transfer_gn_modifier_settings_surface(old_gn_modifier, new_gn_modifier)
for idx, bl_fluid_particle in enumerate(fluid_particle_cache_objects):
old_gn_modifier = fluid_particle_gn_modifiers[idx]
new_gn_modifier = add_geometry_node_modifier(bl_fluid_particle, resource_filepath, fluid_particle_resource)
if old_gn_modifier and new_gn_modifier:
self.transfer_gn_modifier_settings_fluid_particle(old_gn_modifier, new_gn_modifier)
for idx, bl_whitewater in enumerate(whitewater_cache_objects):
whitewater_resource = ""
if bl_whitewater == dprops.mesh_cache.foam.get_cache_object():
whitewater_resource = whitewater_foam_resource
elif bl_whitewater == dprops.mesh_cache.bubble.get_cache_object():
whitewater_resource = whitewater_bubble_resource
elif bl_whitewater == dprops.mesh_cache.spray.get_cache_object():
whitewater_resource = whitewater_spray_resource
elif bl_whitewater == dprops.mesh_cache.dust.get_cache_object():
whitewater_resource = whitewater_dust_resource
old_gn_modifier = whitewater_gn_modifiers[idx]
new_gn_modifier = add_geometry_node_modifier(bl_whitewater, resource_filepath, whitewater_resource)
if old_gn_modifier and new_gn_modifier:
self.transfer_gn_modifier_settings_whitewater(old_gn_modifier, new_gn_modifier)
self.report({'INFO'}, "Updated FLIP Fluids Geometry Node Modifiers")
return {'FINISHED'}
class FlipFluidHelperInitializeCacheObjects(bpy.types.Operator):
bl_idname = "flip_fluid_operators.helper_initialize_cache_objects"
bl_label = "Initialize Cache Objects"
bl_description = ("Initialize simulation meshes, modifiers, and data")
cache_object_type = StringProperty(default="CACHE_OBJECT_TYPE_NONE")
exec(vcu.convert_attribute_to_28("cache_object_type"))
cache_object_type: StringProperty(default="CACHE_OBJECT_TYPE_NONE")
@classmethod
@@ -2139,8 +2344,7 @@ class FlipFluidHelperStableRendering28(bpy.types.Operator):
" during render (Blender > Render > Lock Interface) and is highly"
" recommended")
enable_state = BoolProperty(True)
exec(vcu.convert_attribute_to_28("enable_state"))
enable_state: BoolProperty(True)
@classmethod
@@ -2188,8 +2392,7 @@ class FlipFluidHelperSaveBlendFile(bpy.types.Operator):
bl_label = "Save File"
bl_description = "Open the Blender file window to save the current .blend file"
save_as_blend_file = BoolProperty(default=True)
exec(vcu.convert_attribute_to_28("save_as_blend_file"))
save_as_blend_file: BoolProperty(default=True)
@classmethod
@@ -2241,8 +2444,7 @@ class FlipFluidHelperBatchExportAnimatedMesh(bpy.types.Operator):
bl_description = "Enable or Disable the 'Export Animated Mesh' option for all objects in list"
enable_state = BoolProperty(True)
exec(vcu.convert_attribute_to_28("enable_state"))
enable_state: BoolProperty(True)
@classmethod
@@ -2265,8 +2467,7 @@ class FlipFluidHelperBatchSkipReexport(bpy.types.Operator):
bl_description = "Enable or Disable the 'Skip Re-Export' option for all objects in list"
enable_state = BoolProperty(True)
exec(vcu.convert_attribute_to_28("enable_state"))
enable_state: BoolProperty(True)
@classmethod
@@ -2289,8 +2490,7 @@ class FlipFluidHelperBatchForceReexport(bpy.types.Operator):
bl_description = "Enable or Disable the 'Force Re-Export On Next Bake' option for all objects in list"
enable_state = BoolProperty(True)
exec(vcu.convert_attribute_to_28("enable_state"))
enable_state: BoolProperty(True)
@classmethod
@@ -2538,20 +2738,12 @@ class FlipFluidCopySettingsFromActive(bpy.types.Operator):
def toggle_cycles_ray_visibility(self, obj, is_enabled):
# Cycles may not be enabled in the user's preferences
try:
if vcu.is_blender_30():
obj.visible_camera = is_enabled
obj.visible_diffuse = is_enabled
obj.visible_glossy = is_enabled
obj.visible_transmission = is_enabled
obj.visible_volume_scatter = is_enabled
obj.visible_shadow = is_enabled
else:
obj.cycles_visibility.camera = is_enabled
obj.cycles_visibility.transmission = is_enabled
obj.cycles_visibility.diffuse = is_enabled
obj.cycles_visibility.scatter = is_enabled
obj.cycles_visibility.glossy = is_enabled
obj.cycles_visibility.shadow = is_enabled
obj.visible_camera = is_enabled
obj.visible_diffuse = is_enabled
obj.visible_glossy = is_enabled
obj.visible_transmission = is_enabled
obj.visible_volume_scatter = is_enabled
obj.visible_shadow = is_enabled
except:
pass
@@ -4507,11 +4699,7 @@ class FlipFluidPassesImportMedia(bpy.types.Operator):
bl_idname = "flip_fluid.passes_import_media"
bl_label = "Import Media"
option_path_supports_blend_relative = set()
if vcu.is_blender_45():
# required for relative path support in Blender 4.5+
# https://docs.blender.org/api/4.5/bpy_types_enum_items/property_flag_items.html#rna-enum-property-flag-items
option_path_supports_blend_relative = {'PATH_SUPPORTS_BLEND_RELATIVE'}
option_path_supports_blend_relative = {'PATH_SUPPORTS_BLEND_RELATIVE'}
filter_glob: StringProperty(
default="*.png;*.jpg;*.jpeg;*.bmp;*.tiff;*.tif;*.mp4;*.avi;*.mov",
@@ -5992,6 +6180,7 @@ def register():
FlipFluidHelperInitializeMotionBlur,
FlipFluidHelperRemoveMotionBlur,
FlipFluidHelperToggleMotionBlurRendering,
FlipFluidHelperUpdateGeometryNodeModifiers,
FlipFluidHelperInitializeCacheObjects,
FlipFluidHelperStableRendering279,
FlipFluidHelperStableRendering28,
@@ -6013,6 +6202,9 @@ def register():
FlipFluidEnableViscosityAttribute,
FlipFluidEnableViscosityAttributeMenu,
FlipFluidEnableViscosityAttributeTooltip,
FlipFluidEnableDensityAttribute,
FlipFluidEnableDensityAttributeMenu,
FlipFluidEnableDensityAttributeTooltip,
FlipFluidEnableLifetimeAttribute,
FlipFluidEnableLifetimeAttributeMenu,
FlipFluidEnableLifetimeAttributeTooltip,
@@ -6103,6 +6295,7 @@ def unregister():
bpy.utils.unregister_class(FlipFluidHelperInitializeMotionBlur)
bpy.utils.unregister_class(FlipFluidHelperRemoveMotionBlur)
bpy.utils.unregister_class(FlipFluidHelperToggleMotionBlurRendering)
bpy.utils.unregister_class(FlipFluidHelperUpdateGeometryNodeModifiers)
bpy.utils.unregister_class(FlipFluidHelperInitializeCacheObjects)
bpy.utils.unregister_class(FlipFluidHelperStableRendering279)
bpy.utils.unregister_class(FlipFluidHelperStableRendering28)
@@ -6124,6 +6317,9 @@ def unregister():
bpy.utils.unregister_class(FlipFluidEnableViscosityAttribute)
bpy.utils.unregister_class(FlipFluidEnableViscosityAttributeMenu)
bpy.utils.unregister_class(FlipFluidEnableViscosityAttributeTooltip)
bpy.utils.unregister_class(FlipFluidEnableDensityAttribute)
bpy.utils.unregister_class(FlipFluidEnableDensityAttributeMenu)
bpy.utils.unregister_class(FlipFluidEnableDensityAttributeTooltip)
bpy.utils.unregister_class(FlipFluidEnableLifetimeAttribute)
bpy.utils.unregister_class(FlipFluidEnableLifetimeAttributeMenu)
bpy.utils.unregister_class(FlipFluidEnableLifetimeAttributeTooltip)
@@ -47,8 +47,7 @@ class FLIPFluidPreferencesExportUserData(bpy.types.Operator):
bl_description = ("Creates a backup of your user settings and presets as a" +
" .zip file. All user data will be lost after uninstalling the addon.")
filepath = StringProperty(subtype="FILE_PATH")
exec(vcu.convert_attribute_to_28("filepath"))
filepath: StringProperty(subtype="FILE_PATH")
@classmethod
@@ -180,12 +179,11 @@ class FLIPFluidPreferencesImportUserData(bpy.types.Operator, ImportHelper):
bl_description = "Load user settings and presets from a previous installation"
filename_ext = "*.zip"
filter_glob = StringProperty(
filter_glob: StringProperty(
default="*.zip",
options={'HIDDEN'},
maxlen=255,
)
exec(vcu.convert_attribute_to_28("filter_glob"))
@@ -264,12 +262,11 @@ class FLIPFluidInstallMixboxPlugin(bpy.types.Operator, ImportHelper):
" Fluids addon downloads")
filename_ext = "*.plugin"
filter_glob = StringProperty(
filter_glob: StringProperty(
default="*.plugin;*.zip",
options={'HIDDEN'},
maxlen=255,
)
exec(vcu.convert_attribute_to_28("filter_glob"))
@classmethod
@@ -377,12 +374,11 @@ class FLIPFluidInstallPresetLibrary(bpy.types.Operator, ImportHelper):
" The Preset Scenes file can be found in the FLIP Fluids addon downloads")
filename_ext = "*.zip"
filter_glob = StringProperty(
filter_glob: StringProperty(
default="*.zip",
options={'HIDDEN'},
maxlen=255,
)
exec(vcu.convert_attribute_to_28("filter_glob"))
@classmethod
@@ -512,9 +508,7 @@ class FLIPFluidInstallPresetLibrary(bpy.types.Operator, ImportHelper):
for lib_entry in bl_filepaths.asset_libraries:
if self.is_path_equal(lib_entry.path, preset_library_directory):
lib_entry.name = preset_library_name
if vcu.is_blender_35():
# Only available in Blender >= 3.5
lib_entry.import_method = 'APPEND'
lib_entry.import_method = 'APPEND'
installation_utils.update_preset_library_installation_status()
success_message = "The Preset Scenes Library has been installed successfully into the Blender Asset Browser."
@@ -529,11 +523,9 @@ class FLIPFluidSelectPresetLibraryFolder(bpy.types.Operator):
bl_label = "Install Preset Folder"
bl_description = ("Select an existing Preset Library installation folder and add it to the Blender Asset Browser")
directory = bpy.props.StringProperty(name="Directory", options={"HIDDEN"})
exec(vcu.convert_attribute_to_28("directory"))
directory: bpy.props.StringProperty(name="Directory", options={"HIDDEN"})
filter_folder = bpy.props.BoolProperty(default=True, options={"HIDDEN"})
exec(vcu.convert_attribute_to_28("filter_folder"))
filter_folder: bpy.props.BoolProperty(default=True, options={"HIDDEN"})
@classmethod
@@ -643,9 +635,7 @@ class FLIPFluidSelectPresetLibraryFolder(bpy.types.Operator):
lib_version_str = "v" + str(version[0]) + "." + str(version[1]) + "." + str(version[2])
preset_library_name = "FLIP Fluids Addon Presets " + lib_version_str
lib_entry.name = preset_library_name
if vcu.is_blender_35():
# Only available in Blender >= 3.5
lib_entry.import_method = 'APPEND'
lib_entry.import_method = 'APPEND'
installation_utils.update_preset_library_installation_status()
success_message = "The Preset Scenes Library has been installed successfully into the Blender Asset Browser."
@@ -667,8 +657,7 @@ class FLIPFluidPresetLibraryCopyInstallLocation(bpy.types.Operator):
bl_description = ("Copy the preset library location to the Install Location field and" +
" system clipboard. The install location is the parent directory of the path listed below")
install_location = StringProperty(default="")
exec(vcu.convert_attribute_to_28("install_location"))
install_location: StringProperty(default="")
def execute(self, context):
preferences = vcu.get_addon_preferences()
@@ -683,8 +672,7 @@ class FLIPFluidUninstallPresetLibrary(bpy.types.Operator):
bl_description = ("Uninstall the preset library. The preset library will be removed from" +
" the Blender Asset Browser and the files deleted from your system")
install_info_json_string = StringProperty(default="")
exec(vcu.convert_attribute_to_28("install_info_json_string"))
install_info_json_string: StringProperty(default="")
def tag_redraw(self, context):
@@ -751,8 +739,7 @@ class FLIPFluidUninstallPresetLibrary(bpy.types.Operator):
class VersionDataTextEntry(bpy.types.PropertyGroup):
text = StringProperty(default="")
exec(vcu.convert_attribute_to_28("text"))
text: StringProperty(default="")
def get_gpu_string():
@@ -899,12 +886,11 @@ def get_system_info_dict():
addons_list = []
addons_string = "Unknown"
try:
if vcu.is_blender_42():
for addon in bpy.context.preferences.addons:
addon_name = addon.module
addon_name = addon_name.split('.')[-1]
if addon_name and addon_name not in default_addons:
addons_list.append(addon_name)
for addon in bpy.context.preferences.addons:
addon_name = addon.module
addon_name = addon_name.split('.')[-1]
if addon_name and addon_name not in default_addons:
addons_list.append(addon_name)
if addons_list:
addons_string = ', '.join(addons_list)
else:
@@ -913,7 +899,7 @@ def get_system_info_dict():
print(traceback.format_exc())
print(e)
developer_tools_string = "Uknown"
developer_tools_string = "Unknown"
try:
preferences = vcu.get_addon_preferences()
developer_tools_string = "Enabled" if preferences.enable_extra_features else "Disabled"
@@ -1198,6 +1184,7 @@ def get_system_info_dict():
d = {}
d['blender_version'] = blender_version
d['addon_version'] = bl_info.get('description', "Missing Version Label")
d['addon_support_license'] = installation_utils.get_support_license_label()
d['operating_system'] = platform.platform()
d['cpu'] = cpu_string
d['threads'] = threads_string
@@ -1256,6 +1243,7 @@ class FlipFluidReportBugPrefill(bpy.types.Operator):
user_info += "#### System and Blend File Information\n\n"
user_info += "**Blender Version:** " + sys_info['blender_version'] + "\n"
user_info += "**Addon Version:** " + sys_info['addon_version'] + "\n"
user_info += "**Addon Build:** " + sys_info['addon_support_license'] + "\n"
user_info += "**OS:** " + sys_info['operating_system'] + "\n"
user_info += "**GPU:** " + sys_info['gpu'] + "\n"
user_info += "**CPU:** " + sys_info['cpu'] + "\n"
@@ -1318,6 +1306,7 @@ def get_system_info_string():
user_info = ""
user_info += "Blender Version: " + sys_info['blender_version'] + "\n"
user_info += "Addon Version: " + sys_info['addon_version'] + "\n"
user_info += "Addon Build: " + sys_info['addon_support_license'] + "\n"
user_info += "OS: " + sys_info['operating_system'] + "\n"
user_info += "GPU: " + sys_info['gpu'] + "\n"
user_info += "CPU: " + sys_info['cpu'] + "\n"
@@ -1383,8 +1372,7 @@ class FlipFluidOpenPreferences(bpy.types.Operator):
bl_label = "FLIP Fluids Preferences"
bl_description = ("Open the FLIP Fluids addon preferences menu")
view_mode = StringProperty(default="NONE")
exec(vcu.convert_attribute_to_28("view_mode"))
view_mode: StringProperty(default="NONE")
def execute(self, context):
@@ -1402,12 +1390,8 @@ class FlipFluidOpenPreferences(bpy.types.Operator):
if self.view_mode in valid_view_modes:
prefs = vcu.get_addon_preferences()
prefs.preferences_menu_view_mode = self.view_mode
if vcu.is_blender_42():
module_name = base_package
else:
module_name = installation_utils.get_module_name()
bpy.ops.preferences.addon_show(module=module_name)
bpy.ops.preferences.addon_show(module=base_package)
return {'FINISHED'}
@@ -1435,9 +1419,7 @@ class FLIPFLUIDS_MT_help_menu(bpy.types.Menu):
def draw(self, context):
self.layout.operator("flip_fluid_operators.report_bug_prefill", icon="URL")
self.layout.operator("flip_fluid_operators.copy_system_info", icon="COPYDOWN")
if vcu.is_blender_28():
self.layout.operator("flip_fluid_operators.open_preferences", icon="PREFERENCES").view_mode = 'NONE'
self.layout.operator("flip_fluid_operators.open_preferences", icon="PREFERENCES").view_mode = 'NONE'
def draw_flip_fluids_help_menu(self, context):
@@ -243,8 +243,7 @@ class FlipFluidPresetCreateNewPresetEnableAll(bpy.types.Operator):
bl_label = "Enable All"
bl_description = "Enable all preset attributes"
collection_id = StringProperty(default="")
exec(vcu.convert_attribute_to_28("collection_id"))
collection_id: StringProperty(default="")
@classmethod
@@ -271,8 +270,7 @@ class FlipFluidPresetCreateNewPresetDisableAll(bpy.types.Operator):
bl_label = "Disable All"
bl_description = "Disable all preset attributes"
collection_id = StringProperty(default="")
exec(vcu.convert_attribute_to_28("collection_id"))
collection_id: StringProperty(default="")
@classmethod
def poll(cls, context):
@@ -299,8 +297,7 @@ class FlipFluidPresetCreateNewPresetEnableAuto(bpy.types.Operator):
bl_description = ("Automatically enable/disable preset attributes by" +
" comparing to system default settings")
collection_id = StringProperty(default="")
exec(vcu.convert_attribute_to_28("collection_id"))
collection_id: StringProperty(default="")
@classmethod
def poll(cls, context):
@@ -936,8 +933,7 @@ class FlipFluidPresetDisplayInfo(bpy.types.Operator):
bl_label = "Preset Info"
bl_description = "Display preset information"
identifier = StringProperty(default="")
exec(vcu.convert_attribute_to_28("identifier"))
identifier: StringProperty(default="")
@classmethod
def poll(cls, context):
@@ -1245,12 +1241,11 @@ class SelectPresetPackageZipFile(bpy.types.Operator, ImportHelper):
bl_label = "Select Package Zipfile"
filename_ext = "*.zip"
filter_glob = StringProperty(
filter_glob: StringProperty(
default="*.zip",
options={'HIDDEN'},
maxlen=255,
)
exec(vcu.convert_attribute_to_28("filter_glob"))
def execute(self, context):
@@ -1691,8 +1686,7 @@ class FlipFluidPresetRemovePresetFromStack(bpy.types.Operator):
bl_label = "Remove From Preset Stack"
bl_description = "Remove from preset stack"
stack_index = IntProperty(default=-1)
exec(vcu.convert_attribute_to_28("stack_index"))
stack_index: IntProperty(default=-1)
@classmethod
def poll(cls, context):
@@ -1714,8 +1708,7 @@ class FlipFluidPresetMovePresetUpInStack(bpy.types.Operator):
bl_label = "Move Up"
bl_description = "Move preset up in the stack"
stack_index = IntProperty(default=-1)
exec(vcu.convert_attribute_to_28("stack_index"))
stack_index: IntProperty(default=-1)
@classmethod
def poll(cls, context):
@@ -1737,8 +1730,7 @@ class FlipFluidPresetMovePresetDownInStack(bpy.types.Operator):
bl_label = "Move Down"
bl_description = "Move preset down in the stack"
stack_index = IntProperty(default=-1)
exec(vcu.convert_attribute_to_28("stack_index"))
stack_index: IntProperty(default=-1)
@classmethod
def poll(cls, context):
@@ -1760,8 +1752,7 @@ class FlipFluidPresetApplyAndRemoveFromStack(bpy.types.Operator):
bl_label = "Apply"
bl_description = "Apply preset and remove from the stack"
stack_index = IntProperty(default=-1)
exec(vcu.convert_attribute_to_28("stack_index"))
stack_index: IntProperty(default=-1)
@classmethod
def poll(cls, context):
@@ -1804,8 +1795,7 @@ class FlipFluidPresetEditPresetEnableAll(bpy.types.Operator):
bl_label = "Enable All"
bl_description = "Enable all preset attributes"
collection_id = StringProperty(default="")
exec(vcu.convert_attribute_to_28("collection_id"))
collection_id: StringProperty(default="")
@classmethod
def poll(cls, context):
@@ -1831,8 +1821,7 @@ class FlipFluidPresetEditPresetDisableAll(bpy.types.Operator):
bl_label = "Disable All"
bl_description = "Disable all preset attributes"
collection_id = StringProperty(default="")
exec(vcu.convert_attribute_to_28("collection_id"))
collection_id: StringProperty(default="")
@classmethod
def poll(cls, context):