2026-02-16

This commit is contained in:
2026-03-17 15:25:32 -06:00
parent d5dd373de0
commit 60100fbab2
560 changed files with 33397 additions and 20776 deletions
@@ -29,8 +29,6 @@ from .. import render
def frame_change_post_apply_T71908_workaround(context, depsgraph=None):
if not render.is_rendering():
return
if not vcu.is_blender_281():
return
dprops = context.scene.flip_fluid.get_domain_properties()
if dprops is None:
@@ -76,24 +74,97 @@ def frame_change_post_apply_T71908_workaround(context, depsgraph=None):
# Also apply to other FF_GeometryNodes inputs in case the user wants to keyframe these values.
input_name_list_surface = [
"Input_4", # Motion Blur Scale
"Input_6", # Enable Motion Blur
"Socket_0", # Blur Velocity For Fading
"Socket_5", # Shade Smooth Surface
"Socket_6", # Blur Iterations
"Input_6", # Enable Motion Blur
"Input_4", # Motion Blur Scale
"Socket_8", # Apply Simulation Time Scale
"Socket_9", # Apply Simulation World Scale
"Socket_5", # Shade Smooth Surface
"Socket_11", # Remove Mesh Near Domain Boundary
"Socket_12", # X-
"Socket_13", # Y-
"Socket_14", # Z-
"Socket_15", # X+
"Socket_16", # Y+
"Socket_17", # Z+
"Socket_18", # Distance
"Socket_20", # Flatten Mesh Near Domain Boundary
"Socket_21", # Water Level Mode
"Socket_22", # Water Level
"Socket_24", # Flattened Width
"Socket_25", # Transition Width
"Socket_27", # Store Displacement Attribute
"Socket_26", # Store Transition Mask Attribute
"Socket_0", # Blur Velocity For Fading
"Socket_6", # Blur Iterations
]
input_name_list_particles = [
"Input_4", # Motion Blur Scale
"Input_6", # Particle Scale
"Input_8", # Enable Motion Blur
"Input_9", # Enable Point Cloud
"Input_10", # Enable Instancing
"Socket_0", # Fading Strength
"Socket_1", # Fading Width
"Socket_2", # Particle Scale Random
"Socket_4", # Fading Density
"Socket_9", # Shade Smooth Instancing
input_name_list_fluid_particles = [
"Socket_16", # Apply Material
"Input_8", # Enable Motion Blur
"Input_4", # Motion Blur Scale
"Socket_47", # Apply Simulation Time Scale
"Socket_48", # Apply Simulation World Scale
"Socket_12", # Particle Display Mode
"Input_6", # Particle Scale
"Socket_11", # Particle Scale Multiplier
"Socket_2", # Particle Scale Random
"Socket_21", # Random Bias
"Socket_14", # Instancing Mode
"Socket_18", # Randomize Instance Rotation
"Socket_19", # Align Instance to Velocity
"Socket_10", # Shade Smooth Instances
"Socket_17", # Realize Instances
"Socket_51", # Matched Flattened Surface Displacement
"Socket_30", # Age Based Particle Scaling
"Socket_31", # Starting Scale Factor
"Socket_32", # Scaling Duration (Age)
"Socket_33", # Age Offset
"Socket_34", # Store Age Scaling Transition Attribute
"Socket_24", # Lifetime Based Particle Scaling
"Socket_23", # Final Scale Factor
"Socket_25", # Scaling Duration (Lifetime)
"Socket_26", # Lifetime Offset
"Socket_28", # Store Lifetime Scaling Transition Attribute
"Socket_36", # Filter Particle by Source ID
"Socket_37", # Source ID 0
"Socket_38", # Source ID 1
"Socket_39", # Source ID 2
"Socket_40", # Source ID 3
"Socket_41", # Source ID 4
"Socket_42", # Source ID 5
"Socket_43", # Source ID 6
"Socket_44", # Source ID 7
"Socket_45", # Source ID 8
"Socket_1", # Fading Width
"Socket_0", # Fading Strength
"Socket_4", # Fading Density
]
input_name_list_whitewater = [
"Socket_16", # Apply Material
"Input_8", # Enable Motion Blur
"Input_4", # Motion Blur Scale
"Socket_30", # Apply Simulation Time Scale
"Socket_31", # Apply Simulation World Scale
"Socket_12", # Particle Display Mode
"Input_6", # Particle Scale
"Socket_11", # Particle Scale Multiplier
"Socket_2", # Particle Scale Random
"Socket_21", # Random Bias
"Socket_14", # Instancing Mode
"Socket_18", # Randomize Instance Rotation
"Socket_19", # Align Instance to Velocity
"Socket_10", # Shade Smooth Instances
"Socket_17", # Realize Instances
"Socket_34", # Matched Flattened Surface Displacement
"Socket_24", # Lifetime Based Particle Scaling
"Socket_23", # Final Scale Factor
"Socket_25", # Scaling Duration (Lifetime)
"Socket_26", # Lifetime Offset
"Socket_28", # Store Lifetime Scaling Transition Attribute
"Socket_1", # Fading Width
"Socket_0", # Fading Strength
"Socket_4", # Fading Density
]
for obj in cache_objects:
@@ -103,8 +174,10 @@ def frame_change_post_apply_T71908_workaround(context, depsgraph=None):
mod_name = obj.modifiers[i].name
if mod_name.startswith("FF_GeometryNodesSurface"):
input_name_list = input_name_list_surface
elif mod_name.startswith("FF_GeometryNodesFluidParticles") or mod_name.startswith("FF_GeometryNodesWhitewater"):
input_name_list = input_name_list_particles
elif mod_name.startswith("FF_GeometryNodesFluidParticles"):
input_name_list = input_name_list_fluid_particles
elif mod_name.startswith("FF_GeometryNodesWhitewater"):
input_name_list = input_name_list_whitewater
else:
continue
@@ -153,20 +226,12 @@ def load_post_update_cycles_visibility_forward_compatibility_from_blender_3():
def set_cycles_ray_visibility(bl_object, is_enabled):
# Cycles may not be enabled in the user's preferences
try:
if vcu.is_blender_30():
bl_object.visible_camera = is_enabled
bl_object.visible_diffuse = is_enabled
bl_object.visible_glossy = is_enabled
bl_object.visible_transmission = is_enabled
bl_object.visible_volume_scatter = is_enabled
bl_object.visible_shadow = is_enabled
else:
bl_object.cycles_visibility.camera = is_enabled
bl_object.cycles_visibility.transmission = is_enabled
bl_object.cycles_visibility.diffuse = is_enabled
bl_object.cycles_visibility.scatter = is_enabled
bl_object.cycles_visibility.glossy = is_enabled
bl_object.cycles_visibility.shadow = is_enabled
bl_object.visible_camera = is_enabled
bl_object.visible_diffuse = is_enabled
bl_object.visible_glossy = is_enabled
bl_object.visible_transmission = is_enabled
bl_object.visible_volume_scatter = is_enabled
bl_object.visible_shadow = is_enabled
except:
pass
@@ -394,10 +459,10 @@ def is_keyframed_hide_render_issue_relevant(scene):
if not obj.animation_data:
continue
anim_data = obj.animation_data
if not anim_data.action or not anim_data.action.fcurves:
if not anim_data.action or not anim_data.action.layers[0].strips[0].channelbag(anim_data.action.slots[0]).fcurves:
continue
for fcurve in anim_data.action.fcurves:
for fcurve in anim_data.action.layers[0].strips[0].channelbag(anim_data.action.slots[0]).fcurves:
if fcurve.data_path == "hide_render":
is_relevant = True
break
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import bpy, os, json, time
import bpy, os, json, time, aud
from . import version_compatibility_utils as vcu
@@ -24,12 +24,6 @@ def get_sounds_directory():
def play_sound(json_audio_filepath, block=False):
if not vcu.is_blender_28():
# aud not supported in Blender 2.79 or lower
return
import aud
with open(json_audio_filepath, 'r', encoding='utf-8') as f:
json_data = json.loads(f.read())
@@ -54,10 +54,11 @@ def flip_fluid_object_to_dict(obj, object_properties):
def is_property_animated(obj, prop_name, index=0, use_exact_path=False):
anim_data = obj.animation_data
if not anim_data or not anim_data.action or not anim_data.action.fcurves:
if not anim_data or not anim_data.action or not anim_data.action.layers[0].strips[0].channelbag(anim_data.action.slots[0]).fcurves:
return False
for fcurve in anim_data.action.fcurves:
for fcurve in anim_data.action.layers[0].strips[0].channelbag(anim_data.action.slots[0]).fcurves:
path = fcurve.data_path
is_match = path.endswith(prop_name)
if use_exact_path:
@@ -69,10 +70,11 @@ def is_property_animated(obj, prop_name, index=0, use_exact_path=False):
def is_property_path_animated(obj, path_name, index = 0):
anim_data = obj.animation_data
if not anim_data or not anim_data.action or not anim_data.action.fcurves:
if not anim_data or not anim_data.action or not anim_data.action.layers[0].strips[0].channelbag(anim_data.action.slots[0]).fcurves:
return False
for fcurve in anim_data.action.fcurves:
for fcurve in anim_data.action.layers[0].strips[0].channelbag(anim_data.action.slots[0]).fcurves:
if fcurve.data_path == path_name and fcurve.array_index == index:
return True
return False
@@ -87,7 +89,7 @@ def is_vector_animated(obj, prop_name, vector_size = 3):
def get_property_fcurve(obj, prop_name, index=0, use_exact_path=False):
anim_data = obj.animation_data
for fcurve in anim_data.action.fcurves:
for fcurve in anim_data.action.layers[0].strips[0].channelbag(anim_data.action.slots[0]).fcurves:
path = fcurve.data_path
is_match = path.endswith(prop_name)
@@ -99,7 +101,7 @@ def get_property_fcurve(obj, prop_name, index=0, use_exact_path=False):
def get_property_fcurve_from_path(obj, path_name, index = 0):
anim_data = obj.animation_data
for fcurve in anim_data.action.fcurves:
for fcurve in anim_data.action.layers[0].strips[0].channelbag(anim_data.action.slots[0]).fcurves:
if fcurve.data_path == path_name and fcurve.array_index == index:
return fcurve
@@ -22,6 +22,9 @@ IS_INSTALLATION_UTILS_INITIALIZED = False
IS_INSTALLATION_COMPLETE = False
IS_STABLE_BUILD = True
SUPPORT_LICENSE_TYPE = "Individual"
SUPPORT_LICENSE_ID = "854NV"
IS_MIXBOX_SUPPORTED = True
IS_MIXBOX_INSTALLATION_COMPLETE = False
MIXBOX_BOOST_FACTOR = 1.2
@@ -164,10 +167,6 @@ def update_preset_library_installation_status():
IS_PRESET_LIBRARY_INSTALLATION_COMPLETE = False
PRESET_LIBRARY_INSTALLATIONS = []
if not vcu.is_blender_33():
# Asset library is only available in Blender 3.3 or later
return
bl_preferences = bpy.context.preferences
bl_filepaths = bl_preferences.filepaths
for lib_entry in bl_filepaths.asset_libraries:
@@ -251,6 +250,12 @@ def get_library_list():
return library_list
def get_support_license_label():
global SUPPORT_LICENSE_TYPE
global SUPPORT_LICENSE_ID
return SUPPORT_LICENSE_TYPE + " " + SUPPORT_LICENSE_ID
def __load_post_update_is_addon_active():
tag_addon_inactive()
if bpy.context.scene.flip_fluid.is_domain_object_set():
@@ -20,9 +20,12 @@ from . import version_compatibility_utils as vcu
IS_INSTALLATION_UTILS_INITIALIZED = False
IS_INSTALLATION_COMPLETE = False
IS_STABLE_BUILD = @FLUIDENGINE_VERSION_TYPE_IS_STABLE_BUILD_PYTHON@
IS_STABLE_BUILD = @FFENGINE_VERSION_TYPE_IS_STABLE_BUILD_PYTHON@
IS_MIXBOX_SUPPORTED = @FLUIDENGINE_IS_MIXBOX_SUPPORTED@
SUPPORT_LICENSE_TYPE = "@FFENGINE_VERSION_SUPPORT_LICENSE_TYPE@"
SUPPORT_LICENSE_ID = "@FFENGINE_VERSION_SUPPORT_LICENSE_ID@"
IS_MIXBOX_SUPPORTED = @FFENGINE_IS_MIXBOX_SUPPORTED@
IS_MIXBOX_INSTALLATION_COMPLETE = False
MIXBOX_BOOST_FACTOR = 1.2
@@ -164,10 +167,6 @@ def update_preset_library_installation_status():
IS_PRESET_LIBRARY_INSTALLATION_COMPLETE = False
PRESET_LIBRARY_INSTALLATIONS = []
if not vcu.is_blender_33():
# Asset library is only available in Blender 3.3 or later
return
bl_preferences = bpy.context.preferences
bl_filepaths = bl_preferences.filepaths
for lib_entry in bl_filepaths.asset_libraries:
@@ -251,6 +250,12 @@ def get_library_list():
return library_list
def get_support_license_label():
global SUPPORT_LICENSE_TYPE
global SUPPORT_LICENSE_ID
return SUPPORT_LICENSE_TYPE + " " + SUPPORT_LICENSE_ID
def __load_post_update_is_addon_active():
tag_addon_inactive()
if bpy.context.scene.flip_fluid.is_domain_object_set():
@@ -87,102 +87,59 @@ def is_blender_44():
def is_blender_45():
return bpy.app.version >= (4, 5, 0)
def register_dict_property(dict_object, name_str, prop):
if is_blender_28():
# must use exec as the statement will result in invalid syntax
# if script is run in Python versions that do nupport annotation syntax
exec("dict_object[name_str]: prop")
else:
dict_object[name_str] = prop
def convert_attribute_to_28(prop_name):
if is_blender_28():
p = prop_name
return "temp_prop = " + p + "; del " + p + "; " + p + ": temp_prop; del temp_prop"
else:
return ""
print("FLIP Fluids Warning: 'convert_attribute_to_28' method is deprecated. Contact the developers if you see this message. This message is not an error and can be safely ignored.")
p = prop_name
return "temp_prop = " + p + "; del " + p + "; " + p + ": temp_prop; del temp_prop"
def get_active_object(context=None):
if context is None:
context = bpy.context
if is_blender_28():
return context.active_object
else:
return context.scene.objects.active
return context.active_object
def set_active_object(obj, context=None):
if context is None:
context = bpy.context
if is_blender_28():
context.view_layer.objects.active = obj
else:
context.scene.objects.active = obj
context.view_layer.objects.active = obj
def select_get(obj):
if is_blender_28():
return obj.select_get()
else:
return obj.select
return obj.select_get()
def select_set(obj, boolval):
if is_blender_28():
obj.select_set(boolval)
else:
obj.select = boolval
obj.select_set(boolval)
def get_object_display_type(obj):
if is_blender_28():
return obj.display_type
else:
return obj.draw_type
return obj.display_type
def set_object_display_type(obj, display_type):
if is_blender_28():
obj.display_type = display_type
else:
obj.draw_type = display_type
obj.display_type = display_type
def set_object_hide_viewport(obj, display_bool):
if is_blender_28():
if obj.hide_get() != display_bool:
obj.hide_set(display_bool)
else:
if obj.hide != display_bool:
obj.hide = display_bool
if obj.hide_get() != display_bool:
obj.hide_set(display_bool)
def get_object_hide_viewport(obj):
if is_blender_28():
return obj.hide_get()
else:
return obj.hide
return obj.hide_get()
def toggle_outline_eye_icon(obj):
if is_blender_28():
obj.hide_viewport = not obj.hide_viewport
else:
obj.hide = not obj.hide
obj.hide_viewport = not obj.hide_viewport
def set_object_instance_type(obj, display_type):
if is_blender_28():
if obj.instance_type != display_type:
obj.instance_type = display_type
else:
if obj.dupli_type != display_type:
obj.dupli_type = display_type
if obj.instance_type != display_type:
obj.instance_type = display_type
def get_flip_fluids_collection(context):
@@ -205,62 +162,52 @@ def get_flip_mesh_collection(context):
def link_fluid_mesh_object(obj, context=None):
if context is None:
context = bpy.context
if is_blender_28():
mesh_collection = get_flip_mesh_collection(context)
mesh_collection.objects.link(obj)
else:
context.scene.objects.link(obj)
mesh_collection = get_flip_mesh_collection(context)
mesh_collection.objects.link(obj)
def link_object(obj, context=None):
if context is None:
context = bpy.context
if is_blender_28():
flip_collection = get_flip_fluids_collection(context)
flip_collection.objects.link(obj)
else:
context.scene.objects.link(obj)
flip_collection = get_flip_fluids_collection(context)
flip_collection.objects.link(obj)
def link_object_to_master_scene(obj, context=None):
if context is None:
context = bpy.context
if is_blender_28():
context.scene.collection.objects.link(obj)
else:
context.scene.objects.link(obj)
context.scene.collection.objects.link(obj)
def add_to_flip_fluids_collection(obj, context):
if context is None:
context = bpy.context
if is_blender_28():
flip_collection = get_flip_fluids_collection(context)
if flip_collection.objects.get(obj.name):
return
flip_collection.objects.link(obj)
flip_collection = get_flip_fluids_collection(context)
if flip_collection.objects.get(obj.name):
return
flip_collection.objects.link(obj)
def remove_from_flip_fluids_collection(obj, context):
if context is None:
context = bpy.context
if is_blender_28():
flip_collection = get_flip_fluids_collection(context)
num_collections = 0
for collection in bpy.data.collections:
if collection.name.startswith("RigidBodyWorld"):
# The RigidBodyWorld collection (for RBD objects) is more hidden within the Blend file
# and may not be apparent to many users. Ignore this collection in the count so that
# it does not appear that the objects dissappear.
continue
if collection.objects.get(obj.name):
num_collections += 1
if num_collections == 1 and context.scene.collection.objects.get(obj.name) is None:
context.scene.collection.objects.link(obj)
flip_collection = get_flip_fluids_collection(context)
if flip_collection.objects.get(obj.name):
flip_collection.objects.unlink(obj)
num_collections = 0
for collection in bpy.data.collections:
if collection.name.startswith("RigidBodyWorld"):
# The RigidBodyWorld collection (for RBD objects) is more hidden within the Blend file
# and may not be apparent to many users. Ignore this collection in the count so that
# it does not appear that the objects dissappear.
continue
if collection.objects.get(obj.name):
num_collections += 1
if num_collections == 1 and context.scene.collection.objects.get(obj.name) is None:
context.scene.collection.objects.link(obj)
if flip_collection.objects.get(obj.name):
flip_collection.objects.unlink(obj)
def delete_object(obj, remove_mesh_data=True):
@@ -282,51 +229,36 @@ def delete_mesh_data(mesh_data):
def get_scene_collection(context=None):
if context is None:
context = bpy.context
if is_blender_28():
return context.scene.collection
else:
return context.scene
return context.scene.collection
def get_all_scene_objects(context=None):
if context is None:
context = bpy.context
if is_blender_28():
return context.scene.collection.all_objects
else:
return context.scene.objects
return context.scene.collection.all_objects
def element_multiply(v1, v2):
if is_blender_28():
return v1 @ v2
else:
return v1 * v2
return v1 @ v2
def depsgraph_update(context=None):
if context is None:
context = bpy.context
if is_blender_28():
depsgraph = context.evaluated_depsgraph_get()
depsgraph.update()
else:
context.scene.update()
depsgraph = context.evaluated_depsgraph_get()
depsgraph.update()
def object_to_triangle_mesh(obj, matrix_world=None):
is_b3d_28 = is_blender_28()
# To ensure the modifier stack is processed in 2.8, the object's 'hide in viewport'
# must be False. This is a limitation of how meshes or exported in Blender.
# The 'hide in viewport' status will be set back to the original value at the
# end of this method.
#
# More info: https://developer.blender.org/T71556
if is_b3d_28:
hide_viewport_status = obj.hide_viewport
if hide_viewport_status:
obj.hide_viewport = False
hide_viewport_status = obj.hide_viewport
if hide_viewport_status:
obj.hide_viewport = False
# The 'Edge Split' modifier will disconnect faces from eachother, resulting in
# a non-manifold mesh. Disable the edge split modifier from the modifier stack
@@ -343,14 +275,9 @@ def object_to_triangle_mesh(obj, matrix_world=None):
triangulation_mod = obj.modifiers.new("flip_triangulate", "TRIANGULATE")
triangulation_mod.quad_method = 'FIXED'
if is_b3d_28:
depsgraph = bpy.context.evaluated_depsgraph_get()
obj_eval = obj.evaluated_get(depsgraph)
new_mesh = obj_eval.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
else:
new_mesh = obj.to_mesh(scene=bpy.context.scene,
apply_modifiers=True,
settings='RENDER')
depsgraph = bpy.context.evaluated_depsgraph_get()
obj_eval = obj.evaluated_get(depsgraph)
new_mesh = obj_eval.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
vertex_components = []
if matrix_world is None:
@@ -360,18 +287,11 @@ def object_to_triangle_mesh(obj, matrix_world=None):
vertex_components.append(v.y)
vertex_components.append(v.z)
else:
if is_b3d_28:
for mv in new_mesh.vertices:
v = matrix_world @ mv.co
vertex_components.append(v.x)
vertex_components.append(v.y)
vertex_components.append(v.z)
else:
for mv in new_mesh.vertices:
v = matrix_world * mv.co
vertex_components.append(v.x)
vertex_components.append(v.y)
vertex_components.append(v.z)
for mv in new_mesh.vertices:
v = matrix_world @ mv.co
vertex_components.append(v.x)
vertex_components.append(v.y)
vertex_components.append(v.z)
triangle_indices = []
for t in new_mesh.polygons:
@@ -382,11 +302,7 @@ def object_to_triangle_mesh(obj, matrix_world=None):
tmesh.vertices = array.array('f', vertex_components)
tmesh.triangles = array.array('i', triangle_indices)
if is_b3d_28:
obj_eval.to_mesh_clear()
else:
new_mesh.user_clear()
bpy.data.meshes.remove(new_mesh)
obj_eval.to_mesh_clear()
obj.modifiers.remove(triangulation_mod)
@@ -395,9 +311,8 @@ def object_to_triangle_mesh(obj, matrix_world=None):
m.show_render = edge_split_show_render_values.pop(0)
m.show_viewport = edge_split_show_viewport_values.pop(0)
if is_b3d_28:
if hide_viewport_status != obj.hide_viewport:
obj.hide_viewport = hide_viewport_status
if hide_viewport_status != obj.hide_viewport:
obj.hide_viewport = hide_viewport_status
return tmesh
@@ -428,92 +343,63 @@ def swap_object_mesh_data_geometry(bl_object, vertices=[], triangles=[],
mesh_name="Untitled",
smooth_mesh=False,
octane_mesh_type='Global'):
if is_blender_281():
# Vertex Groups (Blender >= 3.4)
if is_blender_34():
vg_layer_names = [vg.name for vg in bl_object.vertex_groups]
active_vertex_layer_index = bl_object.vertex_groups.active_index
# UV Maps
uv_layer_names = [uv.name for uv in bl_object.data.uv_layers]
is_uv_layer_active = [uv.active for uv in bl_object.data.uv_layers]
is_uv_layer_active_render = [uv.active_render for uv in bl_object.data.uv_layers]
vg_layer_names = [vg.name for vg in bl_object.vertex_groups]
active_vertex_layer_index = bl_object.vertex_groups.active_index
# Color Attributes (Blender >= 3.2)
if is_blender_32():
ca_layer_names = [ca.name for ca in bl_object.data.color_attributes]
ca_layer_data_types = [ca.data_type for ca in bl_object.data.color_attributes]
ca_layer_domain_types = [ca.domain for ca in bl_object.data.color_attributes]
active_color_layer_index = bl_object.data.color_attributes.active_color_index
active_color_render_index = bl_object.data.color_attributes.render_color_index
else:
# Vertex Colors (Blender <= 3.1)
vc_layer_names = [vc.name for vc in bl_object.data.vertex_colors]
is_vc_layer_active = [vc.active for vc in bl_object.data.vertex_colors]
is_vc_layer_active_render = [vc.active_render for vc in bl_object.data.vertex_colors]
# UV Maps
uv_layer_names = [uv.name for uv in bl_object.data.uv_layers]
is_uv_layer_active = [uv.active for uv in bl_object.data.uv_layers]
is_uv_layer_active_render = [uv.active_render for uv in bl_object.data.uv_layers]
vertices = numpy.array(vertices, dtype=numpy.float32)
num_vertices = vertices.shape[0] // 3
vertex_index = numpy.array(triangles, dtype=numpy.int32)
loop_start = numpy.array(list(range(0, len(triangles), 3)), dtype=numpy.int32)
num_loops = loop_start.shape[0]
loop_total = numpy.array([3] * (len(triangles) // 3), dtype=numpy.int32)
# Color Attributes
ca_layer_names = [ca.name for ca in bl_object.data.color_attributes]
ca_layer_data_types = [ca.data_type for ca in bl_object.data.color_attributes]
ca_layer_domain_types = [ca.domain for ca in bl_object.data.color_attributes]
active_color_layer_index = bl_object.data.color_attributes.active_color_index
active_color_render_index = bl_object.data.color_attributes.render_color_index
bl_object.data.clear_geometry()
bl_object.data.from_pydata(vertices, [], triangles)
vertices = numpy.array(vertices, dtype=numpy.float32)
num_vertices = vertices.shape[0] // 3
vertex_index = numpy.array(triangles, dtype=numpy.int32)
loop_start = numpy.array(list(range(0, len(triangles), 3)), dtype=numpy.int32)
num_loops = loop_start.shape[0]
loop_total = numpy.array([3] * (len(triangles) // 3), dtype=numpy.int32)
_set_mesh_smoothness(bl_object.data, smooth_mesh)
_set_octane_mesh_type(bl_object, octane_mesh_type)
bl_object.data.clear_geometry()
bl_object.data.from_pydata(vertices, [], triangles)
# Vertex Groups (Blender >= 3.4)
if is_blender_34():
for i, name in enumerate(vg_layer_names):
vg_layer = bl_object.vertex_groups.new(name=name)
if active_vertex_layer_index >= 0:
bl_object.vertex_groups.active_index = active_vertex_layer_index
_set_mesh_smoothness(bl_object.data, smooth_mesh)
_set_octane_mesh_type(bl_object, octane_mesh_type)
# UV Maps
for i, name in enumerate(uv_layer_names):
uv_layer = bl_object.data.uv_layers.new(name=name)
uv_layer.active = is_uv_layer_active[i]
uv_layer.active_render = is_uv_layer_active_render[i]
# Vertex Groups
for i, name in enumerate(vg_layer_names):
vg_layer = bl_object.vertex_groups.new(name=name)
if active_vertex_layer_index >= 0:
bl_object.vertex_groups.active_index = active_vertex_layer_index
# Color Attributes (Blender >= 3.2)
if is_blender_32():
for i, name in enumerate(ca_layer_names):
ca_layer = bl_object.data.color_attributes.new(
name=name,
type=ca_layer_data_types[i],
domain=ca_layer_domain_types[i]
)
# Unable to set active color/render index > 0. Possibly a Blender bug that we need
# to report. For now, the Color Attributes layer will be limited to a single layer.
# As far as we know, this feature does not need to be used outside of Octane Render.
"""
if active_color_layer_index >= 0:
bl_object.data.color_attributes.active_color_index = active_color_layer_index
if active_color_render_index >= 0:
bl_object.data.color_attributes.render_color_index = active_color_render_index
"""
else:
# Vertex Colors (Blender <= 3.1)
for i, name in enumerate(vc_layer_names):
vc_layer = bl_object.data.vertex_colors.new(name=name)
vc_layer.active = is_vc_layer_active[i]
vc_layer.active_render = is_vc_layer_active_render[i]
# UV Maps
for i, name in enumerate(uv_layer_names):
uv_layer = bl_object.data.uv_layers.new(name=name)
uv_layer.active = is_uv_layer_active[i]
uv_layer.active_render = is_uv_layer_active_render[i]
else:
old_mesh_data = bl_object.data
new_mesh_data = bpy.data.meshes.new(mesh_name)
new_mesh_data.from_pydata(vertices, [], triangles)
bl_object.data = new_mesh_data
_transfer_mesh_materials(old_mesh_data, new_mesh_data)
_set_mesh_smoothness(new_mesh_data, smooth_mesh)
_set_octane_mesh_type(bl_object, octane_mesh_type)
old_mesh_data.user_clear()
bpy.data.meshes.remove(old_mesh_data)
# Color Attributes
for i, name in enumerate(ca_layer_names):
ca_layer = bl_object.data.color_attributes.new(
name=name,
type=ca_layer_data_types[i],
domain=ca_layer_domain_types[i]
)
# Unable to set active color/render index > 0. Possibly a Blender bug that we need
# to report. For now, the Color Attributes layer will be limited to a single layer.
# As far as we know, this feature does not need to be used outside of Octane Render.
"""
if active_color_layer_index >= 0:
bl_object.data.color_attributes.active_color_index = active_color_layer_index
if active_color_render_index >= 0:
bl_object.data.color_attributes.render_color_index = active_color_render_index
"""
def get_addon_directory():
@@ -525,10 +411,7 @@ def get_addon_directory():
def get_blender_preferences(context=None):
if context is None:
context = bpy.context
if is_blender_28():
return context.preferences
else:
return context.user_preferences
return context.preferences
def get_blender_preferences_temporary_directory(context=None):
@@ -548,45 +431,26 @@ def get_addon_preferences(context=None):
def ui_split(ui_element, factor=None, align=None):
if is_blender_28():
if factor is None and align is None:
return ui_element.split()
elif factor is None:
return ui_element.split(align=align)
elif align is None:
return ui_element.split(factor=factor)
else:
return ui_element.split(factor=factor, align=align)
if factor is None and align is None:
return ui_element.split()
elif factor is None:
return ui_element.split(align=align)
elif align is None:
return ui_element.split(factor=factor)
else:
if factor is None and align is None:
return ui_element.split()
elif factor is None:
return ui_element.split(align=align)
elif align is None:
return ui_element.split(percentage=factor)
else:
return ui_element.split(percentage=factor, align=align)
return ui_element.split(factor=factor, align=align)
def get_file_folder_icon():
if is_blender_28():
return "FILEBROWSER"
else:
return "FILESEL"
return "FILEBROWSER"
def get_hide_off_icon():
if is_blender_28():
return "HIDE_OFF"
else:
return "RESTRICT_VIEW_OFF"
return "HIDE_OFF"
def get_hide_on_icon():
if is_blender_28():
return "HIDE_ON"
else:
return "RESTRICT_VIEW_ON"
return "HIDE_ON"
def str_removesuffix(input_string, suffix):