diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2022-04-14 14:11:15 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2022-04-14 14:11:15 +0300 |
commit | c8b749d316c69bab5488f02eaa9f3d42fcbdeee7 (patch) | |
tree | 986784938eacfd905881d9b152447aacba48ccc2 | |
parent | d902a2063c8a163655a67339a01060c356763a41 (diff) | |
parent | 1440074cac0a0e2c659539c241947409c3bbdb16 (diff) |
Merge branch 'master' into tmp-new-gpu-codegen
# Conflicts:
# source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
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 Binary files differindex 5d422130ea3..9e4df45b2d2 100644 --- a/release/datafiles/icons/brush.sculpt.displacement_smear.dat +++ b/release/datafiles/icons/brush.sculpt.displacement_smear.dat diff --git a/release/datafiles/icons/brush.sculpt.draw_sharp.dat b/release/datafiles/icons/brush.sculpt.draw_sharp.dat Binary files differindex 1877c0ae4d4..9bea1b02894 100644 --- a/release/datafiles/icons/brush.sculpt.draw_sharp.dat +++ b/release/datafiles/icons/brush.sculpt.draw_sharp.dat 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 |