Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2022-04-14 14:11:15 +0300
committerClément Foucault <foucault.clem@gmail.com>2022-04-14 14:11:15 +0300
commitc8b749d316c69bab5488f02eaa9f3d42fcbdeee7 (patch)
tree986784938eacfd905881d9b152447aacba48ccc2
parentd902a2063c8a163655a67339a01060c356763a41 (diff)
parent1440074cac0a0e2c659539c241947409c3bbdb16 (diff)
Merge branch 'master' into tmp-new-gpu-codegen
# Conflicts: # source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
-rw-r--r--release/datafiles/icons/brush.sculpt.displacement_smear.datbin4436 -> 3968 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.draw_sharp.datbin2492 -> 2492 bytes
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py11
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py6
-rw-r--r--release/scripts/startup/bl_ui/space_nla.py24
-rw-r--r--release/scripts/startup/bl_ui/space_time.py8
-rw-r--r--release/scripts/startup/bl_ui/space_topbar.py59
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py6
-rw-r--r--source/blender/blenfont/intern/blf_font.c5
-rw-r--r--source/blender/blenfont/intern/blf_internal_types.h1
-rw-r--r--source/blender/blenkernel/BKE_animsys.h13
-rw-r--r--source/blender/blenkernel/BKE_curves.hh5
-rw-r--r--source/blender/blenkernel/CMakeLists.txt6
-rw-r--r--source/blender/blenkernel/intern/anim_path.c8
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c924
-rw-r--r--source/blender/blenkernel/intern/armature_update.c10
-rw-r--r--source/blender/blenkernel/intern/constraint.c8
-rw-r--r--source/blender/blenkernel/intern/curve_bezier.cc5
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc4
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc13
-rw-r--r--source/blender/blenkernel/intern/editmesh.c16
-rw-r--r--source/blender/blenkernel/intern/editmesh_cache.cc (renamed from source/blender/blenkernel/intern/editmesh_cache.c)38
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curves.cc9
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.cc2
-rw-r--r--source/blender/blenkernel/intern/mesh_runtime.cc (renamed from source/blender/blenkernel/intern/mesh_runtime.c)104
-rw-r--r--source/blender/blenkernel/intern/mesh_wrapper.cc (renamed from source/blender/blenkernel/intern/mesh_wrapper.c)136
-rw-r--r--source/blender/blenkernel/intern/nla.c7
-rw-r--r--source/blender/blenkernel/intern/object.cc6
-rw-r--r--source/blender/blenkernel/intern/particle.c25
-rw-r--r--source/blender/blenkernel/intern/particle_child.c2
-rw-r--r--source/blender/blenkernel/intern/pointcache.c23
-rw-r--r--source/blender/blenkernel/intern/vfont.c8
-rw-r--r--source/blender/blenkernel/nla_private.h51
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c9
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c14
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c78
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c51
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h14
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c2
-rw-r--r--source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl9
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl10
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl9
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl21
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl11
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl12
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl44
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl10
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl1
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl6
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl3
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c6
-rw-r--r--source/blender/draw/engines/overlay/shaders/background_frag.glsl8
-rw-r--r--source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl6
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl6
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl9
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl25
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c3
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc1
-rw-r--r--source/blender/draw/intern/draw_cache_impl_subdivision.cc8
-rw-r--r--source/blender/draw/intern/draw_common.h5
-rw-r--r--source/blender/draw/intern/draw_hair.c159
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc67
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc2
-rw-r--r--source/blender/draw/intern/shaders/common_hair_lib.glsl5
-rw-r--r--source/blender/draw/intern/shaders/common_hair_refine_vert.glsl7
-rw-r--r--source/blender/draw/intern/shaders/common_smaa_lib.glsl28
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c6
-rw-r--r--source/blender/editors/animation/anim_markers.c78
-rw-r--r--source/blender/editors/animation/keyframing.c238
-rw-r--r--source/blender/editors/curves/intern/curves_ops.cc270
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt4
-rw-r--r--source/blender/editors/geometry/geometry_attributes.cc2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_draw_utils.c34
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c11
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c109
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c26
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c15
-rw-r--r--source/blender/editors/interface/interface_icons.c4
-rw-r--r--source/blender/editors/interface/interface_templates.c2
-rw-r--r--source/blender/editors/object/object_modifier.c4
-rw-r--r--source/blender/editors/render/render_shading.cc2
-rw-r--r--source/blender/editors/space_file/filelist.c2
-rw-r--r--source/blender/editors/space_nla/nla_edit.c15
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c43
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c1
-rw-r--r--source/blender/gpu/GPU_immediate_util.h11
-rw-r--r--source/blender/gpu/intern/gpu_immediate_util.c143
-rw-r--r--source/blender/gpu/opengl/gl_texture.cc2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl10
-rw-r--r--source/blender/makesdna/DNA_anim_types.h2
-rw-r--r--source/blender/makesdna/DNA_particle_types.h7
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc2
-rw-r--r--source/blender/python/gpu/gpu_py_shader.c9
-rw-r--r--source/blender/python/gpu/gpu_py_shader_create_info.cc35
-rw-r--r--tests/python/CMakeLists.txt8
99 files changed, 2454 insertions, 847 deletions
diff --git a/release/datafiles/icons/brush.sculpt.displacement_smear.dat b/release/datafiles/icons/brush.sculpt.displacement_smear.dat
index 5d422130ea3..9e4df45b2d2 100644
--- a/release/datafiles/icons/brush.sculpt.displacement_smear.dat
+++ b/release/datafiles/icons/brush.sculpt.displacement_smear.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.draw_sharp.dat b/release/datafiles/icons/brush.sculpt.draw_sharp.dat
index 1877c0ae4d4..9bea1b02894 100644
--- a/release/datafiles/icons/brush.sculpt.draw_sharp.dat
+++ b/release/datafiles/icons/brush.sculpt.draw_sharp.dat
Binary files differ
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index be6ea28e72e..fc86d02b83e 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -1026,7 +1026,7 @@ def km_markers(params):
*_template_items_select_actions(params, "marker.select_all"),
("marker.delete", {"type": 'X', "value": 'PRESS'}, None),
("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
+ op_panel("TOPBAR_PT_name_marker", {"type": 'F2', "value": 'PRESS'}, [("keep_open", False)]),
("marker.move", {"type": 'G', "value": 'PRESS'}, None),
("marker.camera_bind", {"type": 'B', "value": 'PRESS', "ctrl": True}, None),
])
@@ -1387,6 +1387,8 @@ def km_view3d(params):
("view3d.localview", {"type": 'NUMPAD_SLASH', "value": 'PRESS'}, None),
("view3d.localview", {"type": 'SLASH', "value": 'PRESS'}, None),
("view3d.localview", {"type": 'MOUSESMARTZOOM', "value": 'ANY'}, None),
+ ("view3d.localview_remove_from", {"type": 'NUMPAD_SLASH', "value": 'PRESS', "alt": True}, None),
+ ("view3d.localview_remove_from", {"type": 'SLASH', "value": 'PRESS', "alt": True}, None),
# Navigation.
("view3d.rotate", {"type": 'MOUSEROTATE', "value": 'ANY'}, None),
*((
@@ -1831,7 +1833,6 @@ def km_graph_editor(params):
*_template_items_proportional_editing(
params, connected=False, toggle_data_path='tool_settings.use_proportional_fcurve'),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
*_template_items_context_menu("GRAPH_MT_context_menu", params.context_menu_event),
])
@@ -2485,7 +2486,6 @@ def km_dopesheet(params):
*_template_items_proportional_editing(
params, connected=False, toggle_data_path='tool_settings.use_proportional_action'),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
("marker.camera_bind", {"type": 'B', "value": 'PRESS', "ctrl": True}, None),
*_template_items_context_menu("DOPESHEET_MT_context_menu", params.context_menu_event),
*_template_items_change_frame(params),
@@ -2509,7 +2509,8 @@ def km_nla_generic(_params):
*_template_space_region_type_toggle(
sidebar_key={"type": 'N', "value": 'PRESS'},
),
- ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'}, None),
+ ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'},
+ {"properties": [("use_upper_stack_evaluation", False)]}),
("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS'}, None),
("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS', "shift": True},
{"properties": [("isolate_action", True)]}),
@@ -2619,7 +2620,6 @@ def km_nla_editor(params):
("transform.transform", {"type": 'S', "value": 'PRESS'},
{"properties": [("mode", 'TIME_SCALE')]}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
*_template_items_context_menu("NLA_MT_context_menu", params.context_menu_event),
*_template_items_change_frame(params),
])
@@ -2928,7 +2928,6 @@ def km_sequencer(params):
("transform.transform", {"type": 'E', "value": 'PRESS'},
{"properties": [("mode", 'TIME_EXTEND')]}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
("sequencer.select_side_of_frame", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
{"properties": [("side", 'LEFT')]}),
("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
index 4c1b905ac05..64039f200e9 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -836,7 +836,7 @@ def km_markers(params):
("marker.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
("marker.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
+ op_panel("TOPBAR_PT_name_marker", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]),
("marker.move", {"type": 'W', "value": 'PRESS'}, None),
])
@@ -937,7 +937,6 @@ def km_graph_editor(params):
("wm.context_menu_enum", {"type": 'X', "value": 'PRESS'},
{"properties": [("data_path", 'space_data.auto_snap')]}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
op_menu_pie("GRAPH_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}),
])
@@ -1438,7 +1437,6 @@ def km_dopesheet(params):
("wm.context_toggle", {"type": 'B', "value": 'PRESS'},
{"properties": [("data_path", 'tool_settings.use_proportional_action')]}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
("anim.start_frame_set", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True}, None),
("anim.end_frame_set", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True}, None),
])
@@ -1548,7 +1546,6 @@ def km_nla_editor(params):
*_template_items_context_menu("NLA_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
op_menu_pie("NLA_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
])
return keymap
@@ -1835,7 +1832,6 @@ def km_sequencer(params):
{"properties": [("mode", 'TIME_EXTEND')]}),
*_template_items_context_menu("SEQUENCER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
# Tools
op_tool_cycle("builtin.select_box", {"type": 'Q', "value": 'PRESS'}),
op_tool_cycle("builtin.blade", {"type": 'B', "value": 'PRESS'}),
diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py
index 99324ac5d50..f0e991c768d 100644
--- a/release/scripts/startup/bl_ui/space_nla.py
+++ b/release/scripts/startup/bl_ui/space_nla.py
@@ -150,6 +150,23 @@ class NLA_MT_marker(Menu):
marker_menu_generic(layout, context)
+class NLA_MT_marker_select(Menu):
+ bl_label = 'Select'
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("marker.select_all", text="All").action = 'SELECT'
+ layout.operator("marker.select_all", text="None").action = 'DESELECT'
+ layout.operator("marker.select_all", text="Invert").action = 'INVERT'
+
+ layout.separator()
+
+ layout.operator("marker.select_leftright", text="Before Current Frame").mode = 'LEFT'
+ layout.operator("marker.select_leftright", text="After Current Frame").mode = 'RIGHT'
+
+
+
class NLA_MT_edit(Menu):
bl_label = "Edit"
@@ -197,7 +214,8 @@ class NLA_MT_edit(Menu):
layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions")
else:
layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True
- layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions")
+ layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Full Stack)").use_upper_stack_evaluation = True
+ layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Lower Stack)").use_upper_stack_evaluation = False
class NLA_MT_add(Menu):
@@ -271,7 +289,8 @@ class NLA_MT_context_menu(Menu):
layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions")
else:
layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True
- layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions")
+ layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Full Stack)").use_upper_stack_evaluation = True
+ layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Lower Stack)").use_upper_stack_evaluation = False
layout.separator()
@@ -312,6 +331,7 @@ classes = (
NLA_MT_view,
NLA_MT_select,
NLA_MT_marker,
+ NLA_MT_marker_select,
NLA_MT_add,
NLA_MT_edit_transform,
NLA_MT_snap_pie,
diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py
index 5296900fa30..13ab6e67b00 100644
--- a/release/scripts/startup/bl_ui/space_time.py
+++ b/release/scripts/startup/bl_ui/space_time.py
@@ -187,11 +187,17 @@ def marker_menu_generic(layout, context):
layout.separator()
- layout.operator("marker.rename", text="Rename Marker")
+ props = layout.operator("wm.call_panel", text="Rename Marker")
+ props.name = "TOPBAR_PT_name_marker"
+ props.keep_open = False
layout.operator("marker.move", text="Move Marker")
layout.separator()
+ layout.menu('NLA_MT_marker_select')
+
+ layout.separator()
+
layout.operator("marker.camera_bind")
layout.separator()
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index 55dcb4a20eb..2cf50bdbf95 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -851,6 +851,64 @@ class TOPBAR_PT_name(Panel):
row.label(text="No active item")
+class TOPBAR_PT_name_marker(Panel):
+ bl_space_type = 'TOPBAR' # dummy
+ bl_region_type = 'HEADER'
+ bl_label = "Rename Marker"
+ bl_ui_units_x = 14
+
+ @staticmethod
+ def is_using_pose_markers(context):
+ sd = context.space_data
+ return (sd.type == 'DOPESHEET_EDITOR' and sd.mode in {'ACTION', 'SHAPEKEY'} and
+ sd.show_pose_markers and sd.action)
+
+ @staticmethod
+ def get_selected_marker(context):
+ if TOPBAR_PT_name_marker.is_using_pose_markers(context):
+ markers = context.space_data.action.pose_markers
+ else:
+ markers = context.scene.timeline_markers
+
+ for marker in markers:
+ if marker.select:
+ return marker
+ return None
+
+ @staticmethod
+ def row_with_icon(layout, icon):
+ row = layout.row()
+ row.activate_init = True
+ row.label(icon=icon)
+ return row
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.label(text="Marker Name")
+
+ scene = context.scene
+ if scene.tool_settings.lock_markers:
+ row = self.row_with_icon(layout, 'ERROR')
+ label = "Markers are locked"
+ row.label(text=label)
+ return
+
+ marker = self.get_selected_marker(context)
+ if marker is None:
+ row = self.row_with_icon(layout, 'ERROR')
+ row.label(text="No active marker")
+ return
+
+ icon = 'TIME'
+ if marker.camera is not None:
+ icon = 'CAMERA_DATA'
+ elif self.is_using_pose_markers(context):
+ icon = 'ARMATURE_DATA'
+ row = self.row_with_icon(layout, icon)
+ row.prop(marker, "name", text="")
+
+
classes = (
TOPBAR_HT_upper_bar,
TOPBAR_MT_file_context_menu,
@@ -877,6 +935,7 @@ classes = (
TOPBAR_PT_gpencil_layers,
TOPBAR_PT_gpencil_primitive,
TOPBAR_PT_name,
+ TOPBAR_PT_name_marker,
)
if __name__ == "__main__": # only for live edit.
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 035c4fd1352..b954d726ca3 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -2281,7 +2281,8 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
),
)
-
+# Keep this as tweaks can be useful to restore.
+"""
class USERPREF_PT_experimental_tweaks(ExperimentalPanel, Panel):
bl_label = "Tweaks"
@@ -2292,6 +2293,7 @@ class USERPREF_PT_experimental_tweaks(ExperimentalPanel, Panel):
),
)
+"""
class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel):
bl_label = "Debugging"
@@ -2413,7 +2415,7 @@ classes = (
USERPREF_PT_experimental_new_features,
USERPREF_PT_experimental_prototypes,
- USERPREF_PT_experimental_tweaks,
+ # USERPREF_PT_experimental_tweaks,
USERPREF_PT_experimental_debugging,
# Add dynamically generated editor theme panels last,
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 51b65dab8fc..f93cb8b2d64 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -921,6 +921,9 @@ static void blf_font_wrap_apply(FontBLF *font,
int lines = 0;
ft_pix pen_x_next = 0;
+ /* Space between lines needs to be aligned to the pixel grid (T97310). */
+ ft_pix line_height = FT_PIX_FLOOR(blf_font_height_max_ft_pix(font));
+
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
struct WordWrapVars {
@@ -978,7 +981,7 @@ static void blf_font_wrap_apply(FontBLF *font,
wrap.start = wrap.last[0];
i = wrap.last[1];
pen_x = 0;
- pen_y -= blf_font_height_max_ft_pix(font);
+ pen_y -= line_height;
g_prev = NULL;
lines += 1;
continue;
diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h
index 79388752969..62bce36dda0 100644
--- a/source/blender/blenfont/intern/blf_internal_types.h
+++ b/source/blender/blenfont/intern/blf_internal_types.h
@@ -76,7 +76,6 @@ BLI_INLINE ft_pix ft_pix_round_advance(ft_pix v, ft_pix step)
return FT_PIX_DEFAULT_ROUNDING(v) + FT_PIX_DEFAULT_ROUNDING(step);
}
-#undef FT_PIX_FLOOR
#undef FT_PIX_ROUND
#undef FT_PIX_CEIL
#undef FT_PIX_DEFAULT_ROUNDING
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h
index ded64b68f79..91ecfe09f38 100644
--- a/source/blender/blenkernel/BKE_animsys.h
+++ b/source/blender/blenkernel/BKE_animsys.h
@@ -7,6 +7,7 @@
* \ingroup bke
*/
+#include "BLI_bitmap.h"
#include "BLI_sys_types.h" /* for bool */
#ifdef __cplusplus
@@ -258,16 +259,20 @@ struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
* \param count: Number of values in the array.
* \param index: Index of the element about to be updated, or -1.
* \param[out] r_force_all: Set to true if all channels must be inserted. May be NULL.
- * \return False if correction fails due to a division by zero,
- * or null r_force_all when all channels are required.
+ * \param[out] r_successful_remaps: Bits will be enabled for indices that are both intended to be
+ * remapped and succeeded remapping. With both, it allows caller to check successfully remapped
+ * indices without having to explicitly check whether the index was intended to be remapped.
*/
-bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
+void BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
struct PointerRNA *prop_ptr,
struct PropertyRNA *prop,
float *values,
int count,
int index,
- bool *r_force_all);
+ const struct AnimationEvalContext *anim_eval_context,
+ bool *r_force_all,
+ BLI_bitmap *r_successful_remaps);
+
/**
* Free all cached contexts from the list.
*/
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 9dafe2095e7..06971a2243a 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -76,6 +76,11 @@ class CurvesGeometryRuntime {
mutable Vector<float3> evaluated_position_cache;
mutable std::mutex position_cache_mutex;
mutable bool position_cache_dirty = true;
+ /**
+ * The evaluated positions result, using a separate span in case all curves are poly curves,
+ * in which case a separate array of evaluated positions is unnecessary.
+ */
+ mutable Span<float3> evaluated_positions_span;
/**
* Cache of lengths along each evaluated curve for for each evaluated point. If a curve is
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index c8af4bb69b8..ce4131a0627 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -126,7 +126,7 @@ set(SRC
intern/editlattice.c
intern/editmesh.c
intern/editmesh_bvh.c
- intern/editmesh_cache.c
+ intern/editmesh_cache.cc
intern/editmesh_tangent.c
intern/effect.c
intern/fcurve.c
@@ -203,12 +203,12 @@ set(SRC
intern/mesh_normals.cc
intern/mesh_remap.c
intern/mesh_remesh_voxel.cc
- intern/mesh_runtime.c
+ intern/mesh_runtime.cc
intern/mesh_sample.cc
intern/mesh_tangent.c
intern/mesh_tessellate.c
intern/mesh_validate.cc
- intern/mesh_wrapper.c
+ intern/mesh_wrapper.cc
intern/modifier.c
intern/movieclip.c
intern/multires.c
diff --git a/source/blender/blenkernel/intern/anim_path.c b/source/blender/blenkernel/intern/anim_path.c
index 1f8c6df6147..a8c25069c19 100644
--- a/source/blender/blenkernel/intern/anim_path.c
+++ b/source/blender/blenkernel/intern/anim_path.c
@@ -295,10 +295,12 @@ bool BKE_where_on_path(const Object *ob,
key_curve_tangent_weights(frac, w, KEY_BSPLINE);
- interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w);
+ if (r_dir) {
+ interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w);
- /* Make compatible with #vec_to_quat. */
- negate_v3(r_dir);
+ /* Make compatible with #vec_to_quat. */
+ negate_v3(r_dir);
+ }
//}
const ListBase *nurbs = BKE_curve_editNurbs_get(cu);
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 8a5bf2b81dd..54fee079947 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -1531,6 +1531,192 @@ static NlaEvalChannel *nlaevalchan_verify(PointerRNA *ptr, NlaEvalData *nlaeval,
/* ---------------------- */
+/** \returns true if a solution exists and the output was written to. */
+static bool nla_blend_get_inverted_lower_value(const int blendmode,
+ const float strip_value,
+ const float blended_value,
+ const float influence,
+ float *r_lower_value)
+{
+ if (IS_EQF(influence, 0.0f)) {
+ *r_lower_value = blended_value;
+ return true;
+ }
+
+ switch (blendmode) {
+ case NLASTRIP_MODE_ADD:
+ /* Simply subtract the scaled value on to the stack. */
+ *r_lower_value = blended_value - (strip_value * influence);
+ return true;
+
+ case NLASTRIP_MODE_SUBTRACT:
+ /* Simply add the scaled value from the stack. */
+ *r_lower_value = blended_value + (strip_value * influence);
+ return true;
+
+ case NLASTRIP_MODE_MULTIPLY: {
+ /* Check for division by zero. */
+ const float denominator = (influence * strip_value + (1.0f - influence));
+ if (IS_EQF(denominator, 0.0f)) {
+ /* For 0/0, any r_lower_value is a solution. We'll just choose 1.
+ *
+ * Any r_lower_value is a solution. In this case, ideally we would insert redundant
+ * keyframes, those that don't change the fcurve's shape. Otherwise, we're likely messing
+ * up interpolation for the animator, requiring further cleanup on their part.
+ */
+ if (IS_EQF(blended_value, 0.0f)) {
+ /* When denominator==0:
+ *
+ * denominator = (inf * strip_value + (1.0f - inf))
+ * 0 = inf * strip_value + (1-inf)
+ * -inf * strip_value = 1 - inf
+ * -strip_value = (1 - inf) / inf
+ * strip_value = (inf - 1) / inf
+ * strip_value = 1 - (1/inf)
+ *
+ * For blending, nla_blend_value(), this results in:
+ *
+ * blended_value = inf * (lower_value * strip_value) + (1 - inf) * lower_value;
+ * = inf * (lower_value * (1 - (1/inf))) + ...
+ * = inf * (1 - (1/inf)) * lower_value + ...
+ * = (inf - (inf/inf)) * lower_value + ...
+ * = -(inf - 1) * lower_value + (1 - inf) * lower_value;
+ * blended_value = 0
+ *
+ * Effectively, blended_value will equal 0 no matter what lower_value is. Put another
+ * way, when (blended_value==0 and denominator==0), then lower_value can be any value and
+ * blending will give us back blended_value=0. We have infinite solutions for this case.
+ */
+ *r_lower_value = 1;
+ return true;
+ }
+ /* No solution for division by zero. */
+ return false;
+ }
+ /* Math:
+ * blended_value = inf * (lower_value * strip_value) + (1 - inf) * lower_value
+ * = lower_value * (inf * strip_value + (1-inf))
+ * lower_value = blended_value / (inf * strip_value + (1-inf))
+ * lower_value = blended_value / denominator
+ */
+ *r_lower_value = blended_value / denominator;
+ return true;
+ }
+ case NLASTRIP_MODE_COMBINE:
+ BLI_assert_msg(0, "Use nla_combine_get_inverted_lower_value()");
+ return false;
+
+ case NLASTRIP_MODE_REPLACE:
+
+ /* No solution if lower strip has 0 influence. */
+ if (IS_EQF(influence, 1.0f)) {
+ return false;
+ }
+
+ /* Math:
+ *
+ * blended_value = lower_value * (1.0f - inf) + (strip_value * inf)
+ * blended_value - (strip_value * inf) = lower_value * (1.0f - inf)
+ * blended_value - (strip_value * inf) / (1.0f - inf) = lower_value
+ *
+ * lower_value = blended_value - (strip_value * inf) / (1.0f - inf)
+ */
+ *r_lower_value = (blended_value - (strip_value * influence)) / (1.0f - influence);
+ return true;
+ }
+
+ BLI_assert_msg(0, "invalid blend mode");
+ return false;
+}
+
+/** \returns true if solution exists and output written to. */
+static bool nla_combine_get_inverted_lower_value(const int mix_mode,
+ float base_value,
+ const float strip_value,
+ const float blended_value,
+ const float influence,
+ float *r_lower_value)
+{
+ if (IS_EQF(influence, 0.0f)) {
+ *r_lower_value = blended_value;
+ return true;
+ }
+
+ /* Perform blending. */
+ switch (mix_mode) {
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ *r_lower_value = blended_value - (strip_value - base_value) * influence;
+ return true;
+ case NEC_MIX_MULTIPLY:
+ /* Division by zero. */
+ if (IS_EQF(strip_value, 0.0f)) {
+ /* Resolve 0/0 to 1.
+ *
+ * Any r_lower_value is a solution. In this case, ideally we would insert redundant
+ * keyframes, those that don't change the fcurve's shape. Otherwise, we're likely messing
+ * up interpolation for the animator, requiring further cleanup on their part.
+ */
+ if (IS_EQF(blended_value, 0.0f)) {
+ /* For blending, nla_combine_value(), when strip_value==0:
+ *
+ * blended_value = lower_value * powf(strip_value / base_value, infl);
+ * blended_value = lower_value * powf(0, infl);
+ * blended_value = lower_value * 0;
+ * blended_value = 0;
+ *
+ * Effectively, blended_value will equal 0 no matter what lower_value is. Put another
+ * way, when (blended_value==0 and strip_value==0), then lower_value can be any value and
+ * blending will give us back blended_value=0. We have infinite solutions for this case.
+ */
+ *r_lower_value = 1.0f;
+ return true;
+ }
+ /* No solution. */
+ return false;
+ }
+
+ if (IS_EQF(base_value, 0.0f)) {
+ base_value = 1.0f;
+ }
+
+ *r_lower_value = blended_value / powf(strip_value / base_value, influence);
+ return true;
+
+ case NEC_MIX_QUATERNION:
+ BLI_assert_msg(0, "Use nla_combine_quaternion_get_inverted_lower_values()");
+ return false;
+ }
+
+ BLI_assert_msg(0, "Mixmode not implemented");
+ return false;
+}
+
+static void nla_combine_quaternion_get_inverted_lower_values(const float strip_values[4],
+ const float blended_values[4],
+ const float influence,
+ float r_lower_value[4])
+{
+ if (IS_EQF(influence, 0.0f)) {
+ normalize_qt_qt(r_lower_value, blended_values);
+ return;
+ }
+
+ /* blended_value = lower_values @ strip_values^infl
+ * blended_value @ inv(strip_values^inf) = lower_values
+ *
+ * Returns: lower_values = blended_value @ inv(strip_values^inf) */
+ float tmp_strip_values[4], tmp_blended[4];
+
+ normalize_qt_qt(tmp_strip_values, strip_values);
+ normalize_qt_qt(tmp_blended, blended_values);
+
+ pow_qt_fl_normalized(tmp_strip_values, influence);
+ invert_qt_normalized(tmp_strip_values);
+
+ mul_qt_qtqt(r_lower_value, tmp_blended, tmp_strip_values);
+}
+
/* Blend the lower nla stack value and upper strip value of a channel according to mode and
* influence. */
static float nla_blend_value(const int blendmode,
@@ -1772,6 +1958,25 @@ static void nlaevalchan_assert_blendOrcombine_compatible(NlaEvalChannelSnapshot
BLI_assert(lower_necs->length == blended_necs->length);
}
+/** Check each remap domain of blended values individually in case animator had a non-combine NLA
+ * strip with a subset of quaternion channels and remapping through any of them failed and thus
+ * potentially has undefined values.
+ *
+ * \returns true if case occured and handled. Returns false if case didn't occur.
+ */
+static bool nlaevalchan_combine_quaternion_handle_undefined_blend_values(
+ NlaEvalChannelSnapshot *blended_necs, NlaEvalChannelSnapshot *upper_or_lower_necs)
+{
+ for (int j = 0; j < 4; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_bitmap_set_all(upper_or_lower_necs->remap_domain.ptr, false, 4);
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* Assert that the channels given can be blended or combined together as a quaternion. */
static void nlaevalchan_assert_blendOrcombine_compatible_quaternion(
NlaEvalChannelSnapshot *lower_necs,
@@ -1787,14 +1992,12 @@ static void nlaevalchan_copy_values(NlaEvalChannelSnapshot *dst, NlaEvalChannelS
memcpy(dst->values, src->values, src->length * sizeof(float));
}
-/**
- * Copies lower necs to blended necs if upper necs is NULL or has zero influence.
- * \return true if copied.
- */
-static bool nlaevalchan_blendOrcombine_try_copy_lower(NlaEvalChannelSnapshot *lower_necs,
- NlaEvalChannelSnapshot *upper_necs,
- const float upper_influence,
- NlaEvalChannelSnapshot *r_blended_necs)
+/** Copies from lower necs to blended necs if upper necs is NULL or has zero influence.
+ * \return true if copied. */
+static bool nlaevalchan_blendOrcombine_try_copy_from_lower(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
{
const bool has_influence = !IS_EQF(upper_influence, 0.0f);
if (upper_necs != NULL && has_influence) {
@@ -1805,10 +2008,35 @@ static bool nlaevalchan_blendOrcombine_try_copy_lower(NlaEvalChannelSnapshot *lo
return true;
}
-/**
- * Based on blend-mode, blend lower necs with upper necs into blended necs.
+/** Copies to lower necs from blended necs if upper necs is NULL or has zero influence. If
+ * successful, copies blended_necs remap domains to lower_necs.
*
- * Each upper value's blend domain determines whether to blend or to copy directly from lower.
+ * Does not check upper value blend domains.
+ *
+ * \return true if copied. */
+static bool nlaevalchan_blendOrcombine_try_copy_to_lower(NlaEvalChannelSnapshot *blended_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_lower_necs)
+{
+ const bool has_influence = !IS_EQF(upper_influence, 0.0f);
+ if (upper_necs != NULL && has_influence) {
+ return false;
+ }
+
+ nlaevalchan_copy_values(r_lower_necs, blended_necs);
+
+ /* Must copy remap domain to handle case where some blended values are out of domain. */
+ BLI_bitmap_copy_all(
+ r_lower_necs->remap_domain.ptr, blended_necs->remap_domain.ptr, r_lower_necs->length);
+
+ return true;
+}
+
+/** Based on blendmode, blend lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly
+ * from lower.
*/
static void nlaevalchan_blend_value(NlaEvalChannelSnapshot *lower_necs,
NlaEvalChannelSnapshot *upper_necs,
@@ -1817,7 +2045,7 @@ static void nlaevalchan_blend_value(NlaEvalChannelSnapshot *lower_necs,
NlaEvalChannelSnapshot *r_blended_necs)
{
nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
- if (nlaevalchan_blendOrcombine_try_copy_lower(
+ if (nlaevalchan_blendOrcombine_try_copy_from_lower(
lower_necs, upper_necs, upper_influence, r_blended_necs)) {
return;
}
@@ -1846,7 +2074,7 @@ static void nlaevalchan_combine_value(NlaEvalChannelSnapshot *lower_necs,
NlaEvalChannelSnapshot *r_blended_necs)
{
nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
- if (nlaevalchan_blendOrcombine_try_copy_lower(
+ if (nlaevalchan_blendOrcombine_try_copy_from_lower(
lower_necs, upper_necs, upper_influence, r_blended_necs)) {
return;
}
@@ -1879,7 +2107,7 @@ static void nlaevalchan_combine_quaternion(NlaEvalChannelSnapshot *lower_necs,
NlaEvalChannelSnapshot *r_blended_necs)
{
nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, upper_necs, r_blended_necs);
- if (nlaevalchan_blendOrcombine_try_copy_lower(
+ if (nlaevalchan_blendOrcombine_try_copy_from_lower(
lower_necs, upper_necs, upper_influence, r_blended_necs)) {
return;
}
@@ -2033,14 +2261,8 @@ static void nlaevalchan_combine_quaternion_get_inverted_upper_evalchan(
nlaevalchan_assert_nonNull(r_upper_necs);
nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, r_upper_necs, blended_necs);
- /* Must check each domain index individually in case animator had a non-combine NLA strip with a
- * subset of quaternion channels and remapping through any of them failed and thus potentially
- * has undefined values. */
- for (int j = 0; j < 4; j++) {
- if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
- BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, 4);
- return;
- }
+ if (nlaevalchan_combine_quaternion_handle_undefined_blend_values(blended_necs, r_upper_necs)) {
+ return;
}
const bool success = nla_combine_quaternion_get_inverted_strip_values(
@@ -2109,6 +2331,173 @@ static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
}
}
+static void nlaevalchan_blend_value_get_inverted_lower_evalchan(
+ NlaEvalChannelSnapshot *blended_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_lower_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(r_lower_necs, upper_necs, blended_necs);
+
+ if (nlaevalchan_blendOrcombine_try_copy_to_lower(
+ blended_necs, upper_necs, upper_influence, r_lower_necs)) {
+ return;
+ }
+
+ const int length = r_lower_necs->length;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_lower_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ /* If upper value was not blended, then the blended value was directly copied from the lower
+ * value. */
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_lower_necs->values[j] = blended_necs->values[j];
+ BLI_BITMAP_ENABLE(r_lower_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_blend_get_inverted_lower_value(upper_blendmode,
+ upper_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_lower_necs->values[j]);
+
+ BLI_BITMAP_SET(r_lower_necs->remap_domain.ptr, j, success);
+ }
+}
+
+static void nlaevalchan_combine_value_get_inverted_lower_evalchan(
+ NlaEvalChannelSnapshot *blended_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_lower_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(r_lower_necs, upper_necs, blended_necs);
+
+ if (nlaevalchan_blendOrcombine_try_copy_to_lower(
+ blended_necs, upper_necs, upper_influence, r_lower_necs)) {
+ return;
+ }
+
+ float *base_values = r_lower_necs->channel->base_snapshot.values;
+ const int mix_mode = r_lower_necs->channel->mix_mode;
+ const int length = r_lower_necs->length;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_lower_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ /* If upper value was not blended, then the blended value was directly copied from the lower
+ * value. */
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_lower_necs->values[j] = blended_necs->values[j];
+ BLI_BITMAP_ENABLE(r_lower_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_combine_get_inverted_lower_value(mix_mode,
+ base_values[j],
+ upper_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_lower_necs->values[j]);
+
+ BLI_BITMAP_SET(r_lower_necs->remap_domain.ptr, j, success);
+ }
+}
+
+static void nlaevalchan_combine_quaternion_get_inverted_lower_evalchan(
+ NlaEvalChannelSnapshot *blended_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_lower_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible_quaternion(r_lower_necs, upper_necs, blended_necs);
+
+ if (nlaevalchan_combine_quaternion_handle_undefined_blend_values(blended_necs, r_lower_necs)) {
+ return;
+ }
+
+ if (nlaevalchan_blendOrcombine_try_copy_to_lower(
+ blended_necs, upper_necs, upper_influence, r_lower_necs)) {
+ return;
+ }
+
+ /* If upper value was not blended, then the blended value was directly copied from the lower
+ * value. */
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) {
+ memcpy(r_lower_necs->values, blended_necs->values, 4 * sizeof(float));
+ BLI_bitmap_set_all(r_lower_necs->remap_domain.ptr, true, 4);
+ return;
+ }
+
+ nla_combine_quaternion_get_inverted_lower_values(
+ upper_necs->values, blended_necs->values, upper_influence, r_lower_necs->values);
+
+ BLI_bitmap_set_all(r_lower_necs->remap_domain.ptr, true, 4);
+}
+
+/** Based on blendmode and mix mode, solve for the lower values such that when lower blended or
+ * combined with upper then we get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped lower values are placed
+ * in the remap domain so caller knows which values are usable.
+ *
+ * \param blended_necs: Never NULL.
+ * \param upper_necs: Can be NULL.
+ * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode.
+ * \param upper_influence: Value in range [0, 1].
+ * \param r_lower_necs: Never NULL.
+ */
+static void nlaevalchan_blendOrCombine_get_inverted_lower_evalchan(
+ NlaEvalChannelSnapshot *blended_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_lower_necs)
+
+{
+ nlaevalchan_assert_nonNull(r_lower_necs);
+
+ switch (upper_blendmode) {
+ case NLASTRIP_MODE_COMBINE: {
+ switch (r_lower_necs->channel->mix_mode) {
+ case NEC_MIX_QUATERNION: {
+ nlaevalchan_combine_quaternion_get_inverted_lower_evalchan(
+ blended_necs, upper_necs, upper_influence, r_lower_necs);
+ return;
+ }
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ case NEC_MIX_MULTIPLY: {
+ nlaevalchan_combine_value_get_inverted_lower_evalchan(
+ blended_necs, upper_necs, upper_influence, r_lower_necs);
+ return;
+ }
+ }
+ BLI_assert_msg(0, "Mix mode should've been handled");
+ return;
+ }
+ case NLASTRIP_MODE_ADD:
+ case NLASTRIP_MODE_SUBTRACT:
+ case NLASTRIP_MODE_MULTIPLY:
+ case NLASTRIP_MODE_REPLACE: {
+ nlaevalchan_blend_value_get_inverted_lower_evalchan(
+ blended_necs, upper_necs, upper_blendmode, upper_influence, r_lower_necs);
+ return;
+ }
+ }
+
+ BLI_assert_msg(0, "Blend mode should've been handled");
+}
+
/* ---------------------- */
/* F-Modifier stack joining/separation utilities -
* should we generalize these for BLI_listbase.h interface? */
@@ -2223,7 +2612,8 @@ static void nlasnapshot_from_action(PointerRNA *ptr,
}
/* evaluate action-clip strip */
-static void nlastrip_evaluate_actionclip(PointerRNA *ptr,
+static void nlastrip_evaluate_actionclip(const int evaluation_mode,
+ PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
@@ -2247,22 +2637,49 @@ static void nlastrip_evaluate_actionclip(PointerRNA *ptr,
/* join this strip's modifiers to the parent's modifiers (own modifiers first) */
nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers);
- NlaEvalSnapshot strip_snapshot;
- nlaeval_snapshot_init(&strip_snapshot, channels, NULL);
+ switch (evaluation_mode) {
+ case STRIP_EVAL_BLEND: {
+
+ NlaEvalSnapshot strip_snapshot;
+ nlaeval_snapshot_init(&strip_snapshot, channels, NULL);
+
+ nlasnapshot_from_action(
+ ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot);
+ nlasnapshot_blend(
+ channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot);
- nlasnapshot_from_action(
- ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot);
- nlasnapshot_blend(
- channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot);
+ nlaeval_snapshot_free_data(&strip_snapshot);
+
+ break;
+ }
+ case STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT: {
- nlaeval_snapshot_free_data(&strip_snapshot);
+ NlaEvalSnapshot strip_snapshot;
+ nlaeval_snapshot_init(&strip_snapshot, channels, NULL);
+
+ nlasnapshot_from_action(
+ ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot);
+ nlasnapshot_blend_get_inverted_lower_snapshot(
+ channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot);
+
+ nlaeval_snapshot_free_data(&strip_snapshot);
+
+ break;
+ }
+ case STRIP_EVAL_NOBLEND: {
+ nlasnapshot_from_action(
+ ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, snapshot);
+ break;
+ }
+ }
/* unlink this strip's modifiers from the parent's modifiers again */
nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers);
}
/* evaluate transition strip */
-static void nlastrip_evaluate_transition(PointerRNA *ptr,
+static void nlastrip_evaluate_transition(const int evaluation_mode,
+ PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
@@ -2294,49 +2711,126 @@ static void nlastrip_evaluate_transition(PointerRNA *ptr,
s2 = nes->strip->next;
}
- /* prepare template for 'evaluation strip'
- * - based on the transition strip's evaluation strip data
- * - strip_mode is NES_TIME_TRANSITION_* based on which endpoint
- * - strip_time is the 'normalized' (i.e. in-strip) time for evaluation,
- * which doubles up as an additional weighting factor for the strip influences
- * which allows us to appear to be 'interpolating' between the two extremes
- */
- tmp_nes = *nes;
-
- /* evaluate these strips into a temp-buffer (tmp_channels) */
- /* FIXME: modifier evaluation here needs some work... */
- /* first strip */
- tmp_nes.strip_mode = NES_TIME_TRANSITION_START;
- tmp_nes.strip = s1;
- tmp_nes.strip_time = s1->strip_time;
- nlaeval_snapshot_init(&snapshot1, channels, snapshot);
- nlastrip_evaluate(
- ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot1, anim_eval_context, flush_to_original);
+ switch (evaluation_mode) {
+ case STRIP_EVAL_BLEND: {
- /* second strip */
- tmp_nes.strip_mode = NES_TIME_TRANSITION_END;
- tmp_nes.strip = s2;
- tmp_nes.strip_time = s2->strip_time;
- nlaeval_snapshot_init(&snapshot2, channels, snapshot);
- nlastrip_evaluate(
- ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot2, anim_eval_context, flush_to_original);
+ /* prepare template for 'evaluation strip'
+ * - based on the transition strip's evaluation strip data
+ * - strip_mode is NES_TIME_TRANSITION_* based on which endpoint
+ * - strip_time is the 'normalized' (i.e. in-strip) time for evaluation,
+ * which doubles up as an additional weighting factor for the strip influences
+ * which allows us to appear to be 'interpolating' between the two extremes
+ */
+ tmp_nes = *nes;
+
+ /* evaluate these strips into a temp-buffer (tmp_channels) */
+ /* FIXME: modifier evaluation here needs some work... */
+ /* first strip */
+ tmp_nes.strip_mode = NES_TIME_TRANSITION_START;
+ tmp_nes.strip = s1;
+ tmp_nes.strip_time = s1->strip_time;
+ nlaeval_snapshot_init(&snapshot1, channels, snapshot);
+ nlasnapshot_blend_strip(ptr,
+ channels,
+ &tmp_modifiers,
+ &tmp_nes,
+ &snapshot1,
+ anim_eval_context,
+ flush_to_original);
+
+ /* second strip */
+ tmp_nes.strip_mode = NES_TIME_TRANSITION_END;
+ tmp_nes.strip = s2;
+ tmp_nes.strip_time = s2->strip_time;
+ nlaeval_snapshot_init(&snapshot2, channels, snapshot);
+ nlasnapshot_blend_strip(ptr,
+ channels,
+ &tmp_modifiers,
+ &tmp_nes,
+ &snapshot2,
+ anim_eval_context,
+ flush_to_original);
+
+ /** Replace \a snapshot2 NULL channels with base or default values so all channels blend. */
+ nlasnapshot_ensure_channels(channels, &snapshot2);
+ /** Mark all \a snapshot2 channel's values to blend. */
+ nlasnapshot_enable_all_blend_domain(&snapshot2);
+ nlasnapshot_blend(
+ channels, &snapshot1, &snapshot2, NLASTRIP_MODE_REPLACE, nes->strip_time, snapshot);
+
+ nlaeval_snapshot_free_data(&snapshot1);
+ nlaeval_snapshot_free_data(&snapshot2);
+
+ break;
+ }
+ case STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT: {
+ /* No support for remapping values through a transition. Mark all channel values affected by
+ * transition as non-remappable. */
+ tmp_nes = *nes;
+
+ /* Process first strip. */
+ tmp_nes.strip = s1;
+ tmp_nes.strip_time = s1->strip_time;
+ nlaeval_snapshot_init(&snapshot1, channels, snapshot);
+ nlasnapshot_blend_strip_no_blend(
+ ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot1, anim_eval_context);
+
+ /* Remove channel values affected by transition from the remap domain. */
+ LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) {
+ NlaEvalChannelSnapshot *necs = nlaeval_snapshot_get(&snapshot1, nec->index);
+ if (necs == NULL) {
+ continue;
+ }
+ NlaEvalChannelSnapshot *output_necs = nlaeval_snapshot_ensure_channel(snapshot, nec);
+ for (int i = 0; i < necs->length; i++) {
+ if (BLI_BITMAP_TEST_BOOL(necs->blend_domain.ptr, i)) {
+ BLI_BITMAP_DISABLE(output_necs->remap_domain.ptr, i);
+ }
+ }
+ }
+
+ nlaeval_snapshot_free_data(&snapshot1);
+
+ /* Process second strip. */
+ tmp_nes.strip = s2;
+ tmp_nes.strip_time = s2->strip_time;
+ nlaeval_snapshot_init(&snapshot2, channels, snapshot);
+ nlasnapshot_blend_strip_no_blend(
+ ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot2, anim_eval_context);
+
+ /* Remove channel values affected by transition from the remap domain. */
+ LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) {
+ NlaEvalChannelSnapshot *necs = nlaeval_snapshot_get(&snapshot2, nec->index);
+ if (necs == NULL) {
+ continue;
+ }
+ NlaEvalChannelSnapshot *output_necs = nlaeval_snapshot_ensure_channel(snapshot, nec);
+ for (int i = 0; i < necs->length; i++) {
+ if (BLI_BITMAP_TEST_BOOL(necs->blend_domain.ptr, i)) {
+ BLI_BITMAP_DISABLE(output_necs->remap_domain.ptr, i);
+ }
+ }
+ }
- /** Replace \a snapshot2 NULL channels with base or default values so all channels blend. */
- nlasnapshot_ensure_channels(channels, &snapshot2);
- /** Mark all \a snapshot2 channel's values to blend. */
- nlasnapshot_enable_all_blend_domain(&snapshot2);
- nlasnapshot_blend(
- channels, &snapshot1, &snapshot2, NLASTRIP_MODE_REPLACE, nes->strip_time, snapshot);
+ nlaeval_snapshot_free_data(&snapshot2);
- nlaeval_snapshot_free_data(&snapshot1);
- nlaeval_snapshot_free_data(&snapshot2);
+ break;
+ }
+ case STRIP_EVAL_NOBLEND: {
+ BLI_assert(
+ !"This case shouldn't occur. Transitions assumed to not reference other "
+ "transitions. ");
+ break;
+ }
+ }
/* unlink this strip's modifiers from the parent's modifiers again */
nlaeval_fmodifiers_split_stacks(&nes->strip->modifiers, modifiers);
}
/* evaluate meta-strip */
-static void nlastrip_evaluate_meta(PointerRNA *ptr,
+static void nlastrip_evaluate_meta(const int evaluation_mode,
+ PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
@@ -2366,12 +2860,31 @@ static void nlastrip_evaluate_meta(PointerRNA *ptr,
evaltime);
tmp_nes = nlastrips_ctime_get_strip(NULL, &strip->strips, -1, &child_context, flush_to_original);
+ /* Assert currently supported modes. If new mode added, then assertion marks potentially missed
+ * area.
+ *
+ * Note: In the future if support is ever added to metastrips to support nested tracks, then
+ * STRIP_EVAL_BLEND and STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT cases are no longer
+ * equivalent. The output of nlastrips_ctime_get_strip() may return a list of strips. The only
+ * case difference should be the evaluation order.
+ */
+ BLI_assert(ELEM(evaluation_mode,
+ STRIP_EVAL_BLEND,
+ STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT,
+ STRIP_EVAL_NOBLEND));
+
/* directly evaluate child strip into accumulation buffer...
* - there's no need to use a temporary buffer (as it causes issues [T40082])
*/
if (tmp_nes) {
- nlastrip_evaluate(
- ptr, channels, &tmp_modifiers, tmp_nes, snapshot, &child_context, flush_to_original);
+ nlastrip_evaluate(evaluation_mode,
+ ptr,
+ channels,
+ &tmp_modifiers,
+ tmp_nes,
+ snapshot,
+ &child_context,
+ flush_to_original);
/* free temp eval-strip */
MEM_freeN(tmp_nes);
@@ -2381,7 +2894,8 @@ static void nlastrip_evaluate_meta(PointerRNA *ptr,
nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers);
}
-void nlastrip_evaluate(PointerRNA *ptr,
+void nlastrip_evaluate(const int evaluation_mode,
+ PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
@@ -2406,15 +2920,27 @@ void nlastrip_evaluate(PointerRNA *ptr,
/* actions to take depend on the type of strip */
switch (strip->type) {
case NLASTRIP_TYPE_CLIP: /* action-clip */
- nlastrip_evaluate_actionclip(ptr, channels, modifiers, nes, snapshot);
+ nlastrip_evaluate_actionclip(evaluation_mode, ptr, channels, modifiers, nes, snapshot);
break;
case NLASTRIP_TYPE_TRANSITION: /* transition */
- nlastrip_evaluate_transition(
- ptr, channels, modifiers, nes, snapshot, anim_eval_context, flush_to_original);
+ nlastrip_evaluate_transition(evaluation_mode,
+ ptr,
+ channels,
+ modifiers,
+ nes,
+ snapshot,
+ anim_eval_context,
+ flush_to_original);
break;
case NLASTRIP_TYPE_META: /* meta */
- nlastrip_evaluate_meta(
- ptr, channels, modifiers, nes, snapshot, anim_eval_context, flush_to_original);
+ nlastrip_evaluate_meta(evaluation_mode,
+ ptr,
+ channels,
+ modifiers,
+ nes,
+ snapshot,
+ anim_eval_context,
+ flush_to_original);
break;
default: /* do nothing */
@@ -2425,6 +2951,53 @@ void nlastrip_evaluate(PointerRNA *ptr,
strip->flag &= ~NLASTRIP_FLAG_EDIT_TOUCHED;
}
+void nlasnapshot_blend_strip(PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context,
+ const bool flush_to_original)
+{
+ nlastrip_evaluate(STRIP_EVAL_BLEND,
+ ptr,
+ channels,
+ modifiers,
+ nes,
+ snapshot,
+ anim_eval_context,
+ flush_to_original);
+}
+
+void nlasnapshot_blend_strip_get_inverted_lower_snapshot(
+ PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context)
+{
+ nlastrip_evaluate(STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT,
+ ptr,
+ channels,
+ modifiers,
+ nes,
+ snapshot,
+ anim_eval_context,
+ false);
+}
+
+void nlasnapshot_blend_strip_no_blend(PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context)
+{
+ nlastrip_evaluate(
+ STRIP_EVAL_NOBLEND, ptr, channels, modifiers, nes, snapshot, anim_eval_context, false);
+}
+
void nladata_flush_channels(PointerRNA *ptr,
NlaEvalData *channels,
NlaEvalSnapshot *snapshot,
@@ -2524,8 +3097,14 @@ static void animsys_evaluate_nla_domain(PointerRNA *ptr, NlaEvalData *channels,
{
GSet *touched_actions = BLI_gset_ptr_new(__func__);
- if (adt->action) {
- nla_eval_domain_action(ptr, channels, adt->action, touched_actions);
+ /* Include domain of Action Track. */
+ if ((adt->flag & ADT_NLA_EDIT_ON) == 0) {
+ if (adt->action) {
+ nla_eval_domain_action(ptr, channels, adt->action, touched_actions);
+ }
+ }
+ else if (adt->tmpact && (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS)) {
+ nla_eval_domain_action(ptr, channels, adt->tmpact, touched_actions);
}
/* NLA Data - Animation Data for Strips */
@@ -2634,7 +3213,8 @@ static void animsys_create_action_track_strip(const AnimData *adt,
const bool tweaking = (adt->flag & ADT_NLA_EDIT_ON) != 0;
const bool soloing = (adt->flag & ADT_NLA_SOLO_TRACK) != 0;
- const bool actionstrip_evaluated = r_action_strip->act && !soloing && !tweaking;
+ const bool eval_upper = !tweaking || (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) != 0;
+ const bool actionstrip_evaluated = r_action_strip->act && !soloing && eval_upper;
if (!actionstrip_evaluated) {
r_action_strip->flag |= NLASTRIP_FLAG_MUTED;
}
@@ -2779,13 +3359,13 @@ static bool animsys_evaluate_nla_for_flush(NlaEvalData *echannels,
/* Per strip, evaluate and accumulate on top of existing channels. */
for (nes = estrips.first; nes; nes = nes->next) {
- nlastrip_evaluate(ptr,
- echannels,
- NULL,
- nes,
- &echannels->eval_snapshot,
- anim_eval_context,
- flush_to_original);
+ nlasnapshot_blend_strip(ptr,
+ echannels,
+ NULL,
+ nes,
+ &echannels->eval_snapshot,
+ anim_eval_context,
+ flush_to_original);
}
/* Free temporary evaluation data that's not used elsewhere. */
@@ -2816,6 +3396,7 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr,
short track_index = 0;
bool has_strips = false;
+ ListBase *upper_estrips = &r_context->upper_estrips;
ListBase lower_estrips = {NULL, NULL};
NlaEvalStrip *nes;
@@ -2845,6 +3426,30 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr,
}
}
+ /* Get the upper stack of strips to evaluate at current time (influence calculated here).
+ * Var nlt exists only if tweak strip exists. */
+ if (nlt) {
+
+ /* Skip tweaked strip. */
+ nlt = nlt->next;
+ track_index++;
+
+ for (; nlt; nlt = nlt->next, track_index++) {
+
+ if (!is_nlatrack_evaluatable(adt, nlt)) {
+ continue;
+ }
+
+ if (nlt->strips.first) {
+ has_strips = true;
+ }
+
+ /* Get strip to evaluate for this channel. */
+ nes = nlastrips_ctime_get_strip(
+ upper_estrips, &nlt->strips, track_index, anim_eval_context, false);
+ }
+ }
+
/** NOTE: Although we early out, we can still keyframe to the non-pushed action since the
* keyframe remap function detects (r_context->strip.act == NULL) and will keyframe without
* remapping.
@@ -2856,6 +3461,10 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr,
/* Write r_context->eval_strip. */
if (adt->flag & ADT_NLA_EDIT_ON) {
+ /* Append action_track_strip to upper estrips. */
+ NlaStrip *action_strip = &r_context->action_track_strip;
+ animsys_create_action_track_strip(adt, false, action_strip);
+ nlastrips_ctime_get_strip_single(upper_estrips, action_strip, anim_eval_context, false);
NlaStrip *tweak_strip = &r_context->strip;
animsys_create_tweak_strip(adt, true, tweak_strip);
@@ -2885,13 +3494,13 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr,
/* For each strip, evaluate then accumulate on top of existing channels. */
for (nes = lower_estrips.first; nes; nes = nes->next) {
- nlastrip_evaluate(ptr,
- &r_context->lower_eval_data,
- NULL,
- nes,
- &r_context->lower_eval_data.eval_snapshot,
- anim_eval_context,
- false);
+ nlasnapshot_blend_strip(ptr,
+ &r_context->lower_eval_data,
+ NULL,
+ nes,
+ &r_context->lower_eval_data.eval_snapshot,
+ anim_eval_context,
+ false);
}
/* Free temporary evaluation data that's not used elsewhere. */
@@ -3009,6 +3618,41 @@ void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
}
}
+/** Using \a blended_snapshot and \a upper_snapshot, we can solve for the \a r_lower_snapshot.
+ *
+ * Only channels that exist within \a blended_snapshot are processed.
+ * Only blended values within the \a remap_domain are processed.
+ *
+ * Writes to \a r_upper_snapshot NlaEvalChannelSnapshot->remap_domain to match remapping success.
+ *
+ * Assumes caller marked upper values that are in the \a blend_domain. This determines whether the
+ * blended value came directly from the lower snapshot or a result of blending.
+ **/
+void nlasnapshot_blend_get_inverted_lower_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *blended_snapshot,
+ NlaEvalSnapshot *upper_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_lower_snapshot)
+{
+ nlaeval_snapshot_ensure_size(r_lower_snapshot, eval_data->num_channels);
+
+ LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
+ NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_get(blended_snapshot, nec->index);
+ if (blended_necs == NULL) {
+ /* We assume the caller only wants a subset of channels to be inverted, those that exist
+ * within \a blended_snapshot. */
+ continue;
+ }
+
+ NlaEvalChannelSnapshot *upper_necs = nlaeval_snapshot_get(upper_snapshot, nec->index);
+ NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_lower_snapshot, nec);
+
+ nlaevalchan_blendOrCombine_get_inverted_lower_evalchan(
+ blended_necs, upper_necs, upper_blendmode, upper_influence, result_necs);
+ }
+}
+
/* ---------------------- */
NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
@@ -3023,9 +3667,11 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
return NULL;
}
- /* No remapping if editing an ordinary Replace action with full influence. */
+ /* No remapping if editing an ordinary Replace action with full influence and upper tracks not
+ * evaluated. */
if (!(adt->flag & ADT_NLA_EDIT_ON) &&
- (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f)) {
+ (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f) &&
+ (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) == 0) {
return NULL;
}
@@ -3047,39 +3693,60 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
return ctx;
}
-bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
+void BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
struct PointerRNA *prop_ptr,
struct PropertyRNA *prop,
float *values,
int count,
int index,
- bool *r_force_all)
+ const struct AnimationEvalContext *anim_eval_context,
+ bool *r_force_all,
+ BLI_bitmap *r_successful_remaps)
{
+ BLI_bitmap_set_all(r_successful_remaps, false, count);
+
if (r_force_all != NULL) {
*r_force_all = false;
}
+ BLI_bitmap *remap_domain = BLI_BITMAP_NEW(count, __func__);
+ for (int i = 0; i < count; i++) {
+ if (!ELEM(index, i, -1)) {
+ continue;
+ }
+
+ BLI_BITMAP_ENABLE(remap_domain, i);
+ }
+
/* No context means no correction. */
if (context == NULL || context->strip.act == NULL) {
- return true;
+ BLI_bitmap_copy_all(r_successful_remaps, remap_domain, count);
+ MEM_freeN(remap_domain);
+ return;
}
/* If the strip is not evaluated, it is the same as zero influence. */
if (context->eval_strip == NULL) {
- return false;
+ MEM_freeN(remap_domain);
+ return;
}
- /* Full influence Replace strips also require no correction. */
+ /* Full influence Replace strips also require no correction if there are no upper tracks
+ * evaluating. */
int blend_mode = context->strip.blendmode;
float influence = context->strip.influence;
- if (blend_mode == NLASTRIP_MODE_REPLACE && influence == 1.0f) {
- return true;
+ if (blend_mode == NLASTRIP_MODE_REPLACE && influence == 1.0f &&
+ BLI_listbase_is_empty(&context->upper_estrips)) {
+ BLI_bitmap_copy_all(r_successful_remaps, remap_domain, count);
+ MEM_freeN(remap_domain);
+ return;
}
/* Zero influence is division by zero. */
if (influence <= 0.0f) {
- return false;
+ MEM_freeN(remap_domain);
+ return;
}
/** Create \a blended_snapshot and fill with input \a values. */
@@ -3097,12 +3764,38 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
if (nec->base_snapshot.length != count) {
BLI_assert_msg(0, "invalid value count");
nlaeval_snapshot_free_data(&blended_snapshot);
- return false;
+ MEM_freeN(remap_domain);
+ return;
}
NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_ensure_channel(&blended_snapshot, nec);
memcpy(blended_necs->values, values, sizeof(float) * count);
- BLI_bitmap_set_all(blended_necs->remap_domain.ptr, true, count);
+
+ /* Force all channels to be remapped for quaternions in a Combine strip, otherwise it will
+ * always fail. See nlaevalchan_combine_quaternion_handle_undefined_blend_values().
+ */
+ const bool can_force_all = r_force_all != NULL;
+ if (blended_necs->channel->mix_mode == NEC_MIX_QUATERNION &&
+ blend_mode == NLASTRIP_MODE_COMBINE && can_force_all) {
+
+ *r_force_all = true;
+ index = -1;
+ BLI_bitmap_set_all(remap_domain, true, 4);
+ }
+
+ BLI_bitmap_copy_all(blended_necs->remap_domain.ptr, remap_domain, count);
+
+ /* Need to send id_ptr instead of prop_ptr so fcurve RNA paths resolve properly. */
+ PointerRNA id_ptr;
+ RNA_id_pointer_create(prop_ptr->owner_id, &id_ptr);
+
+ /* Per iteration, remove effect of upper strip which gives output of nla stack below it. */
+ LISTBASE_FOREACH_BACKWARD (NlaEvalStrip *, nes, &context->upper_estrips) {
+ /* This will disable blended_necs->remap_domain bits if an upper strip is not invertible
+ * (full replace, multiply zero, or transition). Then there is no remap solution. */
+ nlasnapshot_blend_strip_get_inverted_lower_snapshot(
+ &id_ptr, eval_data, NULL, nes, &blended_snapshot, anim_eval_context);
+ }
/** Remove lower NLA stack effects. */
nlasnapshot_blend_get_inverted_upper_snapshot(eval_data,
@@ -3112,40 +3805,25 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
influence,
&blended_snapshot);
- /** Write results into \a values. */
- bool successful_remap = true;
- if (blended_necs->channel->mix_mode == NEC_MIX_QUATERNION &&
- blend_mode == NLASTRIP_MODE_COMBINE) {
-
- if (r_force_all != NULL) {
- *r_force_all = true;
- index = -1;
- }
- else {
- successful_remap = false;
- }
- }
-
+ /* Write results into \a values for successfully remapped values. */
for (int i = 0; i < count; i++) {
- if (!ELEM(index, i, -1)) {
- continue;
- }
if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, i)) {
- successful_remap = false;
+ continue;
}
-
values[i] = blended_necs->values[i];
}
- nlaeval_snapshot_free_data(&blended_snapshot);
+ BLI_bitmap_copy_all(r_successful_remaps, blended_necs->remap_domain.ptr, blended_necs->length);
- return successful_remap;
+ nlaeval_snapshot_free_data(&blended_snapshot);
+ MEM_freeN(remap_domain);
}
void BKE_animsys_free_nla_keyframing_context_cache(struct ListBase *cache)
{
LISTBASE_FOREACH (NlaKeyframingContext *, ctx, cache) {
MEM_SAFE_FREE(ctx->eval_strip);
+ BLI_freelistN(&ctx->upper_estrips);
nlaeval_free(&ctx->lower_eval_data);
}
diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index 361ab176abd..2db4c086e04 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -405,8 +405,8 @@ static void splineik_evaluate_bone(
if (pchan->bone->length < FLT_EPSILON) {
/* Only move the bone position with zero length bones. */
- float bone_pos[4], dir[3], rad;
- BKE_where_on_path(ik_data->tar, state->curve_position, bone_pos, dir, NULL, &rad, NULL);
+ float bone_pos[4], rad;
+ BKE_where_on_path(ik_data->tar, state->curve_position, bone_pos, NULL, NULL, &rad, NULL);
apply_curve_transform(ik_data, ob, rad, bone_pos, &rad);
@@ -445,13 +445,13 @@ static void splineik_evaluate_bone(
/* Step 1: determine the positions for the endpoints of the bone. */
if (point_start < 1.0f) {
- float vec[4], dir[3], rad;
+ float vec[4], rad;
radius = 0.0f;
/* Calculate head position. */
if (point_start == 0.0f) {
/* Start of the path. We have no previous tail position to copy. */
- BKE_where_on_path(ik_data->tar, point_start, vec, dir, NULL, &rad, NULL);
+ BKE_where_on_path(ik_data->tar, point_start, vec, NULL, NULL, &rad, NULL);
}
else {
copy_v3_v3(vec, state->prev_tail_loc);
@@ -486,7 +486,7 @@ static void splineik_evaluate_bone(
}
else {
/* Scale to fit curve end position. */
- if (BKE_where_on_path(ik_data->tar, point_end, vec, dir, NULL, &rad, NULL)) {
+ if (BKE_where_on_path(ik_data->tar, point_end, vec, NULL, NULL, &rad, NULL)) {
state->prev_tail_radius = rad;
copy_v3_v3(state->prev_tail_loc, vec);
copy_v3_v3(pose_tail, vec);
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index e85524d4bcb..35f2f94bc91 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -1495,7 +1495,7 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVES_LEGACY)) {
Curve *cu = ct->tar->data;
- float vec[4], dir[3], radius;
+ float vec[4], radius;
float curvetime;
unit_m4(ct->matrix);
@@ -1532,7 +1532,7 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
if (BKE_where_on_path(ct->tar,
curvetime,
vec,
- dir,
+ NULL,
(data->followflag & FOLLOWPATH_FOLLOW) ? quat : NULL,
&radius,
NULL)) { /* quat_pt is quat or NULL. */
@@ -3886,7 +3886,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
/* get targetmatrix */
if (data->tar->runtime.curve_cache && data->tar->runtime.curve_cache->anim_path_accum_length) {
- float vec[4], dir[3], totmat[4][4];
+ float vec[4], totmat[4][4];
float curvetime;
short clamp_axis;
@@ -3969,7 +3969,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
}
/* 3. position on curve */
- if (BKE_where_on_path(ct->tar, curvetime, vec, dir, NULL, NULL, NULL)) {
+ if (BKE_where_on_path(ct->tar, curvetime, vec, NULL, NULL, NULL, NULL)) {
unit_m4(totmat);
copy_v3_v3(totmat[3], vec);
diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc
index dfe462d8566..13695525616 100644
--- a/source/blender/blenkernel/intern/curve_bezier.cc
+++ b/source/blender/blenkernel/intern/curve_bezier.cc
@@ -281,6 +281,11 @@ static void interpolate_to_evaluated(const Span<T> src,
BLI_assert(!src.is_empty());
BLI_assert(evaluated_offsets.size() == src.size());
BLI_assert(evaluated_offsets.last() == dst.size());
+ if (src.size() == 1) {
+ BLI_assert(dst.size() == 1);
+ dst.first() = src.first();
+ return;
+ }
linear_interpolation(src.first(), src[1], dst.take_front(evaluated_offsets.first()));
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 6e09d1e8f10..a893359b911 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -385,8 +385,8 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves)
VArray_Span<float> nurbs_weights{
src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)};
- VArray_Span<int> nurbs_orders{
- src_component.attribute_get_for_read<int>("nurbs_order", ATTR_DOMAIN_CURVE, 4)};
+ VArray_Span<int8_t> nurbs_orders{
+ src_component.attribute_get_for_read<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)};
VArray_Span<int8_t> nurbs_knots_modes{
src_component.attribute_get_for_read<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)};
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 5c89dfd4df5..bdd8b3fc3d0 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -577,18 +577,25 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const
Span<float3> CurvesGeometry::evaluated_positions() const
{
if (!this->runtime->position_cache_dirty) {
- return this->runtime->evaluated_position_cache;
+ return this->runtime->evaluated_positions_span;
}
/* A double checked lock. */
std::scoped_lock lock{this->runtime->position_cache_mutex};
if (!this->runtime->position_cache_dirty) {
- return this->runtime->evaluated_position_cache;
+ return this->runtime->evaluated_positions_span;
}
threading::isolate_task([&]() {
+ if (this->is_single_type(CURVE_TYPE_POLY)) {
+ this->runtime->evaluated_positions_span = this->positions();
+ this->runtime->evaluated_position_cache.clear_and_make_inline();
+ return;
+ }
+
this->runtime->evaluated_position_cache.resize(this->evaluated_points_num());
MutableSpan<float3> evaluated_positions = this->runtime->evaluated_position_cache;
+ this->runtime->evaluated_positions_span = evaluated_positions;
VArray<int8_t> types = this->curve_types();
VArray<bool> cyclic = this->cyclic();
@@ -645,7 +652,7 @@ Span<float3> CurvesGeometry::evaluated_positions() const
});
this->runtime->position_cache_dirty = false;
- return this->runtime->evaluated_position_cache;
+ return this->runtime->evaluated_positions_span;
}
Span<float3> CurvesGeometry::evaluated_tangents() const
diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c
index 114d33b1a3d..d176bf41254 100644
--- a/source/blender/blenkernel/intern/editmesh.c
+++ b/source/blender/blenkernel/intern/editmesh.c
@@ -214,18 +214,14 @@ float (*BKE_editmesh_vert_coords_alloc(struct Depsgraph *depsgraph,
Object *ob,
int *r_vert_len))[3]
{
- Mesh *cage;
- BLI_bitmap *visit_bitmap;
- struct CageUserData data;
- float(*cos_cage)[3];
+ Mesh *cage = editbmesh_get_eval_cage(depsgraph, scene, ob, em, &CD_MASK_BAREMESH);
+ float(*cos_cage)[3] = MEM_callocN(sizeof(*cos_cage) * em->bm->totvert, "bmbvh cos_cage");
- cage = editbmesh_get_eval_cage(depsgraph, scene, ob, em, &CD_MASK_BAREMESH);
- cos_cage = MEM_callocN(sizeof(*cos_cage) * em->bm->totvert, "bmbvh cos_cage");
-
- /* when initializing cage verts, we only want the first cage coordinate for each vertex,
- * so that e.g. mirror or array use original vertex coordinates and not mirrored or duplicate */
- visit_bitmap = BLI_BITMAP_NEW(em->bm->totvert, __func__);
+ /* When initializing cage verts, we only want the first cage coordinate for each vertex,
+ * so that e.g. mirror or array use original vertex coordinates and not mirrored or duplicate. */
+ BLI_bitmap *visit_bitmap = BLI_BITMAP_NEW(em->bm->totvert, __func__);
+ struct CageUserData data;
data.totvert = em->bm->totvert;
data.cos_cage = cos_cage;
data.visit_bitmap = visit_bitmap;
diff --git a/source/blender/blenkernel/intern/editmesh_cache.c b/source/blender/blenkernel/intern/editmesh_cache.cc
index f80f300d149..438d287fb28 100644
--- a/source/blender/blenkernel/intern/editmesh_cache.c
+++ b/source/blender/blenkernel/intern/editmesh_cache.cc
@@ -8,7 +8,9 @@
#include "MEM_guardedalloc.h"
+#include "BLI_bounds.hh"
#include "BLI_math_vector.h"
+#include "BLI_span.hh"
#include "DNA_mesh_types.h"
@@ -21,23 +23,21 @@
void BKE_editmesh_cache_ensure_poly_normals(BMEditMesh *em, EditMeshData *emd)
{
- if (!(emd->vertexCos && (emd->polyNos == NULL))) {
+ if (!(emd->vertexCos && (emd->polyNos == nullptr))) {
return;
}
BMesh *bm = em->bm;
- const float(*vertexCos)[3];
- float(*polyNos)[3];
-
BMFace *efa;
BMIter fiter;
int i;
BM_mesh_elem_index_ensure(bm, BM_VERT);
- polyNos = MEM_mallocN(sizeof(*polyNos) * bm->totface, __func__);
+ float(*polyNos)[3] = static_cast<float(*)[3]>(
+ MEM_mallocN(sizeof(*polyNos) * bm->totface, __func__));
- vertexCos = emd->vertexCos;
+ const float(*vertexCos)[3] = emd->vertexCos;
BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
BM_elem_index_set(efa, i); /* set_inline */
@@ -50,7 +50,7 @@ void BKE_editmesh_cache_ensure_poly_normals(BMEditMesh *em, EditMeshData *emd)
void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, EditMeshData *emd)
{
- if (!(emd->vertexCos && (emd->vertexNos == NULL))) {
+ if (!(emd->vertexCos && (emd->vertexNos == nullptr))) {
return;
}
@@ -58,14 +58,14 @@ void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, EditMeshData *emd)
const float(*vertexCos)[3], (*polyNos)[3];
float(*vertexNos)[3];
- /* calculate vertex normals from poly normals */
+ /* Calculate vertex normals from poly normals. */
BKE_editmesh_cache_ensure_poly_normals(em, emd);
BM_mesh_elem_index_ensure(bm, BM_FACE);
polyNos = emd->polyNos;
vertexCos = emd->vertexCos;
- vertexNos = MEM_callocN(sizeof(*vertexNos) * bm->totvert, __func__);
+ vertexNos = static_cast<float(*)[3]>(MEM_callocN(sizeof(*vertexNos) * bm->totvert, __func__));
BM_verts_calc_normal_vcos(bm, polyNos, vertexCos, vertexNos);
@@ -74,17 +74,17 @@ void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, EditMeshData *emd)
void BKE_editmesh_cache_ensure_poly_centers(BMEditMesh *em, EditMeshData *emd)
{
- if (emd->polyCos != NULL) {
+ if (emd->polyCos != nullptr) {
return;
}
BMesh *bm = em->bm;
- float(*polyCos)[3];
BMFace *efa;
BMIter fiter;
int i;
- polyCos = MEM_mallocN(sizeof(*polyCos) * bm->totface, __func__);
+ float(*polyCos)[3] = static_cast<float(*)[3]>(
+ MEM_mallocN(sizeof(*polyCos) * bm->totface, __func__));
if (emd->vertexCos) {
const float(*vertexCos)[3];
@@ -116,18 +116,20 @@ bool BKE_editmesh_cache_calc_minmax(struct BMEditMesh *em,
float min[3],
float max[3])
{
+ using namespace blender;
BMesh *bm = em->bm;
- BMVert *eve;
- BMIter iter;
- int i;
if (bm->totvert) {
if (emd->vertexCos) {
- BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
- minmax_v3v3_v3(min, max, emd->vertexCos[i]);
- }
+ Span<float3> vert_coords(reinterpret_cast<const float3 *>(emd->vertexCos), bm->totvert);
+ std::optional<bounds::MinMaxResult<float3>> bounds = bounds::min_max(vert_coords);
+ BLI_assert(bounds.has_value());
+ copy_v3_v3(min, math::min(bounds->min, float3(min)));
+ copy_v3_v3(max, math::max(bounds->max, float3(max)));
}
else {
+ BMVert *eve;
+ BMIter iter;
BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
minmax_v3v3_v3(min, max, eve->co);
}
diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc
index 0bcab0aae7a..4796135b32f 100644
--- a/source/blender/blenkernel/intern/geometry_component_curves.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curves.cc
@@ -462,14 +462,14 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
ATTR_DOMAIN_CURVE,
- CD_PROP_INT32,
- CD_PROP_INT32,
+ CD_PROP_INT8,
+ CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
- make_array_read_attribute<int>,
- make_array_write_attribute<int>,
+ make_array_read_attribute<int8_t>,
+ make_array_write_attribute<int8_t>,
tag_component_topology_changed);
static BuiltinCustomDataLayerProvider normal_mode("normal_mode",
@@ -545,6 +545,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
&handle_type_left,
&normal_mode,
&nurbs_order,
+ &nurbs_knots_mode,
&nurbs_weight,
&curve_type,
&resolution,
diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc
index bce7021bbb9..defca433968 100644
--- a/source/blender/blenkernel/intern/mesh_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_convert.cc
@@ -700,7 +700,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
VertLink *vl;
/* create new 'nurb' within the curve */
- nu = MEM_cnew<Nurb>("MeshNurb");
+ nu = MEM_new<Nurb>("MeshNurb", blender::dna::shallow_zero_initialize());
nu->pntsu = totpoly;
nu->pntsv = 1;
diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.cc
index 7bd52abeb0d..b06e867cf37 100644
--- a/source/blender/blenkernel/intern/mesh_runtime.c
+++ b/source/blender/blenkernel/intern/mesh_runtime.cc
@@ -14,8 +14,7 @@
#include "DNA_object_types.h"
#include "BLI_math_geom.h"
-#include "BLI_task.h"
-#include "BLI_threads.h"
+#include "BLI_task.hh"
#include "BKE_bvhutils.h"
#include "BKE_lib_id.h"
@@ -35,12 +34,12 @@
*/
static void mesh_runtime_init_mutexes(Mesh *mesh)
{
- mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex");
- BLI_mutex_init(mesh->runtime.eval_mutex);
- mesh->runtime.normals_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime normals_mutex");
- BLI_mutex_init(mesh->runtime.normals_mutex);
- mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex");
- BLI_mutex_init(mesh->runtime.render_mutex);
+ mesh->runtime.eval_mutex = MEM_new<ThreadMutex>("mesh runtime eval_mutex");
+ BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.eval_mutex));
+ mesh->runtime.normals_mutex = MEM_new<ThreadMutex>("mesh runtime normals_mutex");
+ BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.normals_mutex));
+ mesh->runtime.render_mutex = MEM_new<ThreadMutex>("mesh runtime render_mutex");
+ BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.render_mutex));
}
/**
@@ -48,20 +47,20 @@ static void mesh_runtime_init_mutexes(Mesh *mesh)
*/
static void mesh_runtime_free_mutexes(Mesh *mesh)
{
- if (mesh->runtime.eval_mutex != NULL) {
- BLI_mutex_end(mesh->runtime.eval_mutex);
+ if (mesh->runtime.eval_mutex != nullptr) {
+ BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.eval_mutex));
MEM_freeN(mesh->runtime.eval_mutex);
- mesh->runtime.eval_mutex = NULL;
+ mesh->runtime.eval_mutex = nullptr;
}
- if (mesh->runtime.normals_mutex != NULL) {
- BLI_mutex_end(mesh->runtime.normals_mutex);
+ if (mesh->runtime.normals_mutex != nullptr) {
+ BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.normals_mutex));
MEM_freeN(mesh->runtime.normals_mutex);
- mesh->runtime.normals_mutex = NULL;
+ mesh->runtime.normals_mutex = nullptr;
}
- if (mesh->runtime.render_mutex != NULL) {
- BLI_mutex_end(mesh->runtime.render_mutex);
+ if (mesh->runtime.render_mutex != nullptr) {
+ BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.render_mutex));
MEM_freeN(mesh->runtime.render_mutex);
- mesh->runtime.render_mutex = NULL;
+ mesh->runtime.render_mutex = nullptr;
}
}
@@ -80,28 +79,28 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag))
{
Mesh_Runtime *runtime = &mesh->runtime;
- runtime->mesh_eval = NULL;
- runtime->edit_data = NULL;
- runtime->batch_cache = NULL;
- runtime->subdiv_ccg = NULL;
- memset(&runtime->looptris, 0, sizeof(runtime->looptris));
- runtime->bvh_cache = NULL;
- runtime->shrinkwrap_data = NULL;
+ runtime->mesh_eval = nullptr;
+ runtime->edit_data = nullptr;
+ runtime->batch_cache = nullptr;
+ runtime->subdiv_ccg = nullptr;
+ runtime->looptris = blender::dna::shallow_zero_initialize();
+ runtime->bvh_cache = nullptr;
+ runtime->shrinkwrap_data = nullptr;
runtime->vert_normals_dirty = true;
runtime->poly_normals_dirty = true;
- runtime->vert_normals = NULL;
- runtime->poly_normals = NULL;
+ runtime->vert_normals = nullptr;
+ runtime->poly_normals = nullptr;
mesh_runtime_init_mutexes(mesh);
}
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
{
- if (mesh->runtime.mesh_eval != NULL) {
- mesh->runtime.mesh_eval->edit_mesh = NULL;
- BKE_id_free(NULL, mesh->runtime.mesh_eval);
- mesh->runtime.mesh_eval = NULL;
+ if (mesh->runtime.mesh_eval != nullptr) {
+ mesh->runtime.mesh_eval->edit_mesh = nullptr;
+ BKE_id_free(nullptr, mesh->runtime.mesh_eval);
+ mesh->runtime.mesh_eval = nullptr;
}
BKE_mesh_runtime_clear_geometry(mesh);
BKE_mesh_batch_cache_free(mesh);
@@ -121,7 +120,7 @@ static void mesh_ensure_looptri_data(Mesh *mesh)
const uint totpoly = mesh->totpoly;
const int looptris_len = poly_to_tri_count(totpoly, mesh->totloop);
- BLI_assert(mesh->runtime.looptris.array_wip == NULL);
+ BLI_assert(mesh->runtime.looptris.array_wip == nullptr);
SWAP(MLoopTri *, mesh->runtime.looptris.array, mesh->runtime.looptris.array_wip);
@@ -133,9 +132,9 @@ static void mesh_ensure_looptri_data(Mesh *mesh)
}
if (totpoly) {
- if (mesh->runtime.looptris.array_wip == NULL) {
- mesh->runtime.looptris.array_wip = MEM_malloc_arrayN(
- looptris_len, sizeof(*mesh->runtime.looptris.array_wip), __func__);
+ if (mesh->runtime.looptris.array_wip == nullptr) {
+ mesh->runtime.looptris.array_wip = static_cast<MLoopTri *>(
+ MEM_malloc_arrayN(looptris_len, sizeof(*mesh->runtime.looptris.array_wip), __func__));
mesh->runtime.looptris.len_alloc = looptris_len;
}
@@ -146,7 +145,7 @@ static void mesh_ensure_looptri_data(Mesh *mesh)
void BKE_mesh_runtime_looptri_recalc(Mesh *mesh)
{
mesh_ensure_looptri_data(mesh);
- BLI_assert(mesh->totpoly == 0 || mesh->runtime.looptris.array_wip != NULL);
+ BLI_assert(mesh->totpoly == 0 || mesh->runtime.looptris.array_wip != nullptr);
BKE_mesh_recalc_looptri(mesh->mloop,
mesh->mpoly,
@@ -155,11 +154,11 @@ void BKE_mesh_runtime_looptri_recalc(Mesh *mesh)
mesh->totpoly,
mesh->runtime.looptris.array_wip);
- BLI_assert(mesh->runtime.looptris.array == NULL);
+ BLI_assert(mesh->runtime.looptris.array == nullptr);
atomic_cas_ptr((void **)&mesh->runtime.looptris.array,
mesh->runtime.looptris.array,
mesh->runtime.looptris.array_wip);
- mesh->runtime.looptris.array_wip = NULL;
+ mesh->runtime.looptris.array_wip = nullptr;
}
int BKE_mesh_runtime_looptri_len(const Mesh *mesh)
@@ -170,12 +169,6 @@ int BKE_mesh_runtime_looptri_len(const Mesh *mesh)
return looptri_len;
}
-static void mesh_runtime_looptri_recalc_isolated(void *userdata)
-{
- Mesh *mesh = userdata;
- BKE_mesh_runtime_looptri_recalc(mesh);
-}
-
const MLoopTri *BKE_mesh_runtime_looptri_ensure(const Mesh *mesh)
{
ThreadMutex *mesh_eval_mutex = (ThreadMutex *)mesh->runtime.eval_mutex;
@@ -183,12 +176,13 @@ const MLoopTri *BKE_mesh_runtime_looptri_ensure(const Mesh *mesh)
MLoopTri *looptri = mesh->runtime.looptris.array;
- if (looptri != NULL) {
+ if (looptri != nullptr) {
BLI_assert(BKE_mesh_runtime_looptri_len(mesh) == mesh->runtime.looptris.len);
}
else {
/* Must isolate multithreaded tasks while holding a mutex lock. */
- BLI_task_isolate(mesh_runtime_looptri_recalc_isolated, (void *)mesh);
+ blender::threading::isolate_task(
+ [&]() { BKE_mesh_runtime_looptri_recalc(const_cast<Mesh *>(mesh)); });
looptri = mesh->runtime.looptris.array;
}
@@ -211,18 +205,18 @@ void BKE_mesh_runtime_verttri_from_looptri(MVertTri *r_verttri,
bool BKE_mesh_runtime_ensure_edit_data(struct Mesh *mesh)
{
- if (mesh->runtime.edit_data != NULL) {
+ if (mesh->runtime.edit_data != nullptr) {
return false;
}
- mesh->runtime.edit_data = MEM_callocN(sizeof(EditMeshData), "EditMeshData");
+ mesh->runtime.edit_data = MEM_cnew<EditMeshData>(__func__);
return true;
}
bool BKE_mesh_runtime_reset_edit_data(Mesh *mesh)
{
EditMeshData *edit_data = mesh->runtime.edit_data;
- if (edit_data == NULL) {
+ if (edit_data == nullptr) {
return false;
}
@@ -236,13 +230,13 @@ bool BKE_mesh_runtime_reset_edit_data(Mesh *mesh)
bool BKE_mesh_runtime_clear_edit_data(Mesh *mesh)
{
- if (mesh->runtime.edit_data == NULL) {
+ if (mesh->runtime.edit_data == nullptr) {
return false;
}
BKE_mesh_runtime_reset_edit_data(mesh);
MEM_freeN(mesh->runtime.edit_data);
- mesh->runtime.edit_data = NULL;
+ mesh->runtime.edit_data = nullptr;
return true;
}
@@ -251,13 +245,13 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
{
if (mesh->runtime.bvh_cache) {
bvhcache_free(mesh->runtime.bvh_cache);
- mesh->runtime.bvh_cache = NULL;
+ mesh->runtime.bvh_cache = nullptr;
}
MEM_SAFE_FREE(mesh->runtime.looptris.array);
/* TODO(sergey): Does this really belong here? */
- if (mesh->runtime.subdiv_ccg != NULL) {
+ if (mesh->runtime.subdiv_ccg != nullptr) {
BKE_subdiv_ccg_destroy(mesh->runtime.subdiv_ccg);
- mesh->runtime.subdiv_ccg = NULL;
+ mesh->runtime.subdiv_ccg = nullptr;
}
BKE_shrinkwrap_discard_boundary_data(mesh);
}
@@ -269,8 +263,8 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
* \{ */
/* Draw Engine */
-void (*BKE_mesh_batch_cache_dirty_tag_cb)(Mesh *me, eMeshBatchDirtyMode mode) = NULL;
-void (*BKE_mesh_batch_cache_free_cb)(Mesh *me) = NULL;
+void (*BKE_mesh_batch_cache_dirty_tag_cb)(Mesh *me, eMeshBatchDirtyMode mode) = nullptr;
+void (*BKE_mesh_batch_cache_free_cb)(Mesh *me) = nullptr;
void BKE_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
{
diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.cc
index f9fcaa0dceb..8291765c2ef 100644
--- a/source/blender/blenkernel/intern/mesh_wrapper.c
+++ b/source/blender/blenkernel/intern/mesh_wrapper.cc
@@ -27,7 +27,7 @@
#include "BLI_ghash.h"
#include "BLI_math.h"
-#include "BLI_task.h"
+#include "BLI_task.hh"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
@@ -51,7 +51,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em,
const float (*vert_coords)[3],
const Mesh *me_settings)
{
- Mesh *me = BKE_id_new_nomain(ID_ME, NULL);
+ Mesh *me = static_cast<Mesh *>(BKE_id_new_nomain(ID_ME, nullptr));
BKE_mesh_copy_parameters_for_eval(me, me_settings);
BKE_mesh_runtime_ensure_edit_data(me);
@@ -63,10 +63,10 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em,
/* Use edit-mesh directly where possible. */
me->runtime.is_original = true;
- me->edit_mesh = MEM_dupallocN(em);
+ me->edit_mesh = static_cast<BMEditMesh *>(MEM_dupallocN(em));
me->edit_mesh->is_shallow_copy = true;
-/* Make sure, we crash if these are ever used. */
+ /* Make sure we crash if these are ever used. */
#ifdef DEBUG
me->totvert = INT_MAX;
me->totedge = INT_MAX;
@@ -88,55 +88,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh(BMEditMesh *em,
const CustomData_MeshMasks *cd_mask_extra,
const Mesh *me_settings)
{
- return BKE_mesh_wrapper_from_editmesh_with_coords(em, cd_mask_extra, NULL, me_settings);
-}
-
-static void mesh_wrapper_ensure_mdata_isolated(void *userdata)
-{
- Mesh *me = userdata;
-
- const eMeshWrapperType geom_type_orig = me->runtime.wrapper_type;
- me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA;
-
- switch (geom_type_orig) {
- case ME_WRAPPER_TYPE_MDATA:
- case ME_WRAPPER_TYPE_SUBD: {
- break; /* Quiet warning. */
- }
- case ME_WRAPPER_TYPE_BMESH: {
- me->totvert = 0;
- me->totedge = 0;
- me->totpoly = 0;
- me->totloop = 0;
-
- BLI_assert(me->edit_mesh != NULL);
- BLI_assert(me->runtime.edit_data != NULL);
-
- BMEditMesh *em = me->edit_mesh;
- BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra);
-
- /* Adding original index layers assumes that all BMesh mesh wrappers are created from
- * original edit mode meshes (the only case where adding original indices makes sense).
- * If that assumption is broken, the layers might be incorrect in that they might not
- * actually be "original".
- *
- * There is also a performance aspect, where this also assumes that original indices are
- * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not
- * harmful. */
- BKE_mesh_ensure_default_orig_index_customdata(me);
-
- EditMeshData *edit_data = me->runtime.edit_data;
- if (edit_data->vertexCos) {
- BKE_mesh_vert_coords_apply(me, edit_data->vertexCos);
- me->runtime.is_original = false;
- }
- break;
- }
- }
-
- if (me->runtime.wrapper_type_finalize) {
- BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra);
- }
+ return BKE_mesh_wrapper_from_editmesh_with_coords(em, cd_mask_extra, nullptr, me_settings);
}
void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
@@ -150,7 +102,51 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
}
/* Must isolate multithreaded tasks while holding a mutex lock. */
- BLI_task_isolate(mesh_wrapper_ensure_mdata_isolated, me);
+ blender::threading::isolate_task([&]() {
+ const eMeshWrapperType geom_type_orig = static_cast<eMeshWrapperType>(
+ me->runtime.wrapper_type);
+ me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA;
+
+ switch (geom_type_orig) {
+ case ME_WRAPPER_TYPE_MDATA:
+ case ME_WRAPPER_TYPE_SUBD: {
+ break; /* Quiet warning. */
+ }
+ case ME_WRAPPER_TYPE_BMESH: {
+ me->totvert = 0;
+ me->totedge = 0;
+ me->totpoly = 0;
+ me->totloop = 0;
+
+ BLI_assert(me->edit_mesh != nullptr);
+ BLI_assert(me->runtime.edit_data != nullptr);
+
+ BMEditMesh *em = me->edit_mesh;
+ BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra);
+
+ /* Adding original index layers assumes that all BMesh mesh wrappers are created from
+ * original edit mode meshes (the only case where adding original indices makes sense).
+ * If that assumption is broken, the layers might be incorrect in that they might not
+ * actually be "original".
+ *
+ * There is also a performance aspect, where this also assumes that original indices are
+ * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not
+ * harmful. */
+ BKE_mesh_ensure_default_orig_index_customdata(me);
+
+ EditMeshData *edit_data = me->runtime.edit_data;
+ if (edit_data->vertexCos) {
+ BKE_mesh_vert_coords_apply(me, edit_data->vertexCos);
+ me->runtime.is_original = false;
+ }
+ break;
+ }
+ }
+
+ if (me->runtime.wrapper_type_finalize) {
+ BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra);
+ }
+ });
BLI_mutex_unlock(mesh_eval_mutex);
}
@@ -181,7 +177,7 @@ void BKE_mesh_wrapper_vert_coords_copy(const Mesh *me,
BMesh *bm = me->edit_mesh->bm;
BLI_assert(vert_coords_len <= bm->totvert);
EditMeshData *edit_data = me->runtime.edit_data;
- if (edit_data->vertexCos != NULL) {
+ if (edit_data->vertexCos != nullptr) {
for (int i = 0; i < vert_coords_len; i++) {
copy_v3_v3(vert_coords[i], edit_data->vertexCos[i]);
}
@@ -219,7 +215,7 @@ void BKE_mesh_wrapper_vert_coords_copy_with_mat4(const Mesh *me,
BMesh *bm = me->edit_mesh->bm;
BLI_assert(vert_coords_len == bm->totvert);
EditMeshData *edit_data = me->runtime.edit_data;
- if (edit_data->vertexCos != NULL) {
+ if (edit_data->vertexCos != nullptr) {
for (int i = 0; i < vert_coords_len; i++) {
mul_v3_m4v3(vert_coords[i], mat, edit_data->vertexCos[i]);
}
@@ -340,7 +336,7 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd);
Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(smd, &subdiv_settings, me, false);
- if (subdiv == NULL) {
+ if (subdiv == nullptr) {
/* Happens on bad topology, but also on empty input mesh. */
return me;
}
@@ -352,8 +348,8 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
}
if (subdiv_mesh != me) {
- if (me->runtime.mesh_eval != NULL) {
- BKE_id_free(NULL, me->runtime.mesh_eval);
+ if (me->runtime.mesh_eval != nullptr) {
+ BKE_id_free(nullptr, me->runtime.mesh_eval);
}
me->runtime.mesh_eval = subdiv_mesh;
me->runtime.wrapper_type = ME_WRAPPER_TYPE_SUBD;
@@ -362,20 +358,6 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
return me->runtime.mesh_eval;
}
-typedef struct SubdivisionWrapperIsolatedTaskData {
- const Object *ob;
- Mesh *me;
- Mesh *result;
-} SubdivisionWrapperIsolatedTaskData;
-
-static void mesh_wrapper_ensure_subdivision_isolated(void *userdata)
-{
- SubdivisionWrapperIsolatedTaskData *task_data = (SubdivisionWrapperIsolatedTaskData *)userdata;
- const Object *ob = task_data->ob;
- Mesh *me = task_data->me;
- task_data->result = mesh_wrapper_ensure_subdivision(ob, me);
-}
-
Mesh *BKE_mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
{
ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex;
@@ -386,15 +368,13 @@ Mesh *BKE_mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
return me->runtime.mesh_eval;
}
- SubdivisionWrapperIsolatedTaskData task_data;
- task_data.ob = ob;
- task_data.me = me;
+ Mesh *result;
/* Must isolate multithreaded tasks while holding a mutex lock. */
- BLI_task_isolate(mesh_wrapper_ensure_subdivision_isolated, &task_data);
+ blender::threading::isolate_task([&]() { result = mesh_wrapper_ensure_subdivision(ob, me); });
BLI_mutex_unlock(mesh_eval_mutex);
- return task_data.result;
+ return result;
}
/** \} */
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index a94562a32ec..9b6d768b2d3 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -1977,8 +1977,11 @@ bool BKE_nla_tweakmode_enter(AnimData *adt)
/* go over all the tracks after AND INCLUDING the active one, tagging them as being disabled
* - the active track needs to also be tagged, otherwise, it'll overlap with the tweaks going on
*/
- for (nlt = activeTrack; nlt; nlt = nlt->next) {
- nlt->flag |= NLATRACK_DISABLED;
+ activeTrack->flag |= NLATRACK_DISABLED;
+ if ((adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) == 0) {
+ for (nlt = activeTrack->next; nlt; nlt = nlt->next) {
+ nlt->flag |= NLATRACK_DISABLED;
+ }
}
/* handle AnimData level changes:
diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc
index a54e2910b79..92c350c5208 100644
--- a/source/blender/blenkernel/intern/object.cc
+++ b/source/blender/blenkernel/intern/object.cc
@@ -3087,12 +3087,12 @@ void BKE_object_matrix_local_get(struct Object *ob, float r_mat[4][4])
static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
{
Curve *cu = (Curve *)par->data;
- float vec[4], dir[3], quat[4], radius, ctime;
+ float vec[4], quat[4], radius, ctime;
/* NOTE: Curve cache is supposed to be evaluated here already, however there
* are cases where we can not guarantee that. This includes, for example,
* dependency cycles. We can't correct anything from here, since that would
- * cause a threading conflicts.
+ * cause threading conflicts.
*
* TODO(sergey): Some of the legit looking cases like T56619 need to be
* looked into, and maybe curve cache (and other dependencies) are to be
@@ -3125,7 +3125,7 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
/* vec: 4 items! */
if (BKE_where_on_path(
- par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : nullptr, &radius, nullptr)) {
+ par, ctime, vec, nullptr, (cu->flag & CU_FOLLOW) ? quat : nullptr, &radius, nullptr)) {
if (cu->flag & CU_FOLLOW) {
quat_apply_track(quat, ob->trackflag, ob->upflag);
normalize_qt(quat);
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 9ea1336a95a..cc4738b1faa 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -1249,7 +1249,9 @@ typedef struct ParticleInterpolationData {
PTCacheEditPoint *epoint;
PTCacheEditKey *ekey[2];
- float birthtime, dietime;
+ float birthtime;
+ /** Die on this frame, see #ParticleData.dietime for details. */
+ float dietime;
int bspline;
} ParticleInterpolationData;
/**
@@ -1311,15 +1313,15 @@ static void get_pointcache_keys_for_time(Object *UNUSED(ob),
}
static int get_pointcache_times_for_particle(PointCache *cache,
int index,
- float *start,
- float *end)
+ float *r_start,
+ float *r_dietime)
{
PTCacheMem *pm;
int ret = 0;
for (pm = cache->mem_cache.first; pm; pm = pm->next) {
if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
- *start = pm->frame;
+ *r_start = pm->frame;
ret++;
break;
}
@@ -1327,7 +1329,8 @@ static int get_pointcache_times_for_particle(PointCache *cache,
for (pm = cache->mem_cache.last; pm; pm = pm->prev) {
if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
- *end = pm->frame;
+ /* Die *after* the last available frame. */
+ *r_dietime = pm->frame + 1;
ret++;
break;
}
@@ -1343,7 +1346,9 @@ float psys_get_dietime_from_cache(PointCache *cache, int index)
for (pm = cache->mem_cache.last; pm; pm = pm->prev) {
if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
- return (float)pm->frame;
+ /* Die *after* the last available frame. */
+ dietime = pm->frame + 1;
+ break;
}
}
@@ -1374,14 +1379,14 @@ static void init_particle_interpolation(Object *ob,
pind->dietime = (key + pa->totkey - 1)->time;
}
else if (pind->cache) {
- float start = 0.0f, end = 0.0f;
+ float start = 0.0f, dietime = 0.0f;
get_pointcache_keys_for_time(ob, pind->cache, &pind->pm, -1, 0.0f, NULL, NULL);
pind->birthtime = pa ? pa->time : pind->cache->startframe;
- pind->dietime = pa ? pa->dietime : pind->cache->endframe;
+ pind->dietime = pa ? pa->dietime : (pind->cache->endframe + 1);
- if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) {
+ if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &dietime)) {
pind->birthtime = MAX2(pind->birthtime, start);
- pind->dietime = MIN2(pind->dietime, end);
+ pind->dietime = MIN2(pind->dietime, dietime);
}
}
else {
diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c
index 5dba4d3f003..524ee31229b 100644
--- a/source/blender/blenkernel/intern/particle_child.c
+++ b/source/blender/blenkernel/intern/particle_child.c
@@ -85,7 +85,7 @@ static void do_kink_spiral_deform(ParticleKey *state,
* and goes up to the Golden Spiral for 1.0
* https://en.wikipedia.org/wiki/Golden_spiral
*/
- const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI_4;
+ const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI * 0.25f;
/* angle of the spiral against the curve (rotated opposite to make a smooth transition) */
const float start_angle = ((b != 0.0f) ? atanf(1.0f / b) : (float)-M_PI_2) +
(b > 0.0f ? -(float)M_PI_2 : (float)M_PI_2);
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 19abff19b77..d5d304343df 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -285,8 +285,10 @@ static int ptcache_particle_write(int index, void *psys_v, void **data, int cfra
}
}
else {
- /* Particles are only stored in their lifetime. */
- if (cfra < pa->time - step || cfra > pa->dietime + step) {
+ /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */
+ const int pa_sfra = (int)pa->time - step;
+ const int pa_efra = ((int)pa->dietime - 1) + step;
+ if (!(cfra >= pa_sfra && cfra <= pa_efra)) {
return 0;
}
}
@@ -399,9 +401,12 @@ static void ptcache_particle_interpolate(int index,
pa = psys->particles + index;
- /* particle wasn't read from first cache so can't interpolate */
- if ((int)cfra1 < pa->time - psys->pointcache->step ||
- (int)cfra1 > pa->dietime + psys->pointcache->step) {
+ /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */
+ const int pa_sfra = (int)pa->time - psys->pointcache->step;
+ const int pa_efra = ((int)pa->dietime - 1) + psys->pointcache->step;
+
+ /* Particle wasn't read from first cache so can't interpolate. */
+ if (!(cfra1 >= pa_sfra && cfra1 <= pa_efra)) {
return;
}
@@ -482,12 +487,16 @@ static int ptcache_particle_totwrite(void *psys_v, int cfra)
if (psys->part->flag & PART_DIED) {
/* Also store dead particles when they are displayed. */
for (p = 0; p < psys->totpart; p++, pa++) {
- totwrite += (cfra >= pa->time - step);
+ const int pa_sfra = (int)pa->time - step;
+ totwrite += (cfra >= pa_sfra);
}
}
else {
for (p = 0; p < psys->totpart; p++, pa++) {
- totwrite += (cfra >= pa->time - step && cfra <= pa->dietime + step);
+ /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */
+ const int pa_sfra = (int)pa->time - step;
+ const int pa_efra = ((int)pa->dietime - 1) + step;
+ totwrite += (cfra >= pa_sfra) && (cfra <= pa_efra);
}
}
diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c
index 5f751da1ee1..9a6f861eae8 100644
--- a/source/blender/blenkernel/intern/vfont.c
+++ b/source/blender/blenkernel/intern/vfont.c
@@ -1372,7 +1372,7 @@ static bool vfont_to_curve(Object *ob,
ct = chartransdata;
for (i = 0; i <= slen; i++, ct++) {
- float ctime, dtime, vec[4], tvec[4], rotvec[3];
+ float ctime, dtime, vec[4], rotvec[3];
float si, co;
/* Rotate around center character. */
@@ -1392,9 +1392,9 @@ static bool vfont_to_curve(Object *ob,
CLAMP(ctime, 0.0f, 1.0f);
/* Calculate the right loc AND the right rot separately. */
- /* `vec`, `tvec` need 4 items. */
- BKE_where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL);
- BKE_where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL);
+ /* `vec` needs 4 items. */
+ BKE_where_on_path(cu->textoncurve, ctime, vec, NULL, NULL, NULL, NULL);
+ BKE_where_on_path(cu->textoncurve, ctime + dtime, NULL, rotvec, NULL, NULL, NULL);
mul_v3_fl(vec, sizefac);
diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h
index 47cef2a53ec..cef9e543a59 100644
--- a/source/blender/blenkernel/nla_private.h
+++ b/source/blender/blenkernel/nla_private.h
@@ -142,7 +142,11 @@ typedef struct NlaKeyframingContext {
/* Data of the currently edited strip (copy, or fake strip for the main action). */
NlaStrip strip;
NlaEvalStrip *eval_strip;
+ /* Storage for the action track as a strip. */
+ NlaStrip action_track_strip;
+ /* Strips above tweaked strip. */
+ ListBase upper_estrips;
/* Evaluated NLA stack below the tweak strip. */
NlaEvalData lower_eval_data;
} NlaKeyframingContext;
@@ -173,7 +177,22 @@ NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list,
/**
* Evaluates the given evaluation strip.
*/
-void nlastrip_evaluate(PointerRNA *ptr,
+
+enum eNlaStripEvaluate_Mode {
+ /* Blend upper strip with lower stack. */
+ STRIP_EVAL_BLEND,
+ /* Given upper strip and blended snapshot, solve for lower stack. */
+ STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT,
+ /* Store strip fcurve values in snapshot, properly marking blend_domain values.
+ *
+ * Currently only used for transitions to distinguish fcurve sampled values from default or lower
+ * stack values.
+ */
+ STRIP_EVAL_NOBLEND,
+};
+
+void nlastrip_evaluate(const int evaluation_mode,
+ PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
@@ -222,6 +241,36 @@ void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
float upper_influence,
NlaEvalSnapshot *r_upper_snapshot);
+void nlasnapshot_blend_get_inverted_lower_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *blended_snapshot,
+ NlaEvalSnapshot *upper_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_lower_snapshot);
+
+void nlasnapshot_blend_strip(PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context,
+ const bool flush_to_original);
+
+void nlasnapshot_blend_strip_get_inverted_lower_snapshot(
+ PointerRNA *ptr,
+ NlaEvalData *eval_data,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context);
+
+void nlasnapshot_blend_strip_no_blend(PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index bb6bc0db00d..a983821f15e 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -524,6 +524,15 @@ MINLINE uint max_uu(uint a, uint b)
return (b < a) ? a : b;
}
+MINLINE unsigned long long min_ulul(unsigned long long a, unsigned long long b)
+{
+ return (a < b) ? a : b;
+}
+MINLINE unsigned long long max_ulul(unsigned long long a, unsigned long long b)
+{
+ return (b < a) ? a : b;
+}
+
MINLINE float min_fff(float a, float b, float c)
{
return min_ff(min_ff(a, b), c);
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index ee70cebcfd2..0c56b67ca99 100644
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ b/source/blender/draw/engines/eevee/eevee_data.c
@@ -160,7 +160,7 @@ EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb
{
if (mb_data->hair_data == NULL) {
/* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
- int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1;
+ int psys_len = BLI_listbase_count(&ob->modifiers);
EEVEE_HairMotionData *hair_step = MEM_callocN(
sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, __func__);
hair_step->psys_len = psys_len;
@@ -170,6 +170,18 @@ EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb
return mb_data->hair_data;
}
+EEVEE_HairMotionData *EEVEE_motion_blur_curves_data_get(EEVEE_ObjectMotionData *mb_data)
+{
+ if (mb_data->hair_data == NULL) {
+ EEVEE_HairMotionData *hair_step = MEM_callocN(
+ sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]), __func__);
+ hair_step->psys_len = 1;
+ hair_step->type = EEVEE_MOTION_DATA_HAIR;
+ mb_data->hair_data = hair_step;
+ }
+ return mb_data->hair_data;
+}
+
/* View Layer data. */
void EEVEE_view_layer_data_free(void *storage)
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index 6cd1a31085f..0729b2078a8 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -111,7 +111,7 @@ void EEVEE_cache_populate(void *vedata, Object *ob)
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
}
else if (ob->type == OB_CURVES) {
- EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
+ EEVEE_object_curves_cache_populate(vedata, sldata, ob, &cast_shadow);
}
else if (ob->type == OB_VOLUME) {
EEVEE_volumes_cache_object_add(sldata, vedata, draw_ctx->scene, ob);
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index d9fdba63c03..c46e5dd75d6 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -754,34 +754,6 @@ BLI_INLINE EeveeMaterialCache eevee_material_cache_get(
return matcache;
}
-static void eevee_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- ParticleSystem *psys,
- ModifierData *md,
- int matnr,
- bool *cast_shadow)
-{
- EeveeMaterialCache matcache = eevee_material_cache_get(vedata, sldata, ob, matnr - 1, true);
-
- if (matcache.depth_grp) {
- *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp, NULL);
- DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
- }
- if (matcache.shading_grp) {
- *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(
- ob, psys, md, matcache.shading_grp, matcache.shading_gpumat);
- DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
- }
- if (matcache.shadow_grp) {
- *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp, NULL);
- DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
- *cast_shadow = true;
- }
-
- EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md);
-}
-
#define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) \
do { \
if (oedata) { \
@@ -913,18 +885,56 @@ void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata,
if (draw_as != PART_DRAW_PATH) {
continue;
}
- eevee_hair_cache_populate(vedata, sldata, ob, psys, md, part->omat, cast_shadow);
+ EeveeMaterialCache matcache = eevee_material_cache_get(
+ vedata, sldata, ob, part->omat - 1, true);
+
+ if (matcache.depth_grp) {
+ *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(
+ ob, psys, md, matcache.depth_grp, NULL);
+ DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
+ }
+ if (matcache.shading_grp) {
+ *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(
+ ob, psys, md, matcache.shading_grp, matcache.shading_gpumat);
+ DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
+ }
+ if (matcache.shadow_grp) {
+ *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(
+ ob, psys, md, matcache.shadow_grp, NULL);
+ DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
+ *cast_shadow = true;
+ }
+
+ EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md);
}
}
}
}
-void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- bool *cast_shadow)
+void EEVEE_object_curves_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob,
+ bool *cast_shadow)
{
- eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, CURVES_MATERIAL_NR, cast_shadow);
+ EeveeMaterialCache matcache = eevee_material_cache_get(
+ vedata, sldata, ob, CURVES_MATERIAL_NR - 1, true);
+
+ if (matcache.depth_grp) {
+ *matcache.depth_grp_p = DRW_shgroup_curves_create_sub(ob, matcache.depth_grp, NULL);
+ DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
+ }
+ if (matcache.shading_grp) {
+ *matcache.shading_grp_p = DRW_shgroup_curves_create_sub(
+ ob, matcache.shading_grp, matcache.shading_gpumat);
+ DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
+ }
+ if (matcache.shadow_grp) {
+ *matcache.shadow_grp_p = DRW_shgroup_curves_create_sub(ob, matcache.shadow_grp, NULL);
+ DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
+ *cast_shadow = true;
+ }
+
+ EEVEE_motion_blur_curves_cache_populate(sldata, vedata, ob);
}
void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index fbc19a01a8b..e3342508a14 100644
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c
@@ -270,6 +270,57 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
}
}
+void EEVEE_motion_blur_curves_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
+ EEVEE_Data *vedata,
+ Object *ob)
+{
+ EEVEE_PassList *psl = vedata->psl;
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_EffectsInfo *effects = stl->effects;
+
+ if (!DRW_state_is_scene_render() || psl->velocity_hair == NULL) {
+ return;
+ }
+
+ /* For now we assume curves objects are always moving. */
+ EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob);
+ if (mb_data == NULL) {
+ return;
+ }
+
+ int mb_step = effects->motion_blur_step;
+ /* Store transform. */
+ copy_m4_m4(mb_data->obmat[mb_step], ob->obmat);
+
+ EEVEE_HairMotionData *mb_curves = EEVEE_motion_blur_curves_data_get(mb_data);
+
+ if (mb_step == MB_CURR) {
+ /* Fill missing matrices if the object was hidden in previous or next frame. */
+ if (is_zero_m4(mb_data->obmat[MB_PREV])) {
+ copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]);
+ }
+ if (is_zero_m4(mb_data->obmat[MB_NEXT])) {
+ copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]);
+ }
+
+ GPUTexture *tex_prev = mb_curves->psys[0].step_data[MB_PREV].hair_pos_tx;
+ GPUTexture *tex_next = mb_curves->psys[0].step_data[MB_NEXT].hair_pos_tx;
+
+ DRWShadingGroup *grp = DRW_shgroup_curves_create_sub(ob, effects->motion_blur.hair_grp, NULL);
+ DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
+ DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
+ DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);
+ DRW_shgroup_uniform_texture(grp, "prvBuffer", tex_prev);
+ DRW_shgroup_uniform_texture(grp, "nxtBuffer", tex_next);
+ DRW_shgroup_uniform_bool(grp, "useDeform", &mb_curves->use_deform, 1);
+ }
+ else {
+ /* Store vertex position buffer. */
+ mb_curves->psys[0].step_data[mb_step].hair_pos = DRW_curves_pos_buffer_get(ob);
+ mb_curves->use_deform = true;
+ }
+}
+
void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
Object *ob)
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 30768c98016..effae313acf 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -666,7 +666,7 @@ typedef struct EEVEE_HairMotionData {
/** Needs to be first to ensure casting. */
eEEVEEMotionData type;
int use_deform;
- /** Allocator will alloc enough slot for all particle systems. Or 1 if it's a hair object. */
+ /** Allocator will alloc enough slot for all particle systems. Or 1 if it's a curves object. */
int psys_len;
struct {
/* The vbos and textures are not owned. */
@@ -1095,6 +1095,7 @@ EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob);
EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob);
EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data);
EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob);
+EEVEE_HairMotionData *EEVEE_motion_blur_curves_data_get(EEVEE_ObjectMotionData *mb_data);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob);
EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob);
@@ -1120,10 +1121,10 @@ void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob,
bool *cast_shadow);
-void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- bool *cast_shadow);
+void EEVEE_object_curves_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob,
+ bool *cast_shadow);
void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_materials_free(void);
void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3]);
@@ -1477,6 +1478,9 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *sldata,
Object *ob,
struct ParticleSystem *psys,
struct ModifierData *md);
+void EEVEE_motion_blur_curves_cache_populate(EEVEE_ViewLayerData *sldata,
+ EEVEE_Data *vedata,
+ Object *ob);
void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata);
void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata);
void EEVEE_motion_blur_draw(EEVEE_Data *vedata);
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index e971edbaa44..47e2b95f367 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -230,7 +230,7 @@ void EEVEE_render_cache(void *vedata,
}
}
else if (ob->type == OB_CURVES) {
- EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
+ EEVEE_object_curves_cache_populate(vedata, sldata, ob, &cast_shadow);
if (do_cryptomatte) {
EEVEE_cryptomatte_object_curves_cache_populate(data, sldata, ob);
}
diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
index 7b5d85f9eb8..d8adf302e37 100644
--- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
@@ -44,6 +44,15 @@ struct OcclusionData {
vec4 horizons;
/* Custom large scale occlusion. */
float custom_occlusion;
+
+#ifdef GPU_METAL
+ /* Constructors required for OcclusionData(..) syntax. */
+ inline OcclusionData() = default;
+ inline OcclusionData(vec4 in_horizons, float in_custom_occlusion)
+ : horizons(in_horizons), custom_occlusion(in_custom_occlusion)
+ {
+ }
+#endif
};
vec4 pack_occlusion_data(OcclusionData data)
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
index 269fa1a3c2f..574b24b3650 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
@@ -9,7 +9,15 @@ struct ClosureInputDiffuse {
vec3 albedo; /** Used for multibounce GTAO approximation. Not applied to final radiance. */
};
-#define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0))
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define CLOSURE_INPUT_Diffuse_DEFAULT \
+ { \
+ vec3(0.0), vec3(0.0) \
+ }
+#else
+# define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0))
+#endif
struct ClosureEvalDiffuse {
vec3 probe_sampling_dir; /** Direction to sample probes from. */
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
index c086f79f96a..0deaf4054d2 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
@@ -12,7 +12,14 @@ struct ClosureInputGlossy {
float roughness; /** Input roughness, not squared. */
};
-#define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0)
+#ifdef GPU_METAL
+# define CLOSURE_INPUT_Glossy_DEFAULT \
+ { \
+ vec3(0.0), 0.0 \
+ }
+#else
+# define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0)
+#endif
struct ClosureEvalGlossy {
vec4 ltc_mat; /** LTC matrix values. */
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
index 2d94017e80b..3f07f80571a 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
@@ -163,7 +163,15 @@
#define ClosureInputDummy ClosureOutput
#define ClosureOutputDummy ClosureOutput
#define ClosureEvalDummy ClosureOutput
-#define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0))
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define CLOSURE_EVAL_DUMMY \
+ { \
+ vec3(0) \
+ }
+#else
+# define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0))
+#endif
#define CLOSURE_INPUT_Dummy_DEFAULT CLOSURE_EVAL_DUMMY
#define closure_Dummy_eval_init(cl_in, cl_common, cl_out) CLOSURE_EVAL_DUMMY
#define closure_Dummy_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
@@ -186,8 +194,15 @@ struct ClosureInputCommon {
/** Custom occlusion value set by the user. */
float occlusion;
};
-
-#define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0)
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define CLOSURE_INPUT_COMMON_DEFAULT \
+ { \
+ 1.0 \
+ }
+#else
+# define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0)
+#endif
struct ClosureEvalCommon {
/** Result of SSAO. */
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl
index 8b48ec4ea25..5c6769b185a 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl
@@ -12,8 +12,15 @@ struct ClosureInputRefraction {
float roughness; /** Input roughness, not squared. */
float ior; /** Index of refraction ratio. */
};
-
-#define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0)
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define CLOSURE_INPUT_Refraction_DEFAULT \
+ { \
+ vec3(0.0), 0.0, 0.0 \
+ }
+#else
+# define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0)
+#endif
struct ClosureEvalRefraction {
vec3 P; /** LTC matrix values. */
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
index c1e257ee366..89a6f10e634 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
@@ -9,9 +9,15 @@
struct ClosureInputTranslucent {
vec3 N; /** Shading normal. */
};
-
-#define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0))
-
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define CLOSURE_INPUT_Translucent_DEFAULT \
+ { \
+ vec3(0.0) \
+ }
+#else
+# define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0))
+#endif
/* Stubs. */
#define ClosureEvalTranslucent ClosureEvalDummy
#define ClosureOutputTranslucent ClosureOutput
diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
index 0f35acfcc86..9d59c0439c3 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
@@ -23,6 +23,50 @@ struct Closure {
vec3 transmittance;
float holdout;
#endif
+
+/* Metal Default Constructor - Requred for C++ constructor syntax. */
+#ifdef GPU_METAL
+ inline Closure() = default;
+# ifdef VOLUMETRICS
+ /* Explicit Closure constructors -- To support GLSL syntax */
+ inline Closure(vec3 in_absorption, vec3 in_scatter, vec3 in_emission, float in_anisotropy)
+ : absorption(in_absorption),
+ scatter(in_scatter),
+ emission(in_emission),
+ anisotropy(in_anisotropy)
+ {
+ }
+# else
+ /* Explicit Closure constructors -- To support GLSL syntax */
+ inline Closure(vec3 in_radiance,
+ vec3 in_transmittance,
+ float in_holdout,
+ vec4 in_ssr_data,
+ vec2 in_ssr_normal,
+ int in_flag
+# ifdef USE_SSS
+ ,
+ vec3 in_sss_irradiance,
+ vec3 in_sss_albedo,
+ float in_sss_radius
+# endif /* USE_SSS */
+ )
+ : radiance(in_radiance),
+ transmittance(in_transmittance),
+ holdout(in_holdout),
+ ssr_data(in_ssr_data),
+ ssr_normal(in_ssr_normal),
+ flag(in_flag)
+# ifdef USE_SSS
+ ,
+ sss_irradiance(in_sss_irradiance),
+ sss_albedo(in_sss_albedo),
+ sss_radius(in_sss_radius)
+# endif /* USE_SSS */
+ {
+ }
+# endif /* VOLUMETRICS */
+#endif /* GPU_METAL */
};
#ifndef GPU_METAL
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
index e288e1a55ea..9ed6ffa90c7 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
@@ -334,7 +334,15 @@ struct DofGatherData {
float layer_opacity;
};
-#define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define GATHER_DATA_INIT \
+ { \
+ vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 \
+ }
+#else
+# define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
+#endif
void dof_gather_ammend_weight(inout DofGatherData sample_data, float weight)
{
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
index d25ef23a706..681e69ae384 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
@@ -15,7 +15,6 @@ uniform float visibilityRange;
uniform float visibilityBlur;
uniform float sampleCount;
-uniform float;
out vec4 FragColor;
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
index 052d7906b76..2c2b51d6200 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
@@ -86,7 +86,11 @@ void main()
volumeOrco = (volumeObjectToTexture * vec4(volumeOrco, 1.0)).xyz;
if (any(lessThan(volumeOrco, vec3(0.0))) || any(greaterThan(volumeOrco, vec3(1.0)))) {
+ /* Note: Discard is not an explicit return in Metal prior to versions 2.3.
+ * adding return after discard ensures consistent behaviour and avoids GPU
+ * side-effects where control flow continues with undefined values. */
discard;
+ return;
}
#endif
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
index 025cada5041..26b60c992e1 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
@@ -72,7 +72,11 @@ void main()
vec3 Tr = exp(-s_extinction * s_len);
/* integrate along the current step segment */
- Lscat = (Lscat - Lscat * Tr) / max(vec3(1e-8), s_extinction);
+ /* Note: Original calculation carries precision issues when compiling for AMD GPUs
+ * and running Metal. This version of the equation retains precision well for all
+ * macOS HW configurations. */
+ Lscat = (Lscat * (1.0f - Tr)) / max(vec3(1e-8), s_extinction);
+
/* accumulate and also take into account the transmittance from previous steps */
finalScattering += finalTransmittance * Lscat;
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
index cf38c1fc12c..75bd3d30d68 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
@@ -116,7 +116,7 @@ void blend_mode_output(
color.a *= opacity;
frag_revealage = frag_color = clamp(1.0 / max(vec4(1e-6), 1.0 - color * color.a), 0.0, 1e18);
break;
- case MODE_HARDLIGHT:
+ case MODE_HARDLIGHT: {
/* Reminder: Blending func is multiply blend (dst.rgba * src.rgba). */
/**
* We need to separate the overlay equation into 2 term (one mul and one add).
@@ -134,6 +134,7 @@ void blend_mode_output(
frag_revealage = frag_color = 2.0 * s + 2.0 * color * (1.0 - s * 2.0);
frag_revealage = max(vec4(0.0), frag_revealage);
break;
+ }
case MODE_HARDLIGHT_SECOND_PASS:
/* Reminder: Blending func is additive blend (dst.rgba + src.rgba). */
color = mix(vec4(0.5), color, color.a * opacity);
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index 73450db8eea..abcca5525c7 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -528,13 +528,13 @@ static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLay
case PFIELD_GUIDE:
if (cu && (cu->flag & CU_PATH) && ob->runtime.curve_cache->anim_path_accum_length) {
instdata.size_x = instdata.size_y = instdata.size_z = pd->f_strength;
- float pos[4], tmp[3];
- BKE_where_on_path(ob, 0.0f, pos, tmp, NULL, NULL, NULL);
+ float pos[4];
+ BKE_where_on_path(ob, 0.0f, pos, NULL, NULL, NULL, NULL);
copy_v3_v3(instdata.pos, ob->obmat[3]);
translate_m4(instdata.mat, pos[0], pos[1], pos[2]);
DRW_buffer_add_entry(cb->field_curve, color, &instdata);
- BKE_where_on_path(ob, 1.0f, pos, tmp, NULL, NULL, NULL);
+ BKE_where_on_path(ob, 1.0f, pos, NULL, NULL, NULL, NULL);
copy_v3_v3(instdata.pos, ob->obmat[3]);
translate_m4(instdata.mat, pos[0], pos[1], pos[2]);
DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata);
diff --git a/source/blender/draw/engines/overlay/shaders/background_frag.glsl b/source/blender/draw/engines/overlay/shaders/background_frag.glsl
index 19313c0415b..6b45b341ca4 100644
--- a/source/blender/draw/engines/overlay/shaders/background_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/background_frag.glsl
@@ -57,13 +57,13 @@ void main()
/* XXX do interpolation in a non-linear space to have a better visual result. */
col_high = pow(colorBackground.rgb, vec3(1.0 / 2.2));
col_low = pow(colorBackgroundGradient.rgb, vec3(1.0 / 2.2));
- bg_col = mix(col_low, col_high, uvcoordsvar.t);
+ bg_col = mix(col_low, col_high, uvcoordsvar.y);
/* Convert back to linear. */
bg_col = pow(bg_col, vec3(2.2));
/* Dither to hide low precision buffer. (Could be improved) */
bg_col += dither();
break;
- case BG_RADIAL:
+ case BG_RADIAL: {
/* Do interpolation in a non-linear space to have a better visual result. */
col_high = pow(colorBackground.rgb, vec3(1.0 / 2.2));
col_low = pow(colorBackgroundGradient.rgb, vec3(1.0 / 2.2));
@@ -76,12 +76,14 @@ void main()
/* Dither to hide low precision buffer. (Could be improved) */
bg_col += dither();
break;
- case BG_CHECKER:
+ }
+ case BG_CHECKER: {
float size = sizeChecker * sizePixel;
ivec2 p = ivec2(floor(gl_FragCoord.xy / size));
bool check = mod(p.x, 2) == mod(p.y, 2);
bg_col = (check) ? colorCheckerPrimary.rgb : colorCheckerSecondary.rgb;
break;
+ }
case BG_MASK:
fragColor = vec4(vec3(1.0 - alpha), 0.0);
return;
diff --git a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl
index 19d54a57479..ba0a4c0da81 100644
--- a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl
@@ -358,6 +358,12 @@ void main()
line_end = vec2(0.0, 0.5);
break;
default:
+ /* Ensure values are assigned to, avoids undefined behaviour for
+ * divergent control-flow. This can occur if discard is called
+ * as discard is not treated as a return in Metal 2.2. So
+ * side-effects can still cause problems. */
+ line_start = vec2(0.0);
+ line_end = vec2(0.0);
break;
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl
index 71cf08b7e8c..cfc94ef7c9a 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl
@@ -8,8 +8,10 @@
/* From http://libnoise.sourceforge.net/noisegen/index.html */
float integer_noise(int n)
{
- n = (n >> 13) ^ n;
- int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
+ /* Integer bit-shifts cause precision issues due to overflow
+ * in a number of workbench tests. Use uint instead. */
+ uint nn = (uint(n) >> 13u) ^ uint(n);
+ nn = (nn * (nn * nn * 60493u + 19990303u) + 1376312589u) & 0x7fffffffu;
return (float(nn) / 1073741824.0);
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
index 4ff281ccd29..36059b6076f 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
@@ -218,7 +218,15 @@ void main()
/* Manual depth test. TODO: remove. */
float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r;
if (gl_FragCoord.z >= depth) {
+ /* Note: In the Metal API, prior to Metal 2.3, Discard is not an explicit return and can
+ * produce undefined behaviour. This is especially prominent with derivatives if control-flow
+ * divergence is present.
+ *
+ * Adding a return call eliminates undefined behaviour and a later out-of-bounds read causing
+ * a crash on AMD platforms.
+ * This behaviour can also affect OpenGL on certain devices. */
discard;
+ return;
}
vec3 Lscat;
@@ -268,6 +276,7 @@ void main()
/* Start is further away than the end.
* That means no volume is intersected. */
discard;
+ return;
}
fragColor = volume_integration(ls_ray_ori,
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl
index 531ed461057..20053b8917c 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl
@@ -64,22 +64,35 @@ vec3 get_world_lighting(vec3 base_color, float roughness, float metallic, vec3 N
if (world_data.use_specular) {
/* Prepare Specular computation. Eval 4 lights at once. */
vec3 R = -reflect(I, N);
+
+#ifdef GPU_METAL
+ /* Split vectors into arrays of floats. Partial vector references are unsupported by MSL. */
+ float spec_angle[4], spec_NL[4], wrap_NL[4];
+# define AS_VEC4(a) vec4(a[0], a[1], a[2], a[3])
+#else
vec4 spec_angle, spec_NL, wrap_NL;
- prep_specular(world_data.lights[0].direction.xyz, I, N, R, spec_NL.x, wrap_NL.x, spec_angle.x);
- prep_specular(world_data.lights[1].direction.xyz, I, N, R, spec_NL.y, wrap_NL.y, spec_angle.y);
- prep_specular(world_data.lights[2].direction.xyz, I, N, R, spec_NL.z, wrap_NL.z, spec_angle.z);
- prep_specular(world_data.lights[3].direction.xyz, I, N, R, spec_NL.w, wrap_NL.w, spec_angle.w);
+# define AS_VEC4(a) a
+#endif
+ prep_specular(
+ world_data.lights[0].direction.xyz, I, N, R, spec_NL[0], wrap_NL[0], spec_angle[0]);
+ prep_specular(
+ world_data.lights[1].direction.xyz, I, N, R, spec_NL[1], wrap_NL[1], spec_angle[1]);
+ prep_specular(
+ world_data.lights[2].direction.xyz, I, N, R, spec_NL[2], wrap_NL[2], spec_angle[2]);
+ prep_specular(
+ world_data.lights[3].direction.xyz, I, N, R, spec_NL[3], wrap_NL[3], spec_angle[3]);
vec4 gloss = vec4(1.0 - roughness);
/* Reduce gloss for smooth light. (simulate bigger light) */
gloss *= 1.0 - wrap;
vec4 shininess = exp2(10.0 * gloss + 1.0);
- vec4 spec_light = blinn_specular(shininess, spec_angle, spec_NL);
+ vec4 spec_light = blinn_specular(shininess, AS_VEC4(spec_angle), AS_VEC4(spec_NL));
/* Simulate Env. light. */
vec4 w = mix(wrap, vec4(1.0), roughness);
- vec4 spec_env = wrapped_lighting(wrap_NL, w);
+ vec4 spec_env = wrapped_lighting(AS_VEC4(wrap_NL), w);
+#undef AS_VEC4
spec_light = mix(spec_light, spec_env, wrap * wrap);
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index 85c8f9c420a..566fd30096d 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -425,7 +425,8 @@ void workbench_cache_populate(void *ved, Object *ob)
}
else if (ob->type == OB_CURVES) {
int color_type = workbench_color_type_get(wpd, ob, NULL, NULL, NULL);
- workbench_cache_hair_populate(wpd, ob, NULL, NULL, color_type, false, CURVES_MATERIAL_NR);
+ DRWShadingGroup *grp = workbench_material_hair_setup(wpd, ob, CURVES_MATERIAL_NR, color_type);
+ DRW_shgroup_curves_create_sub(ob, grp, NULL);
}
else if (ob->type == OB_VOLUME) {
if (wpd->shading.type != OB_WIRE) {
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
index 717ea00fc0c..8a7b4fc9703 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.cc
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -844,6 +844,7 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache,
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle);
EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask);
EXTRACT_ADD_REQUESTED(ibo, lines_adjacency);
+ EXTRACT_ADD_REQUESTED(vbo, orco);
EXTRACT_ADD_REQUESTED(vbo, vcol);
EXTRACT_ADD_REQUESTED(vbo, weights);
EXTRACT_ADD_REQUESTED(vbo, sculpt_data);
diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
index 2653035a39f..c859a72b371 100644
--- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc
+++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
@@ -1162,8 +1162,8 @@ static void draw_subdiv_ubo_update_and_bind(const DRWSubdivCache *cache,
GPU_uniformbuf_update(cache->ubo, &storage);
- const int location = GPU_shader_get_uniform_block(shader, "shader_data");
- GPU_uniformbuf_bind(cache->ubo, location);
+ const int binding = GPU_shader_get_uniform_block_binding(shader, "shader_data");
+ GPU_uniformbuf_bind(cache->ubo, binding);
}
/** \} */
@@ -1942,7 +1942,9 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene,
return false;
}
- const bool optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges);
+ /* Edges which do not come from coarse edges should not be drawn in edit mode, only in object
+ * mode when optimal display in turned off. */
+ const bool optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges) || is_editmode;
draw_cache->bm = bm;
draw_cache->mesh = mesh_eval;
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index 34b930ae9c8..d302140d9ac 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -53,12 +53,17 @@ struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object,
struct ModifierData *md,
struct DRWShadingGroup *shgrp,
struct GPUMaterial *gpu_material);
+
+struct DRWShadingGroup *DRW_shgroup_curves_create_sub(struct Object *object,
+ struct DRWShadingGroup *shgrp,
+ struct GPUMaterial *gpu_material);
/**
* \note Only valid after #DRW_hair_update().
*/
struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md);
+struct GPUVertBuf *DRW_curves_pos_buffer_get(struct Object *object);
void DRW_hair_duplimat_get(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,
diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c
index 920bca05e6e..aac6f7e58c5 100644
--- a/source/blender/draw/intern/draw_hair.c
+++ b/source/blender/draw/intern/draw_hair.c
@@ -164,17 +164,28 @@ static ParticleHairCache *drw_hair_particle_cache_get(Object *object,
int subdiv,
int thickness_res)
{
- bool update;
ParticleHairCache *cache;
- if (psys) {
- /* Old particle hair. */
- update = particles_ensure_procedural_data(
- object, psys, md, &cache, gpu_material, subdiv, thickness_res);
- }
- else {
- /* New curves object. */
- update = curves_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res);
+ bool update = particles_ensure_procedural_data(
+ object, psys, md, &cache, gpu_material, subdiv, thickness_res);
+
+ if (update) {
+ if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) {
+ drw_hair_particle_cache_update_compute(cache, subdiv);
+ }
+ else {
+ drw_hair_particle_cache_update_transform_feedback(cache, subdiv);
+ }
}
+ return cache;
+}
+
+static ParticleHairCache *drw_curves_cache_get(Object *object,
+ GPUMaterial *gpu_material,
+ int subdiv,
+ int thickness_res)
+{
+ ParticleHairCache *cache;
+ bool update = curves_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res);
if (update) {
if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) {
@@ -201,37 +212,44 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi
return cache->final[subdiv].proc_buf;
}
+GPUVertBuf *DRW_curves_pos_buffer_get(Object *object)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+
+ int subdiv = scene->r.hair_subdiv;
+ int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
+
+ ParticleHairCache *cache = drw_curves_cache_get(object, NULL, subdiv, thickness_res);
+
+ return cache->final[subdiv].proc_buf;
+}
+
void DRW_hair_duplimat_get(Object *object,
- ParticleSystem *psys,
+ ParticleSystem *UNUSED(psys),
ModifierData *UNUSED(md),
float (*dupli_mat)[4])
{
Object *dupli_parent = DRW_object_get_dupli_parent(object);
DupliObject *dupli_object = DRW_object_get_dupli(object);
- if (psys) {
- if ((dupli_parent != NULL) && (dupli_object != NULL)) {
- if (dupli_object->type & OB_DUPLICOLLECTION) {
- unit_m4(dupli_mat);
- Collection *collection = dupli_parent->instance_collection;
- if (collection != NULL) {
- sub_v3_v3(dupli_mat[3], collection->instance_offset);
- }
- mul_m4_m4m4(dupli_mat, dupli_parent->obmat, dupli_mat);
- }
- else {
- copy_m4_m4(dupli_mat, dupli_object->ob->obmat);
- invert_m4(dupli_mat);
- mul_m4_m4m4(dupli_mat, object->obmat, dupli_mat);
+ if ((dupli_parent != NULL) && (dupli_object != NULL)) {
+ if (dupli_object->type & OB_DUPLICOLLECTION) {
+ unit_m4(dupli_mat);
+ Collection *collection = dupli_parent->instance_collection;
+ if (collection != NULL) {
+ sub_v3_v3(dupli_mat[3], collection->instance_offset);
}
+ mul_m4_m4m4(dupli_mat, dupli_parent->obmat, dupli_mat);
}
else {
- unit_m4(dupli_mat);
+ copy_m4_m4(dupli_mat, dupli_object->ob->obmat);
+ invert_m4(dupli_mat);
+ mul_m4_m4m4(dupli_mat, object->obmat, dupli_mat);
}
}
else {
- /* New curves object. */
- copy_m4_m4(dupli_mat, object->obmat);
+ unit_m4(dupli_mat);
}
}
@@ -280,23 +298,11 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
DRW_hair_duplimat_get(object, psys, md, dupli_mat);
/* Get hair shape parameters. */
- float hair_rad_shape, hair_rad_root, hair_rad_tip;
- bool hair_close_tip;
- if (psys) {
- /* Old particle hair. */
- ParticleSettings *part = psys->part;
- hair_rad_shape = part->shape;
- hair_rad_root = part->rad_root * part->rad_scale * 0.5f;
- hair_rad_tip = part->rad_tip * part->rad_scale * 0.5f;
- hair_close_tip = (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0;
- }
- else {
- /* TODO: implement for new curves object. */
- hair_rad_shape = 1.0f;
- hair_rad_root = 0.005f;
- hair_rad_tip = 0.0f;
- hair_close_tip = true;
- }
+ ParticleSettings *part = psys->part;
+ float hair_rad_shape = part->shape;
+ float hair_rad_root = part->rad_root * part->rad_scale * 0.5f;
+ float hair_rad_tip = part->rad_tip * part->rad_scale * 0.5f;
+ bool hair_close_tip = (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0;
DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex);
if (hair_cache->length_tex) {
@@ -317,6 +323,71 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
return shgrp;
}
+DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object,
+ DRWShadingGroup *shgrp_parent,
+ GPUMaterial *gpu_material)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+
+ int subdiv = scene->r.hair_subdiv;
+ int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
+
+ ParticleHairCache *curves_cache = drw_curves_cache_get(
+ object, gpu_material, subdiv, thickness_res);
+
+ DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent);
+
+ /* TODO: optimize this. Only bind the ones GPUMaterial needs. */
+ for (int i = 0; i < curves_cache->num_uv_layers; i++) {
+ for (int n = 0; n < MAX_LAYER_NAME_CT && curves_cache->uv_layer_names[i][n][0] != '\0'; n++) {
+ DRW_shgroup_uniform_texture(
+ shgrp, curves_cache->uv_layer_names[i][n], curves_cache->uv_tex[i]);
+ }
+ }
+ for (int i = 0; i < curves_cache->num_col_layers; i++) {
+ for (int n = 0; n < MAX_LAYER_NAME_CT && curves_cache->col_layer_names[i][n][0] != '\0'; n++) {
+ DRW_shgroup_uniform_texture(
+ shgrp, curves_cache->col_layer_names[i][n], curves_cache->col_tex[i]);
+ }
+ }
+
+ /* Fix issue with certain driver not drawing anything if there is no texture bound to
+ * "ac", "au", "u" or "c". */
+ if (curves_cache->num_uv_layers == 0) {
+ DRW_shgroup_uniform_texture(shgrp, "u", g_dummy_texture);
+ DRW_shgroup_uniform_texture(shgrp, "au", g_dummy_texture);
+ }
+ if (curves_cache->num_col_layers == 0) {
+ DRW_shgroup_uniform_texture(shgrp, "c", g_dummy_texture);
+ DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture);
+ }
+
+ /* TODO: Generalize radius implementation for curves data type. */
+ float hair_rad_shape = 1.0f;
+ float hair_rad_root = 0.005f;
+ float hair_rad_tip = 0.0f;
+ bool hair_close_tip = true;
+
+ DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", curves_cache->final[subdiv].proc_tex);
+ if (curves_cache->length_tex) {
+ DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex);
+ }
+ DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &curves_cache->final[subdiv].strands_res, 1);
+ DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
+ DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape);
+ DRW_shgroup_uniform_mat4_copy(shgrp, "hairDupliMatrix", object->obmat);
+ DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root);
+ DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip);
+ DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip);
+ /* TODO(fclem): Until we have a better way to cull the curves and render with orco, bypass
+ * culling test. */
+ GPUBatch *geom = curves_cache->final[subdiv].proc_hairs[thickness_res - 1];
+ DRW_shgroup_call_no_cull(shgrp, geom, object);
+
+ return shgrp;
+}
+
void DRW_hair_update(void)
{
#ifndef USE_TRANSFORM_FEEDBACK
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
index d4c801ab066..ed1a0ccd178 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
@@ -7,6 +7,8 @@
#include "extract_mesh.h"
+#include "draw_subdivision.h"
+
namespace blender::draw {
/* ---------------------------------------------------------------------- */
@@ -77,12 +79,77 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr,
}
}
+static void extract_orco_init_subdiv(const DRWSubdivCache *subdiv_cache,
+ const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buffer,
+ void *UNUSED(data))
+{
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
+ * attributes. This is a substantial waste of video-ram and should be done another way.
+ * Unfortunately, at the time of writing, I did not found any other "non disruptive"
+ * alternative. */
+ GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer);
+ GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops);
+
+ GPUVertBuf *coarse_vbo = GPU_vertbuf_calloc();
+ /* Dynamic as we upload and interpolate layers one at a time. */
+ GPU_vertbuf_init_with_format_ex(coarse_vbo, &format, GPU_USAGE_DYNAMIC);
+ GPU_vertbuf_data_alloc(coarse_vbo, mr->loop_len);
+
+ float(*coarse_vbo_data)[4] = static_cast<float(*)[4]>(GPU_vertbuf_get_data(coarse_vbo));
+
+ CustomData *cd_vdata = &mr->me->vdata;
+ float(*orco)[3] = static_cast<float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO));
+
+ if (mr->extract_type == MR_EXTRACT_MESH) {
+ const MLoop *mloop = mr->mloop;
+ const MPoly *mp = mr->mpoly;
+
+ int ml_index = 0;
+ for (int i = 0; i < mr->poly_len; i++, mp++) {
+ const MLoop *ml = &mloop[mp->loopstart];
+
+ for (int j = 0; j < mp->totloop; j++, ml++, ml_index++) {
+ float *loop_orco = coarse_vbo_data[ml_index];
+ copy_v3_v3(loop_orco, orco[ml->v]);
+ loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ }
+ }
+ }
+ else {
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter;
+ BMLoop *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ float *loop_orco = coarse_vbo_data[l_index];
+ copy_v3_v3(loop_orco, orco[BM_elem_index_get(l_iter->v)]);
+ loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+
+ draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, 0, false);
+
+ GPU_vertbuf_discard(coarse_vbo);
+}
+
constexpr MeshExtract create_extractor_orco()
{
MeshExtract extractor = {nullptr};
extractor.init = extract_orco_init;
extractor.iter_poly_bm = extract_orco_iter_poly_bm;
extractor.iter_poly_mesh = extract_orco_iter_poly_mesh;
+ extractor.init_subdiv = extract_orco_init_subdiv;
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_Orco_Data);
extractor.use_threading = true;
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
index 225d1676151..25f78d68914 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
@@ -317,7 +317,7 @@ static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache,
GPU_vertbuf_tag_dirty(coarse_vbo);
/* Include stride in offset. */
const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 4 * pack_layer_index++;
- draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, true);
+ draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, false);
}
CustomData_free(&loop_data, mr->loop_len);
diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl
index 6a8f1132e1b..ff52b483d77 100644
--- a/source/blender/draw/intern/shaders/common_hair_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl
@@ -211,6 +211,11 @@ void hair_get_pos_tan_binor_time(bool is_persp,
wpos += wbinor * thick_time * scale;
}
+ else {
+ /* Note: Ensures 'hairThickTime' is initialised -
+ * avoids undefined behaviour on certain macOS configurations. */
+ thick_time = 0.0;
+ }
}
float hair_get_customdata_float(const samplerBuffer cd_buf)
diff --git a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
index 371d43827b9..2eccae5bceb 100644
--- a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
@@ -18,7 +18,7 @@ void main(void)
vec4 weights = hair_get_weights_cardinal(interp_time);
finalColor = hair_interp_data(data0, data1, data2, data3, weights);
-#ifdef TF_WORKAROUND
+#if defined(TF_WORKAROUND)
int id = gl_VertexID - idOffset;
gl_Position.x = ((float(id % targetWidth) + 0.5) / float(targetWidth)) * 2.0 - 1.0;
gl_Position.y = ((float(id / targetWidth) + 0.5) / float(targetHeight)) * 2.0 - 1.0;
@@ -26,5 +26,10 @@ void main(void)
gl_Position.w = 1.0;
gl_PointSize = 1.0;
+#else
+# ifdef GPU_METAL
+ /* Metal still expects an output position for TF shaders. */
+ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
+# endif
#endif
}
diff --git a/source/blender/draw/intern/shaders/common_smaa_lib.glsl b/source/blender/draw/intern/shaders/common_smaa_lib.glsl
index 73f65fb0799..dbc4c998b34 100644
--- a/source/blender/draw/intern/shaders/common_smaa_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_smaa_lib.glsl
@@ -569,7 +569,7 @@ SamplerState PointSampler
# define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0)
# endif
#endif
-#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4)
+#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) || defined(GPU_METAL)
# define SMAATexture2D(tex) sampler2D tex
# define SMAATexturePass2D(tex) tex
# define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0)
@@ -641,14 +641,14 @@ float2 SMAACalculatePredicatedThreshold(float2 texcoord,
*/
void SMAAMovc(bool2 cond, inout float2 variable, float2 value)
{
- SMAA_FLATTEN if (cond.x) variable.x = value.x;
- SMAA_FLATTEN if (cond.y) variable.y = value.y;
+ /* Use select function (select(genType A, genType B, genBType cond)). */
+ variable = select(variable, value, cond);
}
void SMAAMovc(bool4 cond, inout float4 variable, float4 value)
{
- SMAAMovc(cond.xy, variable.xy, value.xy);
- SMAAMovc(cond.zw, variable.zw, value.zw);
+ /* Use select function (select(genType A, genType B, genBType cond)). */
+ variable = select(variable, value, cond);
}
#if SMAA_INCLUDE_VS
@@ -1281,7 +1281,15 @@ float4 SMAABlendingWeightCalculationPS(float2 texcoord,
// Fix corners:
coords.y = texcoord.y;
- SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d);
+
+# ifdef GPU_METAL
+ /* Partial vector references are unsupported in MSL. */
+ vec2 _weights = weights.rg;
+ SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), _weights, coords.xyzy, d);
+ weights.rg = _weights;
+# else
+ SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d);
+# endif
# if !defined(SMAA_DISABLE_DIAG_DETECTION)
}
@@ -1324,7 +1332,15 @@ float4 SMAABlendingWeightCalculationPS(float2 texcoord,
// Fix corners:
coords.x = texcoord.x;
+
+# ifdef GPU_METAL
+ /* Partial vector references are unsupported in MSL. */
+ vec2 _weights = weights.ba;
+ SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), _weights, coords.xyxz, d);
+ weights.ba = _weights;
+# else
SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d);
+# endif
}
return weights;
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index ead189c0389..eb7f8e8ad83 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -2796,7 +2796,7 @@ static bAnimChannelType ACF_DSSPK = {
acf_dsspk_setting_ptr, /* pointer for setting */
};
-/* Hair Expander ------------------------------------------- */
+/* Curves Expander ------------------------------------------- */
/* TODO: just get this from RNA? */
static int acf_dscurves_icon(bAnimListElem *UNUSED(ale))
@@ -2859,7 +2859,7 @@ static void *acf_dscurves_setting_ptr(bAnimListElem *ale,
}
/** Curves expander type define. */
-static bAnimChannelType ACF_DSHAIR = {
+static bAnimChannelType ACF_DSCURVES = {
"Curves Expander", /* type name */
ACHANNEL_ROLE_EXPANDER, /* role */
@@ -4129,7 +4129,7 @@ static void ANIM_init_channel_typeinfo_data(void)
animchannelTypeInfo[type++] = &ACF_DSSPK; /* Speaker Channel */
animchannelTypeInfo[type++] = &ACF_DSGPENCIL; /* GreasePencil Channel */
animchannelTypeInfo[type++] = &ACF_DSMCLIP; /* MovieClip Channel */
- animchannelTypeInfo[type++] = &ACF_DSHAIR; /* Hair Channel */
+ animchannelTypeInfo[type++] = &ACF_DSCURVES; /* Curves Channel */
animchannelTypeInfo[type++] = &ACF_DSPOINTCLOUD; /* PointCloud Channel */
animchannelTypeInfo[type++] = &ACF_DSVOLUME; /* Volume Channel */
animchannelTypeInfo[type++] = &ACF_DSSIMULATION; /* Simulation Channel */
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index d7bbc0eab2b..1a3ab100768 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -1465,6 +1465,83 @@ static void MARKER_OT_select_all(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Select Left/Right of Frame
+ * \{ */
+
+typedef enum eMarkers_LeftRightSelect_Mode {
+ MARKERS_LRSEL_LEFT = 0,
+ MARKERS_LRSEL_RIGHT,
+} eMarkers_LeftRightSelect_Mode;
+
+static const EnumPropertyItem prop_markers_select_leftright_modes[] = {
+ {MARKERS_LRSEL_LEFT, "LEFT", 0, "Before Current Frame", ""},
+ {MARKERS_LRSEL_RIGHT, "RIGHT", 0, "After Current Frame", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static void ED_markers_select_leftright(bAnimContext *ac,
+ const eMarkers_LeftRightSelect_Mode mode,
+ const bool extend)
+{
+ ListBase *markers = ac->markers;
+ Scene *scene = ac->scene;
+
+ if (markers == NULL) {
+ return;
+ }
+
+ if (!extend) {
+ deselect_markers(markers);
+ }
+
+ LISTBASE_FOREACH (TimeMarker *, marker, markers) {
+ if ((mode == MARKERS_LRSEL_LEFT && marker->frame <= CFRA) ||
+ (mode == MARKERS_LRSEL_RIGHT && marker->frame >= CFRA)) {
+ marker->flag |= SELECT;
+ }
+ }
+}
+
+static int ed_marker_select_leftright_exec(bContext *C, wmOperator *op)
+{
+ const eMarkers_LeftRightSelect_Mode mode = RNA_enum_get(op->ptr, "mode");
+ const bool extend = RNA_boolean_get(op->ptr, "extend");
+
+ bAnimContext ac;
+ if (ANIM_animdata_get_context(C, &ac) == 0) {
+ return OPERATOR_CANCELLED;
+ }
+
+ ED_markers_select_leftright(&ac, mode, extend);
+
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void MARKER_OT_select_leftright(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Markers Before/After Current Frame";
+ ot->description = "Select markers on and left/right of the current frame";
+ ot->idname = "MARKER_OT_select_leftright";
+
+ /* api callbacks */
+ ot->exec = ed_marker_select_leftright_exec;
+ ot->poll = ed_markers_poll_markers_exist;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* rna storage */
+ RNA_def_enum(
+ ot->srna, "mode", prop_markers_select_leftright_modes, MARKERS_LRSEL_LEFT, "mode", "Mode");
+ RNA_def_boolean(ot->srna, "extend", false, "extend", "Extend");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Remove Marker
*
* Remove selected time-markers.
@@ -1735,6 +1812,7 @@ void ED_operatortypes_marker(void)
WM_operatortype_append(MARKER_OT_select);
WM_operatortype_append(MARKER_OT_select_box);
WM_operatortype_append(MARKER_OT_select_all);
+ WM_operatortype_append(MARKER_OT_select_leftright);
WM_operatortype_append(MARKER_OT_delete);
WM_operatortype_append(MARKER_OT_rename);
WM_operatortype_append(MARKER_OT_make_links_scene);
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 0d0d13e2f74..d42efcd81e5 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -14,6 +14,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -1109,9 +1110,62 @@ static float *visualkey_get_values(
/* ------------------------- Insert Key API ------------------------- */
+/* Check indices that were intended to be remapped and report any failed remaps. */
+static void get_keyframe_values_create_reports(ReportList *reports,
+ PointerRNA ptr,
+ PropertyRNA *prop,
+ const int index,
+ const int count,
+ const bool force_all,
+ const BLI_bitmap *successful_remaps)
+{
+
+ DynStr *ds_failed_indices = BLI_dynstr_new();
+
+ int total_failed = 0;
+ for (int i = 0; i < count; i++) {
+ const bool cur_index_evaluated = ELEM(index, i, -1) || force_all;
+ if (!cur_index_evaluated) {
+ /* values[i] was never intended to be remapped. */
+ continue;
+ }
+
+ if (BLI_BITMAP_TEST_BOOL(successful_remaps, i)) {
+ /* values[i] succesfully remapped. */
+ continue;
+ }
+
+ total_failed++;
+ /* Report that values[i] were intended to be remapped but failed remapping process. */
+ BLI_dynstr_appendf(ds_failed_indices, "%d, ", i);
+ }
+
+ if (total_failed == 0) {
+ BLI_dynstr_free(ds_failed_indices);
+ return;
+ }
+
+ char *str_failed_indices = BLI_dynstr_get_cstring(ds_failed_indices);
+ BLI_dynstr_free(ds_failed_indices);
+
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Could not insert %i keyframe(s) due to zero NLA influence, base value, or value "
+ "remapping failed: %s.%s for indices [%s]",
+ total_failed,
+ ptr.owner_id->name,
+ RNA_property_ui_name(prop),
+ str_failed_indices);
+
+ MEM_freeN(str_failed_indices);
+}
+
/**
* Retrieve current property values to keyframe,
* possibly applying NLA correction when necessary.
+ *
+ * \param r_successful_remaps: Enables bits for indices which are both intended to be remapped and
+ * were successfully remapped. Bitmap allocated so it must be freed afterward.
*/
static float *get_keyframe_values(ReportList *reports,
PointerRNA ptr,
@@ -1121,8 +1175,10 @@ static float *get_keyframe_values(ReportList *reports,
eInsertKeyFlags flag,
float *buffer,
int buffer_size,
+ const struct AnimationEvalContext *anim_eval_context,
int *r_count,
- bool *r_force_all)
+ bool *r_force_all,
+ BLI_bitmap **r_successful_remaps)
{
float *values;
@@ -1138,17 +1194,20 @@ static float *get_keyframe_values(ReportList *reports,
values = setting_get_rna_values(&ptr, prop, buffer, buffer_size, r_count);
}
- /* adjust the value for NLA factors */
- if (!BKE_animsys_nla_remap_keyframe_values(
- nla_context, &ptr, prop, values, *r_count, index, r_force_all)) {
- BKE_report(
- reports, RPT_ERROR, "Could not insert keyframe due to zero NLA influence or base value");
+ *r_successful_remaps = BLI_BITMAP_NEW(*r_count, __func__);
- if (values != buffer) {
- MEM_freeN(values);
- }
- return NULL;
- }
+ /* adjust the value for NLA factors */
+ BKE_animsys_nla_remap_keyframe_values(nla_context,
+ &ptr,
+ prop,
+ values,
+ *r_count,
+ index,
+ anim_eval_context,
+ r_force_all,
+ *r_successful_remaps);
+ get_keyframe_values_create_reports(
+ reports, ptr, prop, index, *r_count, *r_force_all, *r_successful_remaps);
return values;
}
@@ -1284,6 +1343,7 @@ bool insert_keyframe_direct(ReportList *reports,
int value_count;
int index = fcu->array_index;
+ BLI_bitmap *successful_remaps = NULL;
float *values = get_keyframe_values(reports,
ptr,
prop,
@@ -1292,13 +1352,10 @@ bool insert_keyframe_direct(ReportList *reports,
flag,
value_buffer,
RNA_MAX_ARRAY_LENGTH,
+ anim_eval_context,
&value_count,
- NULL);
-
- if (values == NULL) {
- /* This happens if NLA rejects this insertion. */
- return false;
- }
+ NULL,
+ &successful_remaps);
if (index >= 0 && index < value_count) {
curval = values[index];
@@ -1308,6 +1365,14 @@ bool insert_keyframe_direct(ReportList *reports,
MEM_freeN(values);
}
+ const bool curval_valid = BLI_BITMAP_TEST_BOOL(successful_remaps, index);
+ MEM_freeN(successful_remaps);
+
+ /* This happens if NLA rejects this insertion. */
+ if (!curval_valid) {
+ return false;
+ }
+
return insert_keyframe_value(reports, &ptr, prop, fcu, anim_eval_context, curval, keytype, flag);
}
@@ -1461,6 +1526,7 @@ int insert_keyframe(Main *bmain,
int value_count;
bool force_all;
+ BLI_bitmap *successful_remaps = NULL;
float *values = get_keyframe_values(reports,
ptr,
prop,
@@ -1469,77 +1535,72 @@ int insert_keyframe(Main *bmain,
flag,
value_buffer,
RNA_MAX_ARRAY_LENGTH,
+ anim_eval_context,
&value_count,
- &force_all);
+ &force_all,
+ &successful_remaps);
- if (values != NULL) {
- /* Key the entire array. */
- if (array_index == -1 || force_all) {
- /* In force mode, if any of the curves succeeds, drop the replace mode and restart. */
- if (force_all && (flag & (INSERTKEY_REPLACE | INSERTKEY_AVAILABLE)) != 0) {
- int exclude = -1;
+ /* Key the entire array. */
+ if (array_index == -1 || force_all) {
+ /* In force mode, if any of the curves succeeds, drop the replace mode and restart. */
+ if (force_all && (flag & (INSERTKEY_REPLACE | INSERTKEY_AVAILABLE)) != 0) {
+ int exclude = -1;
- for (array_index = 0; array_index < value_count; array_index++) {
- if (insert_keyframe_fcurve_value(bmain,
- reports,
- &ptr,
- prop,
- act,
- group,
- rna_path,
- array_index,
- &remapped_context,
- values[array_index],
- keytype,
- flag)) {
- ret++;
- exclude = array_index;
- break;
- }
+ for (array_index = 0; array_index < value_count; array_index++) {
+ if (!BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) {
+ continue;
}
- if (exclude != -1) {
- flag &= ~(INSERTKEY_REPLACE | INSERTKEY_AVAILABLE);
-
- for (array_index = 0; array_index < value_count; array_index++) {
- if (array_index != exclude) {
- ret += insert_keyframe_fcurve_value(bmain,
- reports,
- &ptr,
- prop,
- act,
- group,
- rna_path,
- array_index,
- &remapped_context,
- values[array_index],
- keytype,
- flag);
- }
- }
+ if (insert_keyframe_fcurve_value(bmain,
+ reports,
+ &ptr,
+ prop,
+ act,
+ group,
+ rna_path,
+ array_index,
+ &remapped_context,
+ values[array_index],
+ keytype,
+ flag)) {
+ ret++;
+ exclude = array_index;
+ break;
}
}
- /* Simply insert all channels. */
- else {
+
+ if (exclude != -1) {
+ flag &= ~(INSERTKEY_REPLACE | INSERTKEY_AVAILABLE);
+
for (array_index = 0; array_index < value_count; array_index++) {
- ret += insert_keyframe_fcurve_value(bmain,
- reports,
- &ptr,
- prop,
- act,
- group,
- rna_path,
- array_index,
- &remapped_context,
- values[array_index],
- keytype,
- flag);
+ if (!BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) {
+ continue;
+ }
+
+ if (array_index != exclude) {
+ ret += insert_keyframe_fcurve_value(bmain,
+ reports,
+ &ptr,
+ prop,
+ act,
+ group,
+ rna_path,
+ array_index,
+ &remapped_context,
+ values[array_index],
+ keytype,
+ flag);
+ }
}
}
}
- /* Key a single index. */
+ /* Simply insert all channels. */
else {
- if (array_index >= 0 && array_index < value_count) {
+ for (array_index = 0; array_index < value_count; array_index++) {
+ if (!BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) {
+ continue;
+ }
+
ret += insert_keyframe_fcurve_value(bmain,
reports,
&ptr,
@@ -1554,12 +1615,31 @@ int insert_keyframe(Main *bmain,
flag);
}
}
-
- if (values != value_buffer) {
- MEM_freeN(values);
+ }
+ /* Key a single index. */
+ else {
+ if (array_index >= 0 && array_index < value_count &&
+ BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) {
+ ret += insert_keyframe_fcurve_value(bmain,
+ reports,
+ &ptr,
+ prop,
+ act,
+ group,
+ rna_path,
+ array_index,
+ &remapped_context,
+ values[array_index],
+ keytype,
+ flag);
}
}
+ if (values != value_buffer) {
+ MEM_freeN(values);
+ }
+
+ MEM_freeN(successful_remaps);
BKE_animsys_free_nla_keyframing_context_cache(&tmp_nla_cache);
if (ret) {
diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc
index 3a4dcd4f5f6..7d07c211542 100644
--- a/source/blender/editors/curves/intern/curves_ops.cc
+++ b/source/blender/editors/curves/intern/curves_ops.cc
@@ -121,147 +121,171 @@ static float4 compute_mface_weights_for_position(const Mesh &mesh,
return mface_weights;
}
-static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *UNUSED(op))
+static void try_convert_single_object(Object &curves_ob,
+ Main &bmain,
+ Scene &scene,
+ bool *r_could_not_convert_some_curves)
{
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
-
- CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
- if (curves_ob->type != OB_CURVES) {
- continue;
- }
- Curves &curves_id = *static_cast<Curves *>(curves_ob->data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- if (curves_id.surface == nullptr) {
- continue;
- }
- Object &surface_ob = *curves_id.surface;
- if (surface_ob.type != OB_MESH) {
- continue;
+ if (curves_ob.type != OB_CURVES) {
+ return;
+ }
+ Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ if (curves_id.surface == nullptr) {
+ return;
+ }
+ Object &surface_ob = *curves_id.surface;
+ if (surface_ob.type != OB_MESH) {
+ return;
+ }
+ Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
+
+ const Span<float3> positions_cu = curves.positions();
+ const VArray<int> looptri_indices = curves.surface_triangle_indices();
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface_me),
+ BKE_mesh_runtime_looptri_len(&surface_me)};
+
+ /* Find indices of curves that can be transferred to the old hair system. */
+ Vector<int> curves_indices_to_transfer;
+ for (const int curve_i : curves.curves_range()) {
+ const int looptri_i = looptri_indices[curve_i];
+ if (looptri_i >= 0 && looptri_i < looptris.size()) {
+ curves_indices_to_transfer.append(curve_i);
}
- Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
-
- const Span<float3> positions_cu = curves.positions();
- const VArray<int> looptri_indices = curves.surface_triangle_indices();
- const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface_me),
- BKE_mesh_runtime_looptri_len(&surface_me)};
-
- /* Find indices of curves that can be transferred to the old hair system. */
- Vector<int> curves_indices_to_transfer;
- for (const int curve_i : curves.curves_range()) {
- const int looptri_i = looptri_indices[curve_i];
- if (looptri_i >= 0 && looptri_i < looptris.size()) {
- curves_indices_to_transfer.append(curve_i);
- }
+ else {
+ *r_could_not_convert_some_curves = true;
}
+ }
- const int hairs_num = curves_indices_to_transfer.size();
- if (hairs_num == 0) {
- continue;
- }
+ const int hairs_num = curves_indices_to_transfer.size();
+ if (hairs_num == 0) {
+ return;
+ }
- ParticleSystem *particle_system = nullptr;
- LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) {
- if (STREQ(psys->name, curves_ob->id.name + 2)) {
- particle_system = psys;
- break;
- }
- }
- if (particle_system == nullptr) {
- ParticleSystemModifierData &psmd = *reinterpret_cast<ParticleSystemModifierData *>(
- object_add_particle_system(bmain, scene, &surface_ob, curves_ob->id.name + 2));
- particle_system = psmd.psys;
+ ParticleSystem *particle_system = nullptr;
+ LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) {
+ if (STREQ(psys->name, curves_ob.id.name + 2)) {
+ particle_system = psys;
+ break;
}
+ }
+ if (particle_system == nullptr) {
+ ParticleSystemModifierData &psmd = *reinterpret_cast<ParticleSystemModifierData *>(
+ object_add_particle_system(&bmain, &scene, &surface_ob, curves_ob.id.name + 2));
+ particle_system = psmd.psys;
+ particle_system->part->draw_step = 3;
+ }
+
+ ParticleSettings &settings = *particle_system->part;
- ParticleSettings &settings = *particle_system->part;
+ psys_free_particles(particle_system);
+ settings.type = PART_HAIR;
+ settings.totpart = 0;
+ psys_changed_type(&surface_ob, particle_system);
- psys_free_particles(particle_system);
- settings.type = PART_HAIR;
- settings.totpart = 0;
- psys_changed_type(&surface_ob, particle_system);
+ MutableSpan<ParticleData> particles{
+ static_cast<ParticleData *>(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)),
+ hairs_num};
- MutableSpan<ParticleData> particles{
- static_cast<ParticleData *>(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)),
- hairs_num};
+ /* The old hair system still uses #MFace, so make sure those are available on the mesh. */
+ BKE_mesh_tessface_calc(&surface_me);
- /* The old hair system still uses #MFace, so make sure those are available on the mesh. */
- BKE_mesh_tessface_calc(&surface_me);
+ /* Prepare utility data structure to map hair roots to mfaces. */
+ const Span<int> mface_to_poly_map{
+ static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)),
+ surface_me.totface};
+ Array<Vector<int>> poly_to_mface_map(surface_me.totpoly);
+ for (const int mface_i : mface_to_poly_map.index_range()) {
+ const int poly_i = mface_to_poly_map[mface_i];
+ poly_to_mface_map[poly_i].append(mface_i);
+ }
- /* Prepare utility data structure to map hair roots to mfaces. */
- const Span<int> mface_to_poly_map{
- static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)),
- surface_me.totface};
- Array<Vector<int>> poly_to_mface_map(surface_me.totpoly);
- for (const int mface_i : mface_to_poly_map.index_range()) {
- const int poly_i = mface_to_poly_map[mface_i];
- poly_to_mface_map[poly_i].append(mface_i);
+ /* Prepare transformation matrices. */
+ const float4x4 curves_to_world_mat = curves_ob.obmat;
+ const float4x4 surface_to_world_mat = surface_ob.obmat;
+ const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
+ const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat;
+
+ for (const int new_hair_i : curves_indices_to_transfer.index_range()) {
+ const int curve_i = curves_indices_to_transfer[new_hair_i];
+ const IndexRange points = curves.points_for_curve(curve_i);
+
+ const int looptri_i = looptri_indices[curve_i];
+ const MLoopTri &looptri = looptris[looptri_i];
+ const int poly_i = looptri.poly;
+
+ const float3 &root_pos_cu = positions_cu[points.first()];
+ const float3 root_pos_su = curves_to_surface_mat * root_pos_cu;
+
+ const int mface_i = find_mface_for_root_position(
+ surface_me, poly_to_mface_map[poly_i], root_pos_su);
+ const MFace &mface = surface_me.mface[mface_i];
+
+ const float4 mface_weights = compute_mface_weights_for_position(
+ surface_me, mface, root_pos_su);
+
+ ParticleData &particle = particles[new_hair_i];
+ const int num_keys = points.size();
+ MutableSpan<HairKey> hair_keys{
+ static_cast<HairKey *>(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)), num_keys};
+
+ particle.hair = hair_keys.data();
+ particle.totkey = hair_keys.size();
+ copy_v4_v4(particle.fuv, mface_weights);
+ particle.num = mface_i;
+ /* Not sure if there is a better way to initialize this. */
+ particle.num_dmcache = DMCACHE_NOTFOUND;
+
+ float4x4 hair_to_surface_mat;
+ psys_mat_hair_to_object(
+ &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.values);
+ /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */
+ copy_v3_v3(hair_to_surface_mat.values[3], root_pos_su);
+ const float4x4 surface_to_hair_mat = hair_to_surface_mat.inverted();
+
+ for (const int key_i : hair_keys.index_range()) {
+ const float3 &key_pos_cu = positions_cu[points[key_i]];
+ const float3 key_pos_su = curves_to_surface_mat * key_pos_cu;
+ const float3 key_pos_ha = surface_to_hair_mat * key_pos_su;
+
+ HairKey &key = hair_keys[key_i];
+ copy_v3_v3(key.co, key_pos_ha);
+ key.time = 100.0f * key_i / (float)(hair_keys.size() - 1);
}
+ }
- /* Prepare transformation matrices. */
- const float4x4 curves_to_world_mat = curves_ob->obmat;
- const float4x4 surface_to_world_mat = surface_ob.obmat;
- const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
- const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat;
+ particle_system->particles = particles.data();
+ particle_system->totpart = particles.size();
+ particle_system->flag |= PSYS_EDITED;
+ particle_system->recalc |= ID_RECALC_PSYS_RESET;
- for (const int new_hair_i : curves_indices_to_transfer.index_range()) {
- const int curve_i = curves_indices_to_transfer[new_hair_i];
- const IndexRange points = curves.points_for_curve(curve_i);
-
- const int looptri_i = looptri_indices[curve_i];
- const MLoopTri &looptri = looptris[looptri_i];
- const int poly_i = looptri.poly;
-
- const float3 &root_pos_cu = positions_cu[points.first()];
- const float3 root_pos_su = curves_to_surface_mat * root_pos_cu;
-
- const int mface_i = find_mface_for_root_position(
- surface_me, poly_to_mface_map[poly_i], root_pos_su);
- const MFace &mface = surface_me.mface[mface_i];
-
- const float4 mface_weights = compute_mface_weights_for_position(
- surface_me, mface, root_pos_su);
-
- ParticleData &particle = particles[new_hair_i];
- const int num_keys = points.size();
- MutableSpan<HairKey> hair_keys{
- static_cast<HairKey *>(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)),
- num_keys};
-
- particle.hair = hair_keys.data();
- particle.totkey = hair_keys.size();
- copy_v4_v4(particle.fuv, mface_weights);
- particle.num = mface_i;
- /* Not sure if there is a better way to initialize this. */
- particle.num_dmcache = DMCACHE_NOTFOUND;
-
- float4x4 hair_to_surface_mat;
- psys_mat_hair_to_object(
- &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.values);
- /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */
- copy_v3_v3(hair_to_surface_mat.values[3], root_pos_su);
- const float4x4 surface_to_hair_mat = hair_to_surface_mat.inverted();
-
- for (const int key_i : hair_keys.index_range()) {
- const float3 &key_pos_cu = positions_cu[points[key_i]];
- const float3 key_pos_su = curves_to_surface_mat * key_pos_cu;
- const float3 key_pos_ha = surface_to_hair_mat * key_pos_su;
-
- HairKey &key = hair_keys[key_i];
- copy_v3_v3(key.co, key_pos_ha);
- key.time = 100.0f * key_i / (float)(hair_keys.size() - 1);
- }
- }
+ DEG_id_tag_update(&surface_ob.id, ID_RECALC_GEOMETRY);
+ DEG_id_tag_update(&settings.id, ID_RECALC_COPY_ON_WRITE);
+}
+
+static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *op)
+{
+ Main &bmain = *CTX_data_main(C);
+ Scene &scene = *CTX_data_scene(C);
- particle_system->particles = particles.data();
- particle_system->totpart = particles.size();
- particle_system->flag |= PSYS_EDITED;
- particle_system->recalc |= ID_RECALC_PSYS_RESET;
+ bool could_not_convert_some_curves = false;
- DEG_id_tag_update(&surface_ob.id, ID_RECALC_GEOMETRY);
- DEG_id_tag_update(&settings.id, ID_RECALC_COPY_ON_WRITE);
+ Object &active_object = *CTX_data_active_object(C);
+ try_convert_single_object(active_object, bmain, scene, &could_not_convert_some_curves);
+
+ CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
+ if (curves_ob != &active_object) {
+ try_convert_single_object(*curves_ob, bmain, scene, &could_not_convert_some_curves);
+ }
}
CTX_DATA_END;
+ if (could_not_convert_some_curves) {
+ BKE_report(op->reports,
+ RPT_INFO,
+ "Some curves could not be converted because they were not attached to the surface");
+ }
+
WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, nullptr);
return OPERATOR_FINISHED;
@@ -473,7 +497,7 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
"Deform",
"Re-attach curves to a deformed surface using the existing attachment information. This "
"only works when the topology of the surface mesh has not changed"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
RNA_def_enum(ot->srna,
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index fb6621c846d..9e7e00d5656 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -767,9 +767,9 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.armature.extrude_cursor
ops.armature.extrude_move
ops.curve.draw
- ops.curve.pen
ops.curve.extrude_cursor
ops.curve.extrude_move
+ ops.curve.pen
ops.curve.radius
ops.curve.vertex_random
ops.curves.sculpt_add
@@ -848,8 +848,8 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.sculpt.border_hide
ops.sculpt.border_mask
ops.sculpt.box_trim
- ops.sculpt.color_filter
ops.sculpt.cloth_filter
+ ops.sculpt.color_filter
ops.sculpt.face_set_edit
ops.sculpt.lasso_face_set
ops.sculpt.lasso_mask
diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc
index 75094b46c8b..467b8efa622 100644
--- a/source/blender/editors/geometry/geometry_attributes.cc
+++ b/source/blender/editors/geometry/geometry_attributes.cc
@@ -380,7 +380,7 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot)
prop = RNA_def_string(ot->srna, "name", "Color", MAX_NAME, "Name", "Name of color attribute");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- static EnumPropertyItem domains[3] = {{ATTR_DOMAIN_POINT, "POINT", 0, "Point", ""},
+ static EnumPropertyItem domains[3] = {{ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""},
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""},
{0, nullptr, 0, nullptr, nullptr}};
diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c
index 8d2aec18451..c6303c197e7 100644
--- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c
+++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c
@@ -82,9 +82,35 @@ void wm_gizmo_vec_draw(
const float color[4], const float (*verts)[3], uint vert_count, uint pos, uint primitive_type)
{
immUniformColor4fv(color);
- immBegin(primitive_type, vert_count);
- for (int i = 0; i < vert_count; i++) {
- immVertex3fv(pos, verts[i]);
+
+ if (primitive_type == GPU_PRIM_LINE_LOOP) {
+ /* Line loop alternative for Metal/Vulkan. */
+ immBegin(GPU_PRIM_LINES, vert_count * 2);
+ immVertex3fv(pos, verts[0]);
+ for (int i = 1; i < vert_count; i++) {
+ immVertex3fv(pos, verts[i]);
+ immVertex3fv(pos, verts[i]);
+ }
+ immVertex3fv(pos, verts[0]);
+ immEnd();
+ }
+ else if (primitive_type == GPU_PRIM_TRI_FAN) {
+ /* Note(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small
+ * primitive counts. */
+ int tri_count = vert_count - 2;
+ immBegin(GPU_PRIM_TRIS, tri_count * 3);
+ for (int i = 0; i < tri_count; i++) {
+ immVertex3fv(pos, verts[0]);
+ immVertex3fv(pos, verts[i + 1]);
+ immVertex3fv(pos, verts[i + 2]);
+ }
+ immEnd();
+ }
+ else {
+ immBegin(primitive_type, vert_count);
+ for (int i = 0; i < vert_count; i++) {
+ immVertex3fv(pos, verts[i]);
+ }
+ immEnd();
}
- immEnd();
}
diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
index 5e20cc73f1a..b326d6d1859 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
@@ -74,20 +74,21 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz,
GPU_viewport_size_get_f(viewport);
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Note(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
/* TODO: other draw styles. */
if (color[3] == 1.0 && fill_alpha == 1.0 && select == false) {
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(color);
- imm_draw_circle_fill_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
immUnbindProgram();
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", gz->line_width * U.pixelsize);
immUniformColor4fv(color);
- imm_draw_circle_wire_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
immUnbindProgram();
}
else {
@@ -96,7 +97,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz,
const float fill_color[4] = {UNPACK3(color), fill_alpha * color[3]};
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(fill_color);
- imm_draw_circle_fill_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
immUnbindProgram();
}
@@ -106,7 +107,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz,
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", gz->line_width * U.pixelsize);
immUniformColor4fv(color);
- imm_draw_circle_wire_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
immUnbindProgram();
}
}
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
index 4c54aa10c33..e4cb6d149f5 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
@@ -99,7 +99,8 @@ static void cage2d_draw_box_corners(const rctf *r,
const float color[3],
const float line_width)
{
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
immUniformColor3fv(color);
@@ -112,25 +113,25 @@ static void cage2d_draw_box_corners(const rctf *r,
immBegin(GPU_PRIM_LINES, 16);
- immVertex2f(pos, r->xmin, r->ymin + margin[1]);
- immVertex2f(pos, r->xmin, r->ymin);
- immVertex2f(pos, r->xmin, r->ymin);
- immVertex2f(pos, r->xmin + margin[0], r->ymin);
+ immVertex3f(pos, r->xmin, r->ymin + margin[1], 0.0f);
+ immVertex3f(pos, r->xmin, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmin, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmin + margin[0], r->ymin, 0.0f);
- immVertex2f(pos, r->xmax, r->ymin + margin[1]);
- immVertex2f(pos, r->xmax, r->ymin);
- immVertex2f(pos, r->xmax, r->ymin);
- immVertex2f(pos, r->xmax - margin[0], r->ymin);
+ immVertex3f(pos, r->xmax, r->ymin + margin[1], 0.0f);
+ immVertex3f(pos, r->xmax, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmax, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmax - margin[0], r->ymin, 0.0f);
- immVertex2f(pos, r->xmax, r->ymax - margin[1]);
- immVertex2f(pos, r->xmax, r->ymax);
- immVertex2f(pos, r->xmax, r->ymax);
- immVertex2f(pos, r->xmax - margin[0], r->ymax);
+ immVertex3f(pos, r->xmax, r->ymax - margin[1], 0.0f);
+ immVertex3f(pos, r->xmax, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmax, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmax - margin[0], r->ymax, 0.0f);
- immVertex2f(pos, r->xmin, r->ymax - margin[1]);
- immVertex2f(pos, r->xmin, r->ymax);
- immVertex2f(pos, r->xmin, r->ymax);
- immVertex2f(pos, r->xmin + margin[0], r->ymax);
+ immVertex3f(pos, r->xmin, r->ymax - margin[1], 0.0f);
+ immVertex3f(pos, r->xmin, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmin, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmin + margin[0], r->ymax, 0.0f);
immEnd();
@@ -440,12 +441,35 @@ static void cage2d_draw_box_interaction(const float color[4],
static void imm_draw_point_aspect_2d(
uint pos, float x, float y, float rad_x, float rad_y, bool solid)
{
- immBegin(solid ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_LOOP, 4);
- immVertex2f(pos, x - rad_x, y - rad_y);
- immVertex2f(pos, x - rad_x, y + rad_y);
- immVertex2f(pos, x + rad_x, y + rad_y);
- immVertex2f(pos, x + rad_x, y - rad_y);
- immEnd();
+ if (solid) {
+ /* Note(Metal/AMD): Small Triangle-list primitives more optimal for GPU HW than Trianglestrip.
+ */
+ immBegin(GPU_PRIM_TRIS, 6);
+ immVertex2f(pos, x - rad_x, y - rad_y);
+ immVertex2f(pos, x - rad_x, y + rad_y);
+ immVertex2f(pos, x + rad_x, y + rad_y);
+
+ immVertex2f(pos, x - rad_x, y - rad_y);
+ immVertex2f(pos, x + rad_x, y + rad_y);
+ immVertex2f(pos, x + rad_x, y - rad_y);
+ immEnd();
+ }
+ else {
+ /* Note(Metal/AMD): Small Line-list primitives more optimal for GPU HW than Linestrip. */
+ immBegin(GPU_PRIM_LINES, 8);
+ immVertex2f(pos, x - rad_x, y - rad_y);
+ immVertex2f(pos, x - rad_x, y + rad_y);
+
+ immVertex2f(pos, x - rad_x, y + rad_y);
+ immVertex2f(pos, x + rad_x, y + rad_y);
+
+ immVertex2f(pos, x + rad_x, y + rad_y);
+ immVertex2f(pos, x + rad_x, y - rad_y);
+
+ immVertex2f(pos, x + rad_x, y - rad_y);
+ immVertex2f(pos, x - rad_x, y - rad_y);
+ immEnd();
+ }
}
static void cage2d_draw_circle_wire(const rctf *r,
@@ -455,7 +479,9 @@ static void cage2d_draw_circle_wire(const rctf *r,
const int draw_options,
const float line_width)
{
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Note(Metal): Prefer using 3D coordinates with 3D shader input, even if rendering 2D gizmo's.
+ */
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
immUniformColor3fv(color);
@@ -465,17 +491,28 @@ static void cage2d_draw_circle_wire(const rctf *r,
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", line_width * U.pixelsize);
- immBegin(GPU_PRIM_LINE_LOOP, 4);
- immVertex2f(pos, r->xmin, r->ymin);
- immVertex2f(pos, r->xmax, r->ymin);
- immVertex2f(pos, r->xmax, r->ymax);
- immVertex2f(pos, r->xmin, r->ymax);
+ /* Small 'lines' primitives more efficient for hardware processing than linestrip. */
+ immBegin(GPU_PRIM_LINES, 8);
+ immVertex3f(pos, r->xmin, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmax, r->ymin, 0.0f);
+
+ immVertex3f(pos, r->xmax, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmax, r->ymax, 0.0f);
+
+ immVertex3f(pos, r->xmax, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmin, r->ymax, 0.0f);
+
+ immVertex3f(pos, r->xmin, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmin, r->ymin, 0.0f);
immEnd();
if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_ROTATE) {
- immBegin(GPU_PRIM_LINE_LOOP, 2);
- immVertex2f(pos, BLI_rctf_cent_x(r), r->ymax);
- immVertex2f(pos, BLI_rctf_cent_x(r), r->ymax + margin[1]);
+ immBegin(GPU_PRIM_LINES, 4);
+ immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax, 0.0f);
+ immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax + margin[1], 0.0f);
+
+ immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax + margin[1], 0.0f);
+ immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax, 0.0f);
immEnd();
}
@@ -485,10 +522,10 @@ static void cage2d_draw_circle_wire(const rctf *r,
const float center[2] = {BLI_rctf_cent_x(r), BLI_rctf_cent_y(r)};
immBegin(GPU_PRIM_LINES, 4);
- immVertex2f(pos, center[0] - rad[0], center[1] - rad[1]);
- immVertex2f(pos, center[0] + rad[0], center[1] + rad[1]);
- immVertex2f(pos, center[0] + rad[0], center[1] - rad[1]);
- immVertex2f(pos, center[0] - rad[0], center[1] + rad[1]);
+ immVertex3f(pos, center[0] - rad[0], center[1] - rad[1], 0.0f);
+ immVertex3f(pos, center[0] + rad[0], center[1] + rad[1], 0.0f);
+ immVertex3f(pos, center[0] + rad[0], center[1] - rad[1], 0.0f);
+ immVertex3f(pos, center[0] - rad[0], center[1] + rad[1], 0.0f);
immEnd();
}
}
diff --git a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
index e1f584bf9e4..a76242404ba 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
@@ -96,7 +96,8 @@ static void dial_geom_draw(const float color[4],
ED_GIZMO_DIAL_DRAW_FLAG_FILL)));
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
if (clip_plane) {
immBindBuiltinProgram(filled ? GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR :
@@ -114,18 +115,19 @@ static void dial_geom_draw(const float color[4],
if (filled) {
if (arc_partial_angle == 0.0f) {
if (arc_inner_factor == 0.0f) {
- imm_draw_circle_fill_2d(pos, 0, 0, 1.0, DIAL_RESOLUTION);
+ imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, DIAL_RESOLUTION);
}
else {
- imm_draw_disk_partial_fill_2d(
- pos, 0, 0, arc_inner_factor, 1.0f, DIAL_RESOLUTION, 0, RAD2DEGF(M_PI * 2));
+ imm_draw_disk_partial_fill_3d(
+ pos, 0.0f, 0.0f, 0.0f, arc_inner_factor, 1.0f, DIAL_RESOLUTION, 0, RAD2DEGF(M_PI * 2));
}
}
else {
float arc_partial_deg = RAD2DEGF((M_PI * 2) - arc_partial_angle);
- imm_draw_disk_partial_fill_2d(pos,
- 0,
- 0,
+ imm_draw_disk_partial_fill_3d(pos,
+ 0.0f,
+ 0.0f,
+ 0.0f,
arc_inner_factor,
1.0f,
DIAL_RESOLUTION,
@@ -140,15 +142,15 @@ static void dial_geom_draw(const float color[4],
immUniform1f("lineWidth", line_width * U.pixelsize);
if (arc_partial_angle == 0.0f) {
- imm_draw_circle_wire_2d(pos, 0, 0, 1.0, DIAL_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, DIAL_RESOLUTION);
if (arc_inner_factor != 0.0f) {
- imm_draw_circle_wire_2d(pos, 0, 0, arc_inner_factor, DIAL_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, arc_inner_factor, DIAL_RESOLUTION);
}
}
else {
float arc_partial_deg = RAD2DEGF((M_PI * 2) - arc_partial_angle);
- imm_draw_circle_partial_wire_2d(
- pos, 0, 0, 1.0, DIAL_RESOLUTION, -arc_partial_deg / 2, arc_partial_deg);
+ imm_draw_circle_partial_wire_3d(
+ pos, 0.0f, 0.0f, 0.0f, 1.0f, DIAL_RESOLUTION, -arc_partial_deg / 2, arc_partial_deg);
# if 0
if (arc_inner_factor != 0.0f) {
BLI_assert(0);
@@ -186,7 +188,7 @@ static void dial_ghostarc_draw_helpline(const float angle,
immUniformColor4fv(color);
immBegin(GPU_PRIM_LINE_STRIP, 2);
- immVertex3f(pos, 0.0f, 0, 0.0f);
+ immVertex3f(pos, 0.0f, 0.0f, 0.0f);
immVertex3fv(pos, co_outer);
immEnd();
diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
index 447fe1005a1..5fb1173521a 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -98,7 +98,8 @@ static void move_geom_draw(const wmGizmo *gz,
ED_GIZMO_MOVE_DRAW_FLAG_FILL)));
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(filled ? GPU_SHADER_3D_UNIFORM_COLOR :
GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
@@ -115,20 +116,20 @@ static void move_geom_draw(const wmGizmo *gz,
if (draw_style == ED_GIZMO_MOVE_STYLE_RING_2D) {
if (filled) {
- imm_draw_circle_fill_2d(pos, 0, 0, radius, DIAL_RESOLUTION);
+ imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, radius, DIAL_RESOLUTION);
}
else {
- imm_draw_circle_wire_2d(pos, 0, 0, radius, DIAL_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, radius, DIAL_RESOLUTION);
}
}
else if (draw_style == ED_GIZMO_MOVE_STYLE_CROSS_2D) {
const float radius_diag = M_SQRT1_2 * radius;
immBegin(GPU_PRIM_LINES, 4);
- immVertex2f(pos, radius_diag, radius_diag);
- immVertex2f(pos, -radius_diag, -radius_diag);
+ immVertex3f(pos, radius_diag, radius_diag, 0.0f);
+ immVertex3f(pos, -radius_diag, -radius_diag, 0.0f);
- immVertex2f(pos, -radius_diag, radius_diag);
- immVertex2f(pos, radius_diag, -radius_diag);
+ immVertex3f(pos, -radius_diag, radius_diag, 0.0f);
+ immVertex3f(pos, radius_diag, -radius_diag, 0.0f);
immEnd();
}
else {
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index e3bf1a48907..332b9b44b0a 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -1584,10 +1584,10 @@ static void icon_draw_cache_texture_flush_ex(GPUTexture *texture,
GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR);
GPU_shader_bind(shader);
- const int data_loc = GPU_shader_get_uniform_block(shader, "multi_rect_data");
+ const int data_binding = GPU_shader_get_uniform_block_binding(shader, "multi_rect_data");
GPUUniformBuf *ubo = GPU_uniformbuf_create_ex(
sizeof(struct MultiRectCallData), texture_draw_calls->drawcall_cache, __func__);
- GPU_uniformbuf_bind(ubo, data_loc);
+ GPU_uniformbuf_bind(ubo, data_binding);
const int img_binding = GPU_shader_get_texture_binding(shader, "image");
GPU_texture_bind_ex(texture, GPU_SAMPLER_ICON, img_binding, false);
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 38499a7f089..a5e2e9353bf 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -787,7 +787,7 @@ static const char *template_id_browse_tip(const StructRNA *type)
case ID_LP:
return N_("Browse LightProbe to be linked");
case ID_CV:
- return N_("Browse Hair Curves Data to be linked");
+ return N_("Browse Curves Data to be linked");
case ID_PT:
return N_("Browse Point Cloud Data to be linked");
case ID_VO:
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 545265b18b1..6a230669056 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -698,7 +698,7 @@ static bool modifier_apply_shape(Main *bmain,
BKE_id_free(NULL, mesh_applied);
}
else {
- /* TODO: implement for hair, point clouds and volumes. */
+ /* TODO: implement for curves, point clouds and volumes. */
BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type");
return false;
}
@@ -800,7 +800,7 @@ static bool modifier_apply_obdata(
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
else {
- /* TODO: implement for hair, point clouds and volumes. */
+ /* TODO: implement for curves, point clouds and volumes. */
BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type");
return false;
}
diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc
index 46f62354fce..da2290f7372 100644
--- a/source/blender/editors/render/render_shading.cc
+++ b/source/blender/editors/render/render_shading.cc
@@ -1167,7 +1167,7 @@ void SCENE_OT_view_layer_add_lightgroup(wmOperatorType *ot)
ot->prop = RNA_def_string(ot->srna,
"name",
nullptr,
- sizeof(((ViewLayerLightgroup *)NULL)->name),
+ sizeof(((ViewLayerLightgroup *)nullptr)->name),
"Name",
"Name of newly created lightgroup");
}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 08f4ecc0dd9..9f71d6f77c7 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -3403,7 +3403,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action");
filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree");
filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker");
- filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair");
+ filelist->filelist.entries[23].entry->relpath = BLI_strdup("Curves");
filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud");
filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume");
# ifdef WITH_FREESTYLE
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index 79bfaa92f80..2aa9b347ed7 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -98,6 +98,7 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op)
int filter;
const bool do_solo = RNA_boolean_get(op->ptr, "isolate_action");
+ const bool use_upper_stack_evaluation = RNA_boolean_get(op->ptr, "use_upper_stack_evaluation");
bool ok = false;
/* get editor data */
@@ -119,6 +120,13 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op)
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ale->data;
+ if (use_upper_stack_evaluation) {
+ adt->flag |= ADT_NLA_EVAL_UPPER_TRACKS;
+ }
+ else {
+ adt->flag &= ~ADT_NLA_EVAL_UPPER_TRACKS;
+ }
+
/* Try entering tweak-mode if valid. */
ok |= BKE_nla_tweakmode_enter(adt);
@@ -181,6 +189,13 @@ void NLA_OT_tweakmode_enter(wmOperatorType *ot)
"Enable 'solo' on the NLA Track containing the active strip, "
"to edit it without seeing the effects of the NLA stack");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(ot->srna,
+ "use_upper_stack_evaluation",
+ false,
+ "Evaluate Upper Stack",
+ "In tweak mode, display the effects of the tracks above the tweak strip");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 520d234e261..03862c6bdff 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -2125,49 +2125,6 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
hit_index = a;
}
}
-
- /* Find the best active & non-active hits.
- * NOTE(@campbellbarton): Checking if `hits > 1` isn't a reliable way to know
- * if there are multiple objects selected since it's possible the same object
- * generates multiple hits, either from:
- * - Multiple sub-components (bones & camera tracks).
- * - Multiple selectable elements such as the object center and the geometry.
- *
- * For this reason, keep track of the best hit as well as the best hit that
- * excludes the selected & active object, using this value when it's valid. */
- if ((hit_index != -1) &&
- /* Special case, cycling away from the active object should only be done when it
- * doesn't have a bone selection, otherwise selecting sub-elements is difficult. */
- ((buffer[hit_index].id & 0xFFFF0000) == 0) &&
- /* Only exclude active object when it is selected. */
- (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED)) &&
- /* Allow disabling this behavior entirely. */
- (U.experimental.use_select_nearest_on_first_click == false)) {
-
- const int select_id_active = BASACT(view_layer)->object->runtime.select_id;
-
- /* Check if `hit_index` is the current active object. */
- if ((buffer[hit_index].id & 0xFFFF) == select_id_active) {
- uint min_not_active = 0xFFFFFFFF;
- int hit_index_not_active = -1;
- for (a = 0; a < hits; a++) {
- /* Any object other than the active-selected. */
- if (select_id_active == (buffer[a].id & 0xFFFF)) {
- continue;
- }
- if (min_not_active > buffer[a].depth) {
- min_not_active = buffer[a].depth;
- hit_index_not_active = a;
- }
- }
-
- /* When the active was selected, first try to use the index
- * for the best non-active hit that was found. */
- if (hit_index_not_active != -1) {
- hit_index = hit_index_not_active;
- }
- }
- }
}
if (hit_index != -1) {
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index a56bbb1c1df..fc88737ca70 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -1129,7 +1129,6 @@ void VIEW3D_OT_localview_remove_from(wmOperatorType *ot)
/* api callbacks */
ot->exec = localview_remove_from_exec;
- ot->invoke = WM_operator_confirm;
ot->poll = localview_remove_from_poll;
ot->flag = OPTYPE_UNDO;
}
diff --git a/source/blender/gpu/GPU_immediate_util.h b/source/blender/gpu/GPU_immediate_util.h
index 924a735af79..cb7ca877b1d 100644
--- a/source/blender/gpu/GPU_immediate_util.h
+++ b/source/blender/gpu/GPU_immediate_util.h
@@ -80,6 +80,8 @@ void imm_draw_circle_fill_3d(uint pos, float x, float y, float radius, int nsegm
*/
void imm_draw_circle_partial_wire_2d(
uint pos, float x, float y, float radius, int nsegments, float start, float sweep);
+void imm_draw_circle_partial_wire_3d(
+ uint pos, float x, float y, float z, float radius, int nsegments, float start, float sweep);
/**
* Draw a filled arc with the given inner and outer radius.
@@ -104,6 +106,15 @@ void imm_draw_disk_partial_fill_2d(uint pos,
int nsegments,
float start,
float sweep);
+void imm_draw_disk_partial_fill_3d(uint pos,
+ float x,
+ float y,
+ float z,
+ float rad_inner,
+ float rad_outer,
+ int nsegments,
+ float start,
+ float sweep);
/**
* Draw a lined box.
diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c
index cf8837ab26e..67035853594 100644
--- a/source/blender/gpu/intern/gpu_immediate_util.c
+++ b/source/blender/gpu/intern/gpu_immediate_util.c
@@ -142,12 +142,27 @@ static void imm_draw_circle(GPUPrimType prim_type,
float radius_y,
int nsegments)
{
- immBegin(prim_type, nsegments);
- for (int i = 0; i < nsegments; i++) {
- const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
- immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
+ if (prim_type == GPU_PRIM_LINE_LOOP) {
+ /* Note(Metal/AMD): For small primitives, line list more efficient than line strip.. */
+ immBegin(GPU_PRIM_LINES, nsegments * 2);
+
+ immVertex2f(shdr_pos, x + (radius_x * cosf(0.0f)), y + (radius_y * sinf(0.0f)));
+ for (int i = 1; i < nsegments; i++) {
+ const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
+ immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
+ immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
+ }
+ immVertex2f(shdr_pos, x + (radius_x * cosf(0.0f)), y + (radius_y * sinf(0.0f)));
+ immEnd();
+ }
+ else {
+ immBegin(prim_type, nsegments);
+ for (int i = 0; i < nsegments; i++) {
+ const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
+ immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
+ }
+ immEnd();
}
- immEnd();
}
void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float radius, int nsegments)
@@ -194,12 +209,42 @@ static void imm_draw_circle_partial(GPUPrimType prim_type,
immEnd();
}
+static void imm_draw_circle_partial_3d(GPUPrimType prim_type,
+ uint pos,
+ float x,
+ float y,
+ float z,
+ float rad,
+ int nsegments,
+ float start,
+ float sweep)
+{
+ /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
+ const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2);
+ const float angle_end = -(DEG2RADF(sweep) - angle_start);
+ nsegments += 1;
+ immBegin(prim_type, nsegments);
+ for (int i = 0; i < nsegments; i++) {
+ const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1)));
+ const float angle_sin = sinf(angle);
+ const float angle_cos = cosf(angle);
+ immVertex3f(pos, x + rad * angle_cos, y + rad * angle_sin, z);
+ }
+ immEnd();
+}
+
void imm_draw_circle_partial_wire_2d(
uint pos, float x, float y, float radius, int nsegments, float start, float sweep)
{
imm_draw_circle_partial(GPU_PRIM_LINE_STRIP, pos, x, y, radius, nsegments, start, sweep);
}
+void imm_draw_circle_partial_wire_3d(
+ uint pos, float x, float y, float z, float rad, int nsegments, float start, float sweep)
+{
+ imm_draw_circle_partial_3d(GPU_PRIM_LINE_STRIP, pos, x, y, z, rad, nsegments, start, sweep);
+}
+
static void imm_draw_disk_partial(GPUPrimType prim_type,
uint pos,
float x,
@@ -229,6 +274,36 @@ static void imm_draw_disk_partial(GPUPrimType prim_type,
immEnd();
}
+static void imm_draw_disk_partial_3d(GPUPrimType prim_type,
+ uint pos,
+ float x,
+ float y,
+ float z,
+ float rad_inner,
+ float rad_outer,
+ int nsegments,
+ float start,
+ float sweep)
+{
+ /* to avoid artifacts */
+ const float max_angle = 3 * 360;
+ CLAMP(sweep, -max_angle, max_angle);
+
+ /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
+ const float angle_start = -(DEG2RADF(start)) + (float)M_PI_2;
+ const float angle_end = -(DEG2RADF(sweep) - angle_start);
+ nsegments += 1;
+ immBegin(prim_type, nsegments * 2);
+ for (int i = 0; i < nsegments; i++) {
+ const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1)));
+ const float angle_sin = sinf(angle);
+ const float angle_cos = cosf(angle);
+ immVertex3f(pos, x + rad_inner * angle_cos, y + rad_inner * angle_sin, z);
+ immVertex3f(pos, x + rad_outer * angle_cos, y + rad_outer * angle_sin, z);
+ }
+ immEnd();
+}
+
void imm_draw_disk_partial_fill_2d(uint pos,
float x,
float y,
@@ -241,16 +316,44 @@ void imm_draw_disk_partial_fill_2d(uint pos,
imm_draw_disk_partial(
GPU_PRIM_TRI_STRIP, pos, x, y, rad_inner, rad_outer, nsegments, start, sweep);
}
+void imm_draw_disk_partial_fill_3d(uint pos,
+ float x,
+ float y,
+ float z,
+ float rad_inner,
+ float rad_outer,
+ int nsegments,
+ float start,
+ float sweep)
+{
+ imm_draw_disk_partial_3d(
+ GPU_PRIM_TRI_STRIP, pos, x, y, z, rad_inner, rad_outer, nsegments, start, sweep);
+}
static void imm_draw_circle_3D(
GPUPrimType prim_type, uint pos, float x, float y, float radius, int nsegments)
{
- immBegin(prim_type, nsegments);
- for (int i = 0; i < nsegments; i++) {
- float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
- immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f);
+ if (prim_type == GPU_PRIM_LINE_LOOP) {
+ /* Note(Metal/AMD): For small primitives, line list more efficient than line strip. */
+ immBegin(GPU_PRIM_LINES, nsegments * 2);
+
+ immVertex3f(pos, x + radius * cosf(0.0f), y + radius * sinf(0.0f), 0.0f);
+ for (int i = 1; i < nsegments; i++) {
+ float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
+ immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f);
+ immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f);
+ }
+ immVertex3f(pos, x + radius * cosf(0.0f), y + radius * sinf(0.0f), 0.0f);
+ immEnd();
+ }
+ else {
+ immBegin(prim_type, nsegments);
+ for (int i = 0; i < nsegments; i++) {
+ float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
+ immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f);
+ }
+ immEnd();
}
- immEnd();
}
void imm_draw_circle_wire_3d(uint pos, float x, float y, float radius, int nsegments)
@@ -270,22 +373,38 @@ void imm_draw_circle_fill_3d(uint pos, float x, float y, float radius, int nsegm
void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2)
{
- immBegin(GPU_PRIM_LINE_LOOP, 4);
+ /* Note(Metal/AMD): For small primitives, line list more efficient than line-strip. */
+ immBegin(GPU_PRIM_LINES, 8);
immVertex2f(pos, x1, y1);
immVertex2f(pos, x1, y2);
+
+ immVertex2f(pos, x1, y2);
immVertex2f(pos, x2, y2);
+
+ immVertex2f(pos, x2, y2);
+ immVertex2f(pos, x2, y1);
+
immVertex2f(pos, x2, y1);
+ immVertex2f(pos, x1, y1);
immEnd();
}
void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2)
{
/* use this version when GPUVertFormat has a vec3 position */
- immBegin(GPU_PRIM_LINE_LOOP, 4);
+ /* Note(Metal/AMD): For small primitives, line list more efficient than line-strip. */
+ immBegin(GPU_PRIM_LINES, 8);
immVertex3f(pos, x1, y1, 0.0f);
immVertex3f(pos, x1, y2, 0.0f);
+
+ immVertex3f(pos, x1, y2, 0.0f);
+ immVertex3f(pos, x2, y2, 0.0f);
+
immVertex3f(pos, x2, y2, 0.0f);
immVertex3f(pos, x2, y1, 0.0f);
+
+ immVertex3f(pos, x2, y1, 0.0f);
+ immVertex3f(pos, x1, y1, 0.0f);
immEnd();
}
diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc
index b78e30e8b4c..14f84273925 100644
--- a/source/blender/gpu/opengl/gl_texture.cc
+++ b/source/blender/gpu/opengl/gl_texture.cc
@@ -72,7 +72,7 @@ bool GLTexture::init_internal()
GLenum internal_format = to_gl_internal_format(format_);
const bool is_cubemap = bool(type_ == GPU_TEXTURE_CUBE);
const bool is_layered = bool(type_ & GPU_TEXTURE_ARRAY);
- const bool is_compressed = bool(format_flag_ & GPU_TEXTURE_ARRAY);
+ const bool is_compressed = bool(format_flag_ & GPU_FORMAT_COMPRESSED);
const int dimensions = (is_cubemap) ? 2 : this->dimensions_count();
GLenum gl_format = to_gl_data_format(format_);
GLenum gl_type = to_gl(to_data_format(format_));
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
index cb798047791..32d61f06a65 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
@@ -209,10 +209,12 @@ vec3 hash_vec4_to_vec3(vec4 k)
float integer_noise(int n)
{
- int nn;
- n = (n + 1013) & 0x7fffffff;
- n = (n >> 13) ^ n;
- nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
+ /* Integer bit-shifts for these calculations can cause precision problems on macOS.
+ * Using uint resolves these issues. */
+ uint nn;
+ nn = (uint(n) + 1013u) & 0x7fffffffu;
+ nn = (nn >> 13u) ^ nn;
+ nn = (uint(nn * (nn * nn * 60493u + 19990303u)) + 1376312589u) & 0x7fffffffu;
return 0.5 * (float(nn) / 1073741824.0);
}
diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h
index 5d3c8872d47..99737aa3b67 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -1128,6 +1128,8 @@ typedef enum eAnimData_Flag {
ADT_NLA_EDIT_NOMAP = (1 << 3),
/** NLA-Strip F-Curves are expanded in UI. */
ADT_NLA_SKEYS_COLLAPSED = (1 << 4),
+ /* Evaluate tracks above tweaked strip. Only relevant in tweak mode. */
+ ADT_NLA_EVAL_UPPER_TRACKS = (1 << 5),
/** Drivers expanded in UI. */
ADT_DRIVERS_COLLAPSED = (1 << 10),
diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h
index f3d342e4849..268e1412eef 100644
--- a/source/blender/makesdna/DNA_particle_types.h
+++ b/source/blender/makesdna/DNA_particle_types.h
@@ -109,7 +109,12 @@ typedef struct ParticleData {
/** Die-time is not necessarily time+lifetime as. */
float time, lifetime;
- /** Particles can die unnaturally (collision). */
+ /**
+ * Particles can die unnaturally (collision).
+ *
+ * \note Particles die on this frame, be sure to add 1 when clamping the lifetime of particles
+ * to inclusive ranges such as the scenes end frame. See: T68290.
+ */
float dietime;
/**
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 0f6c32e4ddf..619f4c05875 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -649,9 +649,9 @@ typedef struct UserDef_Experimental {
char use_extended_asset_browser;
char use_override_templates;
char use_named_attribute_nodes;
- char use_select_nearest_on_first_click;
char enable_eevee_next;
char use_sculpt_texture_paint;
+ char _pad0[1];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index cf622818a3d..8c86e44aebf 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -6440,12 +6440,6 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
"Named Attribute Nodes",
"Enable named attribute nodes in the geometry nodes add menu");
- prop = RNA_def_property(srna, "use_select_nearest_on_first_click", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "use_select_nearest_on_first_click", 1);
- RNA_def_property_ui_text(prop,
- "Object Select Nearest on First Click",
- "When enabled, always select the front-most object on the first click");
-
prop = RNA_def_property(srna, "enable_eevee_next", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "enable_eevee_next", 1);
RNA_def_property_ui_text(prop, "EEVEE Next", "Enable the new EEVEE codebase, requires restart");
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc
index 17fff4a7e1e..fcd1d4973ff 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc
@@ -65,7 +65,7 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout,
static void node_shader_init_tex_pointdensity(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeShaderTexPointDensity *point_density = MEM_cnew<NodeShaderTexPointDensity>("new pd node");
+ NodeShaderTexPointDensity *point_density = MEM_new<NodeShaderTexPointDensity>("new pd node");
point_density->resolution = 100;
point_density->radius = 0.3f;
point_density->space = SHD_POINTDENSITY_SPACE_OBJECT;
diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c
index cdcf22df7a1..9fe4bdcbaa0 100644
--- a/source/blender/python/gpu/gpu_py_shader.c
+++ b/source/blender/python/gpu/gpu_py_shader.c
@@ -537,16 +537,15 @@ static PyObject *pygpu_shader_uniform_block(BPyGPUShader *self, PyObject *args)
return NULL;
}
- int slot = GPU_shader_get_uniform_block(self->shader, name);
- if (slot == -1) {
+ int binding = GPU_shader_get_uniform_block_binding(self->shader, name);
+ if (binding == -1) {
PyErr_SetString(
PyExc_BufferError,
- "GPUShader.uniform_buffer: uniform block not found, make sure the name is correct");
+ "GPUShader.uniform_block: uniform block not found, make sure the name is correct");
return NULL;
}
- GPU_uniformbuf_bind(py_ubo->ubo, slot);
- GPU_shader_uniform_1i(self->shader, name, slot);
+ GPU_uniformbuf_bind(py_ubo->ubo, binding);
Py_RETURN_NONE;
}
diff --git a/source/blender/python/gpu/gpu_py_shader_create_info.cc b/source/blender/python/gpu/gpu_py_shader_create_info.cc
index 10824b09d70..3b043c605fa 100644
--- a/source/blender/python/gpu/gpu_py_shader_create_info.cc
+++ b/source/blender/python/gpu/gpu_py_shader_create_info.cc
@@ -321,7 +321,7 @@ static PyObject *pygpu_interface_info__tp_new(PyTypeObject *UNUSED(type),
StageInterfaceInfo *interface = new StageInterfaceInfo(name, "");
GPUStageInterfaceInfo *interface_info = reinterpret_cast<GPUStageInterfaceInfo *>(interface);
- auto self = BPyGPUStageInterfaceInfo_CreatePyObject(interface_info);
+ auto *self = BPyGPUStageInterfaceInfo_CreatePyObject(interface_info);
#ifdef USE_GPU_PY_REFERENCES
PyObject *py_name = PyTuple_GET_ITEM(args, 0);
@@ -483,7 +483,16 @@ static PyObject *pygpu_shader_info_fragment_out(BPyGPUShaderCreateInfo *self,
struct PyC_StringEnum blend_type = {pygpu_dualblend_items, (int)DualBlend::NONE};
static const char *_keywords[] = {"slot", "type", "name", "blend", nullptr};
- static _PyArg_Parser _parser = {"iO&s|$O&:fragment_out", _keywords, 0};
+ static _PyArg_Parser _parser = {
+ "i" /* `slot` */
+ "O&" /* `type` */
+ "s" /* `name` */
+ "|$" /* Optional keyword only arguments. */
+ "O&" /* `blend` */
+ ":fragment_out",
+ _keywords,
+ nullptr,
+ };
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kwds,
&_parser,
@@ -577,7 +586,17 @@ static PyObject *pygpu_shader_info_image(BPyGPUShaderCreateInfo *self,
Qualifier qualifier = Qualifier::NO_RESTRICT;
static const char *_keywords[] = {"slot", "format", "type", "name", "qualifiers", nullptr};
- static _PyArg_Parser _parser = {"iO&O&s|$O:image", _keywords, 0};
+ static _PyArg_Parser _parser = {
+ "i" /* `slot` */
+ "O&" /* `format` */
+ "O&" /* `type` */
+ "s" /* `name` */
+ "|$" /* Optional keyword only arguments. */
+ "O" /* `qualifiers` */
+ ":image",
+ _keywords,
+ nullptr,
+ };
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kwds,
&_parser,
@@ -733,7 +752,15 @@ static PyObject *pygpu_shader_info_push_constant(BPyGPUShaderCreateInfo *self,
int array_size = 0;
static const char *_keywords[] = {"type", "name", "size", nullptr};
- static _PyArg_Parser _parser = {"O&s|I:push_constant", _keywords, 0};
+ static _PyArg_Parser _parser = {
+ "O&" /* `type` */
+ "s" /* `name` */
+ "|" /* Optional arguments. */
+ "I" /* `size` */
+ ":push_constant",
+ _keywords,
+ nullptr,
+ };
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kwds, &_parser, PyC_ParseStringEnum, &pygpu_type, &name, &array_size)) {
return nullptr;
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index a0b2e6207bf..38c3fc4389a 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -627,13 +627,13 @@ endif()
if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS)
if(NOT OPENIMAGEIO_IDIFF)
- MESSAGE(STATUS "Disabling Cycles tests because OIIO idiff does not exist")
+ MESSAGE(WARNING "Disabling render tests because OIIO idiff does not exist")
elseif(NOT EXISTS "${TEST_SRC_DIR}/render/shader")
- MESSAGE(STATUS "Disabling Cycles tests because tests folder does not exist at ${TEST_SRC_DIR}")
+ MESSAGE(WARNING "Disabling render tests because tests folder does not exist at ${TEST_SRC_DIR}")
elseif(NOT WITH_COMPOSITOR)
- MESSAGE(STATUS "Disabling Cycles tests because WITH_COMPOSITOR is disabled")
+ MESSAGE(WARNING "Disabling render tests because WITH_COMPOSITOR is disabled")
elseif(NOT WITH_OPENCOLORIO)
- MESSAGE(STATUS "Disabling Cycles tests because WITH_OPENCOLORIO is disabled")
+ MESSAGE(WARNING "Disabling render tests because WITH_OPENCOLORIO is disabled")
else()
set(render_tests
camera