# 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 . import sys, os, shutil, json, traceback, math, time from .objects import flip_fluid_map from .objects import flip_fluid_geometry_database from .operators import bake_operators from .filesystem import filesystem_protection_layer as fpl from .utils import version_compatibility_utils as vcu from . import bl_info from .ffengine import ( ffengine, mixbox, FluidSimulation, TriangleMesh, Vector3, AABB, MeshObject, MeshFluidSource, ForceFieldPoint, ForceFieldSurface, ForceFieldVolume, ForceFieldCurve, ) from .utils import cache_utils FLUIDSIM_OBJECT = None SIMULATION_DATA = None CACHE_DIRECTORY = "" GEOMETRY_DATABASE = None class LibraryVersionError(Exception): def __init__(self, value): self.value = value def __str__(self): return str(self.value) def __set_simulation_object(fluidsim_object): global FLUIDSIM_OBJECT FLUIDSIM_OBJECT = fluidsim_object def __get_simulation_object(): global FLUIDSIM_OBJECT return FLUIDSIM_OBJECT def __set_simulation_data(data): global SIMULATION_DATA SIMULATION_DATA = data def __get_simulation_data(): global SIMULATION_DATA return SIMULATION_DATA def __set_cache_directory(cache_directory): global CACHE_DIRECTORY CACHE_DIRECTORY = cache_directory def __get_cache_directory(): global CACHE_DIRECTORY return CACHE_DIRECTORY def __set_geometry_database(geometry_database): global GEOMETRY_DATABASE GEOMETRY_DATABASE = geometry_database def __get_geometry_database(): global GEOMETRY_DATABASE return GEOMETRY_DATABASE def __get_export_directory(): return os.path.join(CACHE_DIRECTORY, "export") def __get_geometry_database_filepath(): data = __get_simulation_data() return data.domain_data.initialize.geometry_database_filepath def __get_name_slug(object_name): return cache_utils.string_to_cache_slug(object_name) def __is_object_dynamic(object_name): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() return geometry_database.is_object_dynamic(name_slug) def __get_timeline_frame(): fluidsim = __get_simulation_object() data = __get_simulation_data() frame_start = data.domain_data.initialize.frame_start return fluidsim.get_current_frame() + frame_start def __get_frame_id(): fluidsim = __get_simulation_object() return fluidsim.get_current_frame() def __extract_static_mesh(object_name): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() bobj_data = geometry_database.get_mesh_static(name_slug) if bobj_data is None: msg = "Error extracting mesh data. Exported object not found: <" + object_name + ">" raise Exception(msg) data = __get_simulation_data() scale = data.domain_data.initialize.scale bbox = data.domain_data.initialize.bbox tmesh = TriangleMesh.from_bobj(bobj_data) tmesh.translate(-bbox.x, -bbox.y, -bbox.z) tmesh.scale(scale) return tmesh def __extract_transform_data(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() matrix_coefficients = geometry_database.get_mesh_keyframed_transform(name_slug, frameno) if matrix_coefficients is None: msg = "Error extracting mesh transforms. Exported object not found: <" + object_name + ">" raise Exception(msg) return matrix_coefficients def __extract_keyframed_mesh(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() bobj_data = geometry_database.get_mesh_static(name_slug) if bobj_data is None: msg = "Error extracting mesh data. Exported object not found: <" + object_name + ">" raise Exception(msg) tmesh = TriangleMesh.from_bobj(bobj_data) transform_data = __extract_transform_data(object_name, frameno) tmesh.apply_transform(transform_data) data = __get_simulation_data() scale = data.domain_data.initialize.scale bbox = data.domain_data.initialize.bbox tmesh.translate(-bbox.x, -bbox.y, -bbox.z) tmesh.scale(scale) return tmesh def __extract_animated_mesh(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() bobj_data = geometry_database.get_mesh_animated(name_slug, frameno) data = __get_simulation_data() scale = data.domain_data.initialize.scale bbox = data.domain_data.initialize.bbox tmesh = TriangleMesh.from_bobj(bobj_data) tmesh.translate(-bbox.x, -bbox.y, -bbox.z) tmesh.scale(scale) return tmesh def __extract_mesh(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() motion_type = geometry_database.get_object_motion_export_type(name_slug) if motion_type == 'STATIC': return __extract_static_mesh(object_name) elif motion_type == 'KEYFRAMED': return __extract_keyframed_mesh(object_name, frameno) elif motion_type == 'ANIMATED': return __extract_animated_mesh(object_name, frameno) def __extract_static_frame_mesh(object_name): return __extract_static_mesh(object_name) def __extract_force_field_mesh(object_name, frame_id=0): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() export_dict = geometry_database.get_object_geometry_export_types(name_slug) if export_dict['mesh']: return __extract_mesh(object_name, frame_id) elif export_dict['centroid']: centroid = __extract_centroid(object_name, frame_id) tmesh = TriangleMesh() tmesh.vertices.extend([centroid[0], centroid[1], centroid[2]]) return tmesh elif export_dict['curve']: return __extract_curve_mesh(object_name, frame_id) def __extract_keyframed_frame_meshes(object_name, frameno): mesh_current = __extract_keyframed_mesh(object_name, frameno) if frameno - 1 < 0 or not __keyframed_mesh_exists(object_name, frameno - 1): mesh_previous = mesh_current else: mesh_previous = __extract_keyframed_mesh(object_name, frameno - 1) if not __keyframed_mesh_exists(object_name, frameno + 1): mesh_next = mesh_current else: mesh_next = __extract_keyframed_mesh(object_name, frameno + 1) return mesh_previous, mesh_current, mesh_next def __extract_animated_frame_meshes(object_name, frameno): mesh_current = __extract_animated_mesh(object_name, frameno) if frameno - 1 < 0 or not __animated_mesh_exists(object_name, frameno - 1): mesh_previous = mesh_current else: mesh_previous = __extract_animated_mesh(object_name, frameno - 1) if not __animated_mesh_exists(object_name, frameno + 1): mesh_next = mesh_current else: mesh_next = __extract_animated_mesh(object_name, frameno + 1) return mesh_previous, mesh_current, mesh_next def __extract_keyframed_frame_centroid_meshes(object_name, frameno): mesh_current = __extract_force_field_mesh(object_name, frameno) if frameno - 1 < 0 or not __keyframed_centroid_exists(object_name, frameno - 1): mesh_previous = mesh_current else: mesh_previous = __extract_force_field_mesh(object_name, frameno - 1) if not __keyframed_centroid_exists(object_name, frameno + 1): mesh_next = mesh_current else: mesh_next = __extract_force_field_mesh(object_name, frameno + 1) return mesh_previous, mesh_current, mesh_next def __extract_animated_frame_centroid_meshes(object_name, frameno): mesh_current = __extract_force_field_mesh(object_name, frameno) if frameno - 1 < 0 or not __animated_centroid_exists(object_name, frameno - 1): mesh_previous = mesh_current else: mesh_previous = __extract_force_field_mesh(object_name, frameno - 1) if not __animated_centroid_exists(object_name, frameno + 1): mesh_next = mesh_current else: mesh_next = __extract_force_field_mesh(object_name, frameno + 1) return mesh_previous, mesh_current, mesh_next def __extract_keyframed_frame_curve_meshes(object_name, frameno): mesh_current = __extract_force_field_mesh(object_name, frameno) if frameno - 1 < 0 or not __keyframed_curve_exists(object_name, frameno - 1): mesh_previous = mesh_current else: mesh_previous = __extract_force_field_mesh(object_name, frameno - 1) if not __keyframed_curve_exists(object_name, frameno + 1): mesh_next = mesh_current else: mesh_next = __extract_force_field_mesh(object_name, frameno + 1) return mesh_previous, mesh_current, mesh_next def __extract_animated_frame_curve_meshes(object_name, frameno): mesh_current = __extract_force_field_mesh(object_name, frameno) if frameno - 1 < 0 or not __animated_curve_exists(object_name, frameno - 1): mesh_previous = mesh_current else: mesh_previous = __extract_force_field_mesh(object_name, frameno - 1) if not __animated_curve_exists(object_name, frameno + 1): mesh_next = mesh_current else: mesh_next = __extract_force_field_mesh(object_name, frameno + 1) return mesh_previous, mesh_current, mesh_next def __extract_dynamic_frame_meshes(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() motion_type = geometry_database.get_object_motion_export_type(name_slug) if motion_type == 'KEYFRAMED': return __extract_keyframed_frame_meshes(object_name, frameno) elif motion_type == 'ANIMATED': return __extract_animated_frame_meshes(object_name, frameno) def __extract_dynamic_force_field_frame_meshes(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() motion_type = geometry_database.get_object_motion_export_type(name_slug) export_dict = geometry_database.get_object_geometry_export_types(name_slug) if export_dict['mesh']: if motion_type == 'KEYFRAMED': return __extract_keyframed_frame_meshes(object_name, frameno) elif motion_type == 'ANIMATED': return __extract_animated_frame_meshes(object_name, frameno) elif export_dict['centroid']: if motion_type == 'KEYFRAMED': return __extract_keyframed_frame_centroid_meshes(object_name, frameno) elif motion_type == 'ANIMATED': return __extract_animated_frame_centroid_meshes(object_name, frameno) elif export_dict['curve']: if motion_type == 'KEYFRAMED': return __extract_keyframed_frame_curve_meshes(object_name, frameno) elif motion_type == 'ANIMATED': return __extract_animated_frame_curve_meshes(object_name, frameno) def __extract_local_axis(object_name, frameno=0): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() motion_type = geometry_database.get_object_motion_export_type(name_slug) if motion_type == 'STATIC': local_x, local_y, local_z = geometry_database.get_axis_static(name_slug) elif motion_type == 'KEYFRAMED': local_x, local_y, local_z = geometry_database.get_axis_keyframed(name_slug, frameno) elif motion_type == 'ANIMATED': local_x, local_y, local_z = geometry_database.get_axis_animated(name_slug, frameno) return local_x, local_y, local_z def __keyframed_mesh_exists(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() return geometry_database.mesh_keyframed_exists(name_slug, frameno) def __animated_mesh_exists(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() return geometry_database.mesh_animated_exists(name_slug, frameno) def __keyframed_centroid_exists(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() return geometry_database.centroid_keyframed_exists(name_slug, frameno) def __animated_centroid_exists(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() return geometry_database.centroid_animated_exists(name_slug, frameno) def __extract_centroid(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() motion_type = geometry_database.get_object_motion_export_type(name_slug) if motion_type == 'STATIC': centroid = geometry_database.get_centroid_static(name_slug) elif motion_type == 'KEYFRAMED': centroid = geometry_database.get_centroid_keyframed(name_slug, frameno) elif motion_type == 'ANIMATED': centroid = geometry_database.get_centroid_animated(name_slug, frameno) data = __get_simulation_data() scale = data.domain_data.initialize.scale bbox = data.domain_data.initialize.bbox centroid[0] = (centroid[0] - bbox.x) * scale centroid[1] = (centroid[1] - bbox.y) * scale centroid[2] = (centroid[2] - bbox.z) * scale return centroid def __keyframed_curve_exists(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() return geometry_database.curve_keyframed_exists(name_slug, frameno) def __animated_curve_exists(object_name, frameno): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() return geometry_database.curve_animated_exists(name_slug, frameno) def __extract_curve_mesh(object_name, frameno=0): name_slug = __get_name_slug(object_name) geometry_database = __get_geometry_database() motion_type = geometry_database.get_object_motion_export_type(name_slug) if motion_type == 'STATIC': bobj_data = geometry_database.get_curve_static(name_slug) elif motion_type == 'KEYFRAMED': bobj_data = geometry_database.get_curve_static(name_slug) matrix_coefficients = geometry_database.get_curve_keyframed_transform(name_slug, frameno) elif motion_type == 'ANIMATED': bobj_data = geometry_database.get_curve_animated(name_slug, frameno) data = __get_simulation_data() scale = data.domain_data.initialize.scale bbox = data.domain_data.initialize.bbox curve_tmesh = TriangleMesh.from_bobj(bobj_data) if motion_type == 'KEYFRAMED': curve_tmesh.apply_transform(matrix_coefficients) curve_tmesh.translate(-bbox.x, -bbox.y, -bbox.z) curve_tmesh.scale(scale) return curve_tmesh def __extract_data(data_filepath): with open(data_filepath, 'r', encoding='utf-8') as f: json_data = json.loads(f.read()) data = flip_fluid_map.Map(json_data) return data def __set_output_directories(cache_dir): cache_directories = [ os.path.join(cache_dir, "bakefiles"), os.path.join(cache_dir, "logs"), os.path.join(cache_dir, "savestates"), os.path.join(cache_dir, "temp") ] for d in cache_directories: if not os.path.exists(d): os.makedirs(d) def __check_bake_cancelled(bakedata): if bakedata.is_cancelled or not bake_operators.is_bake_operator_running(): bakedata.is_finished = True return True return False def __get_parameter_data(parameter, frameno=0, value_min=None, value_max=None): if parameter is None: raise IndexError() if hasattr(parameter, 'data') and hasattr(parameter, 'is_animated'): if parameter.is_animated: value = parameter.data[frameno] else: value = parameter.data else: value = parameter if value_min is not None: value = max(value, value_min) if value_max is not None: value = min(value, value_max) return value def __get_limit_behaviour_enum(b): if b == 'BEHAVIOUR_KILL': return 0 if b == 'BEHAVIOUR_BALLISTIC': return 1 if b == 'BEHAVIOUR_COLLIDE': return 2 return int(b) def __get_emission_boundary(settings, fluidsim): dims = fluidsim.get_simulation_dimensions() bounds = AABB(0.0, 0.0, 0.0, dims.x, dims.y, dims.z) generate_near_boundary = \ __get_parameter_data(settings.enable_whitewater_emission_near_boundary) if not generate_near_boundary: pad_cells = 3.5 pad = pad_cells * fluidsim.get_cell_size() bounds.expand(-2*pad) return bounds def __get_obstacle_meshing_offset(obstacle_meshing_mode): if type(obstacle_meshing_mode) == float: if obstacle_meshing_mode == 0.0: obstacle_meshing_mode = 'MESHING_MODE_INSIDE_SURFACE' elif obstacle_meshing_mode == 1.0: obstacle_meshing_mode = 'MESHING_MODE_ON_SURFACE' elif obstacle_meshing_mode == 2.0: obstacle_meshing_mode = 'MESHING_MODE_OUTSIDE_SURFACE' if obstacle_meshing_mode == 'MESHING_MODE_INSIDE_SURFACE': return 0.25 elif obstacle_meshing_mode == 'MESHING_MODE_ON_SURFACE': return 0.0 elif obstacle_meshing_mode == 'MESHING_MODE_OUTSIDE_SURFACE': return -0.5 def __get_viscosity_value(world_data, frameno): base = __get_parameter_data(world_data.viscosity, frameno) exp = __get_parameter_data(world_data.viscosity_exponent, frameno) viscosity = max(base * (10**(-exp)), 0.0) return viscosity def __get_surface_tension_value(world_data, frameno): base = __get_parameter_data(world_data.surface_tension, frameno) exp = __get_parameter_data(world_data.surface_tension_exponent, frameno) value = world_data.native_surface_tension_scale * base * (10**(-exp)) return max(value, 0.0) def __read_save_state_file_data(file_data_path, start_byte, end_byte): with open(file_data_path, 'rb') as f: f.seek(start_byte) data = f.read(end_byte - start_byte) return data def __write_save_state_file_data(file_data_path, data, is_appending_data=False): write_mode = 'wb' if is_appending_data: write_mode = 'ab' with open(file_data_path, write_mode) as f: f.write(data) def __load_save_state_marker_particle_data(fluidsim, save_state_directory, autosave_info, data): num_particles = autosave_info['num_marker_particles'] if num_particles == 0: return d = save_state_directory position_data_file = os.path.join(d, autosave_info['marker_particle_position_filedata']) velocity_data_file = os.path.join(d, autosave_info['marker_particle_velocity_filedata']) velocity_transfer_method = data.domain_data.advanced.velocity_transfer_method.data is_apic_enabled = velocity_transfer_method == 'VELOCITY_TRANSFER_METHOD_APIC' load_apic_data = False if is_apic_enabled: is_apic_data_available = ('marker_particle_affinex_filedata' in autosave_info and 'marker_particle_affiney_filedata' in autosave_info and 'marker_particle_affinez_filedata' in autosave_info) is_apic_data_available = (is_apic_data_available and autosave_info['marker_particle_affinex_filedata'] and autosave_info['marker_particle_affiney_filedata'] and autosave_info['marker_particle_affinez_filedata']) if is_apic_data_available: affinex_data_file = os.path.join(d, autosave_info['marker_particle_affinex_filedata']) affiney_data_file = os.path.join(d, autosave_info['marker_particle_affiney_filedata']) affinez_data_file = os.path.join(d, autosave_info['marker_particle_affinez_filedata']) load_apic_data = True is_age_attribute_enabled = (data.domain_data.surface.enable_age_attribute.data or data.domain_data.particles.enable_fluid_particle_age_attribute.data) load_age_data = False if is_age_attribute_enabled: age_path = 'marker_particle_age_filedata' is_age_data_available = (age_path in autosave_info) and autosave_info[age_path] if is_age_data_available: age_data_file = os.path.join(d, autosave_info['marker_particle_age_filedata']) load_age_data = True is_lifetime_attribute_enabled = (data.domain_data.surface.enable_lifetime_attribute.data or data.domain_data.particles.enable_fluid_particle_lifetime_attribute.data) load_lifetime_data = False if is_lifetime_attribute_enabled: lifetime_path = 'marker_particle_lifetime_filedata' is_lifetime_data_available = (lifetime_path in autosave_info) and autosave_info[lifetime_path] if is_lifetime_data_available: lifetime_data_file = os.path.join(d, autosave_info['marker_particle_lifetime_filedata']) load_lifetime_data = True is_color_attribute_enabled = (data.domain_data.surface.enable_color_attribute.data or data.domain_data.particles.enable_fluid_particle_color_attribute.data) load_color_data = False if is_color_attribute_enabled: color_path = 'marker_particle_color_filedata' is_color_data_available = (color_path in autosave_info) and autosave_info[color_path] if is_color_data_available: color_data_file = os.path.join(d, autosave_info['marker_particle_color_filedata']) load_color_data = True is_source_id_attribute_enabled = (data.domain_data.surface.enable_source_id_attribute.data or data.domain_data.particles.enable_fluid_particle_source_id_attribute.data) load_source_id_data = False if is_source_id_attribute_enabled: source_id_path = 'marker_particle_source_id_filedata' is_source_id_data_available = (source_id_path in autosave_info) and autosave_info[source_id_path] if is_source_id_data_available: source_id_data_file = os.path.join(d, autosave_info['marker_particle_source_id_filedata']) load_source_id_data = True is_uid_attribute_enabled = data.domain_data.particles.enable_fluid_particle_uid_attribute.data load_uid_data = False if is_uid_attribute_enabled: uid_path = 'marker_particle_uid_filedata' is_uid_data_available = (uid_path in autosave_info) and autosave_info[uid_path] if is_uid_data_available: uid_data_file = os.path.join(d, autosave_info['marker_particle_uid_filedata']) load_uid_data = True is_viscosity_attribute_enabled = data.domain_data.surface.enable_viscosity_attribute.data load_viscosity_data = False if is_viscosity_attribute_enabled: viscosity_path = 'marker_particle_viscosity_filedata' is_viscosity_data_available = (viscosity_path in autosave_info) and autosave_info[viscosity_path] if is_viscosity_data_available: viscosity_data_file = os.path.join(d, autosave_info['marker_particle_viscosity_filedata']) load_viscosity_data = True is_density_attribute_enabled = data.domain_data.world.enable_density_attribute.data load_density_data = False if is_density_attribute_enabled: density_path = 'marker_particle_density_filedata' is_density_data_available = (density_path in autosave_info) and autosave_info[density_path] if is_density_data_available: density_data_file = os.path.join(d, autosave_info['marker_particle_density_filedata']) load_density_data = True is_id_attribute_enabled = data.domain_data.particles.enable_fluid_particle_output.data load_id_data = False if is_id_attribute_enabled: id_path = 'marker_particle_id_filedata' is_id_data_available = (id_path in autosave_info) and autosave_info[id_path] if is_id_data_available: id_data_file = os.path.join(d, autosave_info['marker_particle_id_filedata']) load_id_data = True particles_per_read = 2**21 bytes_per_vector = 12 bytes_per_float = 4 bytes_per_int = 4 bytes_per_ulong_long_int = 8 bytes_per_short = 2 max_vector_byte = bytes_per_vector * num_particles max_float_byte = bytes_per_float * num_particles max_int_byte = bytes_per_int * num_particles max_ulong_long_int_byte = bytes_per_ulong_long_int * num_particles max_short_byte = bytes_per_short * num_particles num_reads = int((num_particles // particles_per_read) + 1) for i in range(num_reads): start_vector_byte = i * bytes_per_vector * particles_per_read start_float_byte = i * bytes_per_float * particles_per_read start_int_byte = i * bytes_per_int * particles_per_read start_ulong_long_int_byte = i * bytes_per_ulong_long_int * particles_per_read start_short_byte = i * bytes_per_short * particles_per_read end_vector_byte = min((i + 1) * bytes_per_vector * particles_per_read, max_vector_byte) end_float_byte = min((i + 1) * bytes_per_float * particles_per_read, max_float_byte) end_int_byte = min((i + 1) * bytes_per_int * particles_per_read, max_int_byte) end_ulong_long_int_byte = min((i + 1) * bytes_per_ulong_long_int * particles_per_read, max_ulong_long_int_byte) end_short_byte = min((i + 1) * bytes_per_short * particles_per_read, max_short_byte) particle_count = int((end_vector_byte - start_vector_byte) // bytes_per_vector) position_data = __read_save_state_file_data(position_data_file, start_vector_byte, end_vector_byte) velocity_data = __read_save_state_file_data(velocity_data_file, start_vector_byte, end_vector_byte) fluidsim.load_marker_particle_data(particle_count, position_data, velocity_data) if load_apic_data: affinex_data = __read_save_state_file_data(affinex_data_file, start_vector_byte, end_vector_byte) affiney_data = __read_save_state_file_data(affiney_data_file, start_vector_byte, end_vector_byte) affinez_data = __read_save_state_file_data(affinez_data_file, start_vector_byte, end_vector_byte) fluidsim.load_marker_particle_affine_data(particle_count, affinex_data, affiney_data, affinez_data) if load_age_data: age_data = __read_save_state_file_data(age_data_file, start_float_byte, end_float_byte) fluidsim.load_marker_particle_age_data(particle_count, age_data) if load_lifetime_data: lifetime_data = __read_save_state_file_data(lifetime_data_file, start_float_byte, end_float_byte) fluidsim.load_marker_particle_lifetime_data(particle_count, lifetime_data) if load_color_data: color_data = __read_save_state_file_data(color_data_file, start_vector_byte, end_vector_byte) fluidsim.load_marker_particle_color_data(particle_count, color_data) if load_source_id_data: source_id_data = __read_save_state_file_data(source_id_data_file, start_int_byte, end_int_byte) fluidsim.load_marker_particle_source_id_data(particle_count, source_id_data) if load_uid_data: uid_data = __read_save_state_file_data(uid_data_file, start_int_byte, end_int_byte) fluidsim.load_marker_particle_uid_data(particle_count, uid_data) if load_viscosity_data: viscosity_data = __read_save_state_file_data(viscosity_data_file, start_float_byte, end_float_byte) fluidsim.load_marker_particle_viscosity_data(particle_count, viscosity_data) if load_density_data: density_data = __read_save_state_file_data(density_data_file, start_float_byte, end_float_byte) fluidsim.load_marker_particle_density_data(particle_count, density_data) if load_id_data: id_data = __read_save_state_file_data(id_data_file, start_short_byte, end_short_byte) fluidsim.load_marker_particle_id_data(particle_count, id_data) def __load_save_state_diffuse_particle_data(fluidsim, save_state_directory, autosave_info): num_particles = autosave_info['num_diffuse_particles'] if num_particles == 0: return d = save_state_directory position_data_file = os.path.join(d, autosave_info['diffuse_particle_position_filedata']) velocity_data_file = os.path.join(d, autosave_info['diffuse_particle_velocity_filedata']) lifetime_data_file = os.path.join(d, autosave_info['diffuse_particle_lifetime_filedata']) type_data_file = os.path.join(d, autosave_info['diffuse_particle_type_filedata']) id_data_file = os.path.join(d, autosave_info['diffuse_particle_id_filedata']) particles_per_read = 2**21 bytes_per_vector = 12 bytes_per_lifetime = 4 bytes_per_type = 1 bytes_per_id = 1 max_byte_vector = bytes_per_vector * num_particles max_byte_lifetime = bytes_per_lifetime * num_particles max_byte_type = bytes_per_type * num_particles max_byte_id = bytes_per_id * num_particles num_reads = int((num_particles // particles_per_read) + 1) for i in range(num_reads): start_byte = i * bytes_per_vector * particles_per_read end_byte = min((i + 1) * bytes_per_vector * particles_per_read, max_byte_vector) particle_count = int((end_byte - start_byte) // bytes_per_vector) position_data = __read_save_state_file_data(position_data_file, start_byte, end_byte) velocity_data = __read_save_state_file_data(velocity_data_file, start_byte, end_byte) start_byte = i * bytes_per_lifetime * particles_per_read end_byte = min((i + 1) * bytes_per_lifetime * particles_per_read, max_byte_lifetime) lifetime_data = __read_save_state_file_data(lifetime_data_file, start_byte, end_byte) start_byte = i * bytes_per_type * particles_per_read end_byte = min((i + 1) * bytes_per_type * particles_per_read, max_byte_type) type_data = __read_save_state_file_data(type_data_file, start_byte, end_byte) start_byte = i * bytes_per_id * particles_per_read end_byte = min((i + 1) * bytes_per_id * particles_per_read, max_byte_id) id_data = __read_save_state_file_data(id_data_file, start_byte, end_byte) fluidsim.load_diffuse_particle_data(particle_count, position_data, velocity_data, lifetime_data, type_data, id_data) def __load_save_state_simulator_data(fluidsim, autosave_info): next_frame = autosave_info["frame_id"] + 1 fluidsim.set_current_frame(next_frame) if "current_fluid_particle_uid" in autosave_info: current_uid = int(autosave_info["current_fluid_particle_uid"]) fluidsim.set_current_fluid_particle_uid(current_uid) def __delete_outdated_savestates(cache_directory, savestate_id): savestate_directory = os.path.join(cache_directory, "savestates") subdirs = [d for d in os.listdir(savestate_directory) if os.path.isdir(os.path.join(savestate_directory, d)) ] if "autosave" in subdirs: subdirs.remove("autosave") extensions = [".state", ".data"] for d in subdirs: try: savestate_number = int(d[-6:]) if savestate_number < 0: continue except ValueError: continue if savestate_number > savestate_id: path = os.path.join(savestate_directory, d) try: fpl.delete_files_in_directory( path, extensions, remove_directory=True, display_popup_on_error=False ) except: print("Error: unable to delete directory <" + path + "> (skipping)") def __delete_outdated_meshes(cache_directory, savestate_id): bakefiles_directory = os.path.join(cache_directory, "bakefiles") files = os.listdir(bakefiles_directory) for f in files: filename = f.split(".")[0] try: filenum = int(filename[-6:]) except ValueError: continue if filenum > savestate_id: path = os.path.join(bakefiles_directory, f) try: fpl.delete_file(path) except: print("Error: unable to delete file <" + path + "> (skipping)") stats_filepath = os.path.join(cache_directory, "flipstats.data") with open(stats_filepath, 'r', encoding='utf-8') as f: stats_info = json.loads(f.read()) for key in stats_info.copy().keys(): if int(key) > savestate_id: del stats_info[key] stats_json = json.dumps(stats_info, sort_keys=True, indent=4) with open(stats_filepath, 'w', encoding='utf-8') as f: f.write(stats_json) def __load_save_state_data(fluidsim, data, cache_directory, savestate_id): if savestate_id is None: return savestate_directory = os.path.join(cache_directory, "savestates") savestate_name = "autosave" + str(savestate_id).zfill(6) autosave_directory = os.path.join(savestate_directory, savestate_name) if not os.path.isdir(autosave_directory): savestate_name = "autosave" autosave_directory = os.path.join(savestate_directory, savestate_name) if not os.path.isdir(autosave_directory): return autosave_info_file = os.path.join(autosave_directory, "autosave.state") if not os.path.isfile(autosave_info_file): return with open(autosave_info_file, 'r', encoding='utf-8') as f: autosave_info = json.loads(f.read()) __load_save_state_marker_particle_data(fluidsim, autosave_directory, autosave_info, data) __load_save_state_diffuse_particle_data(fluidsim, autosave_directory, autosave_info) __load_save_state_simulator_data(fluidsim, autosave_info) init_data = data.domain_data.initialize if init_data.delete_outdated_savestates: __delete_outdated_savestates(cache_directory, autosave_info["frame"]) # Replace autosave directory with most current savestate_id directory current_autosave_directory = os.path.join(savestate_directory, "autosave") if savestate_name != "autosave" and os.path.isdir(current_autosave_directory): backup_autosave_directory = current_autosave_directory + ".backup" if os.path.isdir(backup_autosave_directory): # Backup autosave directory may already exist from a previous bake, remove if so fpl.delete_files_in_directory( backup_autosave_directory, [".state", ".data"], remove_directory=True, display_popup_on_error=False ) os.rename(current_autosave_directory, backup_autosave_directory) try: old_directory = autosave_directory new_directory = os.path.join(savestate_directory, "autosave") shutil.copytree(old_directory, new_directory, dirs_exist_ok=True) fpl.delete_files_in_directory( backup_autosave_directory, [".state", ".data"], remove_directory=True, display_popup_on_error=False ) except Exception as e: try: os.rename(backup_autosave_directory, current_autosave_directory) except FileNotFoundError: pass raise if init_data.delete_outdated_meshes: __delete_outdated_meshes(cache_directory, autosave_info["frame"]) def __initialize_fluid_simulation_settings(fluidsim, data): dprops = data.domain_data frameno = fluidsim.get_current_frame() # Domain Settings init_data = data.domain_data.initialize fluidsim.set_timeline_frame_start(init_data.frame_start) fluidsim.set_timeline_frame_end(init_data.frame_end) fluidsim.enable_preview_mesh_output = dprops.initialize.preview_dx if dprops.initialize.upscale_simulation: save_isize = dprops.initialize.savestate_isize save_jsize = dprops.initialize.savestate_jsize save_ksize = dprops.initialize.savestate_ksize save_dx = dprops.initialize.savestate_dx fluidsim.upscale_on_initialization(save_isize, save_jsize, save_ksize, save_dx) bbox = dprops.initialize.bbox fluidsim.set_domain_offset(bbox.x, bbox.y, bbox.z) fluidsim.set_domain_scale(1.0 / dprops.initialize.scale) fluid_boundary_collisions = __get_parameter_data(dprops.simulation.fluid_boundary_collisions, frameno) fluidsim.fluid_boundary_collisions = fluid_boundary_collisions open_boundary_width = __get_parameter_data(dprops.simulation.fluid_open_boundary_width, frameno) fluidsim.fluid_open_boundary_width = open_boundary_width # Whitewater Simulation Settings whitewater = dprops.whitewater is_whitewater_enabled = __get_parameter_data(whitewater.enable_whitewater_simulation, frameno) fluidsim.enable_diffuse_material_output = is_whitewater_enabled if is_whitewater_enabled: is_foam_enabled = __get_parameter_data(whitewater.enable_foam, frameno) is_bubbles_enabled = __get_parameter_data(whitewater.enable_bubbles, frameno) is_spray_enabled = __get_parameter_data(whitewater.enable_spray, frameno) is_dust_enabled = __get_parameter_data(whitewater.enable_dust, frameno) is_dust_boundary_emission_enabled = __get_parameter_data(whitewater.enable_dust_emission_near_boundary, frameno) fluidsim.enable_diffuse_foam = is_foam_enabled fluidsim.enable_diffuse_bubbles = is_bubbles_enabled fluidsim.enable_diffuse_spray = is_spray_enabled fluidsim.enable_diffuse_dust = is_dust_enabled fluidsim.enable_boundary_diffuse_dust_emission = is_dust_boundary_emission_enabled fluidsim.enable_whitewater_motion_blur = \ __get_parameter_data(whitewater.generate_whitewater_motion_blur_data, frameno) fluidsim.enable_whitewater_velocity_attribute = \ __get_parameter_data(whitewater.enable_velocity_vector_attribute, frameno) fluidsim.enable_whitewater_id_attribute = \ __get_parameter_data(whitewater.enable_id_attribute, frameno) fluidsim.enable_whitewater_lifetime_attribute = \ __get_parameter_data(whitewater.enable_lifetime_attribute, frameno) is_generating_whitewater = __get_parameter_data(whitewater.enable_whitewater_emission, frameno) fluidsim.enable_diffuse_particle_emission = is_generating_whitewater emitter_pct = __get_parameter_data(whitewater.whitewater_emitter_generation_rate, frameno) fluidsim.diffuse_emitter_generation_rate = emitter_pct / 100 wavecrest_rate = __get_parameter_data(whitewater.wavecrest_emission_rate, frameno) turbulence_rate = __get_parameter_data(whitewater.turbulence_emission_rate, frameno) dust_rate = __get_parameter_data(whitewater.dust_emission_rate, frameno) fluidsim.diffuse_particle_wavecrest_emission_rate = wavecrest_rate fluidsim.diffuse_particle_turbulence_emission_rate = turbulence_rate fluidsim.diffuse_particle_dust_emission_rate = dust_rate spray_emission_speed = __get_parameter_data(whitewater.spray_emission_speed, frameno) fluidsim.diffuse_spray_emission_speed = spray_emission_speed min_speed, max_speed = __get_parameter_data(whitewater.min_max_whitewater_energy_speed, frameno) fluidsim.min_diffuse_emitter_energy = 0.5 * min_speed * min_speed fluidsim.max_diffuse_emitter_energy = 0.5 * max_speed * max_speed mink, maxk = __get_parameter_data(whitewater.min_max_whitewater_wavecrest_curvature, frameno) fluidsim.min_diffuse_wavecrest_curvature = mink fluidsim.max_diffuse_wavecrest_curvature = maxk mint, maxt = __get_parameter_data(whitewater.min_max_whitewater_turbulence, frameno) fluidsim.min_diffuse_turbulence = mint fluidsim.max_diffuse_turbulence = maxt max_particles = __get_parameter_data(whitewater.max_whitewater_particles, frameno) fluidsim.max_num_diffuse_particles = int(max_particles * 1e6) bbox = __get_emission_boundary(whitewater, fluidsim) fluidsim.diffuse_emitter_generation_bounds = bbox min_lifespan, max_lifespan = __get_parameter_data(whitewater.min_max_whitewater_lifespan, frameno) lifespan_variance = __get_parameter_data(whitewater.whitewater_lifespan_variance, frameno) fluidsim.min_diffuse_particle_lifetime = min_lifespan fluidsim.max_diffuse_particle_lifetime = max_lifespan fluidsim.diffuse_particle_lifetime_variance = lifespan_variance foam_modifier = __get_parameter_data(whitewater.foam_lifespan_modifier, frameno) bubble_modifier = __get_parameter_data(whitewater.bubble_lifespan_modifier, frameno) spray_modifier = __get_parameter_data(whitewater.spray_lifespan_modifier, frameno) dust_modifier = __get_parameter_data(whitewater.dust_lifespan_modifier, frameno) fluidsim.foam_particle_lifetime_modifier = 1.0 / max(foam_modifier, 1e-6) fluidsim.bubble_particle_lifetime_modifier = 1.0 / max(bubble_modifier, 1e-6) fluidsim.spray_particle_lifetime_modifier = 1.0 / max(spray_modifier, 1e-6) fluidsim.dust_particle_lifetime_modifier = 1.0 / max(dust_modifier, 1e-6) boundary_collisions_mode = __get_parameter_data(whitewater.whitewater_boundary_collisions_mode, frameno) if boundary_collisions_mode == 'BOUNDARY_COLLISIONS_MODE_INHERIT': fluid_boundary_collisions = __get_parameter_data(dprops.simulation.fluid_boundary_collisions, frameno) foam_boundary_collisions = fluid_boundary_collisions bubble_boundary_collisions = fluid_boundary_collisions spray_boundary_collisions = fluid_boundary_collisions dust_boundary_collisions = fluid_boundary_collisions else: foam_boundary_collisions = __get_parameter_data(whitewater.foam_boundary_collisions, frameno) bubble_boundary_collisions = __get_parameter_data(whitewater.bubble_boundary_collisions, frameno) spray_boundary_collisions = __get_parameter_data(whitewater.spray_boundary_collisions, frameno) dust_boundary_collisions = __get_parameter_data(whitewater.dust_boundary_collisions, frameno) fluidsim.foam_boundary_collisions = foam_boundary_collisions fluidsim.bubble_boundary_collisions = bubble_boundary_collisions fluidsim.spray_boundary_collisions = spray_boundary_collisions fluidsim.dust_boundary_collisions = dust_boundary_collisions """ foam_behaviour = __get_parameter_data(whitewater.foam_boundary_behaviour, frameno) bubble_behaviour = __get_parameter_data(whitewater.bubble_boundary_behaviour, frameno) spray_behaviour = __get_parameter_data(whitewater.spray_boundary_behaviour, frameno) dust_behaviour = __get_parameter_data(whitewater.bubble_boundary_behaviour, frameno) # Same as bubble for now foam_behaviour = __get_limit_behaviour_enum(foam_behaviour) bubble_behaviour = __get_limit_behaviour_enum(bubble_behaviour) spray_behaviour = __get_limit_behaviour_enum(spray_behaviour) dust_behaviour = __get_limit_behaviour_enum(dust_behaviour) fluidsim.diffuse_foam_limit_behaviour = foam_behaviour fluidsim.diffuse_bubble_limit_behaviour = bubble_behaviour fluidsim.diffuse_spray_limit_behaviour = spray_behaviour fluidsim.diffuse_dust_limit_behaviour = dust_behaviour foam_active_sides = __get_parameter_data(whitewater.foam_boundary_active, frameno) bubble_active_sides = __get_parameter_data(whitewater.bubble_boundary_active, frameno) spray_active_sides = __get_parameter_data(whitewater.spray_boundary_active, frameno) dust_active_sides = __get_parameter_data(whitewater.bubble_boundary_active, frameno) # Same as bubble for now fluidsim.diffuse_foam_active_boundary_sides = foam_active_sides fluidsim.diffuse_bubble_active_boundary_sides = bubble_active_sides fluidsim.diffuse_spray_active_boundary_sides = spray_active_sides fluidsim.diffuse_dust_active_boundary_sides = dust_active_sides """ strength = __get_parameter_data(whitewater.foam_advection_strength, frameno) foam_depth = __get_parameter_data(whitewater.foam_layer_depth, frameno) foam_offset = __get_parameter_data(whitewater.foam_layer_offset, frameno) fluidsim.diffuse_foam_layer_depth = foam_depth fluidsim.diffuse_foam_layer_offset = foam_offset fluidsim.diffuse_foam_advection_strength = strength preserve_foam = __get_parameter_data(whitewater.preserve_foam, frameno) preserve_rate = __get_parameter_data(whitewater.foam_preservation_rate, frameno) min_density, max_density = __get_parameter_data(whitewater.min_max_foam_density, frameno) fluidsim.enable_diffuse_preserve_foam = preserve_foam fluidsim.diffuse_foam_preservation_rate = preserve_rate fluidsim.min_diffuse_foam_density = min_density fluidsim.max_diffuse_foam_density = max_density drag = __get_parameter_data(whitewater.bubble_drag_coefficient, frameno) bouyancy = __get_parameter_data(whitewater.bubble_bouyancy_coefficient, frameno) fluidsim.diffuse_bubble_drag_coefficient = drag fluidsim.diffuse_bubble_bouyancy_coefficient = bouyancy drag = __get_parameter_data(whitewater.dust_drag_coefficient, frameno) bouyancy = __get_parameter_data(whitewater.dust_bouyancy_coefficient, frameno) fluidsim.diffuse_dust_drag_coefficient = drag fluidsim.diffuse_dust_bouyancy_coefficient = bouyancy drag = __get_parameter_data(whitewater.spray_drag_coefficient, frameno) fluidsim.diffuse_spray_drag_coefficient = drag base_level = __get_parameter_data(whitewater.obstacle_influence_base_level, frameno) fluidsim.diffuse_obstacle_influence_base_level = base_level decay_rate = __get_parameter_data(whitewater.obstacle_influence_decay_rate, frameno) fluidsim.diffuse_obstacle_influence_decay_rate = decay_rate # World Settings world = dprops.world fluidsim.add_body_force(__get_parameter_data(world.gravity, frameno)) fluidsim.force_field_weight_fluid_particles = __get_parameter_data(world.force_field_weight_fluid_particles, frameno) fluidsim.force_field_weight_whitewater_foam = __get_parameter_data(world.force_field_weight_whitewater_foam, frameno) fluidsim.force_field_weight_whitewater_bubble = __get_parameter_data(world.force_field_weight_whitewater_bubble, frameno) fluidsim.force_field_weight_whitewater_spray = __get_parameter_data(world.force_field_weight_whitewater_spray, frameno) fluidsim.force_field_weight_whitewater_dust = __get_parameter_data(world.force_field_weight_whitewater_dust, frameno) # Caches created in older versions may not contain force field data. Ignore these features # if force field data cannot be found in the cache is_force_field_data_available = data.force_field_data is not None if is_force_field_data_available: force_fields_exist = (len(data.force_field_data) > 0) is_debugging_force_fields = __get_parameter_data(dprops.debug.export_force_field, frameno) is_force_fields_enabled = force_fields_exist or is_debugging_force_fields fluidsim.enable_force_fields = is_force_fields_enabled if is_force_fields_enabled: field_quality = __get_parameter_data(world.force_field_resolution, frameno) if field_quality == 'FORCE_FIELD_RESOLUTION_LOW': reduction = 4 elif field_quality == 'FORCE_FIELD_RESOLUTION_NORMAL': reduction = 3 elif field_quality == 'FORCE_FIELD_RESOLUTION_HIGH': reduction = 2 elif field_quality == 'FORCE_FIELD_RESOLUTION_ULTRA': reduction = 1 fluidsim.force_field_reduction_level = reduction is_viscosity_enabled = __get_parameter_data(world.enable_viscosity, frameno) if is_viscosity_enabled: surface = dprops.surface is_variable_viscosity_enabled = __get_parameter_data(surface.enable_viscosity_attribute, frameno) if is_variable_viscosity_enabled: fluidsim.viscosity = 0.0 else: fluidsim.viscosity = __get_viscosity_value(world, frameno) tolerance_int = __get_parameter_data(world.viscosity_solver_error_tolerance, frameno) fluidsim.viscosity_solver_error_tolerance = 1.0 * 10.0**(-tolerance_int) is_surface_tension_enabled = __get_parameter_data(world.enable_surface_tension, frameno) if is_surface_tension_enabled: surface_tension = __get_surface_tension_value(world, frameno) fluidsim.surface_tension = surface_tension mincfl, maxcfl = world.minimum_surface_tension_cfl, world.maximum_surface_tension_cfl accuracy_pct = __get_parameter_data(world.surface_tension_accuracy, frameno) / 100.0 surface_tension_number = mincfl + (1.0 - accuracy_pct) * (maxcfl - mincfl) fluidsim.surface_tension_condition_number = surface_tension_number is_sheet_seeding_enabled = __get_parameter_data(world.enable_sheet_seeding, frameno) fluidsim.enable_sheet_seeding = is_sheet_seeding_enabled if is_sheet_seeding_enabled: fluidsim.sheet_fill_rate = __get_parameter_data(world.sheet_fill_rate, frameno, value_min=0.0, value_max=1.0) threshold = __get_parameter_data(world.sheet_fill_threshold, frameno, value_min=0.0, value_max=1.0) fluidsim.sheet_fill_threshold = threshold - 1 friction = __get_parameter_data(world.boundary_friction, frameno, value_min=0.0, value_max=1.0) fluidsim.boundary_friction = friction # Fluid Particle Settings particles = dprops.particles enable_fluid_particle_output = __get_parameter_data(particles.enable_fluid_particle_output, frameno) fluidsim.enable_fluid_particle_output = enable_fluid_particle_output output_amount = max(__get_parameter_data(particles.fluid_particle_output_amount, frameno), 0.0) fluidsim.fluid_particle_output_amount = output_amount enable_fluid_particle_surface_output = __get_parameter_data(particles.enable_fluid_particle_surface_output, frameno) fluidsim.enable_fluid_particle_surface_output = enable_fluid_particle_surface_output enable_fluid_particle_boundary_output = __get_parameter_data(particles.enable_fluid_particle_boundary_output, frameno) fluidsim.enable_fluid_particle_boundary_output = enable_fluid_particle_boundary_output enable_fluid_particle_interior_output = __get_parameter_data(particles.enable_fluid_particle_interior_output, frameno) fluidsim.enable_fluid_particle_interior_output = enable_fluid_particle_interior_output source_id = __get_parameter_data(particles.fluid_particle_source_id_blacklist, frameno) fluidsim.fluid_particle_source_id_blacklist = source_id enable_velocity_attribute = __get_parameter_data(particles.enable_fluid_particle_velocity_vector_attribute, frameno) fluidsim.enable_fluid_particle_velocity_attribute = enable_velocity_attribute enable_speed_attribute = __get_parameter_data(particles.enable_fluid_particle_speed_attribute, frameno) fluidsim.enable_fluid_particle_speed_attribute = enable_speed_attribute enable_vorticity_attribute = __get_parameter_data(particles.enable_fluid_particle_vorticity_vector_attribute, frameno) fluidsim.enable_fluid_particle_vorticity_attribute = enable_vorticity_attribute enable_color_attribute = __get_parameter_data(particles.enable_fluid_particle_color_attribute, frameno) fluidsim.enable_fluid_particle_color_attribute = enable_color_attribute enable_age_attribute = __get_parameter_data(particles.enable_fluid_particle_age_attribute, frameno) fluidsim.enable_fluid_particle_age_attribute = enable_age_attribute enable_lifetime_attribute = __get_parameter_data(particles.enable_fluid_particle_lifetime_attribute, frameno) fluidsim.enable_fluid_particle_lifetime_attribute = enable_lifetime_attribute enable_proximity_attribute = __get_parameter_data(particles.enable_fluid_particle_whitewater_proximity_attribute, frameno) fluidsim.enable_fluid_particle_whitewater_proximity_attribute = enable_proximity_attribute enable_source_id_attribute = __get_parameter_data(particles.enable_fluid_particle_source_id_attribute, frameno) fluidsim.enable_fluid_particle_source_id_attribute = enable_source_id_attribute enable_uid_attribute = __get_parameter_data(particles.enable_fluid_particle_uid_attribute, frameno) fluidsim.enable_fluid_particle_uid_attribute = enable_uid_attribute reused_uid_attribute = __get_parameter_data(particles.enable_fluid_particle_uid_attribute_reuse, frameno) fluidsim.enable_fluid_particle_uid_attribute_reuse = reused_uid_attribute enable_density_attribute = __get_parameter_data(world.enable_density_attribute, frameno) fluidsim.enable_fluid_particle_density_attribute = enable_density_attribute # Surface Settings surface = dprops.surface enable_surface_mesh_generation = __get_parameter_data(surface.enable_surface_mesh_generation, frameno) fluidsim.enable_surface_reconstruction = enable_surface_mesh_generation subdivisions = __get_parameter_data(surface.subdivisions, frameno) + 1 fluidsim.surface_subdivision_level = subdivisions compute_chunk_mode = __get_parameter_data(surface.compute_chunk_mode, frameno) if compute_chunk_mode == 'COMPUTE_CHUNK_MODE_AUTO': num_chunks = __get_parameter_data(surface.compute_chunks_auto, frameno) elif compute_chunk_mode == 'COMPUTE_CHUNK_MODE_FIXED': num_chunks = __get_parameter_data(surface.compute_chunks_fixed, frameno) fluidsim.num_polygonizer_slices = num_chunks particle_scale = __get_parameter_data(surface.particle_scale, frameno) particle_scale *= surface.native_particle_scale fluidsim.marker_particle_scale = particle_scale fluidsim.surface_smoothing_value = __get_parameter_data(surface.smoothing_value, frameno) fluidsim.surface_smoothing_iterations = __get_parameter_data(surface.smoothing_iterations, frameno) enable_meshing_offset = __get_parameter_data(surface.enable_meshing_offset, frameno) fluidsim.enable_obstacle_meshing_offset = enable_meshing_offset meshing_mode = __get_parameter_data(surface.obstacle_meshing_mode, frameno) meshing_offset = __get_obstacle_meshing_offset(meshing_mode) fluidsim.obstacle_meshing_offset = meshing_offset fluidsim.enable_remove_surface_near_domain = __get_parameter_data(surface.remove_mesh_near_domain, frameno) fluidsim.remove_surface_near_domain_distance = __get_parameter_data(surface.remove_mesh_near_domain_distance, frameno) - 1 domain_sides = __get_parameter_data(surface.remove_mesh_near_domain_sides, frameno) fluidsim.remove_surface_near_domain_sides = domain_sides fluidsim.enable_inverted_contact_normals = __get_parameter_data(surface.invert_contact_normals, frameno) fluidsim.enable_surface_motion_blur = __get_parameter_data(surface.generate_motion_blur_data, frameno) fluidsim.enable_surface_velocity_attribute = __get_parameter_data(surface.enable_velocity_vector_attribute, frameno) # Option should always be enabled # fluidsim.enable_surface_velocity_attribute_against_obstacles = \ # __get_parameter_data(surface.enable_velocity_vector_attribute_against_obstacles, frameno) fluidsim.enable_surface_velocity_attribute_against_obstacles = True fluidsim.enable_surface_speed_attribute = __get_parameter_data(surface.enable_speed_attribute, frameno) fluidsim.enable_surface_vorticity_attribute = __get_parameter_data(surface.enable_vorticity_vector_attribute, frameno) fluidsim.enable_surface_age_attribute = __get_parameter_data(surface.enable_age_attribute, frameno) fluidsim.surface_age_attribute_radius = __get_parameter_data(surface.age_attribute_radius, frameno) fluidsim.enable_surface_lifetime_attribute = __get_parameter_data(surface.enable_lifetime_attribute, frameno) fluidsim.surface_lifetime_attribute_radius = __get_parameter_data(surface.lifetime_attribute_radius, frameno) fluidsim.surface_lifetime_attribute_death_time = __get_parameter_data(surface.lifetime_attribute_death_time, frameno) fluidsim.enable_surface_whitewater_proximity_attribute = __get_parameter_data(surface.enable_whitewater_proximity_attribute, frameno) fluidsim.surface_whitewater_proximity_attribute_radius = __get_parameter_data(surface.whitewater_proximity_attribute_radius, frameno) fluidsim.enable_surface_color_attribute = __get_parameter_data(surface.enable_color_attribute, frameno) fluidsim.surface_color_attribute_radius = __get_parameter_data(surface.color_attribute_radius, frameno) fluidsim.enable_surface_color_attribute_mixing = __get_parameter_data(surface.enable_color_attribute_mixing, frameno) fluidsim.surface_color_attribute_mixing_rate = __get_parameter_data(surface.color_attribute_mixing_rate, frameno) fluidsim.surface_color_attribute_mixing_radius = __get_parameter_data(surface.color_attribute_mixing_radius, frameno) if fluidsim.enable_surface_color_attribute_mixing: mixing_mode = __get_parameter_data(surface.color_attribute_mixing_mode, frameno) if mixing_mode == 'COLOR_MIXING_MODE_MIXBOX': __initialize_mixbox(fluidsim) fluidsim.enable_mixbox = True else: fluidsim.enable_mixbox = False fluidsim.enable_surface_source_id_attribute = __get_parameter_data(surface.enable_source_id_attribute, frameno) is_viscosity_enabled = __get_parameter_data(world.enable_viscosity, frameno) if is_viscosity_enabled: fluidsim.enable_surface_viscosity_attribute = __get_parameter_data(surface.enable_viscosity_attribute, frameno) fluidsim.enable_surface_density_attribute = __get_parameter_data(world.enable_density_attribute, frameno) __set_meshing_volume_object(fluidsim, data, frameno) # Advanced Settings advanced = dprops.advanced min_substeps, max_substeps = __get_parameter_data(advanced.min_max_time_steps_per_frame, frameno) fluidsim.min_time_steps_per_frame = min_substeps fluidsim.max_time_steps_per_frame = max_substeps fluidsim.enable_adaptive_obstacle_time_stepping = \ __get_parameter_data(advanced.enable_adaptive_obstacle_time_stepping, frameno) fluidsim.enable_adaptive_force_field_time_stepping = \ __get_parameter_data(advanced.enable_adaptive_force_field_time_stepping, frameno) fluidsim.marker_particle_jitter_factor = \ __get_parameter_data(advanced.particle_jitter_factor, frameno) fluidsim.jitter_surface_marker_particles = \ __get_parameter_data(advanced.jitter_surface_particles, frameno) fluidsim.pressure_solver_max_iterations = \ __get_parameter_data(advanced.pressure_solver_max_iterations, frameno) fluidsim.viscosity_solver_max_iterations = \ __get_parameter_data(advanced.viscosity_solver_max_iterations, frameno) velocity_transfer_method = __get_parameter_data(advanced.velocity_transfer_method, frameno) if velocity_transfer_method == 'VELOCITY_TRANSFER_METHOD_FLIP': fluidsim.set_velocity_transfer_method_FLIP() elif velocity_transfer_method == 'VELOCITY_TRANSFER_METHOD_APIC': fluidsim.set_velocity_transfer_method_APIC() fluidsim.PICFLIP_ratio = __get_parameter_data(advanced.PICFLIP_ratio, frameno) fluidsim.PICAPIC_ratio = __get_parameter_data(advanced.PICAPIC_ratio, frameno) CFL_number = __get_parameter_data(advanced.CFL_condition_number, frameno) fluidsim.CFL_condition_number = CFL_number fluidsim.enable_extreme_velocity_removal = \ __get_parameter_data(advanced.enable_extreme_velocity_removal, frameno) threading_mode = __get_parameter_data(advanced.threading_mode, frameno) if threading_mode == 'THREADING_MODE_AUTO_DETECT': num_threads = __get_parameter_data(advanced.num_threads_auto_detect, frameno) elif threading_mode == 'THREADING_MODE_FIXED': num_threads = __get_parameter_data(advanced.num_threads_fixed, frameno) fluidsim.max_thread_count = num_threads fluidsim.enable_asynchronous_meshing = \ __get_parameter_data(advanced.enable_asynchronous_meshing, frameno) fluidsim.enable_fracture_optimization = \ __get_parameter_data(advanced.enable_fracture_optimization, frameno) fluidsim.enable_static_solid_levelset_precomputation = \ __get_parameter_data(advanced.precompute_static_obstacles, frameno) fluidsim.enable_temporary_mesh_levelset = \ __get_parameter_data(advanced.reserve_temporary_grids, frameno) # Debug Settings fluidsim.enable_fluid_particle_debug_output = \ __get_parameter_data(dprops.debug.enable_fluid_particle_debug_output, frameno) fluidsim.enable_internal_obstacle_mesh_output = \ __get_parameter_data(dprops.debug.export_internal_obstacle_mesh, frameno) if is_force_field_data_available: fluidsim.enable_force_field_debug_output = \ __get_parameter_data(dprops.debug.export_force_field, frameno) # Internal Settings fluidsim.set_mesh_output_format_as_bobj() if is_whitewater_enabled: fluidsim.output_diffuse_material_as_separate_files = True def __get_mesh_centroid(tmesh): vsum = [0.0, 0.0, 0.0] for i in range(0, len(tmesh.vertices), 3): vsum[0] += tmesh.vertices[i] vsum[1] += tmesh.vertices[i + 1] vsum[2] += tmesh.vertices[i + 2] vsum[0] /= (len(tmesh.vertices) / 3) vsum[1] /= (len(tmesh.vertices) / 3) vsum[2] /= (len(tmesh.vertices) / 3) return vsum def __get_fluid_object_velocity(fluid_object, frameid): if fluid_object.fluid_velocity_mode.data == 'FLUID_VELOCITY_MANUAL': return __get_parameter_data(fluid_object.initial_velocity, frameid) elif fluid_object.fluid_velocity_mode.data == 'FLUID_VELOCITY_AXIS': timeline_frame = __get_timeline_frame() local_x, local_y, local_z = __extract_local_axis(fluid_object.name, timeline_frame) axis_mode = __get_parameter_data(fluid_object.fluid_axis_mode, frameid) if axis_mode == 'LOCAL_AXIS_POS_X' or axis_mode == 0.0: local_axis = local_x elif axis_mode == 'LOCAL_AXIS_POS_Y' or axis_mode == 1.0: local_axis = local_y elif axis_mode == 'LOCAL_AXIS_POS_Z' or axis_mode == 2.0: local_axis = local_z elif axis_mode == 'LOCAL_AXIS_NEG_X' or axis_mode == 3.0: local_axis = [-local_x[0], -local_x[1], -local_x[2]] elif axis_mode == 'LOCAL_AXIS_NEG_Y' or axis_mode == 4.0: local_axis = [-local_y[0], -local_y[1], -local_y[2]] elif axis_mode == 'LOCAL_AXIS_NEG_Z' or axis_mode == 5.0: local_axis = [-local_z[0], -local_z[1], -local_z[2]] initial_speed = __get_parameter_data(fluid_object.initial_speed, frameid) velocity = [initial_speed * local_axis[0], initial_speed * local_axis[1], initial_speed * local_axis[2]] return velocity # Use target if not fluid_object.target_object: return [0, 0, 0] target_object_name = fluid_object.target_object initial_speed = __get_parameter_data(fluid_object.initial_speed, frameid) timeline_frame = __get_timeline_frame() fluid_object_mesh = __extract_mesh(fluid_object.name, timeline_frame) c1 = __get_mesh_centroid(fluid_object_mesh) c2 = __extract_centroid(target_object_name, timeline_frame) vdir = [c2[0] - c1[0], c2[1] - c1[1], c2[2] - c1[2]] vlen = math.sqrt(vdir[0] * vdir[0] + vdir[1] * vdir[1] + vdir[2] * vdir[2]) eps = 1e-6 if vlen < eps: return [0, 0, 0] vdir[0] /= vlen vdir[1] /= vlen vdir[2] /= vlen vdir[0] *= initial_speed vdir[1] *= initial_speed vdir[2] *= initial_speed return vdir def __get_inflow_object_velocity(inflow_object, frameid): if inflow_object.inflow_velocity_mode.data == 'INFLOW_VELOCITY_MANUAL': return __get_parameter_data(inflow_object.inflow_velocity, frameid) elif inflow_object.inflow_velocity_mode.data == 'INFLOW_VELOCITY_AXIS': timeline_frame = __get_timeline_frame() local_x, local_y, local_z = __extract_local_axis(inflow_object.name, timeline_frame) axis_mode = __get_parameter_data(inflow_object.inflow_axis_mode, frameid) if axis_mode == 'LOCAL_AXIS_POS_X' or axis_mode == 0.0: local_axis = local_x elif axis_mode == 'LOCAL_AXIS_POS_Y' or axis_mode == 1.0: local_axis = local_y elif axis_mode == 'LOCAL_AXIS_POS_Z' or axis_mode == 2.0: local_axis = local_z elif axis_mode == 'LOCAL_AXIS_NEG_X' or axis_mode == 3.0: local_axis = [-local_x[0], -local_x[1], -local_x[2]] elif axis_mode == 'LOCAL_AXIS_NEG_Y' or axis_mode == 4.0: local_axis = [-local_y[0], -local_y[1], -local_y[2]] elif axis_mode == 'LOCAL_AXIS_NEG_Z' or axis_mode == 5.0: local_axis = [-local_z[0], -local_z[1], -local_z[2]] inflow_speed = __get_parameter_data(inflow_object.inflow_speed, frameid) velocity = [inflow_speed * local_axis[0], inflow_speed * local_axis[1], inflow_speed * local_axis[2]] return velocity # Use target if not inflow_object.target_object: return [0, 0, 0] target_object_name = inflow_object.target_object inflow_speed = __get_parameter_data(inflow_object.inflow_speed, frameid) timeline_frame = __get_timeline_frame() fluid_object_mesh = __extract_mesh(inflow_object.name, timeline_frame) c1 = __get_mesh_centroid(fluid_object_mesh) c2 = __extract_centroid(target_object_name, timeline_frame) vdir = [c2[0] - c1[0], c2[1] - c1[1], c2[2] - c1[2]] vlen = math.sqrt(vdir[0] * vdir[0] + vdir[1] * vdir[1] + vdir[2] * vdir[2]) eps = 1e-6 if vlen < eps: return [0, 0, 0] vdir[0] /= vlen vdir[1] /= vlen vdir[2] /= vlen vdir[0] *= inflow_speed vdir[1] *= inflow_speed vdir[2] *= inflow_speed return vdir def __add_fluid_objects(fluidsim, data, bakedata, frameid=0): init_data = data.domain_data.initialize bbox = init_data.bbox isize, jsize, ksize = init_data.isize, init_data.jsize, init_data.ksize dx = init_data.dx for obj in list(data.fluid_data): if obj.frame_offset_type.data == 'OFFSET_TYPE_FRAME': frame_offset = __get_parameter_data(obj.frame_offset, frameid) if frame_offset != frameid: continue elif obj.frame_offset_type.data == 'OFFSET_TYPE_TIMELINE': frame_offset = __get_parameter_data(obj.timeline_offset, frameid) if frame_offset != __get_timeline_frame(): continue fluid_object = MeshObject(isize, jsize, ksize, dx) if __is_object_dynamic(obj.name): timeline_frame = __get_timeline_frame() previous_mesh, current_mesh, next_mesh = __extract_dynamic_frame_meshes(obj.name, timeline_frame) fluid_object.update_mesh_animated(previous_mesh, current_mesh, next_mesh) else: mesh = __extract_static_frame_mesh(obj.name) fluid_object.update_mesh_static(mesh) fluid_object.enable_append_object_velocity = __get_parameter_data(obj.append_object_velocity, frameid) fluid_object.object_velocity_influence = __get_parameter_data(obj.append_object_velocity_influence, frameid) fluid_object.priority = __get_parameter_data(obj.priority, frameid) fluid_object.source_id = __get_parameter_data(obj.source_id, frameid) fluid_object.viscosity = __get_parameter_data(obj.viscosity, frameid) fluid_object.density = __get_parameter_data(obj.density, frameid) fluid_object.lifetime = __get_parameter_data(obj.lifetime, frameid) fluid_object.lifetime_variance = __get_parameter_data(obj.lifetime_variance, frameid) fluid_object.set_source_color(__get_parameter_data(obj.color, frameid)) velocity = __get_fluid_object_velocity(obj, frameid) fluidsim.add_mesh_fluid(fluid_object, velocity[0], velocity[1], velocity[2]) data.fluid_data.remove(obj) if __check_bake_cancelled(bakedata): return def __add_obstacle_objects(fluidsim, data, bakedata): init_data = data.domain_data.initialize bbox = init_data.bbox isize, jsize, ksize = init_data.isize, init_data.jsize, init_data.ksize dx = init_data.dx obstacle_objects = [] for obj in data.obstacle_data: obstacle = MeshObject(isize, jsize, ksize, dx) obstacle.inverse = __get_parameter_data(obj.is_inversed) if not __is_object_dynamic(obj.name): mesh = __extract_static_frame_mesh(obj.name) obstacle.update_mesh_static(mesh) obstacle_objects.append(obstacle) fluidsim.add_mesh_obstacle(obstacle) if __check_bake_cancelled(bakedata): return return obstacle_objects def __add_inflow_objects(fluidsim, data, bakedata): init_data = data.domain_data.initialize bbox = init_data.bbox isize, jsize, ksize = init_data.isize, init_data.jsize, init_data.ksize dx = init_data.dx inflow_objects = [] for obj in data.inflow_data: source = MeshFluidSource(isize, jsize, ksize, dx) if not __is_object_dynamic(obj.name): mesh = __extract_static_frame_mesh(obj.name) source.update_mesh_static(mesh) fluidsim.add_mesh_fluid_source(source) inflow_objects.append(source) if __check_bake_cancelled(bakedata): return return inflow_objects def __add_outflow_objects(fluidsim, data, bakedata): init_data = data.domain_data.initialize bbox = init_data.bbox isize, jsize, ksize = init_data.isize, init_data.jsize, init_data.ksize dx = init_data.dx outflow_objects = [] for obj in data.outflow_data: source = MeshFluidSource(isize, jsize, ksize, dx) source.outflow = True source.outflow_inverse = __get_parameter_data(obj.is_inversed) if not __is_object_dynamic(obj.name): mesh = __extract_static_frame_mesh(obj.name) source.update_mesh_static(mesh) fluidsim.add_mesh_fluid_source(source) outflow_objects.append(source) if __check_bake_cancelled(bakedata): return return outflow_objects def __add_force_field_objects(fluidsim, data, bakedata): force_field_objects = [] if not fluidsim.enable_force_fields: return force_field_objects init_data = data.domain_data.initialize bbox = init_data.bbox isize, jsize, ksize = init_data.isize, init_data.jsize, init_data.ksize dx = init_data.dx force_field_grid = fluidsim.get_force_field_grid() force_field_objects = [] for obj in data.force_field_data: field_type = obj.force_field_type.data field_object = None if field_type == 'FORCE_FIELD_TYPE_POINT': field_object = ForceFieldPoint() elif field_type == 'FORCE_FIELD_TYPE_SURFACE': field_object = ForceFieldSurface() elif field_type == 'FORCE_FIELD_TYPE_VOLUME': field_object = ForceFieldVolume() elif field_type == 'FORCE_FIELD_TYPE_CURVE': field_object = ForceFieldCurve() if not __is_object_dynamic(obj.name): mesh = __extract_force_field_mesh(obj.name) field_object.update_mesh_static(mesh) force_field_grid.add_force_field(field_object) force_field_objects.append(field_object) if __check_bake_cancelled(bakedata): return return force_field_objects def __initialize_mixbox(fluidsim): module_dir = os.path.dirname(os.path.realpath(__file__)) lut_filepath = os.path.join(module_dir, "third_party", "mixbox", "mixbox_lut_data.bin") if os.path.isfile(lut_filepath): with open(lut_filepath, 'rb') as f: lut_data = f.read() lut_data_bytes = len(lut_data) mixbox.initialize(lut_data, lut_data_bytes) # Not necessary to initialize Mixbox again, but this will output data to the simulation log fluidsim.initialize_mixbox(lut_data, lut_data_bytes) def __initialize_fluid_simulation(fluidsim, data, cache_directory, bakedata, savestate_id): set_console_output(bakedata.is_console_output_enabled) __load_save_state_data(fluidsim, data, cache_directory, savestate_id) init_data = data.domain_data.initialize num_frames = init_data.frame_end - init_data.frame_start + 1 bakedata.completed_frames = fluidsim.get_current_frame() bakedata.progress = bakedata.completed_frames / num_frames __initialize_fluid_simulation_settings(fluidsim, data) __add_fluid_objects(fluidsim, data, bakedata, fluidsim.get_current_frame()) data.obstacle_objects = __add_obstacle_objects(fluidsim, data, bakedata) data.inflow_objects = __add_inflow_objects(fluidsim, data, bakedata) data.outflow_objects = __add_outflow_objects(fluidsim, data, bakedata) data.force_field_objects = __add_force_field_objects(fluidsim, data, bakedata) fluidsim.initialize() bakedata.is_initialized = True def __update_dynamic_object_mesh(animated_object, object_data): timeline_frame = __get_timeline_frame() mesh_previous, mesh_current, mesh_next = __extract_dynamic_frame_meshes(object_data.name, timeline_frame) animated_object.update_mesh_animated(mesh_previous, mesh_current, mesh_next) def __update_dynamic_force_field_mesh(animated_object, object_data): timeline_frame = __get_timeline_frame() mesh_previous, mesh_current, mesh_next = __extract_dynamic_force_field_frame_meshes(object_data.name, timeline_frame) animated_object.update_mesh_animated(mesh_previous, mesh_current, mesh_next) def __update_animatable_inflow_properties(data, mesh_geometry_data, frameid): inflow_objects = data.inflow_objects inflow_data = data.inflow_data for idx, inflow in enumerate(inflow_objects): data = inflow_data[idx] name_slug = __get_name_slug(data.name) object_geometry_data = mesh_geometry_data[name_slug] if object_geometry_data["is_motion_dynamic"]: mesh_previous = object_geometry_data["triangle_mesh_previous"] mesh_current = object_geometry_data["triangle_mesh_current"] mesh_next = object_geometry_data["triangle_mesh_next"] inflow.update_mesh_animated(mesh_previous, mesh_current, mesh_next) inflow.enable = __get_parameter_data(data.is_enabled, frameid) inflow.set_velocity(__get_inflow_object_velocity(data, frameid)) inflow.enable_append_object_velocity = \ __get_parameter_data(data.append_object_velocity, frameid) inflow.object_velocity_influence = \ __get_parameter_data(data.append_object_velocity_influence, frameid) inflow.substep_emissions = __get_parameter_data(data.substep_emissions, frameid) inflow.priority = __get_parameter_data(data.priority, frameid) is_constrained = __get_parameter_data(data.constrain_fluid_velocity, frameid) inflow.enable_constrained_fluid_velocity = is_constrained inflow.source_id = __get_parameter_data(data.source_id, frameid) inflow.viscosity = __get_parameter_data(data.viscosity, frameid) inflow.density = __get_parameter_data(data.density, frameid) inflow.lifetime = __get_parameter_data(data.lifetime, frameid) inflow.lifetime_variance = __get_parameter_data(data.lifetime_variance, frameid) inflow.set_source_color(__get_parameter_data(data.color, frameid)) def __update_animatable_outflow_properties(data, mesh_geometry_data, frameid): outflow_objects = data.outflow_objects outflow_data = data.outflow_data for idx, outflow in enumerate(outflow_objects): data = outflow_data[idx] name_slug = __get_name_slug(data.name) object_geometry_data = mesh_geometry_data[name_slug] if object_geometry_data["is_motion_dynamic"]: mesh_previous = object_geometry_data["triangle_mesh_previous"] mesh_current = object_geometry_data["triangle_mesh_current"] mesh_next = object_geometry_data["triangle_mesh_next"] outflow.update_mesh_animated(mesh_previous, mesh_current, mesh_next) outflow.enable = __get_parameter_data(data.is_enabled, frameid) outflow.fluid_outflow = __get_parameter_data(data.remove_fluid, frameid) outflow.diffuse_outflow = __get_parameter_data(data.remove_whitewater, frameid) def __update_animatable_force_field_properties(data, frameid): force_field_objects = data.force_field_objects force_field_data = data.force_field_data for idx, force_field in enumerate(force_field_objects): data = force_field_data[idx] if __is_object_dynamic(data.name): __update_dynamic_force_field_mesh(force_field, data) force_field.enable = __get_parameter_data(data.is_enabled, frameid) force_field.strength = __get_parameter_data(data.strength, frameid) force_field.falloff_power = __get_parameter_data(data.falloff_power, frameid) force_field.max_force_limit_factor = __get_parameter_data(data.maximum_force_limit_factor, frameid) force_field.enable_min_distance = __get_parameter_data(data.enable_min_distance, frameid) force_field.enable_max_distance = __get_parameter_data(data.enable_max_distance, frameid) force_field.min_distance, force_field.max_distance = __get_parameter_data(data.min_max_distance, frameid) field_type = data.force_field_type.data if field_type == 'FORCE_FIELD_TYPE_POINT': force_field.gravity_scale = __get_parameter_data(data.gravity_scale_point, frameid) force_field.gravity_scale_width = __get_parameter_data(data.gravity_scale_width_point, frameid) elif field_type == 'FORCE_FIELD_TYPE_SURFACE': force_field.enable_frontfacing = __get_parameter_data(data.enable_frontfacing, frameid) force_field.enable_backfacing = __get_parameter_data(data.enable_backfacing, frameid) force_field.enable_edgefacing = __get_parameter_data(data.enable_edgefacing, frameid) force_field.gravity_scale = __get_parameter_data(data.gravity_scale_surface, frameid) force_field.gravity_scale_width = __get_parameter_data(data.gravity_scale_width_surface, frameid) elif field_type == 'FORCE_FIELD_TYPE_VOLUME': force_field.gravity_scale = __get_parameter_data(data.gravity_scale_volume, frameid) force_field.gravity_scale_width = __get_parameter_data(data.gravity_scale_width_volume, frameid) elif field_type == 'FORCE_FIELD_TYPE_CURVE': force_field.flow_strength = __get_parameter_data(data.flow_strength, frameid) force_field.spin_strength = __get_parameter_data(data.spin_strength, frameid) force_field.enable_endcaps = __get_parameter_data(data.enable_endcaps, frameid) force_field.gravity_scale = __get_parameter_data(data.gravity_scale_curve, frameid) force_field.gravity_scale_width = __get_parameter_data(data.gravity_scale_width_curve, frameid) def __update_animatable_obstacle_properties(data, mesh_geometry_data, frameid): obstacle_objects = data.obstacle_objects obstacle_data = data.obstacle_data #### WORKAROUND #### # Due to a current design issue in the simulator, if there is a mix of # static and dynamic inverse obstacles in the simulation, then the combined # inverse SDF will not be computed correctly. A workaround in previous # versions of the addon was to enable "Export Animation Mesh" on all inverse # obstacles so that there was not a mix and the SDF would be computed correctly. # # This section automatically applies this workaround by detecting if there # are any dynamic inverse obstacles, and if so, treating all static inverse # obstacles as dynamic ones. This is done by using the update_mesh_animated() # method and supplying the static mesh for all frame_previous, frame_current, and # frame_next. # # In future development, or in the next iteration of the simulator, this should # be fixed properly inside of the simulation engine. is_inverse_dynamic = False for idx, mesh_object in enumerate(obstacle_objects): data = obstacle_data[idx] if mesh_object.inverse: is_inverse_dynamic = is_inverse_dynamic or __is_object_dynamic(data.name) if is_inverse_dynamic: for idx, mesh_object in enumerate(obstacle_objects): data = obstacle_data[idx] if mesh_object.inverse and not __is_object_dynamic(data.name): mesh = __extract_static_frame_mesh(data.name) mesh_object.update_mesh_animated(mesh, mesh, mesh) #### END WORKAROUND #### total_update_time = 0.0 start_mesh_object = time.time() for idx, mesh_object in enumerate(obstacle_objects): data = obstacle_data[idx] name_slug = __get_name_slug(data.name) object_geometry_data = mesh_geometry_data[name_slug] if object_geometry_data["is_motion_dynamic"]: mesh_previous = object_geometry_data["triangle_mesh_previous"] mesh_current = object_geometry_data["triangle_mesh_current"] mesh_next = object_geometry_data["triangle_mesh_next"] mesh_object.update_mesh_animated(mesh_previous, mesh_current, mesh_next) mesh_object.enable = __get_parameter_data(data.is_enabled, frameid) mesh_object.friction = __get_parameter_data(data.friction, frameid, value_min=0.0) mesh_object.velocity_scale = __get_parameter_data(data.velocity_scale, frameid) mesh_object.whitewater_influence = __get_parameter_data(data.whitewater_influence, frameid, value_min=0.0) mesh_object.dust_emission_strength = __get_parameter_data(data.dust_emission_strength, frameid, value_min=0.0) mesh_object.sheeting_strength = __get_parameter_data(data.sheeting_strength, frameid, value_min=0.0) mesh_object.mesh_expansion = __get_parameter_data(data.mesh_expansion, frameid) def __update_animatable_meshing_volume_properties(data, frameid): surface_data = data.domain_data.surface meshing_volume_mode = __get_parameter_data(surface_data.meshing_volume_mode, frameid) if meshing_volume_mode != 'MESHING_VOLUME_MODE_OBJECT': return if not surface_data.meshing_volume_object: return volume_object = surface_data.meshing_volume_object_class volume_name = surface_data.meshing_volume_object if __is_object_dynamic(surface_data.meshing_volume_object): timeline_frame = __get_timeline_frame() mesh_previous, mesh_current, mesh_next = __extract_dynamic_frame_meshes(volume_name, timeline_frame) volume_object.update_mesh_animated(mesh_previous, mesh_current, mesh_next) def __set_property(obj, pname, value, value_min=None, value_max=None): if value_min is not None: value = max(value, value_min) if value_max is not None: value = min(value, value_max) eps = 1e-6 old_value = getattr(obj, pname) if isinstance(value, list): for i, v in enumerate(value): if v != old_value[i]: setattr(obj, pname, value) break elif abs(value - old_value) > eps: setattr(obj, pname, value) def __set_body_force_property(fluidsim, body_force): eps = 1e-6 old_body_force = fluidsim.get_constant_body_force() new_body_force = Vector3(body_force[0], body_force[1], body_force[2]) if (new_body_force - old_body_force).length() > eps: fluidsim.reset_body_force() fluidsim.add_body_force(body_force) def __set_whitewater_emission_boundary_property(fluidsim, bounds): eps = 1e-6 old_bounds = fluidsim.diffuse_emitter_generation_bounds if ((bounds.position - old_bounds.position).length() > eps or abs(bounds.width - old_bounds.width) > eps or abs(bounds.height - old_bounds.height) > eps or abs(bounds.depth - old_bounds.depth) > eps): fluidsim.diffuse_emitter_generation_bounds = bounds def __set_meshing_volume_object(fluidsim, data, frameid=0): init_data = data.domain_data.initialize surface_data = data.domain_data.surface bbox = init_data.bbox isize, jsize, ksize = init_data.isize, init_data.jsize, init_data.ksize dx = init_data.dx meshing_volume_mode = __get_parameter_data(surface_data.meshing_volume_mode, frameid) if meshing_volume_mode != 'MESHING_VOLUME_MODE_OBJECT': return if not surface_data.meshing_volume_object: return object_name = surface_data.meshing_volume_object if __is_object_dynamic(object_name): timeline_frame = __get_timeline_frame() previous_mesh, current_mesh, next_mesh = __extract_dynamic_frame_meshes(object_name, timeline_frame) volume_object = MeshObject(isize, jsize, ksize, dx) volume_object.update_mesh_animated(previous_mesh, current_mesh, next_mesh) else: mesh = __extract_static_frame_mesh(object_name) volume_object = MeshObject(isize, jsize, ksize, dx) volume_object.update_mesh_static(mesh) fluidsim.set_meshing_volume(volume_object) surface_data.meshing_volume_object_class = volume_object def __update_animatable_domain_properties(fluidsim, data, frameno): dprops = data.domain_data # Simulation Settings fluid_boundary_collisions = __get_parameter_data(dprops.simulation.fluid_boundary_collisions, frameno) __set_property(fluidsim, 'fluid_boundary_collisions', fluid_boundary_collisions) open_boundary_width = __get_parameter_data(dprops.simulation.fluid_open_boundary_width, frameno) __set_property(fluidsim, 'fluid_open_boundary_width', open_boundary_width) # Whitewater Simulation Settings whitewater = dprops.whitewater if __get_parameter_data(whitewater.enable_whitewater_simulation): is_generating_whitewater = __get_parameter_data(whitewater.enable_whitewater_emission, frameno) __set_property(fluidsim, 'enable_diffuse_particle_emission', is_generating_whitewater) is_foam_enabled = __get_parameter_data(whitewater.enable_foam, frameno) is_bubbles_enabled = __get_parameter_data(whitewater.enable_bubbles, frameno) is_spray_enabled = __get_parameter_data(whitewater.enable_spray, frameno) is_dust_enabled = __get_parameter_data(whitewater.enable_dust, frameno) is_dust_boundary_emission_enabled = __get_parameter_data(whitewater.enable_dust_emission_near_boundary, frameno) __set_property(fluidsim, 'enable_diffuse_foam', is_foam_enabled) __set_property(fluidsim, 'enable_diffuse_bubbles', is_bubbles_enabled) __set_property(fluidsim, 'enable_diffuse_spray', is_spray_enabled) __set_property(fluidsim, 'enable_diffuse_dust', is_dust_enabled) __set_property(fluidsim, 'enable_boundary_diffuse_dust_emission', is_dust_boundary_emission_enabled) whitewater_motion_blur = __get_parameter_data(whitewater.generate_whitewater_motion_blur_data, frameno) __set_property(fluidsim, 'enable_whitewater_motion_blur', whitewater_motion_blur) emitter_pct = __get_parameter_data(whitewater.whitewater_emitter_generation_rate, frameno) __set_property(fluidsim, 'diffuse_emitter_generation_rate', emitter_pct / 100) wavecrest_rate = __get_parameter_data(whitewater.wavecrest_emission_rate, frameno) turbulence_rate = __get_parameter_data(whitewater.turbulence_emission_rate, frameno) dust_rate = __get_parameter_data(whitewater.dust_emission_rate, frameno) __set_property(fluidsim, 'diffuse_particle_wavecrest_emission_rate', wavecrest_rate) __set_property(fluidsim, 'diffuse_particle_turbulence_emission_rate', turbulence_rate) __set_property(fluidsim, 'diffuse_particle_dust_emission_rate', dust_rate) spray_emission_speed = __get_parameter_data(whitewater.spray_emission_speed, frameno) __set_property(fluidsim, 'diffuse_spray_emission_speed', spray_emission_speed) min_speed, max_speed = __get_parameter_data(whitewater.min_max_whitewater_energy_speed, frameno) __set_property(fluidsim, 'min_diffuse_emitter_energy', 0.5 * min_speed * min_speed) __set_property(fluidsim, 'max_diffuse_emitter_energy', 0.5 * max_speed * max_speed) mink, maxk = __get_parameter_data(whitewater.min_max_whitewater_wavecrest_curvature, frameno) __set_property(fluidsim, 'min_diffuse_wavecrest_curvature', mink) __set_property(fluidsim, 'max_diffuse_wavecrest_curvature', maxk) mint, maxt = __get_parameter_data(whitewater.min_max_whitewater_turbulence, frameno) __set_property(fluidsim, 'min_diffuse_turbulence', mint) __set_property(fluidsim, 'max_diffuse_turbulence', maxt) max_particles = __get_parameter_data(whitewater.max_whitewater_particles, frameno) __set_property(fluidsim, 'max_num_diffuse_particles', int(max_particles * 1e6)) bounds = __get_emission_boundary(whitewater, fluidsim) __set_whitewater_emission_boundary_property(fluidsim, bounds) min_lifespan, max_lifespan = __get_parameter_data(whitewater.min_max_whitewater_lifespan, frameno) lifespan_variance = __get_parameter_data(whitewater.whitewater_lifespan_variance, frameno) __set_property(fluidsim, 'min_diffuse_particle_lifetime', min_lifespan) __set_property(fluidsim, 'max_diffuse_particle_lifetime', max_lifespan) __set_property(fluidsim, 'diffuse_particle_lifetime_variance', lifespan_variance) foam_modifier = __get_parameter_data(whitewater.foam_lifespan_modifier, frameno) bubble_modifier = __get_parameter_data(whitewater.bubble_lifespan_modifier, frameno) spray_modifier = __get_parameter_data(whitewater.spray_lifespan_modifier, frameno) dust_modifier = __get_parameter_data(whitewater.dust_lifespan_modifier, frameno) __set_property(fluidsim, 'foam_particle_lifetime_modifier', 1.0 / max(foam_modifier, 1e-6)) __set_property(fluidsim, 'bubble_particle_lifetime_modifier', 1.0 / max(bubble_modifier, 1e-6)) __set_property(fluidsim, 'spray_particle_lifetime_modifier', 1.0 / max(spray_modifier, 1e-6)) __set_property(fluidsim, 'dust_particle_lifetime_modifier', 1.0 / max(dust_modifier, 1e-6)) boundary_collisions_mode = __get_parameter_data(whitewater.whitewater_boundary_collisions_mode, frameno) if boundary_collisions_mode == 'BOUNDARY_COLLISIONS_MODE_INHERIT': fluid_boundary_collisions = __get_parameter_data(dprops.simulation.fluid_boundary_collisions, frameno) foam_boundary_collisions = fluid_boundary_collisions bubble_boundary_collisions = fluid_boundary_collisions spray_boundary_collisions = fluid_boundary_collisions dust_boundary_collisions = fluid_boundary_collisions else: foam_boundary_collisions = __get_parameter_data(whitewater.foam_boundary_collisions, frameno) bubble_boundary_collisions = __get_parameter_data(whitewater.bubble_boundary_collisions, frameno) spray_boundary_collisions = __get_parameter_data(whitewater.spray_boundary_collisions, frameno) dust_boundary_collisions = __get_parameter_data(whitewater.dust_boundary_collisions, frameno) __set_property(fluidsim, 'foam_boundary_collisions', foam_boundary_collisions) __set_property(fluidsim, 'bubble_boundary_collisions', bubble_boundary_collisions) __set_property(fluidsim, 'spray_boundary_collisions', spray_boundary_collisions) __set_property(fluidsim, 'dust_boundary_collisions', dust_boundary_collisions) """ foam_behaviour = __get_parameter_data(whitewater.foam_boundary_behaviour, frameno) bubble_behaviour = __get_parameter_data(whitewater.bubble_boundary_behaviour, frameno) spray_behaviour = __get_parameter_data(whitewater.spray_boundary_behaviour, frameno) dust_behaviour = __get_parameter_data(whitewater.bubble_boundary_behaviour, frameno) # Same as bubble for now foam_behaviour = __get_limit_behaviour_enum(foam_behaviour) bubble_behaviour = __get_limit_behaviour_enum(bubble_behaviour) spray_behaviour = __get_limit_behaviour_enum(spray_behaviour) dust_behaviour = __get_limit_behaviour_enum(dust_behaviour) __set_property(fluidsim, 'diffuse_foam_limit_behaviour', foam_behaviour) __set_property(fluidsim, 'diffuse_bubble_limit_behaviour', bubble_behaviour) __set_property(fluidsim, 'diffuse_spray_limit_behaviour', spray_behaviour) __set_property(fluidsim, 'diffuse_dust_limit_behaviour', dust_behaviour) foam_active_sides = __get_parameter_data(whitewater.foam_boundary_active, frameno) bubble_active_sides = __get_parameter_data(whitewater.bubble_boundary_active, frameno) spray_active_sides = __get_parameter_data(whitewater.spray_boundary_active, frameno) dust_active_sides = __get_parameter_data(whitewater.bubble_boundary_active, frameno) # Same as bubble for now __set_property(fluidsim, 'diffuse_foam_active_boundary_sides', foam_active_sides) __set_property(fluidsim, 'diffuse_bubble_active_boundary_sides', bubble_active_sides) __set_property(fluidsim, 'diffuse_spray_active_boundary_sides', spray_active_sides) __set_property(fluidsim, 'diffuse_dust_active_boundary_sides', dust_active_sides) """ strength = __get_parameter_data(whitewater.foam_advection_strength, frameno) foam_depth = __get_parameter_data(whitewater.foam_layer_depth, frameno) foam_offset = __get_parameter_data(whitewater.foam_layer_offset, frameno) __set_property(fluidsim, 'diffuse_foam_layer_depth', foam_depth) __set_property(fluidsim, 'diffuse_foam_layer_offset', foam_offset) __set_property(fluidsim, 'diffuse_foam_advection_strength', strength) preserve_foam = __get_parameter_data(whitewater.preserve_foam, frameno) preserve_rate = __get_parameter_data(whitewater.foam_preservation_rate, frameno) min_density, max_density = __get_parameter_data(whitewater.min_max_foam_density, frameno) __set_property(fluidsim, 'enable_diffuse_preserve_foam', preserve_foam) __set_property(fluidsim, 'diffuse_foam_preservation_rate', preserve_rate) __set_property(fluidsim, 'min_diffuse_foam_density', min_density) __set_property(fluidsim, 'max_diffuse_foam_density', max_density) drag = __get_parameter_data(whitewater.bubble_drag_coefficient, frameno) bouyancy = __get_parameter_data(whitewater.bubble_bouyancy_coefficient, frameno) __set_property(fluidsim, 'diffuse_bubble_drag_coefficient', drag) __set_property(fluidsim, 'diffuse_bubble_bouyancy_coefficient', bouyancy) drag = __get_parameter_data(whitewater.dust_drag_coefficient, frameno) bouyancy = __get_parameter_data(whitewater.dust_bouyancy_coefficient, frameno) __set_property(fluidsim, 'diffuse_dust_drag_coefficient', drag) __set_property(fluidsim, 'diffuse_dust_bouyancy_coefficient', bouyancy) drag = __get_parameter_data(whitewater.spray_drag_coefficient, frameno) __set_property(fluidsim, 'diffuse_spray_drag_coefficient', drag) base_level = __get_parameter_data(whitewater.obstacle_influence_base_level, frameno) __set_property(fluidsim, 'diffuse_obstacle_influence_base_level', base_level) decay_rate = __get_parameter_data(whitewater.obstacle_influence_decay_rate, frameno) __set_property(fluidsim, 'diffuse_obstacle_influence_decay_rate', decay_rate) # World Settings world = dprops.world gravity = __get_parameter_data(world.gravity, frameno) if __get_parameter_data(world.gravity_type, frameno) == 'GRAVITY_TYPE_SCENE': use_gravity = bool(__get_parameter_data(world.scene_use_gravity, frameno)) if not use_gravity: gravity = [0.0, 0.0, 0.0] __set_body_force_property(fluidsim, gravity) weight_fluid_particles = __get_parameter_data(world.force_field_weight_fluid_particles, frameno) weight_whitewater_foam = __get_parameter_data(world.force_field_weight_whitewater_foam, frameno) weight_whitewater_bubble = __get_parameter_data(world.force_field_weight_whitewater_bubble, frameno) weight_whitewater_spray = __get_parameter_data(world.force_field_weight_whitewater_spray, frameno) weight_whitewater_dust = __get_parameter_data(world.force_field_weight_whitewater_dust, frameno) __set_property(fluidsim, 'force_field_weight_fluid_particles', weight_fluid_particles) __set_property(fluidsim, 'force_field_weight_whitewater_foam', weight_whitewater_foam) __set_property(fluidsim, 'force_field_weight_whitewater_bubble', weight_whitewater_bubble) __set_property(fluidsim, 'force_field_weight_whitewater_spray', weight_whitewater_spray) __set_property(fluidsim, 'force_field_weight_whitewater_dust', weight_whitewater_dust) is_viscosity_enabled = __get_parameter_data(world.enable_viscosity, frameno) if is_viscosity_enabled: surface = dprops.surface is_variable_viscosity_enabled = __get_parameter_data(surface.enable_viscosity_attribute, frameno) if is_variable_viscosity_enabled: __set_property(fluidsim, 'viscosity', 0.0) else: constant_viscosity = __get_viscosity_value(world, frameno) __set_property(fluidsim, 'viscosity', constant_viscosity) tolerance_int = __get_parameter_data(world.viscosity_solver_error_tolerance, frameno) error_tolerance = 1.0 * 10.0**(-tolerance_int) __set_property(fluidsim, 'viscosity_solver_error_tolerance', error_tolerance) elif fluidsim.viscosity > 0.0: __set_property(fluidsim, 'viscosity', 0.0) is_surface_tension_enabled = __get_parameter_data(world.enable_surface_tension, frameno) if is_surface_tension_enabled: surface_tension = __get_surface_tension_value(world, frameno) __set_property(fluidsim, 'surface_tension', surface_tension) mincfl, maxcfl = world.minimum_surface_tension_cfl, world.maximum_surface_tension_cfl accuracy_pct = __get_parameter_data(world.surface_tension_accuracy, frameno) / 100.0 surface_tension_number = mincfl + (1.0 - accuracy_pct) * (maxcfl - mincfl) __set_property(fluidsim, 'surface_tension_condition_number', surface_tension_number) elif fluidsim.surface_tension > 0.0: __set_property(fluidsim, 'surface_tension', 0.0) is_sheet_seeding_enabled = __get_parameter_data(world.enable_sheet_seeding, frameno) __set_property(fluidsim, 'enable_sheet_seeding', is_sheet_seeding_enabled) if is_sheet_seeding_enabled: sheet_fill_rate = __get_parameter_data(world.sheet_fill_rate, frameno) threshold = __get_parameter_data(world.sheet_fill_threshold, frameno) __set_property(fluidsim, 'sheet_fill_rate', sheet_fill_rate, value_min=0, value_max=1.0) __set_property(fluidsim, 'sheet_fill_threshold', threshold - 1, value_min=-1.0, value_max=0.0) friction = __get_parameter_data(world.boundary_friction, frameno) __set_property(fluidsim, 'boundary_friction', friction, value_min=0.0, value_max=1.0) # Fluid Particle Settings particles = dprops.particles output_amount = __get_parameter_data(particles.fluid_particle_output_amount, frameno) __set_property(fluidsim, 'fluid_particle_output_amount', output_amount, value_min=0.0, value_max=1.0) enable_fluid_particle_surface_output = __get_parameter_data(particles.enable_fluid_particle_surface_output, frameno) __set_property(fluidsim, 'enable_fluid_particle_surface_output', enable_fluid_particle_surface_output) enable_fluid_particle_boundary_output = __get_parameter_data(particles.enable_fluid_particle_boundary_output, frameno) __set_property(fluidsim, 'enable_fluid_particle_boundary_output', enable_fluid_particle_boundary_output) enable_fluid_particle_interior_output = __get_parameter_data(particles.enable_fluid_particle_interior_output, frameno) __set_property(fluidsim, 'enable_fluid_particle_interior_output', enable_fluid_particle_interior_output) source_id = __get_parameter_data(particles.fluid_particle_source_id_blacklist, frameno) __set_property(fluidsim, 'fluid_particle_source_id_blacklist', source_id) # Surface Settings surface = dprops.surface enable_surface_mesh_generation = __get_parameter_data(surface.enable_surface_mesh_generation, frameno) __set_property(fluidsim, 'enable_surface_reconstruction', enable_surface_mesh_generation) subdivisions = __get_parameter_data(surface.subdivisions, frameno) + 1 __set_property(fluidsim, 'surface_subdivision_level', subdivisions) compute_chunk_mode = __get_parameter_data(surface.compute_chunk_mode, frameno) if compute_chunk_mode == 'COMPUTE_CHUNK_MODE_AUTO': num_chunks = __get_parameter_data(surface.compute_chunks_auto, frameno) elif compute_chunk_mode == 'COMPUTE_CHUNK_MODE_FIXED': num_chunks = __get_parameter_data(surface.compute_chunks_fixed, frameno) __set_property(fluidsim, 'num_polygonizer_slices', num_chunks) particle_scale = __get_parameter_data(surface.particle_scale, frameno) particle_scale *= surface.native_particle_scale __set_property(fluidsim, 'marker_particle_scale', particle_scale) smoothing_value = __get_parameter_data(surface.smoothing_value, frameno) smoothing_iterations = __get_parameter_data(surface.smoothing_iterations, frameno) __set_property(fluidsim, 'surface_smoothing_value', smoothing_value) __set_property(fluidsim, 'surface_smoothing_iterations', smoothing_iterations) enable_meshing_offset = __get_parameter_data(surface.enable_meshing_offset, frameno) __set_property(fluidsim, 'enable_obstacle_meshing_offset', enable_meshing_offset) meshing_mode = __get_parameter_data(surface.obstacle_meshing_mode, frameno) meshing_offset = __get_obstacle_meshing_offset(meshing_mode) __set_property(fluidsim, 'obstacle_meshing_offset', meshing_offset) remove_near_domain = __get_parameter_data(surface.remove_mesh_near_domain, frameno) near_domain_distance = __get_parameter_data(surface.remove_mesh_near_domain_distance, frameno) - 1 __set_property(fluidsim, 'enable_remove_surface_near_domain', remove_near_domain) __set_property(fluidsim, 'remove_surface_near_domain_distance', near_domain_distance) domain_sides = __get_parameter_data(surface.remove_mesh_near_domain_sides, frameno) __set_property(fluidsim, 'remove_surface_near_domain_sides', domain_sides) invert_contact = __get_parameter_data(surface.invert_contact_normals, frameno) __set_property(fluidsim, 'enable_inverted_contact_normals', invert_contact) motion_blur = __get_parameter_data(surface.generate_motion_blur_data, frameno) __set_property(fluidsim, 'enable_surface_motion_blur', motion_blur) age_radius = __get_parameter_data(surface.age_attribute_radius, frameno) __set_property(fluidsim, 'surface_age_attribute_radius', age_radius) lifetime_radius = __get_parameter_data(surface.lifetime_attribute_radius, frameno) __set_property(fluidsim, 'surface_lifetime_attribute_radius', lifetime_radius) base_death_time = __get_parameter_data(surface.lifetime_attribute_death_time, frameno) __set_property(fluidsim, 'surface_lifetime_attribute_death_time', base_death_time) whitewater_proximity_radius = __get_parameter_data(surface.whitewater_proximity_attribute_radius, frameno) __set_property(fluidsim, 'surface_whitewater_proximity_attribute_radius', whitewater_proximity_radius) color_radius = __get_parameter_data(surface.color_attribute_radius, frameno) __set_property(fluidsim, 'surface_color_attribute_radius', color_radius) enable_mixing = __get_parameter_data(surface.enable_color_attribute_mixing, frameno) __set_property(fluidsim, 'enable_surface_color_attribute_mixing', enable_mixing) mixing_rate = __get_parameter_data(surface.color_attribute_mixing_rate, frameno) __set_property(fluidsim, 'surface_color_attribute_mixing_rate', mixing_rate) mixing_radius = __get_parameter_data(surface.color_attribute_mixing_radius, frameno) __set_property(fluidsim, 'surface_color_attribute_mixing_radius', mixing_radius) # Advanced Settings advanced = dprops.advanced min_substeps, max_substeps = __get_parameter_data(advanced.min_max_time_steps_per_frame, frameno) __set_property(fluidsim, 'min_time_steps_per_frame', min_substeps) __set_property(fluidsim, 'max_time_steps_per_frame', max_substeps) enable_obstacle_time_stepping = \ __get_parameter_data(advanced.enable_adaptive_obstacle_time_stepping, frameno) __set_property(fluidsim, 'enable_adaptive_obstacle_time_stepping', enable_obstacle_time_stepping) enable_force_field_time_stepping = \ __get_parameter_data(advanced.enable_adaptive_force_field_time_stepping, frameno) __set_property(fluidsim, 'enable_adaptive_force_field_time_stepping', enable_force_field_time_stepping) jitter_factor = __get_parameter_data(advanced.particle_jitter_factor, frameno) __set_property(fluidsim, 'marker_particle_jitter_factor', jitter_factor) jitter_surface = __get_parameter_data(advanced.jitter_surface_particles, frameno) __set_property(fluidsim, 'jitter_surface_marker_particles', jitter_surface) pressure_solver_iterations = __get_parameter_data(advanced.pressure_solver_max_iterations, frameno) __set_property(fluidsim, 'pressure_solver_max_iterations', pressure_solver_iterations) viscosity_solver_iterations = __get_parameter_data(advanced.viscosity_solver_max_iterations, frameno) __set_property(fluidsim, 'viscosity_solver_max_iterations', viscosity_solver_iterations) PICFLIP_ratio = __get_parameter_data(advanced.PICFLIP_ratio, frameno) __set_property(fluidsim, 'PICFLIP_ratio', PICFLIP_ratio) PICAPIC_ratio = __get_parameter_data(advanced.PICAPIC_ratio, frameno) __set_property(fluidsim, 'PICAPIC_ratio', PICAPIC_ratio) CFL_number = __get_parameter_data(advanced.CFL_condition_number, frameno) __set_property(fluidsim, 'CFL_condition_number', CFL_number) enable_velocity_removal = __get_parameter_data(advanced.enable_extreme_velocity_removal, frameno) __set_property(fluidsim, 'enable_extreme_velocity_removal', enable_velocity_removal) threading_mode = __get_parameter_data(advanced.threading_mode, frameno) if threading_mode == 'THREADING_MODE_AUTO_DETECT': num_threads = __get_parameter_data(advanced.num_threads_auto_detect, frameno) elif threading_mode == 'THREADING_MODE_FIXED': num_threads = __get_parameter_data(advanced.num_threads_fixed, frameno) __set_property(fluidsim, 'max_thread_count', num_threads) enable_async_meshing = __get_parameter_data(advanced.enable_asynchronous_meshing, frameno) __set_property(fluidsim, 'enable_asynchronous_meshing', enable_async_meshing) enable_fracture_optimization = __get_parameter_data(advanced.enable_fracture_optimization, frameno) __set_property(fluidsim, 'enable_fracture_optimization', enable_fracture_optimization) precomp_static_sdf = __get_parameter_data(advanced.precompute_static_obstacles, frameno) __set_property(fluidsim, 'enable_static_solid_levelset_precomputation', precomp_static_sdf) reserve_temp_grids = __get_parameter_data(advanced.reserve_temporary_grids, frameno) __set_property(fluidsim, 'enable_temporary_mesh_levelset', reserve_temp_grids) # Debug Settings debug = dprops.debug export_internal_obstacle_mesh = __get_parameter_data(debug.export_internal_obstacle_mesh, frameno) __set_property(fluidsim, 'enable_internal_obstacle_mesh_output', export_internal_obstacle_mesh) # Caches created in older versions may not contain force field data. Ignore these features # if force field data cannot be found in the cache is_force_field_data_available = data.force_field_data is not None if is_force_field_data_available: export_force_field = __get_parameter_data(debug.export_force_field, frameno) __set_property(fluidsim, 'enable_force_field_debug_output', export_force_field) def __update_animatable_properties(fluidsim, data, frameno): geometry_database = __get_geometry_database() simulation_data = __get_simulation_data() timeline_frame = __get_timeline_frame() geometry_data = geometry_database.get_mesh_geometry_data_dict_for_frame(simulation_data, timeline_frame) __update_animatable_inflow_properties(data, geometry_data, frameno) __update_animatable_outflow_properties(data, geometry_data, frameno) __update_animatable_force_field_properties(data, frameno) __update_animatable_obstacle_properties(data, geometry_data, frameno) __update_animatable_meshing_volume_properties(data, frameno) __update_animatable_domain_properties(fluidsim, data, frameno) def __frame_number_to_string(frameno): return str(frameno).zfill(6) def __write_bounds_data(cache_directory, fluidsim, frameno): offset = fluidsim.get_domain_offset() scale = fluidsim.get_domain_scale() dims = fluidsim.get_simulation_dimensions() grid_dims = fluidsim.get_grid_dimensions() dx = fluidsim.get_cell_size() bounds = { "x": offset.x, "y": offset.y, "z": offset.z, "width": dims.x * scale, "height": dims.y * scale, "depth": dims.z * scale, "dx": dx * scale, "isize": grid_dims.i, "jsize": grid_dims.j, "ksize": grid_dims.k } fstring = __frame_number_to_string(frameno) bounds_filename = "bounds" + fstring + ".bbox" bounds_filepath = os.path.join(cache_directory, "bakefiles", bounds_filename) bounds_json = json.dumps(bounds) with open(bounds_filepath, 'w', encoding='utf-8') as f: f.write(bounds_json) def __write_surface_data(cache_directory, fluidsim, frameno): fstring = __frame_number_to_string(frameno) surface_filename = fstring + ".bobj" surface_filepath = os.path.join(cache_directory, "bakefiles", surface_filename) filedata = fluidsim.get_surface_data() with open(surface_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_motion_blur: blur_filename = "blur" + fstring + ".bobj" blur_filepath = os.path.join(cache_directory, "bakefiles", blur_filename) filedata = fluidsim.get_surface_blur_data() with open(blur_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_velocity_attribute: velocity_filename = "velocity" + fstring + ".bobj" velocity_filepath = os.path.join(cache_directory, "bakefiles", velocity_filename) filedata = fluidsim.get_surface_velocity_attribute_data() with open(velocity_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_vorticity_attribute: vorticity_filename = "vorticity" + fstring + ".bobj" vorticity_filepath = os.path.join(cache_directory, "bakefiles", vorticity_filename) filedata = fluidsim.get_surface_vorticity_attribute_data() with open(vorticity_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_speed_attribute: speed_filename = "speed" + fstring + ".data" speed_filepath = os.path.join(cache_directory, "bakefiles", speed_filename) filedata = fluidsim.get_surface_speed_attribute_data() with open(speed_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_age_attribute: age_filename = "age" + fstring + ".data" age_filepath = os.path.join(cache_directory, "bakefiles", age_filename) filedata = fluidsim.get_surface_age_attribute_data() with open(age_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_lifetime_attribute: lifetime_filename = "lifetime" + fstring + ".data" lifetime_filepath = os.path.join(cache_directory, "bakefiles", lifetime_filename) filedata = fluidsim.get_surface_lifetime_attribute_data() with open(lifetime_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_whitewater_proximity_attribute: whitewater_proximity_filename = "whitewaterproximity" + fstring + ".bobj" whitewater_proximity_filepath = os.path.join(cache_directory, "bakefiles", whitewater_proximity_filename) filedata = fluidsim.get_surface_whitewater_proximity_attribute_data() with open(whitewater_proximity_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_color_attribute: color_filename = "color" + fstring + ".bobj" color_filepath = os.path.join(cache_directory, "bakefiles", color_filename) filedata = fluidsim.get_surface_color_attribute_data() with open(color_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_source_id_attribute: source_id_filename = "sourceid" + fstring + ".data" source_id_filepath = os.path.join(cache_directory, "bakefiles", source_id_filename) filedata = fluidsim.get_surface_source_id_attribute_data() with open(source_id_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_viscosity_attribute: viscosity_filename = "viscosity" + fstring + ".data" viscosity_filepath = os.path.join(cache_directory, "bakefiles", viscosity_filename) filedata = fluidsim.get_surface_viscosity_attribute_data() with open(viscosity_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_density_attribute: density_filename = "density" + fstring + ".data" density_filepath = os.path.join(cache_directory, "bakefiles", density_filename) filedata = fluidsim.get_surface_density_attribute_data() with open(density_filepath, 'wb') as f: f.write(filedata) preview_filename = "preview" + fstring + ".bobj" preview_filepath = os.path.join(cache_directory, "bakefiles", preview_filename) filedata = fluidsim.get_surface_preview_data() with open(preview_filepath, 'wb') as f: f.write(filedata) def __write_whitewater_data(cache_directory, fluidsim, frameno): fstring = __frame_number_to_string(frameno) foam_filename = "foam" + fstring + ".wwp" foam_filepath = os.path.join(cache_directory, "bakefiles", foam_filename) filedata = fluidsim.get_diffuse_foam_data() with open(foam_filepath, 'wb') as f: f.write(filedata) bubble_filename = "bubble" + fstring + ".wwp" bubble_filepath = os.path.join(cache_directory, "bakefiles", bubble_filename) filedata = fluidsim.get_diffuse_bubble_data() with open(bubble_filepath, 'wb') as f: f.write(filedata) spray_filename = "spray" + fstring + ".wwp" spray_filepath = os.path.join(cache_directory, "bakefiles", spray_filename) filedata = fluidsim.get_diffuse_spray_data() with open(spray_filepath, 'wb') as f: f.write(filedata) dust_filename = "dust" + fstring + ".wwp" dust_filepath = os.path.join(cache_directory, "bakefiles", dust_filename) filedata = fluidsim.get_diffuse_dust_data() with open(dust_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_whitewater_motion_blur: foam_blur_filename = "blurfoam" + fstring + ".wwp" foam_blur_filepath = os.path.join(cache_directory, "bakefiles", foam_blur_filename) filedata = fluidsim.get_diffuse_foam_blur_data() with open(foam_blur_filepath, 'wb') as f: f.write(filedata) bubble_blur_filename = "blurbubble" + fstring + ".wwp" bubble_blur_filepath = os.path.join(cache_directory, "bakefiles", bubble_blur_filename) filedata = fluidsim.get_diffuse_bubble_blur_data() with open(bubble_blur_filepath, 'wb') as f: f.write(filedata) spray_blur_filename = "blurspray" + fstring + ".wwp" spray_blur_filepath = os.path.join(cache_directory, "bakefiles", spray_blur_filename) filedata = fluidsim.get_diffuse_spray_blur_data() with open(spray_blur_filepath, 'wb') as f: f.write(filedata) dust_blur_filename = "blurdust" + fstring + ".wwp" dust_blur_filepath = os.path.join(cache_directory, "bakefiles", dust_blur_filename) filedata = fluidsim.get_diffuse_dust_blur_data() with open(dust_blur_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_whitewater_velocity_attribute: foam_velocity_filename = "velocityfoam" + fstring + ".wwp" foam_velocity_filepath = os.path.join(cache_directory, "bakefiles", foam_velocity_filename) filedata = fluidsim.get_whitewater_foam_velocity_attribute_data() with open(foam_velocity_filepath, 'wb') as f: f.write(filedata) bubble_velocity_filename = "velocitybubble" + fstring + ".wwp" bubble_velocity_filepath = os.path.join(cache_directory, "bakefiles", bubble_velocity_filename) filedata = fluidsim.get_whitewater_bubble_velocity_attribute_data() with open(bubble_velocity_filepath, 'wb') as f: f.write(filedata) spray_velocity_filename = "velocityspray" + fstring + ".wwp" spray_velocity_filepath = os.path.join(cache_directory, "bakefiles", spray_velocity_filename) filedata = fluidsim.get_whitewater_spray_velocity_attribute_data() with open(spray_velocity_filepath, 'wb') as f: f.write(filedata) dust_velocity_filename = "velocitydust" + fstring + ".wwp" dust_velocity_filepath = os.path.join(cache_directory, "bakefiles", dust_velocity_filename) filedata = fluidsim.get_whitewater_dust_velocity_attribute_data() with open(dust_velocity_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_whitewater_id_attribute: foam_id_filename = "idfoam" + fstring + ".wwi" foam_id_filepath = os.path.join(cache_directory, "bakefiles", foam_id_filename) filedata = fluidsim.get_whitewater_foam_id_attribute_data() with open(foam_id_filepath, 'wb') as f: f.write(filedata) bubble_id_filename = "idbubble" + fstring + ".wwi" bubble_id_filepath = os.path.join(cache_directory, "bakefiles", bubble_id_filename) filedata = fluidsim.get_whitewater_bubble_id_attribute_data() with open(bubble_id_filepath, 'wb') as f: f.write(filedata) spray_id_filename = "idspray" + fstring + ".wwi" spray_id_filepath = os.path.join(cache_directory, "bakefiles", spray_id_filename) filedata = fluidsim.get_whitewater_spray_id_attribute_data() with open(spray_id_filepath, 'wb') as f: f.write(filedata) dust_id_filename = "iddust" + fstring + ".wwi" dust_id_filepath = os.path.join(cache_directory, "bakefiles", dust_id_filename) filedata = fluidsim.get_whitewater_dust_id_attribute_data() with open(dust_id_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_whitewater_lifetime_attribute: foam_lifetime_filename = "lifetimefoam" + fstring + ".wwf" foam_lifetime_filepath = os.path.join(cache_directory, "bakefiles", foam_lifetime_filename) filedata = fluidsim.get_whitewater_foam_lifetime_attribute_data() with open(foam_lifetime_filepath, 'wb') as f: f.write(filedata) bubble_lifetime_filename = "lifetimebubble" + fstring + ".wwf" bubble_lifetime_filepath = os.path.join(cache_directory, "bakefiles", bubble_lifetime_filename) filedata = fluidsim.get_whitewater_bubble_lifetime_attribute_data() with open(bubble_lifetime_filepath, 'wb') as f: f.write(filedata) spray_lifetime_filename = "lifetimespray" + fstring + ".wwf" spray_lifetime_filepath = os.path.join(cache_directory, "bakefiles", spray_lifetime_filename) filedata = fluidsim.get_whitewater_spray_lifetime_attribute_data() with open(spray_lifetime_filepath, 'wb') as f: f.write(filedata) dust_lifetime_filename = "lifetimedust" + fstring + ".wwf" dust_lifetime_filepath = os.path.join(cache_directory, "bakefiles", dust_lifetime_filename) filedata = fluidsim.get_whitewater_dust_lifetime_attribute_data() with open(dust_lifetime_filepath, 'wb') as f: f.write(filedata) def __write_fluid_particle_data(cache_directory, fluidsim, frameno): fstring = __frame_number_to_string(frameno) particle_filename = "fluidparticles" + fstring + ".ffp3" particle_filepath = os.path.join(cache_directory, "bakefiles", particle_filename) filedata = fluidsim.get_fluid_particle_data() with open(particle_filepath, 'wb') as f: f.write(filedata) particle_id_filename = "fluidparticlesid" + fstring + ".ffp3" particle_id_filepath = os.path.join(cache_directory, "bakefiles", particle_id_filename) filedata = fluidsim.get_fluid_particle_id_attribute_data() with open(particle_id_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_fluid_particle_velocity_attribute: particle_velocity_filename = "fluidparticlesvelocity" + fstring + ".ffp3" particle_velocity_filepath = os.path.join(cache_directory, "bakefiles", particle_velocity_filename) filedata = fluidsim.get_fluid_particle_velocity_attribute_data() with open(particle_velocity_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_fluid_particle_speed_attribute: particle_speed_filename = "fluidparticlesspeed" + fstring + ".ffp3" particle_speed_filepath = os.path.join(cache_directory, "bakefiles", particle_speed_filename) filedata = fluidsim.get_fluid_particle_speed_attribute_data() with open(particle_speed_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_fluid_particle_vorticity_attribute: particle_vorticity_filename = "fluidparticlesvorticity" + fstring + ".ffp3" particle_vorticity_filepath = os.path.join(cache_directory, "bakefiles", particle_vorticity_filename) filedata = fluidsim.get_fluid_particle_vorticity_attribute_data() with open(particle_vorticity_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_fluid_particle_color_attribute: particle_color_filename = "fluidparticlescolor" + fstring + ".ffp3" particle_color_filepath = os.path.join(cache_directory, "bakefiles", particle_color_filename) filedata = fluidsim.get_fluid_particle_color_attribute_data() with open(particle_color_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_fluid_particle_age_attribute: particle_age_filename = "fluidparticlesage" + fstring + ".ffp3" particle_age_filepath = os.path.join(cache_directory, "bakefiles", particle_age_filename) filedata = fluidsim.get_fluid_particle_age_attribute_data() with open(particle_age_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_fluid_particle_lifetime_attribute: particle_lifetime_filename = "fluidparticleslifetime" + fstring + ".ffp3" particle_lifetime_filepath = os.path.join(cache_directory, "bakefiles", particle_lifetime_filename) filedata = fluidsim.get_fluid_particle_lifetime_attribute_data() with open(particle_lifetime_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_surface_viscosity_attribute: # Fluid particle viscosity attribute matches surface viscosity attribute particle_viscosity_filename = "fluidparticlesviscosity" + fstring + ".ffp3" particle_viscosity_filepath = os.path.join(cache_directory, "bakefiles", particle_viscosity_filename) filedata = fluidsim.get_fluid_particle_viscosity_attribute_data() with open(particle_viscosity_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_fluid_particle_density_attribute: particle_density_filename = "fluidparticlesdensity" + fstring + ".ffp3" particle_density_filepath = os.path.join(cache_directory, "bakefiles", particle_density_filename) filedata = fluidsim.get_fluid_particle_density_attribute_data() with open(particle_density_filepath, 'wb') as f: f.write(filedata) # flip_density_average attribute needs some more work """ particle_density_average_filename = "fluidparticlesdensityaverage" + fstring + ".ffp3" particle_density_average_filepath = os.path.join(cache_directory, "bakefiles", particle_density_average_filename) filedata = fluidsim.get_fluid_particle_density_average_attribute_data() with open(particle_density_average_filepath, 'wb') as f: f.write(filedata) """ if fluidsim.enable_fluid_particle_whitewater_proximity_attribute: whitewater_proximity_filename = "fluidparticleswhitewaterproximity" + fstring + ".ffp3" whitewater_proximity_filepath = os.path.join(cache_directory, "bakefiles", whitewater_proximity_filename) filedata = fluidsim.get_fluid_particle_whitewater_proximity_attribute_data() with open(whitewater_proximity_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_fluid_particle_source_id_attribute: source_id_filename = "fluidparticlessourceid" + fstring + ".ffp3" source_id_filepath = os.path.join(cache_directory, "bakefiles", source_id_filename) filedata = fluidsim.get_fluid_particle_source_id_attribute_data() with open(source_id_filepath, 'wb') as f: f.write(filedata) if fluidsim.enable_fluid_particle_uid_attribute: uid_filename = "fluidparticlesuid" + fstring + ".ffp3" uid_filepath = os.path.join(cache_directory, "bakefiles", uid_filename) filedata = fluidsim.get_fluid_particle_uid_attribute_data() with open(uid_filepath, 'wb') as f: f.write(filedata) uid_max_filename = "fluidparticlesuidmax" + fstring + ".txt" uid_max_filepath = os.path.join(cache_directory, "bakefiles", uid_max_filename) max_uid_value = fluidsim.get_current_fluid_particle_uid() - 1 with open(uid_max_filepath, 'w') as f: f.write(str(max_uid_value)) def __write_fluid_particle_debug_data(cache_directory, fluidsim, frameno): fstring = __frame_number_to_string(frameno) particle_filename = "particles" + fstring + ".fpd" particle_filepath = os.path.join(cache_directory, "bakefiles", particle_filename) filedata = fluidsim.get_fluid_particle_debug_data() with open(particle_filepath, 'wb') as f: f.write(filedata) def __write_internal_obstacle_mesh_data(cache_directory, fluidsim, frameno): fstring = __frame_number_to_string(frameno) obstacle_filename = "obstacle" + fstring + ".bobj" obstacle_filepath = os.path.join(cache_directory, "bakefiles", obstacle_filename) filedata = fluidsim.get_internal_obstacle_mesh_data() with open(obstacle_filepath, 'wb') as f: f.write(filedata) def __write_force_field_debug_data(cache_directory, fluidsim, frameno): fstring = __frame_number_to_string(frameno) force_field_filename = "forcefield" + fstring + ".ffd" force_field_filepath = os.path.join(cache_directory, "bakefiles", force_field_filename) filedata = fluidsim.get_force_field_debug_data() with open(force_field_filepath, 'wb') as f: f.write(filedata) def __write_logfile_data(cache_directory, logfile_name, fluidsim): filedata = fluidsim.get_logfile_data() logpath = os.path.join(cache_directory, "logs", logfile_name) with open(logpath, 'a', encoding='utf-8') as f: f.write(filedata) def __get_mesh_stats_dict(mstats): stats = {} stats["enabled"] = bool(mstats.enabled) stats["vertices"] = mstats.vertices stats["triangles"] = mstats.triangles stats["bytes"] = mstats.bytes return stats def __get_timing_stats_dict(tstats): stats = {} stats["total"] = tstats.total stats["mesh"] = tstats.mesh stats["advection"] = tstats.advection stats["particles"] = tstats.particles stats["pressure"] = tstats.pressure stats["diffuse"] = tstats.diffuse stats["viscosity"] = tstats.viscosity stats["objects"] = tstats.objects return stats def __get_frame_stats_dict(cstats): stats = {} stats["frame"] = cstats.frame stats["substeps"] = cstats.substeps stats["delta_time"] = cstats.delta_time stats["fluid_particles"] = cstats.fluid_particles stats["diffuse_particles"] = cstats.diffuse_particles stats["substeps"] = cstats.substeps stats["performance_score"] = cstats.performance_score stats["pressure_solver_enabled"] = cstats.pressure_solver_enabled stats["pressure_solver_success"] = cstats.pressure_solver_success stats["pressure_solver_error"] = cstats.pressure_solver_error stats["pressure_solver_iterations"] = cstats.pressure_solver_iterations stats["pressure_solver_max_iterations"] = cstats.pressure_solver_max_iterations stats["viscosity_solver_enabled"] = cstats.viscosity_solver_enabled stats["viscosity_solver_success"] = cstats.viscosity_solver_success stats["viscosity_solver_error"] = cstats.viscosity_solver_error stats["viscosity_solver_iterations"] = cstats.viscosity_solver_iterations stats["viscosity_solver_max_iterations"] = cstats.viscosity_solver_max_iterations stats["surface"] = __get_mesh_stats_dict(cstats.surface) stats["preview"] = __get_mesh_stats_dict(cstats.preview) stats["surfaceblur"] = __get_mesh_stats_dict(cstats.surfaceblur) stats["surfacevelocity"] = __get_mesh_stats_dict(cstats.surfacevelocity) stats["surfacespeed"] = __get_mesh_stats_dict(cstats.surfacespeed) stats["surfacevorticity"] = __get_mesh_stats_dict(cstats.surfacevorticity) stats["surfaceage"] = __get_mesh_stats_dict(cstats.surfaceage) stats["surfacelifetime"] = __get_mesh_stats_dict(cstats.surfacelifetime) stats["surfacewhitewaterproximity"] = __get_mesh_stats_dict(cstats.surfacewhitewaterproximity) stats["surfacecolor"] = __get_mesh_stats_dict(cstats.surfacecolor) stats["surfacesourceid"] = __get_mesh_stats_dict(cstats.surfacesourceid) stats["surfaceviscosity"] = __get_mesh_stats_dict(cstats.surfaceviscosity) stats["surfacedensity"] = __get_mesh_stats_dict(cstats.surfacedensity) stats["foam"] = __get_mesh_stats_dict(cstats.foam) stats["bubble"] = __get_mesh_stats_dict(cstats.bubble) stats["spray"] = __get_mesh_stats_dict(cstats.spray) stats["dust"] = __get_mesh_stats_dict(cstats.dust) stats["foamblur"] = __get_mesh_stats_dict(cstats.foamblur) stats["bubbleblur"] = __get_mesh_stats_dict(cstats.bubbleblur) stats["sprayblur"] = __get_mesh_stats_dict(cstats.sprayblur) stats["dustblur"] = __get_mesh_stats_dict(cstats.dustblur) stats["foamvelocity"] = __get_mesh_stats_dict(cstats.foamvelocity) stats["bubblevelocity"] = __get_mesh_stats_dict(cstats.bubblevelocity) stats["sprayvelocity"] = __get_mesh_stats_dict(cstats.sprayvelocity) stats["dustvelocity"] = __get_mesh_stats_dict(cstats.dustvelocity) stats["foamid"] = __get_mesh_stats_dict(cstats.foamid) stats["bubbleid"] = __get_mesh_stats_dict(cstats.bubbleid) stats["sprayid"] = __get_mesh_stats_dict(cstats.sprayid) stats["dustid"] = __get_mesh_stats_dict(cstats.dustid) stats["foamlifetime"] = __get_mesh_stats_dict(cstats.foamlifetime) stats["bubblelifetime"] = __get_mesh_stats_dict(cstats.bubblelifetime) stats["spraylifetime"] = __get_mesh_stats_dict(cstats.spraylifetime) stats["dustlifetime"] = __get_mesh_stats_dict(cstats.dustlifetime) stats["fluidparticles"] = __get_mesh_stats_dict(cstats.fluidparticles) stats["fluidparticlesid"] = __get_mesh_stats_dict(cstats.fluidparticlesid) stats["fluidparticlesuid"] = __get_mesh_stats_dict(cstats.fluidparticlesuid) stats["fluidparticlesvelocity"] = __get_mesh_stats_dict(cstats.fluidparticlesvelocity) stats["fluidparticlesspeed"] = __get_mesh_stats_dict(cstats.fluidparticlesspeed) stats["fluidparticlesvorticity"] = __get_mesh_stats_dict(cstats.fluidparticlesvorticity) stats["fluidparticlescolor"] = __get_mesh_stats_dict(cstats.fluidparticlescolor) stats["fluidparticlesage"] = __get_mesh_stats_dict(cstats.fluidparticlesage) stats["fluidparticleslifetime"] = __get_mesh_stats_dict(cstats.fluidparticleslifetime) stats["fluidparticlesviscosity"] = __get_mesh_stats_dict(cstats.fluidparticlesviscosity) stats["fluidparticlesdensity"] = __get_mesh_stats_dict(cstats.fluidparticlesdensity) # flip_density_average attribute needs some more work #stats["fluidparticlesdensityaverage"] = __get_mesh_stats_dict(cstats.fluidparticlesdensityaverage) stats["fluidparticleswhitewaterproximity"] = __get_mesh_stats_dict(cstats.fluidparticleswhitewaterproximity) stats["fluidparticlessourceid"] = __get_mesh_stats_dict(cstats.fluidparticlessourceid) stats["particles"] = __get_mesh_stats_dict(cstats.particles) stats["obstacle"] = __get_mesh_stats_dict(cstats.obstacle) stats["timing"] = __get_timing_stats_dict(cstats.timing) return stats def __write_frame_stats_data(cache_directory, fluidsim, frameno): fstring = __frame_number_to_string(frameno) filename = "framestats" + fstring + ".data" tempdir = os.path.join(cache_directory, "temp") statspath = os.path.join(tempdir, filename) if not os.path.exists(tempdir): os.makedirs(tempdir) cstats = fluidsim.get_frame_stats_data() stats = __get_frame_stats_dict(cstats) filedata = json.dumps(stats, sort_keys=True, indent=4) with open(statspath, 'w', encoding='utf-8') as f: f.write(filedata) def __write_autosave_data(domain_data, cache_directory, fluidsim, frameno): autosave_dir = os.path.join(cache_directory, "savestates", "autosave") if not os.path.exists(autosave_dir): os.makedirs(autosave_dir) position_data_path = os.path.join(autosave_dir, "marker_particle_position.data") velocity_data_path = os.path.join(autosave_dir, "marker_particle_velocity.data") affinex_data_path = os.path.join(autosave_dir, "marker_particle_affinex.data") affiney_data_path = os.path.join(autosave_dir, "marker_particle_affiney.data") affinez_data_path = os.path.join(autosave_dir, "marker_particle_affinez.data") age_data_path = os.path.join(autosave_dir, "marker_particle_age.data") lifetime_data_path = os.path.join(autosave_dir, "marker_particle_lifetime.data") color_data_path = os.path.join(autosave_dir, "marker_particle_color.data") source_id_data_path = os.path.join(autosave_dir, "marker_particle_source_id.data") uid_data_path = os.path.join(autosave_dir, "marker_particle_uid.data") viscosity_data_path = os.path.join(autosave_dir, "marker_particle_viscosity.data") density_data_path = os.path.join(autosave_dir, "marker_particle_density.data") id_data_path = os.path.join(autosave_dir, "marker_particle_id.data") diffuse_position_data_path = os.path.join(autosave_dir, "diffuse_particle_position.data") diffuse_velocity_data_path = os.path.join(autosave_dir, "diffuse_particle_velocity.data") diffuse_lifetime_data_path = os.path.join(autosave_dir, "diffuse_particle_lifetime.data") diffuse_type_data_path = os.path.join(autosave_dir, "diffuse_particle_type.data") diffuse_id_data_path = os.path.join(autosave_dir, "diffuse_particle_id.data") autosave_info_path = os.path.join(autosave_dir, "autosave.state") temp_extension = ".backup" autosave_default_filepaths = [ position_data_path, velocity_data_path, autosave_info_path ] autosave_apic_filepaths = [ affinex_data_path, affiney_data_path, affinez_data_path ] autosave_age_filepaths = [ age_data_path ] autosave_lifetime_filepaths = [ lifetime_data_path ] autosave_color_filepaths = [ color_data_path ] autosave_source_id_filepaths = [ source_id_data_path ] autosave_uid_filepaths = [ uid_data_path ] autosave_viscosity_filepaths = [ viscosity_data_path ] autosave_density_filepaths = [ density_data_path ] autosave_id_filepaths = [ id_data_path ] autosave_diffuse_filepaths = [ diffuse_position_data_path, diffuse_velocity_data_path, diffuse_lifetime_data_path, diffuse_type_data_path, diffuse_id_data_path ] num_particles = fluidsim.get_num_marker_particles() marker_particles_per_write = 2**21 num_marker_particle_writes = (num_particles // marker_particles_per_write) + 1 try: for i in range(num_marker_particle_writes): start_idx = i * marker_particles_per_write end_idx = min((i + 1) * marker_particles_per_write, num_particles) is_appending = i != 0 data = fluidsim.get_marker_particle_position_data_range(start_idx, end_idx) __write_save_state_file_data(position_data_path + temp_extension, data, is_appending_data=is_appending) data = fluidsim.get_marker_particle_velocity_data_range(start_idx, end_idx) __write_save_state_file_data(velocity_data_path + temp_extension, data, is_appending_data=is_appending) if fluidsim.is_velocity_transfer_method_APIC(): data = fluidsim.get_marker_particle_affinex_data_range(start_idx, end_idx) __write_save_state_file_data(affinex_data_path + temp_extension, data, is_appending_data=is_appending) data = fluidsim.get_marker_particle_affiney_data_range(start_idx, end_idx) __write_save_state_file_data(affiney_data_path + temp_extension, data, is_appending_data=is_appending) data = fluidsim.get_marker_particle_affinez_data_range(start_idx, end_idx) __write_save_state_file_data(affinez_data_path + temp_extension, data, is_appending_data=is_appending) if fluidsim.enable_surface_age_attribute or fluidsim.enable_fluid_particle_age_attribute: data = fluidsim.get_marker_particle_age_data_range(start_idx, end_idx) __write_save_state_file_data(age_data_path + temp_extension, data, is_appending_data=is_appending) if fluidsim.enable_surface_lifetime_attribute or fluidsim.enable_fluid_particle_lifetime_attribute: data = fluidsim.get_marker_particle_lifetime_data_range(start_idx, end_idx) __write_save_state_file_data(lifetime_data_path + temp_extension, data, is_appending_data=is_appending) if fluidsim.enable_surface_color_attribute or fluidsim.enable_fluid_particle_color_attribute: data = fluidsim.get_marker_particle_color_data_range(start_idx, end_idx) __write_save_state_file_data(color_data_path + temp_extension, data, is_appending_data=is_appending) if fluidsim.enable_surface_source_id_attribute or fluidsim.enable_fluid_particle_source_id_attribute: data = fluidsim.get_marker_particle_source_id_data_range(start_idx, end_idx) __write_save_state_file_data(source_id_data_path + temp_extension, data, is_appending_data=is_appending) if fluidsim.enable_fluid_particle_uid_attribute: data = fluidsim.get_marker_particle_uid_data_range(start_idx, end_idx) __write_save_state_file_data(uid_data_path + temp_extension, data, is_appending_data=is_appending) if fluidsim.enable_surface_viscosity_attribute: data = fluidsim.get_marker_particle_viscosity_data_range(start_idx, end_idx) __write_save_state_file_data(viscosity_data_path + temp_extension, data, is_appending_data=is_appending) if fluidsim.enable_surface_density_attribute or fluidsim.enable_fluid_particle_density_attribute: data = fluidsim.get_marker_particle_density_data_range(start_idx, end_idx) __write_save_state_file_data(density_data_path + temp_extension, data, is_appending_data=is_appending) if fluidsim.enable_fluid_particle_output: data = fluidsim.get_marker_particle_id_data_range(start_idx, end_idx) __write_save_state_file_data(id_data_path + temp_extension, data, is_appending_data=is_appending) if fluidsim.get_num_diffuse_particles() > 0: num_particles = fluidsim.get_num_diffuse_particles() diffuse_particles_per_write = 2**21 num_diffuse_particle_writes = (num_particles // diffuse_particles_per_write) + 1 for i in range(num_diffuse_particle_writes): start_idx = i * diffuse_particles_per_write end_idx = min((i + 1) * diffuse_particles_per_write, num_particles) is_appending = i != 0 data = fluidsim.get_diffuse_particle_position_data_range(start_idx, end_idx) __write_save_state_file_data(diffuse_position_data_path + temp_extension, data, is_appending_data=is_appending) data = fluidsim.get_diffuse_particle_velocity_data_range(start_idx, end_idx) __write_save_state_file_data(diffuse_velocity_data_path + temp_extension, data, is_appending_data=is_appending) data = fluidsim.get_diffuse_particle_lifetime_data_range(start_idx, end_idx) __write_save_state_file_data(diffuse_lifetime_data_path + temp_extension, data, is_appending_data=is_appending) data = fluidsim.get_diffuse_particle_type_data_range(start_idx, end_idx) __write_save_state_file_data(diffuse_type_data_path + temp_extension, data, is_appending_data=is_appending) data = fluidsim.get_diffuse_particle_id_data_range(start_idx, end_idx) __write_save_state_file_data(diffuse_id_data_path + temp_extension, data, is_appending_data=is_appending) init_data = domain_data.initialize frame_start, frame_end = init_data.frame_start, init_data.frame_end autosave_info = {} autosave_info['isize'] = init_data.isize autosave_info['jsize'] = init_data.jsize autosave_info['ksize'] = init_data.ksize autosave_info['dx'] = init_data.dx autosave_info['frame'] = frameno autosave_info['frame_start'] = frame_start autosave_info['frame_end'] = frame_end autosave_info['frame_id'] = fluidsim.get_current_frame() - 1 autosave_info['last_frame_id'] = frame_end - frame_start autosave_info['num_marker_particles'] = fluidsim.get_num_marker_particles() autosave_info['current_fluid_particle_uid'] = str(fluidsim.get_current_fluid_particle_uid()) autosave_info['marker_particle_position_filedata'] = "marker_particle_position.data" autosave_info['marker_particle_velocity_filedata'] = "marker_particle_velocity.data" autosave_info['num_diffuse_particles'] = fluidsim.get_num_diffuse_particles() autosave_info['marker_particle_affinex_filedata'] = "" autosave_info['marker_particle_affiney_filedata'] = "" autosave_info['marker_particle_affinez_filedata'] = "" autosave_info['marker_particle_age_filedata'] = "" autosave_info['marker_particle_lifetime_filedata'] = "" autosave_info['marker_particle_color_filedata'] = "" autosave_info['marker_particle_source_id_filedata'] = "" autosave_info['marker_particle_uid_filedata'] = "" autosave_info['marker_particle_viscosity_filedata'] = "" autosave_info['marker_particle_density_filedata'] = "" autosave_info['marker_particle_id_filedata'] = "" autosave_info['diffuse_particle_position_filedata'] = "" autosave_info['diffuse_particle_velocity_filedata'] = "" autosave_info['diffuse_particle_lifetime_filedata'] = "" autosave_info['diffuse_particle_type_filedata'] = "" autosave_info['diffuse_particle_id_filedata'] = "" if fluidsim.is_velocity_transfer_method_APIC(): autosave_info['marker_particle_affinex_filedata'] = "marker_particle_affinex.data" autosave_info['marker_particle_affiney_filedata'] = "marker_particle_affiney.data" autosave_info['marker_particle_affinez_filedata'] = "marker_particle_affinez.data" if fluidsim.enable_surface_age_attribute or fluidsim.enable_fluid_particle_age_attribute: autosave_info['marker_particle_age_filedata'] = "marker_particle_age.data" if fluidsim.enable_surface_lifetime_attribute or fluidsim.enable_fluid_particle_lifetime_attribute: autosave_info['marker_particle_lifetime_filedata'] = "marker_particle_lifetime.data" if fluidsim.enable_surface_color_attribute or fluidsim.enable_fluid_particle_color_attribute: autosave_info['marker_particle_color_filedata'] = "marker_particle_color.data" if fluidsim.enable_surface_source_id_attribute or fluidsim.enable_fluid_particle_source_id_attribute: autosave_info['marker_particle_source_id_filedata'] = "marker_particle_source_id.data" if fluidsim.enable_fluid_particle_uid_attribute: autosave_info['marker_particle_uid_filedata'] = "marker_particle_uid.data" if fluidsim.enable_surface_viscosity_attribute: autosave_info['marker_particle_viscosity_filedata'] = "marker_particle_viscosity.data" if fluidsim.enable_surface_density_attribute or fluidsim.enable_fluid_particle_density_attribute: autosave_info['marker_particle_density_filedata'] = "marker_particle_density.data" if fluidsim.enable_fluid_particle_output: autosave_info['marker_particle_id_filedata'] = "marker_particle_id.data" if fluidsim.get_num_diffuse_particles() > 0: autosave_info['diffuse_particle_position_filedata'] = "diffuse_particle_position.data" autosave_info['diffuse_particle_velocity_filedata'] = "diffuse_particle_velocity.data" autosave_info['diffuse_particle_lifetime_filedata'] = "diffuse_particle_lifetime.data" autosave_info['diffuse_particle_type_filedata'] = "diffuse_particle_type.data" autosave_info['diffuse_particle_id_filedata'] = "diffuse_particle_id.data" autosave_json = json.dumps(autosave_info, sort_keys=True, indent=4) with open(autosave_info_path + temp_extension, 'w', encoding='utf-8') as f: f.write(autosave_json) except Exception as e: print("FLIP Fluids: OS/Filesystem Error: Unable to write autosave files to storage") print("Error Message: ", e) print("Backup of the last successful autosave located here: <" + autosave_dir + ">") return try: data_filepaths = ( autosave_default_filepaths + autosave_apic_filepaths + autosave_age_filepaths + autosave_lifetime_filepaths + autosave_color_filepaths + autosave_source_id_filepaths + autosave_uid_filepaths + autosave_viscosity_filepaths + autosave_density_filepaths + autosave_id_filepaths + autosave_diffuse_filepaths ) for filepath in data_filepaths: if os.path.isfile(filepath): fpl.delete_file(filepath, display_popup_on_error=False) except Exception as e: print("FLIP Fluids: OS/Filesystem Error: Unable to delete older autosave files from storage") print("Error Message: ", e) print("Backup of the last successful autosave located here: <" + autosave_dir + ">") return try: for filepath in autosave_default_filepaths: os.rename(filepath + temp_extension, filepath) if fluidsim.is_velocity_transfer_method_APIC(): for filepath in autosave_apic_filepaths: os.rename(filepath + temp_extension, filepath) if fluidsim.enable_surface_age_attribute or fluidsim.enable_fluid_particle_age_attribute: for filepath in autosave_age_filepaths: os.rename(filepath + temp_extension, filepath) if fluidsim.enable_surface_lifetime_attribute or fluidsim.enable_fluid_particle_lifetime_attribute: for filepath in autosave_lifetime_filepaths: os.rename(filepath + temp_extension, filepath) if fluidsim.enable_surface_color_attribute or fluidsim.enable_fluid_particle_color_attribute: for filepath in autosave_color_filepaths: os.rename(filepath + temp_extension, filepath) if fluidsim.enable_surface_source_id_attribute or fluidsim.enable_fluid_particle_source_id_attribute: for filepath in autosave_source_id_filepaths: os.rename(filepath + temp_extension, filepath) if fluidsim.enable_fluid_particle_uid_attribute: for filepath in autosave_uid_filepaths: os.rename(filepath + temp_extension, filepath) if fluidsim.enable_surface_viscosity_attribute: for filepath in autosave_viscosity_filepaths: os.rename(filepath + temp_extension, filepath) if fluidsim.enable_surface_density_attribute or fluidsim.enable_fluid_particle_density_attribute: for filepath in autosave_density_filepaths: os.rename(filepath + temp_extension, filepath) if fluidsim.enable_fluid_particle_output: for filepath in autosave_id_filepaths: os.rename(filepath + temp_extension, filepath) if fluidsim.get_num_diffuse_particles() > 0: for filepath in autosave_diffuse_filepaths: os.rename(filepath + temp_extension, filepath) except Exception as e: print("FLIP Fluids: OS/Filesystem Error: Unable to rename autosave files in storage") print("Error Message: ", e) print("Backup of the last successful autosave located here: <" + autosave_dir + ">") return init_data = domain_data.initialize if init_data.enable_savestates: interval = init_data.savestate_interval if (frameno + 1 - frame_start) % interval == 0 or frameno == frame_start: numstr = str(frameno).zfill(6) savestate_dir = os.path.join(cache_directory, "savestates", "autosave" + numstr) if os.path.isdir(savestate_dir): fpl.delete_files_in_directory( savestate_dir, [".state", ".data"], remove_directory=True, display_popup_on_error=False ) shutil.copytree(autosave_dir, savestate_dir, dirs_exist_ok=True) def __write_finished_file(cache_directory, frameno): fstring = __frame_number_to_string(frameno) finished_filename = "finished" + fstring + ".txt" finished_filepath = os.path.join(cache_directory, "bakefiles", finished_filename) filestring = fstring with open(finished_filepath, 'w') as f: f.write(filestring) def __write_metadata_file(domain_data, cache_directory, frameno): fstring = __frame_number_to_string(frameno) metadata_filename = "metadata" + fstring + ".json" metadata_filepath = os.path.join(cache_directory, "bakefiles", metadata_filename) simdata = domain_data.simulation frame_id = __get_frame_id() - 1 data_dict = {} data_dict['time_scale'] = __get_parameter_data(simdata.time_scale, frame_id) data_dict['world_scale'] = __get_parameter_data(domain_data.world.world_scale_relative, frame_id) json_string = json.dumps(data_dict) with open(metadata_filepath, 'w') as f: f.write(json_string) def __write_simulation_output(domain_data, fluidsim, frameno, cache_directory): __write_bounds_data(cache_directory, fluidsim, frameno) if fluidsim.enable_surface_reconstruction: __write_surface_data(cache_directory, fluidsim, frameno) if fluidsim.enable_diffuse_material_output: __write_whitewater_data(cache_directory, fluidsim, frameno) if fluidsim.enable_fluid_particle_output: __write_fluid_particle_data(cache_directory, fluidsim, frameno) if fluidsim.enable_fluid_particle_debug_output: __write_fluid_particle_debug_data(cache_directory, fluidsim, frameno) if fluidsim.enable_internal_obstacle_mesh_output: __write_internal_obstacle_mesh_data(cache_directory, fluidsim, frameno) if fluidsim.enable_force_field_debug_output: __write_force_field_debug_data(cache_directory, fluidsim, frameno) __write_logfile_data(cache_directory, domain_data.initialize.logfile_name, fluidsim) __write_frame_stats_data(cache_directory, fluidsim, frameno) __write_autosave_data(domain_data, cache_directory, fluidsim, frameno) __write_metadata_file(domain_data, cache_directory, frameno) __write_finished_file(cache_directory, frameno) def __get_current_frame_delta_time(domain_data, frameno): simdata = domain_data.simulation init_data = domain_data.initialize time_scale = __get_parameter_data(simdata.time_scale, frameno) fps = __get_parameter_data(simdata.frames_per_second, frameno) dt = (1.0 / fps) * time_scale return dt def __run_simulation(fluidsim, data, cache_directory, bakedata): domain = data.domain_data init_data = domain.initialize num_frames = init_data.frame_end - init_data.frame_start + 1 current_frame = fluidsim.get_current_frame() for i in range(current_frame, num_frames): simulator_frameno = fluidsim.get_current_frame() blender_frameno = simulator_frameno + init_data.frame_start geometry_database = __get_geometry_database() try: geometry_database.open() __update_animatable_properties(fluidsim, data, simulator_frameno) __add_fluid_objects(fluidsim, data, bakedata, simulator_frameno) geometry_database.close() except Exception: geometry_database.close() raise Exception dt = __get_current_frame_delta_time(domain, simulator_frameno) fluidsim.update(dt) if __check_bake_cancelled(bakedata): return bakedata.is_safe_to_exit = False __write_simulation_output(domain, fluidsim, blender_frameno, cache_directory) bakedata.is_safe_to_exit = True bakedata.completed_frames = simulator_frameno + 1 bakedata.progress = (simulator_frameno + 1) / num_frames if __check_bake_cancelled(bakedata): return def set_console_output(boolval): fluidsim_object = __get_simulation_object() if fluidsim_object is None or fluidsim_object._obj is None: return old_value = fluidsim_object.enable_console_output if fluidsim_object.enable_console_output == boolval: return fluidsim_object.enable_console_output = boolval def __get_addon_version(): bl_info_dict = bl_info addon_major, addon_minor, addon_revision = bl_info_dict.get('version', (-1, -1, -1)) return str(addon_major) + "." + str(addon_minor) + "." + str(addon_revision) def __get_engine_version(fluidsim): engine_major, engine_minor, engine_revision = fluidsim.get_version() return str(engine_major) + "." + str(engine_minor) + "." + str(engine_revision) def __launch_bake(datafile, cache_directory, bakedata, savestate_id=None): __set_cache_directory(cache_directory) data = __extract_data(datafile) __set_simulation_data(data) db_filepath = __get_geometry_database_filepath() geometry_database = flip_fluid_geometry_database.GeometryDatabase(db_filepath) __set_geometry_database(geometry_database) if __check_bake_cancelled(bakedata): return __set_output_directories(cache_directory) init_data = data.domain_data.initialize fluidsim = FluidSimulation(init_data.isize, init_data.jsize, init_data.ksize, init_data.dx) __set_simulation_object(fluidsim) if __get_addon_version() != __get_engine_version(fluidsim): errmsg = "The FLIP Fluids engine version <" + __get_engine_version(fluidsim) errmsg += "> is not compatible with the addon version <" + __get_addon_version() + ">." errmsg += " Blender may require a restart if you have updated the addon to a new version during this session" raise LibraryVersionError(errmsg) if __check_bake_cancelled(bakedata): return geometry_database.open() __initialize_fluid_simulation(fluidsim, data, cache_directory, bakedata, savestate_id) geometry_database.close() if __check_bake_cancelled(bakedata): return __run_simulation(fluidsim, data, cache_directory, bakedata) def bake(datafile, cache_directory, bakedata, savestate_id=None, bake_retries=0): max_baking_retries = bake_retries for retry_num in range(max_baking_retries + 1): try: __launch_bake(datafile, cache_directory, bakedata, savestate_id) print("------------------------------------------------------------") print("Simulation Ended.\nThank you for using FLIP Fluids!") print("------------------------------------------------------------") break except Exception as e: database = __get_geometry_database() database.close() error_string = str(e) errmsg = error_string do_not_attempt_relaunch = False if "std::bad_alloc" in errmsg: errmsg = "Out of memory. " elif "No space left on device" in errmsg: do_not_attempt_relaunch = True elif not errmsg: errmsg = "Unknown error. " if not errmsg.endswith(". "): errmsg += ". " errmsg += "See system console for error info." traceback.print_exc() print("------------------------------------------------------------") print("SIMULATION TERMINATED DUE TO ERROR:") if "std::bad_alloc" in error_string: print("\tOut of Memory") else: print("\t" + error_string) print("\nThank you for using FLIP Fluids!") print("------------------------------------------------------------") if retry_num == max_baking_retries or do_not_attempt_relaunch: bakedata.error_message = errmsg break else: # Setting to a negative value will default to the most recent savestate on the next attempt. savestate_id = -1 retry_msg = "Attempting to re-launch bake... " retry_msg += "(retry attempt " + str(retry_num + 1) + "/" + str(max_baking_retries) + ")" print(retry_msg) __set_simulation_object(None) bakedata.is_finished = True