diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
53 files changed, 2897 insertions, 1647 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index f4d3002219d..2709ac3fd91 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -68,7 +68,7 @@ set(SRC sculpt_detail.c sculpt_dyntopo.c sculpt_expand.c - sculpt_face_set.c + sculpt_face_set.cc sculpt_filter_color.c sculpt_filter_mask.c sculpt_filter_mesh.c diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index b5d739ae08e..0d2c2d3f0c9 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -144,8 +144,7 @@ struct AddOperationExecutor { } surface_verts_eval_ = surface_eval_->verts(); surface_loops_eval_ = surface_eval_->loops(); - surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_), - BKE_mesh_runtime_looptri_len(surface_eval_)}; + surface_looptris_eval_ = surface_eval_->looptris(); BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); @@ -206,8 +205,7 @@ struct AddOperationExecutor { return; } - const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(&surface_orig), - BKE_mesh_runtime_looptri_len(&surface_orig)}; + const Span<MLoopTri> surface_looptris_orig = surface_orig.looptris(); /* Find normals. */ if (!CustomData_has_layer(&surface_orig.ldata, CD_NORMAL)) { @@ -420,7 +418,7 @@ struct AddOperationExecutor { *surface_bvh_eval_.tree, brush_pos_su, brush_radius_su, - [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { + [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) { const MLoopTri &looptri = surface_looptris_eval_[index]; const float3 v0_su = surface_verts_eval_[surface_loops_eval_[looptri.tri[0]].v].co; const float3 v1_su = surface_verts_eval_[surface_loops_eval_[looptri.tri[1]].v].co; @@ -438,7 +436,7 @@ struct AddOperationExecutor { *surface_bvh_eval_.tree, brush_pos_su, brush_radius_su, - [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { + [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) { selected_looptri_indices.append(index); }); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index 95261f29914..8172eb8a5d7 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -8,7 +8,6 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" -#include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_report.h" @@ -16,10 +15,6 @@ #include "UI_interface.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BLI_enumerable_thread_specific.hh" #include "BLI_length_parameterize.hh" #include "BLI_task.hh" @@ -59,7 +54,7 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu const Span<float3> positions) { /* This value might have to be adjusted based on user feedback. */ - const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f); + const float brush_inner_radius_re = std::min<float>(brush_radius_re, float(UI_UNIT_X) / 3.0f); const float brush_inner_radius_sq_re = pow2f(brush_inner_radius_re); float4x4 projection; @@ -190,7 +185,7 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, /* Shorten ray when the surface object is hit. */ if (surface_object_eval != nullptr) { - const float4x4 surface_to_world_mat = surface_object->obmat; + const float4x4 surface_to_world_mat = surface_object->object_to_world; const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); Mesh *surface_eval = BKE_object_get_evaluated_mesh(surface_object_eval); @@ -223,7 +218,7 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, } } - const float4x4 curves_to_world_mat = curves_object.obmat; + const float4x4 curves_to_world_mat = curves_object.object_to_world; const float4x4 world_to_curves_mat = curves_to_world_mat.inverted(); const float3 center_ray_start_cu = world_to_curves_mat * center_ray_start_wo; @@ -352,33 +347,37 @@ float transform_brush_radius(const float4x4 &transform, return math::distance(new_position, new_offset_position); } -void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position) +void move_last_point_and_resample(MoveAndResampleBuffers &buffer, + MutableSpan<float3> positions, + const float3 &new_last_position) { /* Find the accumulated length of each point in the original curve, * treating it as a poly curve for performance reasons and simplicity. */ - Array<float> orig_lengths(length_parameterize::segments_num(positions.size(), false)); - length_parameterize::accumulate_lengths<float3>(positions, false, orig_lengths); - const float orig_total_length = orig_lengths.last(); + buffer.orig_lengths.reinitialize(length_parameterize::segments_num(positions.size(), false)); + length_parameterize::accumulate_lengths<float3>(positions, false, buffer.orig_lengths); + const float orig_total_length = buffer.orig_lengths.last(); /* Find the factor by which the new curve is shorter or longer than the original. */ const float new_last_segment_length = math::distance(positions.last(1), new_last_position); - const float new_total_length = orig_lengths.last(1) + new_last_segment_length; + const float new_total_length = buffer.orig_lengths.last(1) + new_last_segment_length; const float length_factor = safe_divide(new_total_length, orig_total_length); /* Calculate the lengths to sample the original curve with by scaling the original lengths. */ - Array<float> new_lengths(positions.size() - 1); - new_lengths.first() = 0.0f; - for (const int i : new_lengths.index_range().drop_front(1)) { - new_lengths[i] = orig_lengths[i - 1] * length_factor; + buffer.new_lengths.reinitialize(positions.size() - 1); + buffer.new_lengths.first() = 0.0f; + for (const int i : buffer.new_lengths.index_range().drop_front(1)) { + buffer.new_lengths[i] = buffer.orig_lengths[i - 1] * length_factor; } - Array<int> indices(positions.size() - 1); - Array<float> factors(positions.size() - 1); - length_parameterize::sample_at_lengths(orig_lengths, new_lengths, indices, factors); + buffer.sample_indices.reinitialize(positions.size() - 1); + buffer.sample_factors.reinitialize(positions.size() - 1); + length_parameterize::sample_at_lengths( + buffer.orig_lengths, buffer.new_lengths, buffer.sample_indices, buffer.sample_factors); - Array<float3> new_positions(positions.size() - 1); - length_parameterize::interpolate<float3>(positions, indices, factors, new_positions); - positions.drop_back(1).copy_from(new_positions); + buffer.new_positions.reinitialize(positions.size() - 1); + length_parameterize::interpolate<float3>( + positions, buffer.sample_indices, buffer.sample_factors, buffer.new_positions); + positions.drop_back(1).copy_from(buffer.new_positions); positions.last() = new_last_position; } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc index a37eb4bb560..78e2d55e6b9 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc @@ -132,8 +132,7 @@ struct DensityAddOperationExecutor { BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); - surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_), - BKE_mesh_runtime_looptri_len(surface_eval_)}; + surface_looptris_eval_ = surface_eval_->looptris(); /* Find UV map. */ VArraySpan<float2> surface_uv_map; if (curves_id_orig_->surface_uv_map != nullptr) { @@ -235,7 +234,7 @@ struct DensityAddOperationExecutor { new_roots_kdtree, root_pos_cu, brush_settings_->minimum_distance, - [&](const int other_new_i, const float *UNUSED(co), float UNUSED(dist_sq)) { + [&](const int other_new_i, const float * /*co*/, float /*dist_sq*/) { if (other_new_i == -1) { new_curve_skipped[new_i] = true; return false; @@ -265,8 +264,7 @@ struct DensityAddOperationExecutor { reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)), surface_orig_->totloop}; - const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(surface_orig_), - BKE_mesh_runtime_looptri_len(surface_orig_)}; + const Span<MLoopTri> surface_looptris_orig = surface_orig_->looptris(); const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig}; geometry::AddCurvesOnMeshInputs add_inputs; @@ -417,7 +415,7 @@ struct DensityAddOperationExecutor { *surface_bvh_eval_.tree, brush_pos_su, brush_radius_su, - [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { + [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) { selected_looptri_indices.append(index); }); @@ -644,7 +642,7 @@ struct DensitySubtractOperationExecutor { * strength. */ Array<bool> allow_remove_curve(curves_->curves_num(), false); threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) { - RandomNumberGenerator rng((int)(PIL_check_seconds_timer() * 1000000.0)); + RandomNumberGenerator rng(int(PIL_check_seconds_timer() * 1000000.0)); for (const int curve_i : range) { if (curves_to_delete[curve_i]) { @@ -689,7 +687,7 @@ struct DensitySubtractOperationExecutor { root_points_kdtree_, orig_pos_cu, minimum_distance_, - [&](const int other_curve_i, const float *UNUSED(co), float UNUSED(dist_sq)) { + [&](const int other_curve_i, const float * /*co*/, float /*dist_sq*/) { if (other_curve_i == curve_i) { return true; } @@ -733,7 +731,7 @@ struct DensitySubtractOperationExecutor { * strength. */ Array<bool> allow_remove_curve(curves_->curves_num(), false); threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) { - RandomNumberGenerator rng((int)(PIL_check_seconds_timer() * 1000000.0)); + RandomNumberGenerator rng(int(PIL_check_seconds_timer() * 1000000.0)); for (const int curve_i : range) { if (curves_to_delete[curve_i]) { @@ -774,7 +772,7 @@ struct DensitySubtractOperationExecutor { root_points_kdtree_, pos_cu, minimum_distance_, - [&](const int other_curve_i, const float *UNUSED(co), float UNUSED(dist_sq)) { + [&](const int other_curve_i, const float * /*co*/, float /*dist_sq*/) { if (other_curve_i == curve_i) { return true; } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index 0ca22004540..2624d70ccf7 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -7,8 +7,6 @@ #include "BLI_length_parameterize.hh" #include "BLI_vector.hh" -#include "PIL_time.h" - #include "DEG_depsgraph.h" #include "BKE_attribute_math.hh" @@ -141,11 +139,11 @@ class ExtrapolateCurvesEffect : public CurvesEffect { { MutableSpan<float3> positions_cu = curves.positions_for_write(); threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) { + MoveAndResampleBuffers resample_buffer; for (const int influence_i : range) { const int curve_i = curve_indices[influence_i]; const float move_distance_cu = move_distances_cu[influence_i]; const IndexRange points = curves.points_for_curve(curve_i); - if (points.size() <= 1) { continue; } @@ -157,7 +155,7 @@ class ExtrapolateCurvesEffect : public CurvesEffect { const float3 direction = math::normalize(old_last_pos_cu - direction_reference_point); const float3 new_last_pos_cu = old_last_pos_cu + direction * move_distance_cu; - move_last_point_and_resample(positions_cu.slice(points), new_last_pos_cu); + move_last_point_and_resample(resample_buffer, positions_cu.slice(points), new_last_pos_cu); } }); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index 5c8c0cedc6f..61e2559f303 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -102,7 +102,23 @@ VArray<float> get_curves_selection(const Curves &curves_id); */ VArray<float> get_point_selection(const Curves &curves_id); -void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position); +/** See #move_last_point_and_resample. */ +struct MoveAndResampleBuffers { + Array<float> orig_lengths; + Array<float> new_lengths; + + Array<int> sample_indices; + Array<float> sample_factors; + + Array<float3> new_positions; +}; + +/** + * \param buffer: Reused memory to avoid reallocations when the function is called many times. + */ +void move_last_point_and_resample(MoveAndResampleBuffers &buffer, + MutableSpan<float3> positions, + const float3 &new_last_position); class CurvesSculptCommonContext { public: diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 6e1ac24e21b..df7dd871a94 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -153,7 +153,7 @@ struct SculptCurvesBrushStrokeData { static bool stroke_get_location(bContext *C, float out[3], const float mouse[2], - bool UNUSED(force_original)) + bool /*force_original*/) { out[0] = mouse[0]; out[1] = mouse[1]; @@ -170,7 +170,7 @@ static bool stroke_test_start(bContext *C, struct wmOperator *op, const float mo static void stroke_update_step(bContext *C, wmOperator *op, - PaintStroke *UNUSED(stroke), + PaintStroke * /*stroke*/, PointerRNA *stroke_element) { SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>( @@ -220,8 +220,10 @@ static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEven int return_value = op->type->modal(C, op, event); if (return_value == OPERATOR_FINISHED) { - paint_stroke_free(C, op, op_data->stroke); - MEM_delete(op_data); + if (op->customdata != nullptr) { + paint_stroke_free(C, op, op_data->stroke); + MEM_delete(op_data); + } return OPERATOR_FINISHED; } @@ -236,16 +238,19 @@ static int sculpt_curves_stroke_modal(bContext *C, wmOperator *op, const wmEvent int return_value = paint_stroke_modal(C, op, event, &op_data->stroke); if (ELEM(return_value, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { MEM_delete(op_data); + op->customdata = nullptr; } return return_value; } static void sculpt_curves_stroke_cancel(bContext *C, wmOperator *op) { - SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>( - op->customdata); - paint_stroke_cancel(C, op, op_data->stroke); - MEM_delete(op_data); + if (op->customdata != nullptr) { + SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>( + op->customdata); + paint_stroke_cancel(C, op, op_data->stroke); + MEM_delete(op_data); + } } static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot) @@ -344,7 +349,7 @@ static int select_random_exec(bContext *C, wmOperator *op) VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C); const int seed = RNA_int_get(op->ptr, "seed"); - RandomNumberGenerator rng{static_cast<uint32_t>(seed)}; + RandomNumberGenerator rng{uint32_t(seed)}; const bool partial = RNA_boolean_get(op->ptr, "partial"); const bool constant_per_curve = RNA_boolean_get(op->ptr, "constant_per_curve"); @@ -448,7 +453,7 @@ static int select_random_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void select_random_ui(bContext *UNUSED(C), wmOperator *op) +static void select_random_ui(bContext * /*C*/, wmOperator *op) { uiLayout *layout = op->layout; @@ -786,7 +791,7 @@ static void select_grow_invoke_per_curve(Curves &curves_id, }); }); - float4x4 curves_to_world_mat = curves_ob.obmat; + float4x4 curves_to_world_mat = curves_ob.object_to_world; float4x4 world_to_curves_mat = curves_to_world_mat.inverted(); float4x4 projection; @@ -914,7 +919,7 @@ static void SCULPT_CURVES_OT_select_grow(wmOperatorType *ot) "Distance", "By how much to grow the selection", -10.0f, - 10.f); + 10.0f); RNA_def_property_subtype(prop, PROP_DISTANCE); } @@ -1003,7 +1008,7 @@ static int calculate_points_per_side(bContext *C, MinDistanceEditData &op_data) return std::min(300, needed_points); } -static void min_distance_edit_draw(bContext *C, int UNUSED(x), int UNUSED(y), void *customdata) +static void min_distance_edit_draw(bContext *C, int /*x*/, int /*y*/, void *customdata) { Scene *scene = CTX_data_scene(C); MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(customdata); @@ -1093,7 +1098,7 @@ static void min_distance_edit_draw(bContext *C, int UNUSED(x), int UNUSED(y), vo GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]); /* Draw the brush circle. */ - GPU_matrix_translate_2f((float)op_data.initial_mouse.x, (float)op_data.initial_mouse.y); + GPU_matrix_translate_2f(float(op_data.initial_mouse.x), float(op_data.initial_mouse.y)); GPUVertFormat *format = immVertexFormat(); uint pos2d = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc index ec69aae372c..b4e949106e7 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc @@ -121,8 +121,7 @@ struct PuffOperationExecutor { surface_verts_ = surface_->verts(); surface_loops_ = surface_->loops(); - surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), - BKE_mesh_runtime_looptri_len(surface_)}; + surface_looptris_ = surface_->looptris(); BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc index 1108f5c72a9..ae89bc1c58b 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc @@ -8,8 +8,6 @@ #include "BLI_float4x4.hh" #include "BLI_vector.hh" -#include "PIL_time.h" - #include "DEG_depsgraph.h" #include "BKE_attribute_math.hh" @@ -17,17 +15,13 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" -#include "BKE_geometry_set.hh" #include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_mesh_sample.hh" -#include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_report.h" #include "DNA_brush_enums.h" -#include "DNA_brush_types.h" #include "DNA_curves_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -37,8 +31,6 @@ #include "ED_screen.h" #include "ED_view3d.h" -#include "UI_interface.h" - #include "WM_api.h" #include "DEG_depsgraph_query.h" @@ -178,8 +170,7 @@ struct SlideOperationExecutor { report_empty_original_surface(stroke_extension.reports); return; } - surface_looptris_orig_ = {BKE_mesh_runtime_looptri_ensure(surface_orig_), - BKE_mesh_runtime_looptri_len(surface_orig_)}; + surface_looptris_orig_ = surface_orig_->looptris(); surface_uv_map_orig_ = surface_orig_->attributes().lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER); if (surface_uv_map_orig_.is_empty()) { @@ -205,8 +196,7 @@ struct SlideOperationExecutor { report_empty_evaluated_surface(stroke_extension.reports); return; } - surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_), - BKE_mesh_runtime_looptri_len(surface_eval_)}; + surface_looptris_eval_ = surface_eval_->looptris(); surface_verts_eval_ = surface_eval_->verts(); surface_loops_eval_ = surface_eval_->loops(); surface_uv_map_eval_ = surface_eval_->attributes().lookup<float2>(uv_map_name, diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc index 54b81fa221d..67757ce5f4a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -188,6 +188,7 @@ struct SnakeHookOperatorExecutor { const float brush_radius_sq_re = pow2f(brush_radius_re); threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { + MoveAndResampleBuffers resample_buffer; for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); @@ -221,8 +222,8 @@ struct SnakeHookOperatorExecutor { const float3 translation_orig = deformation.translation_from_deformed_to_original( last_point_i, translation_eval); - move_last_point_and_resample(positions_cu.slice(points), - positions_cu[last_point_i] + translation_orig); + const float3 last_point_cu = positions_cu[last_point_i] + translation_orig; + move_last_point_and_resample(resample_buffer, positions_cu.slice(points), last_point_cu); } }); } @@ -268,6 +269,7 @@ struct SnakeHookOperatorExecutor { const float brush_radius_sq_cu = pow2f(brush_radius_cu); threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { + MoveAndResampleBuffers resample_buffer; for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); @@ -289,8 +291,8 @@ struct SnakeHookOperatorExecutor { const float3 translation_orig = deformation.translation_from_deformed_to_original( last_point_i, translation_eval); - move_last_point_and_resample(positions_cu.slice(points), - positions_cu[last_point_i] + translation_orig); + const float3 last_point_cu = positions_cu[last_point_i] + translation_orig; + move_last_point_and_resample(resample_buffer, positions_cu.slice(points), last_point_cu); } }); } diff --git a/source/blender/editors/sculpt_paint/paint_canvas.cc b/source/blender/editors/sculpt_paint/paint_canvas.cc index 9262cbebcac..22e1ecbedd7 100644 --- a/source/blender/editors/sculpt_paint/paint_canvas.cc +++ b/source/blender/editors/sculpt_paint/paint_canvas.cc @@ -1,4 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + #include "BLI_compiler_compat.h" #include "DNA_material_types.h" diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 164e13ac3c9..b6e83187c86 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -27,7 +27,6 @@ #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_image.h" -#include "BKE_node.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -563,31 +562,36 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { GPU_matrix_push(); + float center[2] = { + ups->draw_anchored ? ups->anchored_initial_mouse[0] : x, + ups->draw_anchored ? ups->anchored_initial_mouse[1] : y, + }; + /* Brush rotation. */ - GPU_matrix_translate_2f(x, y); + GPU_matrix_translate_2fv(center); GPU_matrix_rotate_2d(-RAD2DEGF(primary ? ups->brush_rotation : ups->brush_rotation_sec)); - GPU_matrix_translate_2f(-x, -y); + GPU_matrix_translate_2f(-center[0], -center[1]); /* Scale based on tablet pressure. */ if (primary && ups->stroke_active && BKE_brush_use_size_pressure(brush)) { const float scale = ups->size_pressure_value; - GPU_matrix_translate_2f(x, y); + GPU_matrix_translate_2fv(center); GPU_matrix_scale_2f(scale, scale); - GPU_matrix_translate_2f(-x, -y); + GPU_matrix_translate_2f(-center[0], -center[1]); } if (ups->draw_anchored) { - quad.xmin = ups->anchored_initial_mouse[0] - ups->anchored_size; - quad.ymin = ups->anchored_initial_mouse[1] - ups->anchored_size; - quad.xmax = ups->anchored_initial_mouse[0] + ups->anchored_size; - quad.ymax = ups->anchored_initial_mouse[1] + ups->anchored_size; + quad.xmin = center[0] - ups->anchored_size; + quad.ymin = center[1] - ups->anchored_size; + quad.xmax = center[0] + ups->anchored_size; + quad.ymax = center[1] + ups->anchored_size; } else { const int radius = BKE_brush_size_get(vc->scene, brush) * zoom; - quad.xmin = x - radius; - quad.ymin = y - radius; - quad.xmax = x + radius; - quad.ymax = y + radius; + quad.xmin = center[0] - radius; + quad.ymin = center[1] - radius; + quad.xmax = center[0] + radius; + quad.ymax = center[1] + radius; } } else if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { @@ -1072,7 +1076,7 @@ static void cursor_draw_tiling_preview(const uint gpuattr, for (int dim = 0; dim < 3; dim++) { location[dim] = cur[dim] * step[dim] + orgLoc[dim]; } - cursor_draw_point_screen_space(gpuattr, region, location, ob->obmat, 3); + cursor_draw_point_screen_space(gpuattr, region, location, ob->object_to_world, 3); } } } @@ -1089,11 +1093,11 @@ static void cursor_draw_point_with_symmetry(const uint gpuattr, float location[3], symm_rot_mat[4][4]; for (int i = 0; i <= symm; i++) { - if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5))))) { + if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || !ELEM(i, 3, 5)))) { /* Axis Symmetry. */ flip_v3_v3(location, true_location, (char)i); - cursor_draw_point_screen_space(gpuattr, region, location, ob->obmat, 3); + cursor_draw_point_screen_space(gpuattr, region, location, ob->object_to_world, 3); /* Tiling. */ cursor_draw_tiling_preview(gpuattr, region, location, sd, ob, radius); @@ -1108,7 +1112,7 @@ static void cursor_draw_point_with_symmetry(const uint gpuattr, mul_m4_v3(symm_rot_mat, location); cursor_draw_tiling_preview(gpuattr, region, location, sd, ob, radius); - cursor_draw_point_screen_space(gpuattr, region, location, ob->obmat, 3); + cursor_draw_point_screen_space(gpuattr, region, location, ob->object_to_world, 3); } } } @@ -1335,7 +1339,7 @@ static void paint_cursor_update_pixel_radius(PaintCursorContext *pcontext) } copy_v3_v3(pcontext->scene_space_location, pcontext->location); - mul_m4_v3(pcontext->vc.obact->obmat, pcontext->scene_space_location); + mul_m4_v3(pcontext->vc.obact->object_to_world, pcontext->scene_space_location); } else { Sculpt *sd = CTX_data_tool_settings(pcontext->C)->sculpt; @@ -1465,7 +1469,7 @@ static void paint_cursor_drawing_setup_cursor_space(PaintCursorContext *pcontext float cursor_trans[4][4], cursor_rot[4][4]; const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; float quat[4]; - copy_m4_m4(cursor_trans, pcontext->vc.obact->obmat); + copy_m4_m4(cursor_trans, pcontext->vc.obact->object_to_world); translate_m4(cursor_trans, pcontext->location[0], pcontext->location[1], pcontext->location[2]); rotation_between_vecs_to_quat(quat, z_axis, pcontext->normal); quat_to_mat4(cursor_rot, quat); @@ -1509,7 +1513,7 @@ static void paint_cursor_pose_brush_origins_draw(PaintCursorContext *pcontext) cursor_draw_point_screen_space(pcontext->pos, pcontext->region, ss->pose_ik_chain_preview->segments[i].initial_orig, - pcontext->vc.obact->obmat, + pcontext->vc.obact->object_to_world, 3); } } @@ -1527,7 +1531,7 @@ static void paint_cursor_preview_boundary_data_pivot_draw(PaintCursorContext *pc pcontext->pos, pcontext->region, SCULPT_vertex_co_get(pcontext->ss, pcontext->ss->boundary_preview->pivot_vertex), - pcontext->vc.obact->obmat, + pcontext->vc.obact->object_to_world, 3); } @@ -1635,7 +1639,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * pcontext->pos, pcontext->region, SCULPT_vertex_co_get(pcontext->ss, pcontext->ss->expand_cache->initial_active_vertex), - pcontext->vc.obact->obmat, + pcontext->vc.obact->object_to_world, 2); } @@ -1657,7 +1661,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * NULL); GPU_matrix_push(); - GPU_matrix_mul(pcontext->vc.obact->obmat); + GPU_matrix_mul(pcontext->vc.obact->object_to_world); /* Drawing Cursor overlays in 3D object space. */ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_GRAB && @@ -1748,7 +1752,7 @@ static void paint_cursor_cursor_draw_3d_view_brush_cursor_active(PaintCursorCont NULL, NULL); GPU_matrix_push(); - GPU_matrix_mul(pcontext->vc.obact->obmat); + GPU_matrix_mul(pcontext->vc.obact->object_to_world); /* Draw the special active cursors different tools may have. */ diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c index 22d6626ab16..26f76d46f85 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.c +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -44,7 +44,7 @@ bool paint_curve_poll(bContext *C) RegionView3D *rv3d = CTX_wm_region_view3d(C); SpaceImage *sima; - if (rv3d && !(ob && ((ob->mode & OB_MODE_ALL_PAINT) != 0))) { + if (rv3d && !(ob && ((ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_SCULPT_CURVES)) != 0))) { return false; } @@ -676,6 +676,9 @@ static int paintcurve_draw_exec(bContext *C, wmOperator *UNUSED(op)) case PAINT_MODE_SCULPT: name = "SCULPT_OT_brush_stroke"; break; + case PAINT_MODE_SCULPT_CURVES: + name = "SCULPT_CURVES_OT_brush_stroke"; + break; default: return OPERATOR_PASS_THROUGH; } diff --git a/source/blender/editors/sculpt_paint/paint_curve_undo.c b/source/blender/editors/sculpt_paint/paint_curve_undo.c index 2678ec6e115..a5daa13b762 100644 --- a/source/blender/editors/sculpt_paint/paint_curve_undo.c +++ b/source/blender/editors/sculpt_paint/paint_curve_undo.c @@ -9,9 +9,6 @@ #include "MEM_guardedalloc.h" #include "DNA_brush_types.h" -#include "DNA_space_types.h" - -#include "BLI_array_utils.h" #include "BKE_context.h" #include "BKE_paint.h" @@ -24,6 +21,10 @@ #include "paint_intern.h" +#ifndef NDEBUG +# include "BLI_array_utils.h" /* #BLI_array_is_zeroed */ +#endif + /* -------------------------------------------------------------------- */ /** \name Undo Conversion * \{ */ diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index c1289364fb2..9e435ee0748 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -383,10 +383,9 @@ static int hide_show_exec(bContext *C, wmOperator *op) * sculpt but it looks wrong when entering editmode otherwise). */ if (pbvh_type == PBVH_FACES) { BKE_mesh_flush_hidden_from_verts(me); + BKE_pbvh_update_hide_attributes_from_mesh(pbvh); } - SCULPT_visibility_sync_all_vertex_to_face_sets(ob->sculpt); - DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); ED_region_tag_redraw(region); diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index c852fd25bc4..f334b2eb8f8 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -13,7 +13,6 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -35,9 +34,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" -#include "BKE_node.h" #include "BKE_paint.h" -#include "BKE_undo_system.h" #include "NOD_texture.h" @@ -50,7 +47,6 @@ #include "ED_object.h" #include "ED_paint.h" #include "ED_screen.h" -#include "ED_view3d.h" #include "WM_api.h" #include "WM_message.h" @@ -60,9 +56,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "GPU_immediate.h" -#include "GPU_state.h" - #include "IMB_colormanagement.h" #include "paint_intern.h" @@ -328,7 +321,7 @@ bool paint_use_opacity_masking(Brush *brush) { return ((brush->flag & BRUSH_AIRBRUSH) || (brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED) || - (ELEM(brush->imagepaint_tool, PAINT_TOOL_SMEAR, PAINT_TOOL_SOFTEN)) || + ELEM(brush->imagepaint_tool, PAINT_TOOL_SMEAR, PAINT_TOOL_SOFTEN) || (brush->imagepaint_tool == PAINT_TOOL_FILL) || (brush->flag & BRUSH_USE_GRADIENT) || (brush->mtex.tex && !ELEM(brush->mtex.brush_map_mode, @@ -546,7 +539,7 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -static void grab_clone_cancel(bContext *UNUSED(C), wmOperator *op) +static void grab_clone_cancel(bContext * /*C*/, wmOperator *op) { GrabClone *cmv = static_cast<GrabClone *>(op->customdata); MEM_delete(cmv); @@ -787,20 +780,7 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob } if (ima) { - wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first); - LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - const bScreen *screen = WM_window_get_active_screen(win); - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first); - if (sl->spacetype == SPACE_IMAGE) { - SpaceImage *sima = (SpaceImage *)sl; - - if (!sima->pin) { - ED_space_image_set(bmain, sima, ima, true); - } - } - } - } + ED_space_image_sync(bmain, ima, false); } ob->mode |= OB_MODE_TEXTURE_PAINT; @@ -911,7 +891,7 @@ void PAINT_OT_texture_paint_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static int brush_colors_flip_exec(bContext *C, wmOperator *UNUSED(op)) +static int brush_colors_flip_exec(bContext *C, wmOperator * /*op*/) { Scene *scene = CTX_data_scene(C); UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; @@ -980,7 +960,7 @@ void ED_imapaint_bucket_fill(struct bContext *C, ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); - const float mouse_init[2] = {static_cast<float>(mouse[0]), static_cast<float>(mouse[1])}; + const float mouse_init[2] = {float(mouse[0]), float(mouse[1])}; paint_2d_bucket_fill(C, color, nullptr, mouse_init, nullptr, nullptr); ED_image_undo_push_end(); diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 5df65e596b9..b7ce4b2973c 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -150,7 +150,7 @@ static void brush_painter_2d_require_imbuf( { BrushPainterCache *cache = &tile->cache; - if ((cache->use_float != use_float)) { + if (cache->use_float != use_float) { if (cache->ibuf) { IMB_freeImBuf(cache->ibuf); } @@ -402,7 +402,8 @@ static ImBuf *brush_painter_imbuf_new( if (is_texbrush) { brush_imbuf_tex_co(&tex_mapping, x, y, texco); - BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); + const MTex *mtex = &brush->mtex; + BKE_brush_sample_tex_3d(scene, brush, mtex, texco, rgba, thread, pool); /* TODO(sergey): Support texture paint color space. */ if (!use_float) { IMB_colormanagement_scene_linear_to_display_v3(rgba, display); @@ -446,6 +447,7 @@ static void brush_painter_imbuf_update(BrushPainter *painter, { Scene *scene = painter->scene; Brush *brush = painter->brush; + const MTex *mtex = &brush->mtex; BrushPainterCache *cache = &tile->cache; const char *display_device = scene->display_settings.display_device; @@ -485,7 +487,7 @@ static void brush_painter_imbuf_update(BrushPainter *painter, if (!use_texture_old) { if (is_texbrush) { brush_imbuf_tex_co(&tex_mapping, x, y, texco); - BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); + BKE_brush_sample_tex_3d(scene, brush, mtex, texco, rgba, thread, pool); /* TODO(sergey): Support texture paint color space. */ if (!use_float) { IMB_colormanagement_scene_linear_to_display_v3(rgba, display); @@ -1178,8 +1180,8 @@ static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, const int *pos) static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[2]) { - ipos[0] = (int)floorf((pos[0] - ibufb->x / 2)); - ipos[1] = (int)floorf((pos[1] - ibufb->y / 2)); + ipos[0] = (int)floorf(pos[0] - ibufb->x / 2); + ipos[1] = (int)floorf(pos[1] - ibufb->y / 2); } static void paint_2d_do_making_brush(ImagePaintState *s, diff --git a/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc index f5657b004e2..6c7029cccd1 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc @@ -52,29 +52,29 @@ static void update_curve_mask(CurveMaskCache *curve_mask_cache, const float cursor_position[2]) { BLI_assert(curve_mask_cache->curve_mask != nullptr); - int offset = (int)floorf(diameter / 2.0f); + int offset = int(floorf(diameter / 2.0f)); int clamped_radius = max_ff(radius, 1.0); - unsigned short *m = curve_mask_cache->curve_mask; + ushort *m = curve_mask_cache->curve_mask; const int aa_samples = aa_samples_per_texel_axis(brush, radius); - const float aa_offset = 1.0f / (2.0f * (float)aa_samples); - const float aa_step = 1.0f / (float)aa_samples; + const float aa_offset = 1.0f / (2.0f * float(aa_samples)); + const float aa_step = 1.0f / float(aa_samples); float bpos[2]; bpos[0] = cursor_position[0] - floorf(cursor_position[0]) + offset; bpos[1] = cursor_position[1] - floorf(cursor_position[1]) + offset; - float weight_factor = 65535.0f / (float)(aa_samples * aa_samples); + float weight_factor = 65535.0f / float(aa_samples * aa_samples); for (int y = 0; y < diameter; y++) { for (int x = 0; x < diameter; x++, m++) { float pixel_xy[2]; - pixel_xy[0] = static_cast<float>(x) + aa_offset; + pixel_xy[0] = float(x) + aa_offset; float total_weight = 0; for (int i = 0; i < aa_samples; i++) { - pixel_xy[1] = static_cast<float>(y) + aa_offset; + pixel_xy[1] = float(y) + aa_offset; for (int j = 0; j < aa_samples; j++) { const float len = len_v2v2(pixel_xy, bpos); const int sample_index = min_ii((len / clamped_radius) * CurveSamplesBaseLen, @@ -87,7 +87,7 @@ static void update_curve_mask(CurveMaskCache *curve_mask_cache, } pixel_xy[0] += aa_step; } - *m = (unsigned short)(total_weight * weight_factor); + *m = ushort(total_weight * weight_factor); } } } @@ -140,8 +140,7 @@ static void curve_mask_free(CurveMaskCache *curve_mask_cache) static void curve_mask_allocate(CurveMaskCache *curve_mask_cache, const int diameter) { const size_t curve_mask_size = diameter_to_curve_mask_size(diameter); - curve_mask_cache->curve_mask = static_cast<unsigned short *>( - MEM_mallocN(curve_mask_size, __func__)); + curve_mask_cache->curve_mask = static_cast<ushort *>(MEM_mallocN(curve_mask_size, __func__)); curve_mask_cache->curve_mask_size = curve_mask_size; } diff --git a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc index 928f3e9a496..d5c5aa5cebd 100644 --- a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc +++ b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc @@ -17,8 +17,6 @@ #include "BKE_paint.h" #include "BKE_undo_system.h" -#include "DEG_depsgraph.h" - #include "ED_paint.h" #include "ED_view3d.h" @@ -28,11 +26,8 @@ #include "MEM_guardedalloc.h" #include "RNA_access.h" -#include "RNA_define.h" #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" #include "ED_image.h" @@ -79,16 +74,13 @@ class AbstractPaintMode { class ImagePaintMode : public AbstractPaintMode { public: - void *paint_new_stroke(bContext *C, - wmOperator *op, - Object *UNUSED(ob), - const float UNUSED(mouse[2]), - int mode) override + void *paint_new_stroke( + bContext *C, wmOperator *op, Object * /*ob*/, const float /*mouse*/[2], int mode) override { return paint_2d_new_stroke(C, op, mode); } - void paint_stroke(bContext *UNUSED(C), + void paint_stroke(bContext * /*C*/, void *stroke_handle, float prev_mouse[2], float mouse[2], @@ -111,9 +103,9 @@ class ImagePaintMode : public AbstractPaintMode { } void paint_gradient_fill(const bContext *C, - const Scene *UNUSED(scene), + const Scene * /*scene*/, Brush *brush, - struct PaintStroke *UNUSED(stroke), + struct PaintStroke * /*stroke*/, void *stroke_handle, float mouse_start[2], float mouse_end[2]) override @@ -143,7 +135,7 @@ class ImagePaintMode : public AbstractPaintMode { class ProjectionPaintMode : public AbstractPaintMode { public: void *paint_new_stroke( - bContext *C, wmOperator *UNUSED(op), Object *ob, const float mouse[2], int mode) override + bContext *C, wmOperator * /*op*/, Object *ob, const float mouse[2], int mode) override { return paint_proj_new_stroke(C, ob, mouse, mode); } @@ -240,7 +232,7 @@ struct PaintOperation { } }; -static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata) +static void gradient_draw_line(bContext * /*C*/, int x, int y, void *customdata) { PaintOperation *pop = (PaintOperation *)customdata; @@ -294,6 +286,7 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const flo copy_v2_v2(pop->startmouse, mouse); ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); /* initialize from context */ @@ -329,7 +322,7 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const flo } static void paint_stroke_update_step(bContext *C, - wmOperator *UNUSED(op), + wmOperator * /*op*/, struct PaintStroke *stroke, PointerRNA *itemptr) { diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 1ca5df47e17..c85044bf915 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -74,6 +74,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "ED_image.h" #include "ED_node.h" #include "ED_object.h" #include "ED_paint.h" @@ -415,6 +416,7 @@ typedef struct ProjPaintState { const float (*vert_normals)[3]; const MEdge *medge_eval; const MPoly *mpoly_eval; + const bool *select_poly_eval; const int *material_indices; const MLoop *mloop_eval; const MLoopTri *mlooptri_eval; @@ -591,8 +593,8 @@ static int project_bucket_offset(const ProjPaintState *ps, const float projCoSS[ * * Second multiplication does similar but for vertical offset */ - return ((int)(((projCoSS[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x)) + - (((int)(((projCoSS[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y)) * + return (int)(((projCoSS[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + + ((int)(((projCoSS[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) * ps->buckets_x); } @@ -1233,12 +1235,12 @@ static VertSeam *find_adjacent_seam(const ProjPaintState *ps, LISTBASE_CIRCULAR_BACKWARD_END(VertSeam *, vert_seams, adjacent, seam); } else { - LISTBASE_CIRCULAR_FORWARD_BEGIN (vert_seams, adjacent, seam) { + LISTBASE_CIRCULAR_FORWARD_BEGIN (VertSeam *, vert_seams, adjacent, seam) { if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) { break; } } - LISTBASE_CIRCULAR_FORWARD_END(vert_seams, adjacent, seam); + LISTBASE_CIRCULAR_FORWARD_END(VertSeam *, vert_seams, adjacent, seam); } BLI_assert(adjacent); @@ -1523,7 +1525,7 @@ static void project_face_seams_init(const ProjPaintState *ps, static void screen_px_from_ortho(const float uv[2], const float v1co[3], const float v2co[3], - const float v3co[3], /* Screenspace coords */ + const float v3co[3], /* Screen-space coords */ const float uv1co[2], const float uv2co[2], const float uv3co[2], @@ -1941,8 +1943,8 @@ static ProjPixel *project_paint_uvpixel_init(const ProjPaintState *ps, } /* which bounding box cell are we in?, needed for undo */ - projPixel->bb_cell_index = ((int)(((float)x_px / (float)ibuf->x) * PROJ_BOUNDBOX_DIV)) + - ((int)(((float)y_px / (float)ibuf->y) * PROJ_BOUNDBOX_DIV)) * + projPixel->bb_cell_index = (int)(((float)x_px / (float)ibuf->x) * PROJ_BOUNDBOX_DIV) + + (int)(((float)y_px / (float)ibuf->y) * PROJ_BOUNDBOX_DIV) * PROJ_BOUNDBOX_DIV; /* done with view3d_project_float inline */ @@ -2411,7 +2413,7 @@ static bool IsectPT2Df_limit( const float pt[2], const float v1[2], const float v2[2], const float v3[2], const float limit) { return ((area_tri_v2(pt, v1, v2) + area_tri_v2(pt, v2, v3) + area_tri_v2(pt, v3, v1)) / - (area_tri_v2(v1, v2, v3))) < limit; + area_tri_v2(v1, v2, v3)) < limit; } /** @@ -3683,7 +3685,7 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps, const char symmet ps->viewDir[1] = 0.0f; ps->viewDir[2] = 1.0f; - copy_m4_m4(ps->obmat, ps->ob->obmat); + copy_m4_m4(ps->obmat, ps->ob->object_to_world); if (symmetry_flag) { int i; @@ -3741,7 +3743,7 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps, const char symmet CameraParams params; /* viewmat & viewinv */ - copy_m4_m4(viewinv, cam_ob_eval->obmat); + copy_m4_m4(viewinv, cam_ob_eval->object_to_world); normalize_m4(viewinv); invert_m4_m4(viewmat, viewinv); @@ -4062,6 +4064,8 @@ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *p } ps->mloop_eval = BKE_mesh_loops(ps->me_eval); ps->mpoly_eval = BKE_mesh_polys(ps->me_eval); + ps->select_poly_eval = (const bool *)CustomData_get_layer_named( + &ps->me_eval->pdata, CD_PROP_BOOL, ".select_poly"); ps->material_indices = (const int *)CustomData_get_layer_named( &ps->me_eval->pdata, CD_PROP_INT32, "material_index"); @@ -4071,7 +4075,7 @@ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *p ps->totloop_eval = ps->me_eval->totloop; ps->mlooptri_eval = BKE_mesh_runtime_looptri_ensure(ps->me_eval); - ps->totlooptri_eval = ps->me_eval->runtime.looptris.len; + ps->totlooptri_eval = BKE_mesh_runtime_looptri_len(ps->me_eval); ps->poly_to_loop_uv = MEM_mallocN(ps->totpoly_eval * sizeof(MLoopUV *), "proj_paint_mtfaces"); @@ -4145,7 +4149,7 @@ static bool project_paint_clone_face_skip(ProjPaintState *ps, } typedef struct { - const MPoly *mpoly_orig; + const bool *select_poly_orig; const int *index_mp_to_orig; } ProjPaintFaceLookup; @@ -4154,8 +4158,10 @@ static void proj_paint_face_lookup_init(const ProjPaintState *ps, ProjPaintFaceL { memset(face_lookup, 0, sizeof(*face_lookup)); if (ps->do_face_sel) { + Mesh *orig_mesh = (Mesh *)ps->ob->data; face_lookup->index_mp_to_orig = CustomData_get_layer(&ps->me_eval->pdata, CD_ORIGINDEX); - face_lookup->mpoly_orig = BKE_mesh_polys((Mesh *)ps->ob->data); + face_lookup->select_poly_orig = CustomData_get_layer_named( + &orig_mesh->pdata, CD_PROP_BOOL, ".select_poly"); } } @@ -4166,17 +4172,12 @@ static bool project_paint_check_face_sel(const ProjPaintState *ps, { if (ps->do_face_sel) { int orig_index; - const MPoly *mp; if ((face_lookup->index_mp_to_orig != NULL) && - (((orig_index = (face_lookup->index_mp_to_orig[lt->poly]))) != ORIGINDEX_NONE)) { - mp = &face_lookup->mpoly_orig[orig_index]; + ((orig_index = (face_lookup->index_mp_to_orig[lt->poly])) != ORIGINDEX_NONE)) { + return face_lookup->select_poly_orig && face_lookup->select_poly_orig[orig_index]; } - else { - mp = &ps->mpoly_eval[lt->poly]; - } - - return ((mp->flag & ME_FACE_SEL) != 0); + return ps->select_poly_eval && ps->select_poly_eval[lt->poly]; } return true; } @@ -4462,7 +4463,7 @@ static void project_paint_begin(const bContext *C, if (ps->source == PROJ_SRC_VIEW) { /* faster clipping lookups */ - ED_view3d_clipping_local(ps->rv3d, ps->ob->obmat); + ED_view3d_clipping_local(ps->rv3d, ps->ob->object_to_world); } ps->do_face_sel = ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) != 0); @@ -5370,7 +5371,7 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v /* Color texture (alpha used as mask). */ if (ps->is_texbrush) { - MTex *mtex = &brush->mtex; + const MTex *mtex = BKE_brush_color_texture_get(brush, OB_MODE_TEXTURE_PAINT); float samplecos[3]; float texrgba[4]; @@ -5386,7 +5387,8 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v /* NOTE: for clone and smear, * we only use the alpha, could be a special function */ - BKE_brush_sample_tex_3d(ps->scene, brush, samplecos, texrgba, thread_index, pool); + BKE_brush_sample_tex_3d( + ps->scene, brush, mtex, samplecos, texrgba, thread_index, pool); copy_v3_v3(texrgb, texrgba); custom_mask *= texrgba[3]; @@ -6042,6 +6044,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) int orig_brush_size; IDProperty *idgroup; IDProperty *view_data = NULL; + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); bool uvs, mat, tex; @@ -6249,8 +6252,8 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) err_out); if (!ibuf) { - /* Mostly happens when OpenGL offscreen buffer was failed to create, */ - /* but could be other reasons. Should be handled in the future. nazgul */ + /* NOTE(@sergey): Mostly happens when OpenGL off-screen buffer was failed to create, */ + /* but could be other reasons. Should be handled in the future. */ BKE_reportf(op->reports, RPT_ERROR, "Failed to create OpenGL off-screen buffer: %s", err_out); return OPERATOR_CANCELLED; } @@ -6702,6 +6705,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) BKE_texpaint_slot_refresh_cache(scene, ma, ob); BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); + ED_space_image_sync(bmain, ima, false); } if (layer) { BKE_texpaint_slot_refresh_cache(scene, ma, ob); diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 99c25953d50..c6fe7ed3072 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -100,6 +100,8 @@ struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke); void *paint_stroke_mode_data(struct PaintStroke *stroke); float paint_stroke_distance_get(struct PaintStroke *stroke); void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data); +bool paint_stroke_started(struct PaintStroke *stroke); + bool PAINT_brush_tool_poll(struct bContext *C); /** * Delete overlay cursor textures to preserve memory and invalidate all overlay flags. diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 437ff7506ba..ce7db91571c 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -31,6 +31,7 @@ #include "BKE_multires.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_scene.h" #include "BKE_subsurf.h" #include "DEG_depsgraph.h" @@ -41,12 +42,10 @@ #include "WM_api.h" #include "WM_types.h" -#include "ED_screen.h" #include "ED_sculpt.h" #include "ED_view3d.h" #include "bmesh.h" -#include "bmesh_tools.h" #include "tools/bmesh_boolean.h" #include "paint_intern.h" @@ -148,7 +147,7 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) value = RNA_float_get(op->ptr, "value"); MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - BKE_sculpt_mask_layers_ensure(ob, mmd); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); pbvh = ob->sculpt->pbvh; @@ -349,7 +348,7 @@ static void sculpt_gesture_context_init_common(bContext *C, copy_m3_m4(mat, sgcontext->vc.rv3d->viewinv); mul_m3_v3(mat, view_dir); normalize_v3_v3(sgcontext->world_space_view_normal, view_dir); - copy_m3_m4(mat, ob->imat); + copy_m3_m4(mat, ob->world_to_object); mul_m3_v3(mat, view_dir); normalize_v3_v3(sgcontext->true_view_normal, view_dir); @@ -461,12 +460,12 @@ static void sculpt_gesture_line_plane_from_tri(float *r_plane, { float normal[3]; normal_tri_v3(normal, p1, p2, p3); - mul_v3_mat3_m4v3(normal, sgcontext->vc.obact->imat, normal); + mul_v3_mat3_m4v3(normal, sgcontext->vc.obact->world_to_object, normal); if (flip) { mul_v3_fl(normal, -1.0f); } float plane_point_object_space[3]; - mul_v3_m4v3(plane_point_object_space, sgcontext->vc.obact->imat, p1); + mul_v3_m4v3(plane_point_object_space, sgcontext->vc.obact->world_to_object, p1); plane_from_point_normal_v3(r_plane, plane_point_object_space, normal); } @@ -863,7 +862,9 @@ static void sculpt_gesture_mask_end(bContext *C, SculptGestureContext *sgcontext BKE_pbvh_update_vertex_data(sgcontext->ss->pbvh, PBVH_UpdateMask); } -static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext, wmOperator *op) +static void sculpt_gesture_init_mask_properties(bContext *C, + SculptGestureContext *sgcontext, + wmOperator *op) { sgcontext->operation = MEM_callocN(sizeof(SculptGestureMaskOperation), "Mask Operation"); @@ -871,7 +872,8 @@ static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext, Object *object = sgcontext->vc.obact; MultiresModifierData *mmd = BKE_sculpt_multires_active(sgcontext->vc.scene, object); - BKE_sculpt_mask_layers_ensure(sgcontext->vc.obact, mmd); + BKE_sculpt_mask_layers_ensure( + CTX_data_depsgraph_pointer(C), CTX_data_main(C), sgcontext->vc.obact, mmd); mask_operation->op.sculpt_gesture_begin = sculpt_gesture_mask_begin; mask_operation->op.sculpt_gesture_apply_for_symmetry_pass = @@ -1007,11 +1009,12 @@ static void sculpt_gesture_trim_shape_origin_normal_get(SculptGestureContext *sg copy_v3_v3(r_normal, sgcontext->world_space_view_normal); break; case SCULPT_GESTURE_TRIM_ORIENTATION_SURFACE: - mul_v3_m4v3(r_origin, sgcontext->vc.obact->obmat, sgcontext->ss->gesture_initial_location); + mul_v3_m4v3( + r_origin, sgcontext->vc.obact->object_to_world, sgcontext->ss->gesture_initial_location); /* Transforming the normal does not take non uniform scaling into account. Sculpt mode is not * expected to work on object with non uniform scaling. */ copy_v3_v3(r_normal, sgcontext->ss->gesture_initial_normal); - mul_mat3_m4_v3(sgcontext->vc.obact->obmat, r_normal); + mul_mat3_m4_v3(sgcontext->vc.obact->object_to_world, r_normal); break; } } @@ -1042,7 +1045,7 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext) * mesh, coordinates are first calculated in world space, then converted to object space to * store them. */ float world_space_vco[3]; - mul_v3_m4v3(world_space_vco, vc->obact->obmat, vco); + mul_v3_m4v3(world_space_vco, vc->obact->object_to_world, vco); const float dist = dist_signed_to_plane_v3(world_space_vco, shape_plane); trim_operation->depth_front = min_ff(dist, trim_operation->depth_front); trim_operation->depth_back = max_ff(dist, trim_operation->depth_back); @@ -1050,8 +1053,9 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext) if (trim_operation->use_cursor_depth) { float world_space_gesture_initial_location[3]; - mul_v3_m4v3( - world_space_gesture_initial_location, vc->obact->obmat, ss->gesture_initial_location); + mul_v3_m4v3(world_space_gesture_initial_location, + vc->obact->object_to_world, + ss->gesture_initial_location); float mid_point_depth; if (trim_operation->orientation == SCULPT_GESTURE_TRIM_ORIENTATION_VIEW) { @@ -1130,7 +1134,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex sculpt_gesture_trim_shape_origin_normal_get(sgcontext, shape_origin, shape_normal); plane_from_point_normal_v3(shape_plane, shape_origin, shape_normal); - const float(*ob_imat)[4] = vc->obact->imat; + const float(*ob_imat)[4] = vc->obact->world_to_object; /* Write vertices coordinates for the front face. */ MVert *verts = BKE_mesh_verts_for_write(trim_operation->mesh); @@ -1354,7 +1358,9 @@ static void sculpt_gesture_trim_end(bContext *UNUSED(C), SculptGestureContext *s { Object *object = sgcontext->vc.obact; SculptSession *ss = object->sculpt; - ss->face_sets = CustomData_get_layer(&((Mesh *)object->data)->pdata, CD_SCULPT_FACE_SETS); + + ss->face_sets = CustomData_get_layer_named( + &((Mesh *)object->data)->pdata, CD_PROP_INT32, ".sculpt_face_set"); if (ss->face_sets) { /* Assign a new Face Set ID to the new faces created by the trim operation. */ const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(object->data); @@ -1514,7 +1520,7 @@ static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) if (!sgcontext) { return OPERATOR_CANCELLED; } - sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_init_mask_properties(C, sgcontext, op); sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; @@ -1526,7 +1532,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) if (!sgcontext) { return OPERATOR_CANCELLED; } - sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_init_mask_properties(C, sgcontext, op); sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; @@ -1538,7 +1544,7 @@ static int paint_mask_gesture_line_exec(bContext *C, wmOperator *op) if (!sgcontext) { return OPERATOR_CANCELLED; } - sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_init_mask_properties(C, sgcontext, op); sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 60f4a9d59a5..f1f864fdf82 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -249,7 +249,7 @@ static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode) static bool paint_tool_raycast_original(Brush *brush, ePaintMode UNUSED(mode)) { - return brush->flag & BRUSH_ANCHORED; + return brush->flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT); } static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode) @@ -564,7 +564,7 @@ static void paint_brush_stroke_add_step( if (SCULPT_stroke_get_location( C, world_space_position, stroke->last_mouse_position, stroke->original)) { copy_v3_v3(stroke->last_world_space_position, world_space_position); - mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + mul_m4_v3(stroke->vc.obact->object_to_world, stroke->last_world_space_position); } else { add_v3_v3(stroke->last_world_space_position, stroke->last_scene_spacing_delta); @@ -681,8 +681,9 @@ static float paint_space_stroke_spacing(bContext *C, if (paint_stroke_use_scene_spacing(brush, mode)) { if (!BKE_brush_use_locked_size(scene, brush)) { float last_object_space_position[3]; - mul_v3_m4v3( - last_object_space_position, stroke->vc.obact->imat, stroke->last_world_space_position); + mul_v3_m4v3(last_object_space_position, + stroke->vc.obact->world_to_object, + stroke->last_world_space_position); size_clamp = paint_calc_object_space_radius(&stroke->vc, last_object_space_position, size); } else { @@ -825,7 +826,7 @@ static int paint_space_stroke(bContext *C, if (use_scene_spacing) { float world_space_position[3]; bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse, stroke->original); - mul_m4_v3(stroke->vc.obact->obmat, world_space_position); + mul_m4_v3(stroke->vc.obact->object_to_world, world_space_position); if (hit && stroke->stroke_over_mesh) { sub_v3_v3v3(d_world_space_position, world_space_position, stroke->last_world_space_position); length = len_v3(d_world_space_position); @@ -1140,7 +1141,7 @@ struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf) struct wmKeyMap *keymap = WM_modalkeymap_find(keyconf, name); - /* this function is called for each spacetype, only needs to add map once */ + /* This function is called for each space-type, only needs to add map once. */ if (!keymap) { keymap = WM_modalkeymap_ensure(keyconf, name, modal_items); } @@ -1216,8 +1217,8 @@ static void paint_line_strokes_spacing(bContext *C, C, world_space_position_old, old_pos, stroke->original); bool hit_new = SCULPT_stroke_get_location( C, world_space_position_new, new_pos, stroke->original); - mul_m4_v3(stroke->vc.obact->obmat, world_space_position_old); - mul_m4_v3(stroke->vc.obact->obmat, world_space_position_new); + mul_m4_v3(stroke->vc.obact->object_to_world, world_space_position_old); + mul_m4_v3(stroke->vc.obact->object_to_world, world_space_position_new); if (hit_old && hit_new && stroke->stroke_over_mesh) { sub_v3_v3v3(d_world_space_position, world_space_position_new, world_space_position_old); length = len_v3(d_world_space_position); @@ -1360,7 +1361,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str if (paint_stroke_use_scene_spacing(br, BKE_paintmode_get_active_from_context(C))) { stroke->stroke_over_mesh = SCULPT_stroke_get_location( C, stroke->last_world_space_position, data + 2 * j, stroke->original); - mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + mul_m4_v3(stroke->vc.obact->object_to_world, stroke->last_world_space_position); } stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position); @@ -1492,7 +1493,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS if (paint_stroke_use_scene_spacing(br, mode)) { stroke->stroke_over_mesh = SCULPT_stroke_get_location( C, stroke->last_world_space_position, sample_average.mouse, stroke->original); - mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + mul_m4_v3(stroke->vc.obact->object_to_world, stroke->last_world_space_position); } stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse); BLI_assert((stroke->stroke_started & ~1) == 0); /* 0/1 */ @@ -1674,6 +1675,11 @@ void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data) stroke->mode_data = mode_data; } +bool paint_stroke_started(PaintStroke *stroke) +{ + return stroke->stroke_started; +} + bool PAINT_brush_tool_poll(bContext *C) { Paint *p = BKE_paint_get_active_from_context(C); diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 1cedcde7035..f87ca073c82 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -135,12 +135,12 @@ float paint_calc_object_space_radius(ViewContext *vc, const float center[3], flo float delta[3], scale, loc[3]; const float xy_delta[2] = {pixel_radius, 0.0f}; - mul_v3_m4v3(loc, ob->obmat, center); + mul_v3_m4v3(loc, ob->object_to_world, center); const float zfac = ED_view3d_calc_zfac(vc->rv3d, loc); ED_view3d_win_to_delta(vc->region, xy_delta, zfac, delta); - scale = fabsf(mat4_to_scale(ob->obmat)); + scale = fabsf(mat4_to_scale(ob->object_to_world)); scale = (scale == 0.0f) ? 1.0f : scale; return len_v3(delta) / scale; @@ -286,7 +286,7 @@ static void imapaint_pick_uv( const ePaintCanvasSource mode = scene->toolsettings->imapaint.mode; const MLoopTri *lt = BKE_mesh_runtime_looptri_ensure(me_eval); - const int tottri = me_eval->runtime.looptris.len; + const int tottri = BKE_mesh_runtime_looptri_len(me_eval); const MVert *mvert = BKE_mesh_verts(me_eval); const MLoop *mloop = BKE_mesh_loops(me_eval); @@ -297,7 +297,7 @@ static void imapaint_pick_uv( GPU_matrix_model_view_get(matrix); GPU_matrix_projection_get(proj); view[0] = view[1] = 0; - mul_m4_m4m4(matrix, matrix, ob_eval->obmat); + mul_m4_m4m4(matrix, matrix, ob_eval->object_to_world); mul_m4_m4m4(matrix, proj, matrix); minabsw = 1e10; @@ -404,6 +404,7 @@ void paint_sample_color( if (v3d && texpaint_proj) { /* first try getting a color directly from the mesh faces if possible */ ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index c38a79cb6bb..8e790ac435e 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -29,25 +29,21 @@ #include "DNA_scene_types.h" #include "RNA_access.h" -#include "RNA_prototypes.h" #include "BKE_attribute.h" +#include "BKE_attribute.hh" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_editmesh.h" -#include "BKE_layer.h" #include "BKE_lib_id.h" -#include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" -#include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_object_deform.h" #include "BKE_paint.h" #include "BKE_report.h" -#include "BKE_subsurf.h" #include "DEG_depsgraph.h" @@ -56,7 +52,6 @@ #include "WM_toolsystem.h" #include "WM_types.h" -#include "ED_armature.h" #include "ED_image.h" #include "ED_mesh.h" #include "ED_object.h" @@ -170,8 +165,8 @@ static void view_angle_limits_init(NormalAnglePrecalc *a, float angle, bool do_m a->angle_inner = a->angle = angle; } - a->angle_inner *= (float)(M_PI_2 / 90); - a->angle *= (float)(M_PI_2 / 90); + a->angle_inner *= float(M_PI_2 / 90); + a->angle *= float(M_PI_2 / 90); a->angle_range = a->angle - a->angle_inner; if (a->angle_range <= 0.0f) { @@ -397,7 +392,7 @@ static float wpaint_blend(const VPaint *wp, float weight, const float alpha, float paintval, - const float UNUSED(brush_alpha_value), + const float /*brush_alpha_value*/, const bool do_flip) { const Brush *brush = wp->paint.brush; @@ -438,9 +433,10 @@ static void paint_and_tex_color_alpha_intern(VPaint *vp, float r_rgba[4]) { const Brush *brush = BKE_paint_brush(&vp->paint); - BLI_assert(brush->mtex.tex != nullptr); - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { - BKE_brush_sample_tex_3d(vc->scene, brush, co, r_rgba, 0, nullptr); + const MTex *mtex = BKE_brush_mask_texture_get(brush, OB_MODE_SCULPT); + BLI_assert(mtex->tex != nullptr); + if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { + BKE_brush_sample_tex_3d(vc->scene, brush, mtex, co, r_rgba, 0, nullptr); } else { float co_ss[2]; /* screenspace */ @@ -450,7 +446,7 @@ static void paint_and_tex_color_alpha_intern(VPaint *vp, co_ss, (eV3DProjTest)(V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR)) == V3D_PROJ_RET_OK) { const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */ - BKE_brush_sample_tex_3d(vc->scene, brush, co_ss_3d, r_rgba, 0, nullptr); + BKE_brush_sample_tex_3d(vc->scene, brush, mtex, co_ss_3d, r_rgba, 0, nullptr); } else { zero_v4(r_rgba); @@ -893,7 +889,7 @@ static void do_weight_paint_vertex_single( else { /* dv and dv_mirr are the same */ int totweight_prev = dv_mirr->totweight; - int dw_offset = (int)(dw - dv_mirr->dw); + int dw_offset = int(dw - dv_mirr->dw); dw_mirr = BKE_defvert_ensure_index(dv_mirr, vgroup_mirr); /* if we added another, get our old one back */ @@ -1651,10 +1647,10 @@ static void vwpaint_update_cache_invariants( /* cache projection matrix */ ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); - invert_m4_m4(ob->imat, ob->obmat); + invert_m4_m4(ob->world_to_object, ob->object_to_world); copy_m3_m4(mat, cache->vc->rv3d->viewinv); mul_m3_v3(mat, view_dir); - copy_m3_m4(mat, ob->imat); + copy_m3_m4(mat, ob->world_to_object); mul_m3_v3(mat, view_dir); normalize_v3_v3(cache->true_view_normal, view_dir); @@ -1901,7 +1897,7 @@ static float wpaint_get_active_weight(const MDeformVert *dv, const WeightPaintIn static void do_wpaint_precompute_weight_cb_ex(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict /*tls*/) { SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; const MDeformVert *dv = &data->wpi->dvert[n]; @@ -1933,7 +1929,7 @@ static void precompute_weight_values( static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict /*tls*/) { SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = data->ob->sculpt; @@ -1958,6 +1954,10 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, data->brush->falloff_shape); + const blender::bke::AttributeAccessor attributes = data->me->attributes(); + const blender::VArray<bool> select_vert = attributes.lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + /* For each vertex */ PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -1967,9 +1967,8 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, * Otherwise, take the current vert. */ const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const char v_flag = ss->mvert[v_index].flag; /* If the vertex is selected */ - if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { + if (!(use_face_sel || use_vert_sel) || select_vert[v_index]) { /* Get the average poly weight */ int total_hit_loops = 0; float weight_final = 0.0f; @@ -2023,7 +2022,7 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict /*tls*/) { SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = data->ob->sculpt; @@ -2045,6 +2044,10 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, sub_v3_v3v3(brush_dir, cache->location, cache->last_location); project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); + const blender::bke::AttributeAccessor attributes = data->me->attributes(); + const blender::VArray<bool> select_vert = attributes.lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { SculptBrushTest test; @@ -2065,7 +2068,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, const MVert *mv_curr = &ss->mvert[v_index]; /* If the vertex is selected */ - if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) { + if (!(use_face_sel || use_vert_sel) || select_vert[v_index]) { float brush_strength = cache->bstrength; const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) : @@ -2120,7 +2123,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, } do_weight_paint_vertex( - data->vp, data->ob, data->wpi, v_index, final_alpha, (float)weight_final); + data->vp, data->ob, data->wpi, v_index, final_alpha, float(weight_final)); } } } @@ -2132,7 +2135,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict /*tls*/) { SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = data->ob->sculpt; @@ -2158,6 +2161,10 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, data->brush->falloff_shape); + const blender::bke::AttributeAccessor attributes = data->me->attributes(); + const blender::VArray<bool> select_vert = attributes.lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + /* For each vertex */ PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -2169,9 +2176,8 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const char v_flag = ss->mvert[v_index].flag; /* If the vertex is selected */ - if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { + if (!(use_face_sel || use_vert_sel) || select_vert[v_index]) { float brush_strength = cache->bstrength; const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) : 1.0f; @@ -2201,8 +2207,9 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_end; } -static void do_wpaint_brush_calc_average_weight_cb_ex( - void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) +static void do_wpaint_brush_calc_average_weight_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /*tls*/) { SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = data->ob->sculpt; @@ -2224,6 +2231,10 @@ static void do_wpaint_brush_calc_average_weight_cb_ex( const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, data->brush->falloff_shape); + const blender::bke::AttributeAccessor attributes = data->me->attributes(); + const blender::VArray<bool> select_vert = attributes.lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + /* For each vertex */ PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -2234,10 +2245,9 @@ static void do_wpaint_brush_calc_average_weight_cb_ex( if (angle_cos > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) { const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const char v_flag = ss->mvert[v_index].flag; /* If the vertex is selected. */ - if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { + if (!(use_face_sel || use_vert_sel) || select_vert[v_index]) { const MDeformVert *dv = &data->wpi->dvert[v_index]; accum->len += 1; accum->value += wpaint_get_active_weight(dv, data->wpi); @@ -2249,7 +2259,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex( } static void calculate_average_weight(SculptThreadedTaskData *data, - PBVHNode **UNUSED(nodes), + PBVHNode ** /*nodes*/, int totnode) { WPaintAverageAccum *accum = (WPaintAverageAccum *)MEM_mallocN(sizeof(*accum) * totnode, @@ -2268,7 +2278,7 @@ static void calculate_average_weight(SculptThreadedTaskData *data, } if (accum_len != 0) { accum_weight /= accum_len; - data->strength = (float)accum_weight; + data->strength = float(accum_weight); } MEM_SAFE_FREE(data->custom_data); /* 'accum' */ @@ -2445,7 +2455,7 @@ static void wpaint_do_symmetrical_brush_actions( /* symm is a bit combination of XYZ - 1 is mirror * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ for (i = 1; i <= symm; i++) { - if ((symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5))))) { + if (symm & i && (symm != 5 || i != 3) && (symm != 6 || !ELEM(i, 3, 5))) { cache->mirror_symmetry_pass = i; cache->radial_symmetry_pass = 0; SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0); @@ -2469,7 +2479,7 @@ static void wpaint_do_symmetrical_brush_actions( } static void wpaint_stroke_update_step(bContext *C, - wmOperator *UNUSED(op), + wmOperator * /*op*/, PaintStroke *stroke, PointerRNA *itemptr) { @@ -2508,7 +2518,7 @@ static void wpaint_stroke_update_step(bContext *C, ED_view3d_init_mats_rv3d(ob, vc->rv3d); /* load projection matrix */ - mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); + mul_m4_m4m4(mat, vc->rv3d->persmat, ob->object_to_world); Mesh *mesh = static_cast<Mesh *>(ob->data); @@ -2546,7 +2556,7 @@ static void wpaint_stroke_update_step(bContext *C, /* Calculate pivot for rotation around selection if needed. * also needed for "Frame Selected" on last stroke. */ float loc_world[3]; - mul_v3_m4v3(loc_world, ob->obmat, ss->cache->true_location); + mul_v3_m4v3(loc_world, ob->object_to_world, ss->cache->true_location); paint_last_stroke_update(scene, loc_world); BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); @@ -2938,7 +2948,7 @@ static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo template<class Color = ColorPaint4b, typename Traits = ByteTraits> static void do_vpaint_brush_blur_loops(bContext *C, - Sculpt *UNUSED(sd), + Sculpt * /*sd*/, VPaint *vp, VPaintData<Color, Traits, ATTR_DOMAIN_CORNER> *vpd, Object *ob, @@ -2956,6 +2966,11 @@ static void do_vpaint_brush_blur_loops(bContext *C, Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint); + const blender::VArray<bool> select_vert = me->attributes().lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + const blender::VArray<bool> select_poly = me->attributes().lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); + blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) { for (int n : range) { const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); @@ -2987,10 +3002,9 @@ static void do_vpaint_brush_blur_loops(bContext *C, const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv = &ss->mvert[v_index]; /* If the vertex is selected for painting. */ - if (!use_vert_sel || mv->flag & SELECT) { + if (!use_vert_sel || select_vert[v_index]) { float brush_strength = cache->bstrength; const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) : @@ -3011,7 +3025,7 @@ static void do_vpaint_brush_blur_loops(bContext *C, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { int p_index = gmap->vert_to_poly[v_index].indices[j]; const MPoly *mp = &ss->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { + if (!use_face_sel || select_poly[p_index]) { total_hit_loops += mp->totloop; for (int k = 0; k < mp->totloop; k++) { const uint l_index = mp->loopstart + k; @@ -3045,8 +3059,7 @@ static void do_vpaint_brush_blur_loops(bContext *C, const int p_index = gmap->vert_to_poly[v_index].indices[j]; const int l_index = gmap->vert_to_loop[v_index].indices[j]; BLI_assert(ss->mloop[l_index].v == v_index); - const MPoly *mp = &ss->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { + if (!use_face_sel || select_poly[p_index]) { Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ if (previous_color != nullptr) { @@ -3080,7 +3093,7 @@ static void do_vpaint_brush_blur_loops(bContext *C, template<class Color = ColorPaint4b, typename Traits = ByteTraits> static void do_vpaint_brush_blur_verts(bContext *C, - Sculpt *UNUSED(sd), + Sculpt * /*sd*/, VPaint *vp, VPaintData<Color, Traits, ATTR_DOMAIN_POINT> *vpd, Object *ob, @@ -3098,6 +3111,11 @@ static void do_vpaint_brush_blur_verts(bContext *C, Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint); + const blender::VArray<bool> select_vert = me->attributes().lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + const blender::VArray<bool> select_poly = me->attributes().lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); + blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) { for (int n : range) { const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); @@ -3129,10 +3147,9 @@ static void do_vpaint_brush_blur_verts(bContext *C, const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv = &ss->mvert[v_index]; /* If the vertex is selected for painting. */ - if (!use_vert_sel || mv->flag & SELECT) { + if (!use_vert_sel || select_vert[v_index]) { float brush_strength = cache->bstrength; const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) : @@ -3153,7 +3170,7 @@ static void do_vpaint_brush_blur_verts(bContext *C, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { int p_index = gmap->vert_to_poly[v_index].indices[j]; const MPoly *mp = &ss->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { + if (!use_face_sel || select_poly[p_index]) { total_hit_loops += mp->totloop; for (int k = 0; k < mp->totloop; k++) { const uint l_index = mp->loopstart + k; @@ -3190,8 +3207,7 @@ static void do_vpaint_brush_blur_verts(bContext *C, BLI_assert(ss->mloop[gmap->vert_to_loop[v_index].indices[j]].v == v_index); - const MPoly *mp = &ss->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { + if (!use_face_sel || select_poly[p_index]) { Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ if (previous_color != nullptr) { @@ -3225,7 +3241,7 @@ static void do_vpaint_brush_blur_verts(bContext *C, template<typename Color = ColorPaint4b, typename Traits, eAttrDomain domain> static void do_vpaint_brush_smear(bContext *C, - Sculpt *UNUSED(sd), + Sculpt * /*sd*/, VPaint *vp, VPaintData<Color, Traits, domain> *vpd, Object *ob, @@ -3247,6 +3263,11 @@ static void do_vpaint_brush_smear(bContext *C, Color *color_prev_smear = static_cast<Color *>(vpd->smear.color_prev); Color *color_prev = reinterpret_cast<Color *>(ss->cache->prev_colors_vpaint); + const blender::VArray<bool> select_vert = me->attributes().lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + const blender::VArray<bool> select_poly = me->attributes().lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); + blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) { for (int n : range) { float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; @@ -3283,7 +3304,7 @@ static void do_vpaint_brush_smear(bContext *C, const MVert *mv_curr = &ss->mvert[v_index]; /* if the vertex is selected for painting. */ - if (!use_vert_sel || mv_curr->flag & SELECT) { + if (!use_vert_sel || select_vert[v_index]) { /* Calc the dot prod. between ray norm on surf and current vert * (ie splash prevention factor), and only paint front facing verts. */ float brush_strength = cache->bstrength; @@ -3312,7 +3333,7 @@ static void do_vpaint_brush_smear(bContext *C, BLI_assert(ss->mloop[l_index].v == v_index); UNUSED_VARS_NDEBUG(l_index); const MPoly *mp = &ss->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { + if (!use_face_sel || select_poly[p_index]) { const MLoop *ml_other = &ss->mloop[mp->loopstart]; for (int k = 0; k < mp->totloop; k++, ml_other++) { const uint v_other_index = ml_other->v; @@ -3366,8 +3387,7 @@ static void do_vpaint_brush_smear(bContext *C, BLI_assert(ss->mloop[l_index].v == v_index); } - const MPoly *mp = &ss->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { + if (!use_face_sel || select_poly[p_index]) { /* Get the previous element color */ Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ @@ -3413,6 +3433,9 @@ static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd, { using Blend = typename Traits::BlendType; + const blender::VArray<bool> select_vert = me->attributes().lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + VPaintAverageAccum<Blend> *accum = (VPaintAverageAccum<Blend> *)MEM_mallocN( sizeof(*accum) * totnode, __func__); blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) { @@ -3443,8 +3466,7 @@ static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd, vd.vert_indices[vd.i]; if (BKE_brush_curve_strength(brush, 0.0, cache->radius) > 0.0) { /* If the vertex is selected for painting. */ - const MVert *mv = &ss->mvert[v_index]; - if (!use_vert_sel || mv->flag & SELECT) { + if (!use_vert_sel || select_vert[v_index]) { accum2->len += gmap->vert_to_loop[v_index].count; /* if a vertex is within the brush region, then add its color to the blend. */ for (int j = 0; j < gmap->vert_to_loop[v_index].count; j++) { @@ -3512,7 +3534,7 @@ static float paint_and_tex_color_alpha(VPaint *vp, template<typename Color, typename Traits, eAttrDomain domain> static void vpaint_do_draw(bContext *C, - Sculpt *UNUSED(sd), + Sculpt * /*sd*/, VPaint *vp, VPaintData<Color, Traits, domain> *vpd, Object *ob, @@ -3529,6 +3551,11 @@ static void vpaint_do_draw(bContext *C, Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint); + const blender::VArray<bool> select_vert = me->attributes().lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + const blender::VArray<bool> select_poly = me->attributes().lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); + blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) { for (int n : range) { const bool has_grids = (pbvh_type == PBVH_GRIDS); @@ -3562,10 +3589,9 @@ static void vpaint_do_draw(bContext *C, const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv = &ss->mvert[v_index]; /* If the vertex is selected for painting. */ - if (!use_vert_sel || mv->flag & SELECT) { + if (!use_vert_sel || select_vert[v_index]) { /* Calc the dot prod. between ray norm on surf and current vert * (ie splash prevention factor), and only paint front facing verts. */ float brush_strength = cache->bstrength; @@ -3617,8 +3643,7 @@ static void vpaint_do_draw(bContext *C, const int p_index = gmap->vert_to_poly[v_index].indices[j]; const int l_index = gmap->vert_to_loop[v_index].indices[j]; BLI_assert(ss->mloop[l_index].v == v_index); - const MPoly *mp = &ss->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { + if (!use_face_sel || select_poly[p_index]) { Color color_orig = Color(0, 0, 0, 0); /* unused when array is nullptr */ if (previous_color != nullptr) { @@ -3778,7 +3803,7 @@ static void vpaint_do_symmetrical_brush_actions( /* symm is a bit combination of XYZ - 1 is mirror * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ for (i = 1; i <= symm; i++) { - if (symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5)))) { + if (symm & i && (symm != 5 || i != 3) && (symm != 6 || !ELEM(i, 3, 5))) { cache->mirror_symmetry_pass = i; cache->radial_symmetry_pass = 0; SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0); @@ -3822,7 +3847,7 @@ static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, P ED_view3d_init_mats_rv3d(ob, vc->rv3d); /* load projection matrix */ - mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); + mul_m4_m4m4(mat, vc->rv3d->persmat, ob->object_to_world); swap_m4m4(vc->rv3d->persmat, mat); @@ -3845,7 +3870,7 @@ static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, P /* Calculate pivot for rotation around selection if needed. * also needed for "Frame Selected" on last stroke. */ float loc_world[3]; - mul_v3_m4v3(loc_world, ob->obmat, ss->cache->true_location); + mul_v3_m4v3(loc_world, ob->object_to_world, ss->cache->true_location); paint_last_stroke_update(scene, loc_world); ED_region_tag_redraw(vc->region); @@ -3854,7 +3879,7 @@ static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, P } static void vpaint_stroke_update_step(bContext *C, - wmOperator *UNUSED(op), + wmOperator * /*op*/, PaintStroke *stroke, PointerRNA *itemptr) { @@ -3883,7 +3908,7 @@ static void vpaint_stroke_update_step(bContext *C, } template<typename Color, typename Traits, eAttrDomain domain> -static void vpaint_free_vpaintdata(Object *UNUSED(ob), void *_vpd) +static void vpaint_free_vpaintdata(Object * /*ob*/, void *_vpd) { VPaintData<Color, Traits, domain> *vpd = static_cast<VPaintData<Color, Traits, domain> *>(_vpd); @@ -4045,6 +4070,11 @@ static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, CustomDataLay return false; } + const blender::VArray<bool> select_vert = me->attributes().lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); + const blender::VArray<bool> select_poly = me->attributes().lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); + Color paintcol = fromFloat<Color>(paintcol_in); const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; @@ -4073,7 +4103,7 @@ static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, CustomDataLay BMLoop *l = f->l_first; do { - if (!(use_vert_sel && !(BM_elem_flag_test(l->v, BM_ELEM_SELECT)))) { + if (!(use_vert_sel && !BM_elem_flag_test(l->v, BM_ELEM_SELECT))) { if constexpr (domain == ATTR_DOMAIN_CORNER) { color = static_cast<Color *>(BM_ELEM_CD_GET_VOID_P(l, cd_offset)); } @@ -4088,21 +4118,20 @@ static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, CustomDataLay } else { Color *color_layer = static_cast<Color *>(layer->data); - const Span<MVert> verts = me->verts(); const Span<MPoly> polys = me->polys(); const Span<MLoop> loops = me->loops(); for (const int i : polys.index_range()) { - const MPoly &poly = polys[i]; - if (use_face_sel && !(poly.flag & ME_FACE_SEL)) { + if (use_face_sel && !select_poly[i]) { continue; } + const MPoly &poly = polys[i]; int j = 0; do { uint vidx = loops[poly.loopstart + j].v; - if (!(use_vert_sel && !(verts[vidx].flag & SELECT))) { + if (!(use_vert_sel && !(select_vert[vidx]))) { if constexpr (domain == ATTR_DOMAIN_CORNER) { color_layer[poly.loopstart + j] = paintcol; } @@ -4177,7 +4206,7 @@ extern "C" bool BKE_object_attributes_active_color_fill(Object *ob, return paint_object_attributes_active_color_fill_ex(ob, ColorPaint4f(fill_color), only_selected); } -static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op)) +static int vertex_color_set_exec(bContext *C, wmOperator * /*op*/) { Scene *scene = CTX_data_scene(C); Object *obact = CTX_data_active_object(C); diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc index 10ad4c2192f..09374422888 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc @@ -49,7 +49,7 @@ static bool vertex_weight_paint_mode_poll(bContext *C) { Object *ob = CTX_data_active_object(C); Mesh *me = BKE_mesh_from_object(ob); - return (ob && (ELEM(ob->mode, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT))) && + return (ob && ELEM(ob->mode, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT)) && (me && me->totpoly && !me->deform_verts().is_empty()); } @@ -73,8 +73,7 @@ static bool vertex_paint_from_weight(Object *ob) using namespace blender; Mesh *me; - if (((me = BKE_mesh_from_object(ob)) == nullptr || - (ED_mesh_color_ensure(me, nullptr)) == false)) { + if ((me = BKE_mesh_from_object(ob)) == nullptr || ED_mesh_color_ensure(me, nullptr) == false) { return false; } @@ -121,7 +120,7 @@ static bool vertex_paint_from_weight(Object *ob) return true; } -static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op)) +static int vertex_paint_from_weight_exec(bContext *C, wmOperator * /*op*/) { Object *obact = CTX_data_active_object(C); if (vertex_paint_from_weight(obact)) { @@ -159,29 +158,19 @@ static IndexMask get_selected_indices(const Mesh &mesh, Vector<int64_t> &indices) { using namespace blender; - const Span<MVert> verts = mesh.verts(); - const Span<MPoly> polys = mesh.polys(); - - bke::AttributeAccessor attributes = mesh.attributes(); + const bke::AttributeAccessor attributes = mesh.attributes(); if (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) { - const VArray<bool> selection = attributes.adapt_domain( - VArray<bool>::ForFunc(polys.size(), - [&](const int i) { return polys[i].flag & ME_FACE_SEL; }), - ATTR_DOMAIN_FACE, - domain); - + const VArray<bool> selection = attributes.lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); return index_mask_ops::find_indices_from_virtual_array( - IndexMask(attributes.domain_size(domain)), selection, 4096, indices); + selection.index_range(), selection, 4096, indices); } if (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) { - const VArray<bool> selection = attributes.adapt_domain( - VArray<bool>::ForFunc(verts.size(), [&](const int i) { return verts[i].flag & SELECT; }), - ATTR_DOMAIN_POINT, - domain); - + const VArray<bool> selection = attributes.lookup_or_default<bool>( + ".select_vert", ATTR_DOMAIN_POINT, false); return index_mask_ops::find_indices_from_virtual_array( - IndexMask(attributes.domain_size(domain)), selection, 4096, indices); + selection.index_range(), selection, 4096, indices); } return IndexMask(attributes.domain_size(domain)); } @@ -228,7 +217,7 @@ static bool vertex_color_smooth(Object *ob) return true; } -static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op)) +static int vertex_color_smooth_exec(bContext *C, wmOperator * /*op*/) { Object *obact = CTX_data_active_object(C); if (vertex_color_smooth(obact)) { @@ -320,7 +309,7 @@ static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op) /* * The algorithm is by Werner D. Streidt * (http://visca.com/ffactory/archives/5-99/msg00021.html) - * Extracted of OpenCV demhist.c + * Extracted of OpenCV `demhist.c`. */ if (contrast > 0) { gain = 1.0f - delta * 2.0f; @@ -430,7 +419,7 @@ void PAINT_OT_vertex_color_hsv(wmOperatorType *ot) RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f); } -static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op)) +static int vertex_color_invert_exec(bContext *C, wmOperator * /*op*/) { Object *obact = CTX_data_active_object(C); diff --git a/source/blender/editors/sculpt_paint/paint_vertex_proj.c b/source/blender/editors/sculpt_paint/paint_vertex_proj.c index b4e2d4901c9..bac3e7a9e52 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_proj.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_proj.c @@ -10,7 +10,6 @@ #include "MEM_guardedalloc.h" -#include "BLI_listbase.h" #include "BLI_math.h" #include "DNA_mesh_types.h" diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c index 0a0d7cff214..fca25ee2e4b 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -7,7 +7,6 @@ #include "MEM_guardedalloc.h" #include "BLI_bitmap.h" -#include "BLI_blenlib.h" #include "BLI_math.h" #include "DNA_brush_types.h" @@ -445,7 +444,6 @@ static bool weight_paint_set(Object *ob, float paintweight) /* mutually exclusive, could be made into a */ const short paint_selmode = ME_EDIT_PAINT_SEL_MODE(me); - const MVert *verts = BKE_mesh_verts(me); const MPoly *polys = BKE_mesh_polys(me); const MLoop *loops = BKE_mesh_loops(me); MDeformVert *dvert = BKE_mesh_deform_verts_for_write(me); @@ -464,10 +462,15 @@ static bool weight_paint_set(Object *ob, float paintweight) struct WPaintPrev wpp; wpaint_prev_create(&wpp, dvert, me->totvert); + const bool *select_vert = (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".select_vert"); + const bool *select_poly = (const bool *)CustomData_get_layer_named( + &me->pdata, CD_PROP_BOOL, ".select_poly"); + for (index = 0, mp = polys; index < me->totpoly; index++, mp++) { uint fidx = mp->totloop - 1; - if ((paint_selmode == SCE_SELECT_FACE) && !(mp->flag & ME_FACE_SEL)) { + if ((paint_selmode == SCE_SELECT_FACE) && !(select_poly && select_poly[index])) { continue; } @@ -475,7 +478,7 @@ static bool weight_paint_set(Object *ob, float paintweight) uint vidx = loops[mp->loopstart + fidx].v; if (!dvert[vidx].flag) { - if ((paint_selmode == SCE_SELECT_VERTEX) && !(verts[vidx].flag & SELECT)) { + if ((paint_selmode == SCE_SELECT_VERTEX) && !(select_vert && select_vert[vidx])) { continue; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c index ac16631f115..9ab36fe1402 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c @@ -203,7 +203,7 @@ BLI_INLINE float wval_screen(float weight, float paintval, float fac) return weight; } mfac = 1.0f - fac; - temp = max_ff(1.0f - (((1.0f - weight) * (1.0f - paintval))), 0); + temp = max_ff(1.0f - ((1.0f - weight) * (1.0f - paintval)), 0); return mfac * weight + temp * fac; } BLI_INLINE float wval_hardlight(float weight, float paintval, float fac) @@ -258,7 +258,7 @@ BLI_INLINE float wval_exclusion(float weight, float paintval, float fac) return weight; } mfac = 1.0f - fac; - temp = 0.5f - ((2.0f * (weight - 0.5f) * (paintval - 0.5f))); + temp = 0.5f - (2.0f * (weight - 0.5f) * (paintval - 0.5f)); return temp * fac + weight * mfac; } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 65e69bd8761..3477285814e 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -12,17 +12,10 @@ #include "BLI_dial_2d.h" #include "BLI_ghash.h" #include "BLI_gsqueue.h" -#include "BLI_hash.h" #include "BLI_math.h" -#include "BLI_math_color.h" -#include "BLI_math_color_blend.h" #include "BLI_task.h" #include "BLI_utildefines.h" -#include "BLT_translation.h" - -#include "PIL_time.h" - #include "DNA_brush_types.h" #include "DNA_customdata_types.h" #include "DNA_mesh_types.h" @@ -37,24 +30,18 @@ #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_image.h" -#include "BKE_kelvinlet.h" #include "BKE_key.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" -#include "BKE_mesh_mirror.h" #include "BKE_modifier.h" #include "BKE_multires.h" -#include "BKE_node.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_particle.h" #include "BKE_pbvh.h" -#include "BKE_pointcache.h" #include "BKE_report.h" #include "BKE_scene.h" -#include "BKE_screen.h" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" @@ -62,29 +49,21 @@ #include "DEG_depsgraph.h" -#include "IMB_colormanagement.h" - #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" -#include "ED_object.h" #include "ED_paint.h" #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_view3d.h" + #include "paint_intern.h" #include "sculpt_intern.h" #include "RNA_access.h" #include "RNA_define.h" -#include "UI_interface.h" -#include "UI_resources.h" - #include "bmesh.h" -#include "bmesh_tools.h" #include <math.h> #include <stdlib.h> @@ -194,9 +173,10 @@ void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3] const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex) { - if (ss->persistent_base) { - return ss->persistent_base[BKE_pbvh_vertex_to_index(ss->pbvh, vertex)].co; + if (ss->attrs.persistent_co) { + return (const float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co); } + return SCULPT_vertex_co_get(ss, vertex); } @@ -240,8 +220,8 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, floa void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]) { - if (ss->persistent_base) { - copy_v3_v3(no, ss->persistent_base[vertex.i].no); + if (ss->attrs.persistent_no) { + copy_v3_v3(no, (float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no)); return; } SCULPT_vertex_normal_get(ss, vertex, no); @@ -249,15 +229,16 @@ void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float SCULPT_vertex_mask_get(SculptSession *ss, PBVHVertRef vertex) { - BMVert *v; - float *mask; switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: return ss->vmask ? ss->vmask[vertex.i] : 0.0f; - case PBVH_BMESH: + case PBVH_BMESH: { + BMVert *v; + int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); + v = (BMVert *)vertex.i; - mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); - return mask ? *mask : 0.0f; + return cd_mask != -1 ? BM_ELEM_CD_GET_FLOAT(v, cd_mask) : 0.0f; + } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; @@ -390,19 +371,15 @@ bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex) void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible) { BLI_assert(ss->face_sets != NULL); + BLI_assert(ss->hide_poly != NULL); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_GRIDS: for (int i = 0; i < ss->totfaces; i++) { - if (abs(ss->face_sets[i]) != face_set) { + if (ss->face_sets[i] != face_set) { continue; } - if (visible) { - ss->face_sets[i] = abs(ss->face_sets[i]); - } - else { - ss->face_sets[i] = -abs(ss->face_sets[i]); - } + ss->hide_poly[i] = !visible; } break; case PBVH_BMESH: @@ -410,62 +387,59 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl } } -void SCULPT_face_sets_visibility_invert(SculptSession *ss) +void SCULPT_face_visibility_all_invert(SculptSession *ss) { BLI_assert(ss->face_sets != NULL); + BLI_assert(ss->hide_poly != NULL); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_GRIDS: for (int i = 0; i < ss->totfaces; i++) { - ss->face_sets[i] *= -1; + ss->hide_poly[i] = !ss->hide_poly[i]; } break; - case PBVH_BMESH: + case PBVH_BMESH: { + BMIter iter; + BMFace *f; + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + BM_elem_flag_toggle(f, BM_ELEM_HIDDEN); + } break; + } } } -void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible) +void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_GRIDS: - if (!ss->face_sets) { - return; - } - for (int i = 0; i < ss->totfaces; i++) { - - /* This can run on geometry without a face set assigned, so its ID sign can't be changed to - * modify the visibility. Force that geometry to the ID 1 to enable changing the visibility - * here. */ - if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) { - ss->face_sets[i] = 1; - } + BLI_assert(ss->hide_poly != NULL); + memset(ss->hide_poly, !visible, sizeof(bool) * ss->totfaces); + break; + case PBVH_BMESH: { + BMIter iter; + BMFace *f; - if (visible) { - ss->face_sets[i] = abs(ss->face_sets[i]); - } - else { - ss->face_sets[i] = -abs(ss->face_sets[i]); - } + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + BM_elem_flag_set(f, BM_ELEM_HIDDEN, !visible); } break; - case PBVH_BMESH: - break; + } } } -bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, PBVHVertRef vertex) +bool SCULPT_vertex_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex) { - const bool *hide_poly = BKE_pbvh_get_poly_hide(ss->pbvh); - if (!hide_poly) { - return true; - } switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[vertex.i]; + if (!ss->hide_poly) { + return true; + } + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; for (int j = 0; j < ss->pmap[vertex.i].count; j++) { - if (!hide_poly[vert_map->indices[j]]) { + if (!ss->hide_poly[vert_map->indices[j]]) { return true; } } @@ -479,29 +453,53 @@ bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, PBVHVertRef verte return true; } -bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, PBVHVertRef vertex) +bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef vertex) { - const bool *hide_poly = BKE_pbvh_get_poly_hide(ss->pbvh); - if (!hide_poly) { - return true; - } switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[vertex.i]; - for (int j = 0; j < ss->pmap[vertex.i].count; j++) { - if (hide_poly[vert_map->indices[j]]) { + if (!ss->hide_poly) { + return true; + } + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; + for (int j = 0; j < vert_map->count; j++) { + if (ss->hide_poly[vert_map->indices[j]]) { return false; } } return true; } - case PBVH_BMESH: + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + BMEdge *e = v->e; + + if (!e) { + return true; + } + + do { + BMLoop *l = e->l; + + if (!l) { + continue; + } + + do { + if (BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) { + return false; + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + return true; + } case PBVH_GRIDS: { + if (!ss->hide_poly) { + return true; + } const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); - return !hide_poly[face_index]; + return !ss->hide_poly[face_index]; } } return true; @@ -512,13 +510,17 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_ switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { BLI_assert(ss->face_sets != NULL); - MeshElemMap *vert_map = &ss->pmap[vertex.i]; - for (int j = 0; j < ss->pmap[vertex.i].count; j++) { - if (ss->face_sets[vert_map->indices[j]] > 0) { - ss->face_sets[vert_map->indices[j]] = abs(face_set); + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; + for (int j = 0; j < vert_map->count; j++) { + const int poly_index = vert_map->indices[j]; + if (ss->hide_poly && ss->hide_poly[poly_index]) { + /* Skip hidden faces connected to the vertex. */ + continue; } + ss->face_sets[poly_index] = face_set; } - } break; + break; + } case PBVH_BMESH: break; case PBVH_GRIDS: { @@ -526,11 +528,13 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_ const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); - if (ss->face_sets[face_index] > 0) { - ss->face_sets[face_index] = abs(face_set); + if (ss->hide_poly && ss->hide_poly[face_index]) { + /* Skip the vertex if it's in a hidden face. */ + return; } - - } break; + ss->face_sets[face_index] = face_set; + break; + } } } @@ -541,9 +545,9 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex) if (!ss->face_sets) { return SCULPT_FACE_SET_NONE; } - MeshElemMap *vert_map = &ss->pmap[vertex.i]; + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; int face_set = 0; - for (int i = 0; i < ss->pmap[vertex.i].count; i++) { + for (int i = 0; i < vert_map->count; i++) { if (ss->face_sets[vert_map->indices[i]] > face_set) { face_set = abs(ss->face_sets[vert_map->indices[i]]); } @@ -572,8 +576,8 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_ if (!ss->face_sets) { return face_set == SCULPT_FACE_SET_NONE; } - MeshElemMap *vert_map = &ss->pmap[vertex.i]; - for (int i = 0; i < ss->pmap[vertex.i].count; i++) { + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; + for (int i = 0; i < vert_map->count; i++) { if (ss->face_sets[vert_map->indices[i]] == face_set) { return true; } @@ -595,61 +599,51 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_ return true; } -void SCULPT_visibility_sync_all_face_sets_to_verts(Object *ob) +void SCULPT_visibility_sync_all_from_faces(Object *ob) { SculptSession *ss = ob->sculpt; Mesh *mesh = BKE_object_get_original_mesh(ob); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - BKE_sculpt_sync_face_sets_visibility_to_base_mesh(mesh); + /* We may have adjusted the ".hide_poly" attribute, now make the hide status attributes for + * vertices and edges consistent. */ + BKE_mesh_flush_hidden_from_polys(mesh); + BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh); break; } case PBVH_GRIDS: { - BKE_sculpt_sync_face_sets_visibility_to_base_mesh(mesh); - BKE_sculpt_sync_face_sets_visibility_to_grids(mesh, ss->subdiv_ccg); - break; - } - case PBVH_BMESH: + /* In addition to making the hide status of the base mesh consistent, we also have to + * propagate the status to the Multires grids. */ + BKE_mesh_flush_hidden_from_polys(mesh); + BKE_sculpt_sync_face_visibility_to_grids(mesh, ss->subdiv_ccg); break; - } -} - -static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSession *ss, - PBVHVertRef vertex) -{ - MeshElemMap *vert_map = &ss->pmap[vertex.i]; - const bool visible = SCULPT_vertex_visible_get(ss, vertex); - for (int i = 0; i < ss->pmap[vertex.i].count; i++) { - if (visible) { - ss->face_sets[vert_map->indices[i]] = abs(ss->face_sets[vert_map->indices[i]]); - } - else { - ss->face_sets[vert_map->indices[i]] = -abs(ss->face_sets[vert_map->indices[i]]); } - } -} + case PBVH_BMESH: { + BMIter iter; + BMFace *f; + + /* Hide all verts and edges attached to faces.*/ + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + do { + BM_elem_flag_enable(l->v, BM_ELEM_HIDDEN); + BM_elem_flag_enable(l->e, BM_ELEM_HIDDEN); + } while ((l = l->next) != f->l_first); + } -void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss) -{ - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - if (ss->face_sets == NULL) { - return; - } - for (int i = 0; i < ss->totfaces; i++) { - const MPoly *poly = &ss->mpoly[i]; - bool poly_visible = true; - for (int l = 0; l < poly->totloop; l++) { - const MLoop *loop = &ss->mloop[poly->loopstart + l]; - if (!SCULPT_vertex_visible_get(ss, BKE_pbvh_make_vref(loop->v))) { - poly_visible = false; + /* Unhide verts and edges attached to visible faces. */ + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + continue; } + + BMLoop *l = f->l_first; + do { + BM_elem_flag_disable(l->v, BM_ELEM_HIDDEN); + BM_elem_flag_disable(l->e, BM_ELEM_HIDDEN); + } while ((l = l->next) != f->l_first); } - if (poly_visible) { - ss->face_sets[i] = abs(ss->face_sets[i]); - } - else { - ss->face_sets[i] = -abs(ss->face_sets[i]); - } + break; } } } @@ -659,14 +653,14 @@ static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int ind if (!ss->face_sets) { return true; } - MeshElemMap *vert_map = &ss->pmap[index]; + const MeshElemMap *vert_map = &ss->pmap[index]; int face_set = -1; - for (int i = 0; i < ss->pmap[index].count; i++) { + for (int i = 0; i < vert_map->count; i++) { if (face_set == -1) { - face_set = abs(ss->face_sets[vert_map->indices[i]]); + face_set = ss->face_sets[vert_map->indices[i]]; } else { - if (abs(ss->face_sets[vert_map->indices[i]]) != face_set) { + if (ss->face_sets[vert_map->indices[i]] != face_set) { return false; } } @@ -680,9 +674,9 @@ static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int ind */ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2) { - MeshElemMap *vert_map = &ss->pmap[v1]; + const MeshElemMap *vert_map = &ss->pmap[v1]; int p1 = -1, p2 = -1; - for (int i = 0; i < ss->pmap[v1].count; i++) { + for (int i = 0; i < vert_map->count; i++) { const MPoly *p = &ss->mpoly[vert_map->indices[i]]; for (int l = 0; l < p->totloop; l++) { const MLoop *loop = &ss->mloop[p->loopstart + l]; @@ -750,8 +744,8 @@ int SCULPT_face_set_next_available_get(SculptSession *ss) } int next_face_set = 0; for (int i = 0; i < ss->totfaces; i++) { - if (abs(ss->face_sets[i]) > next_face_set) { - next_face_set = abs(ss->face_sets[i]); + if (ss->face_sets[i] > next_face_set) { + next_face_set = ss->face_sets[i]; } } next_face_set++; @@ -831,21 +825,20 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, PBVHVertRef vertex, SculptVertexNeighborIter *iter) { - MeshElemMap *vert_map = &ss->pmap[vertex.i]; + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; iter->size = 0; iter->num_duplicates = 0; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; iter->neighbor_indices = iter->neighbor_indices_fixed; - const bool *hide_poly = BKE_pbvh_get_vert_hide(ss->pbvh); - for (int i = 0; i < ss->pmap[vertex.i].count; i++) { - if (hide_poly && hide_poly[vert_map->indices[i]]) { + for (int i = 0; i < vert_map->count; i++) { + if (ss->hide_poly && ss->hide_poly[vert_map->indices[i]]) { /* Skip connectivity from hidden faces. */ continue; } const MPoly *p = &ss->mpoly[vert_map->indices[i]]; - uint f_adj_v[2]; + int f_adj_v[2]; if (poly_get_adj_loops_from_vert(p, ss->mloop, vertex.i, f_adj_v) != -1) { for (int j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { if (f_adj_v[j] != vertex.i) { @@ -939,7 +932,7 @@ bool SCULPT_vertex_is_boundary(const SculptSession *ss, const PBVHVertRef vertex { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - if (!SCULPT_vertex_all_face_sets_visible_get(ss, vertex)) { + if (!SCULPT_vertex_all_faces_visible_get(ss, vertex)) { return true; } return sculpt_check_boundary_vertex_in_base_mesh(ss, vertex.i); @@ -1097,7 +1090,7 @@ PBVHVertRef SCULPT_nearest_vertex_get( bool SCULPT_is_symmetry_iteration_valid(char i, char symm) { - return i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5)))); + return i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || !ELEM(i, 3, 5))); } bool SCULPT_is_vertex_inside_brush_radius_symm(const float vertex[3], @@ -1128,8 +1121,8 @@ void SCULPT_tag_update_overlays(bContext *C) DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); - View3D *v3d = CTX_wm_view3d(C); - if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) { + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (!BKE_sculptsession_use_pbvh_draw(ob, rv3d)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } } @@ -1315,11 +1308,11 @@ static bool sculpt_brush_use_topology_rake(const SculptSession *ss, const Brush /** * Test whether the #StrokeCache.sculpt_normal needs update in #do_brush_action */ -static int sculpt_brush_needs_normal(const SculptSession *ss, const Brush *brush) +static int sculpt_brush_needs_normal(const SculptSession *ss, Sculpt *sd, const Brush *brush) { return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) && (ss->cache->normal_weight > 0.0f)) || - + SCULPT_automasking_needs_normal(ss, sd, brush) || ELEM(brush->sculpt_tool, SCULPT_TOOL_BLOB, SCULPT_TOOL_CREASE, @@ -1989,7 +1982,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, int(*orco_tris)[3]; int orco_tris_num; - BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords); + BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords, NULL); for (int i = 0; i < orco_tris_num; i++) { const float *co_tri[3] = { @@ -2460,11 +2453,12 @@ float SCULPT_brush_strength_factor(SculptSession *ss, const float fno[3], float mask, const PBVHVertRef vertex, - int thread_id) + const int thread_id, + AutomaskingNodeData *automask_data) { StrokeCache *cache = ss->cache; const Scene *scene = cache->vc->scene; - const MTex *mtex = &br->mtex; + const MTex *mtex = BKE_brush_mask_texture_get(br, OB_MODE_SCULPT); float avg = 1.0f; float rgba[4]; float point[3]; @@ -2476,7 +2470,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss, } else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { /* Get strength by feeding the vertex location directly into a texture. */ - avg = BKE_brush_sample_tex_3d(scene, br, point, rgba, 0, ss->tex_pool); + avg = BKE_brush_sample_tex_3d(scene, br, mtex, point, rgba, 0, ss->tex_pool); } else { float symm_point[3], point_2d[2]; @@ -2505,19 +2499,19 @@ float SCULPT_brush_strength_factor(SculptSession *ss, x = symm_point[0]; y = symm_point[1]; - x *= br->mtex.size[0]; - y *= br->mtex.size[1]; + x *= mtex->size[0]; + y *= mtex->size[1]; - x += br->mtex.ofs[0]; - y += br->mtex.ofs[1]; + x += mtex->ofs[0]; + y += mtex->ofs[1]; - avg = paint_get_tex_pixel(&br->mtex, x, y, ss->tex_pool, thread_id); + avg = paint_get_tex_pixel(mtex, x, y, ss->tex_pool, thread_id); avg += br->texture_sample_bias; } else { const float point_3d[3] = {point_2d[0], point_2d[1], 0.0f}; - avg = BKE_brush_sample_tex_3d(scene, br, point_3d, rgba, 0, ss->tex_pool); + avg = BKE_brush_sample_tex_3d(scene, br, mtex, point_3d, rgba, 0, ss->tex_pool); } } @@ -2544,7 +2538,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss, avg *= 1.0f - mask; /* Auto-masking. */ - avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex); + avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex, automask_data); return avg; } @@ -2779,14 +2773,14 @@ static void calc_local_y(ViewContext *vc, const float center[3], float y[3]) float loc[3]; const float xy_delta[2] = {0.0f, 1.0f}; - mul_v3_m4v3(loc, ob->imat, center); + mul_v3_m4v3(loc, ob->world_to_object, center); const float zfac = ED_view3d_calc_zfac(vc->rv3d, loc); ED_view3d_win_to_delta(vc->region, xy_delta, zfac, y); normalize_v3(y); add_v3_v3(y, ob->loc); - mul_m4_v3(ob->imat, y); + mul_m4_v3(ob->world_to_object, y); } static void calc_brush_local_mat(const Brush *brush, Object *ob, float local_mat[4][4]) @@ -2798,8 +2792,8 @@ static void calc_brush_local_mat(const Brush *brush, Object *ob, float local_mat float angle, v[3]; float up[3]; - /* Ensure `ob->imat` is up to date. */ - invert_m4_m4(ob->imat, ob->obmat); + /* Ensure `ob->world_to_object` is up to date. */ + invert_m4_m4(ob->world_to_object, ob->object_to_world); /* Initialize last column of matrix. */ mat[0][3] = 0.0f; @@ -2839,13 +2833,13 @@ void SCULPT_tilt_apply_to_normal(float r_normal[3], StrokeCache *cache, const fl return; } const float rot_max = M_PI_2 * tilt_strength * SCULPT_TILT_SENSITIVITY; - mul_v3_mat3_m4v3(r_normal, cache->vc->obact->obmat, r_normal); + mul_v3_mat3_m4v3(r_normal, cache->vc->obact->object_to_world, r_normal); float normal_tilt_y[3]; rotate_v3_v3v3fl(normal_tilt_y, r_normal, cache->vc->rv3d->viewinv[0], cache->y_tilt * rot_max); float normal_tilt_xy[3]; rotate_v3_v3v3fl( normal_tilt_xy, normal_tilt_y, cache->vc->rv3d->viewinv[1], cache->x_tilt * rot_max); - mul_v3_mat3_m4v3(r_normal, cache->vc->obact->imat, normal_tilt_xy); + mul_v3_mat3_m4v3(r_normal, cache->vc->obact->world_to_object, normal_tilt_xy); normalize_v3(r_normal); } @@ -3031,7 +3025,7 @@ void SCULPT_calc_brush_plane( } /* For area normal. */ - if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) && + if (!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) && (brush->flag & BRUSH_ORIGINAL_NORMAL)) { copy_v3_v3(r_area_no, ss->cache->sculpt_normal); } @@ -3040,7 +3034,7 @@ void SCULPT_calc_brush_plane( } /* For flatten center. */ - if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) && + if (!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) && (brush->flag & BRUSH_ORIGINAL_PLANE)) { copy_v3_v3(r_area_co, ss->cache->last_center); } @@ -3075,7 +3069,7 @@ void SCULPT_calc_brush_plane( int SCULPT_plane_trim(const StrokeCache *cache, const Brush *brush, const float val[3]) { return (!(brush->flag & BRUSH_PLANE_TRIM) || - ((dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared))); + (dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared)); } int SCULPT_plane_point_side(const float co[3], const float plane[4]) @@ -3134,7 +3128,8 @@ static void do_gravity_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + NULL); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -3271,7 +3266,7 @@ static void sculpt_topology_update(Sculpt *sd, if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { BKE_pbvh_node_mark_topology_update(nodes[n]); - BKE_pbvh_bmesh_node_save_orig(ss->bm, nodes[n]); + BKE_pbvh_bmesh_node_save_orig(ss->bm, ss->bm_log, nodes[n], false); } } @@ -3289,7 +3284,7 @@ static void sculpt_topology_update(Sculpt *sd, /* Update average stroke position. */ copy_v3_v3(location, ss->cache->true_location); - mul_m4_v3(ob->obmat, location); + mul_m4_v3(ob->object_to_world, location); } static void do_brush_action_task_cb(void *__restrict userdata, @@ -3348,15 +3343,6 @@ static void do_brush_action(Sculpt *sd, BKE_pbvh_ensure_node_loops(ss->pbvh); } - if (SCULPT_tool_is_mask(brush->sculpt_tool)) { - MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); - BKE_sculpt_mask_layers_ensure(ob, mmd); - } - if (SCULPT_tool_is_face_sets(brush->sculpt_tool)) { - Mesh *mesh = BKE_object_get_original_mesh(ob); - ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); - } - /* Build a list of all nodes that are potentially within the brush's area of influence */ if (SCULPT_tool_needs_all_pbvh_nodes(brush)) { @@ -3420,6 +3406,8 @@ static void do_brush_action(Sculpt *sd, /* Initialize auto-masking cache. */ if (SCULPT_is_automasking_enabled(sd, ss, brush)) { ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob); + ss->last_automasking_settings_hash = SCULPT_automasking_settings_hash( + ob, ss->cache->automasking); } /* Initialize surface smooth cache. */ if ((brush->sculpt_tool == SCULPT_TOOL_SMOOTH) && @@ -3450,7 +3438,7 @@ static void do_brush_action(Sculpt *sd, BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); } - if (sculpt_brush_needs_normal(ss, brush)) { + if (sculpt_brush_needs_normal(ss, sd, brush)) { update_sculpt_normal(sd, ob, nodes, totnode); } @@ -3602,6 +3590,12 @@ static void do_brush_action(Sculpt *sd, SCULPT_bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor); } + if (!SCULPT_tool_can_reuse_automask(brush->sculpt_tool) || + (ss->cache->supports_gravity && sd->gravity_factor > 0.0f)) { + /* Clear cavity mask cache. */ + ss->last_automasking_settings_hash = 0; + } + /* The cloth brush adds the gravity as a regular force and it is processed in the solver. */ if (ss->cache->supports_gravity && !ELEM(brush->sculpt_tool, SCULPT_TOOL_CLOTH, @@ -3621,7 +3615,7 @@ static void do_brush_action(Sculpt *sd, /* Update average stroke position. */ copy_v3_v3(location, ss->cache->true_location); - mul_m4_v3(ob->obmat, location); + mul_m4_v3(ob->object_to_world, location); add_v3_v3(ups->average_stroke_accum, location); ups->average_stroke_counter++; @@ -4207,8 +4201,8 @@ static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss) /* Store matrix for mirror object clipping. */ if (mmd->mirror_ob) { float imtx_mirror_ob[4][4]; - invert_m4_m4(imtx_mirror_ob, mmd->mirror_ob->obmat); - mul_m4_m4m4(ss->cache->clip_mirror_mtx, imtx_mirror_ob, ob->obmat); + invert_m4_m4(imtx_mirror_ob, mmd->mirror_ob->object_to_world); + mul_m4_m4m4(ss->cache->clip_mirror_mtx, imtx_mirror_ob, ob->object_to_world); } } } @@ -4279,7 +4273,8 @@ static void sculpt_update_cache_invariants( bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mval[2]) { StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + ToolSettings *tool_settings = CTX_data_tool_settings(C); + UnifiedPaintSettings *ups = &tool_settings->unified_paint_settings; Brush *brush = BKE_paint_brush(&sd->paint); ViewContext *vc = paint_stroke_view_context(op->customdata); Object *ob = CTX_data_active_object(C); @@ -4359,10 +4354,10 @@ static void sculpt_update_cache_invariants( /* Cache projection matrix. */ ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); - invert_m4_m4(ob->imat, ob->obmat); + invert_m4_m4(ob->world_to_object, ob->object_to_world); copy_m3_m4(mat, cache->vc->rv3d->viewinv); mul_m3_v3(mat, viewDir); - copy_m3_m4(mat, ob->imat); + copy_m3_m4(mat, ob->world_to_object); mul_m3_v3(mat, viewDir); normalize_v3_v3(cache->true_view_normal, viewDir); @@ -4378,7 +4373,7 @@ static void sculpt_update_cache_invariants( if (sd->gravity_object) { Object *gravity_object = sd->gravity_object; - copy_v3_v3(cache->true_gravity_direction, gravity_object->obmat[2]); + copy_v3_v3(cache->true_gravity_direction, gravity_object->object_to_world[2]); } else { cache->true_gravity_direction[0] = cache->true_gravity_direction[1] = 0.0f; @@ -4395,6 +4390,10 @@ static void sculpt_update_cache_invariants( cache->original = true; } + if (SCULPT_automasking_needs_original(sd, brush)) { + cache->original = true; + } + /* Draw sharp does not need the original coordinates to produce the accumulate effect, so it * should work the opposite way. */ if (brush->sculpt_tool == SCULPT_TOOL_DRAW_SHARP) { @@ -4410,6 +4409,13 @@ static void sculpt_update_cache_invariants( } } + /* Original coordinates require the sculpt undo system, which isn't used + * for image brushes. It's also not necessary, just disable it. */ + if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && + SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + cache->original = false; + } + cache->first_time = true; #define PIXEL_INPUT_THRESHHOLD 5 @@ -4521,27 +4527,27 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru } /* Compute 3d coordinate at same z from original location + mval. */ - mul_v3_m4v3(loc, ob->obmat, cache->orig_grab_location); + mul_v3_m4v3(loc, ob->object_to_world, cache->orig_grab_location); ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->region, loc, mval, grab_location); /* Compute delta to move verts by. */ if (!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { if (sculpt_needs_delta_from_anchored_origin(brush)) { sub_v3_v3v3(delta, grab_location, cache->old_grab_location); - invert_m4_m4(imat, ob->obmat); + invert_m4_m4(imat, ob->object_to_world); mul_mat3_m4_v3(imat, delta); add_v3_v3(cache->grab_delta, delta); } else if (sculpt_needs_delta_for_tip_orientation(brush)) { if (brush->flag & BRUSH_ANCHORED) { float orig[3]; - mul_v3_m4v3(orig, ob->obmat, cache->orig_grab_location); + mul_v3_m4v3(orig, ob->object_to_world, cache->orig_grab_location); sub_v3_v3v3(cache->grab_delta, grab_location, orig); } else { sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); } - invert_m4_m4(imat, ob->obmat); + invert_m4_m4(imat, ob->object_to_world); mul_mat3_m4_v3(imat, cache->grab_delta); } else { @@ -4586,7 +4592,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru /* Handle 'rake' */ cache->is_rake_rotation_valid = false; - invert_m4_m4(imat, ob->obmat); + invert_m4_m4(imat, ob->object_to_world); mul_mat3_m4_v3(imat, grab_location); if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { @@ -4807,12 +4813,12 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd, void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) { SculptSession *ss = ob->sculpt; - View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; bool need_pmap = sculpt_needs_connectivity_info(sd, brush, ss, 0); if (ss->shapekey_active || ss->deform_modifiers_active || - (!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) { + (!BKE_sculptsession_use_pbvh_draw(ob, rv3d) && need_pmap)) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); BKE_sculpt_update_object_for_edit( depsgraph, ob, need_pmap, false, SCULPT_tool_is_paint(brush->sculpt_tool)); @@ -4907,7 +4913,7 @@ float SCULPT_raycast_init(ViewContext *vc, ED_view3d_win_to_segment_clipped( vc->depsgraph, vc->region, vc->v3d, mval, ray_start, ray_end, true); - invert_m4_m4(obimat, ob->obmat); + invert_m4_m4(obimat, ob->object_to_world); mul_m4_v3(obimat, ray_start); mul_m4_v3(obimat, ray_end); @@ -5015,10 +5021,10 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, float radius; /* Update cursor data in SculptSession. */ - invert_m4_m4(ob->imat, ob->obmat); + invert_m4_m4(ob->world_to_object, ob->object_to_world); copy_m3_m4(mat, vc.rv3d->viewinv); mul_m3_v3(mat, viewDir); - copy_m3_m4(mat, ob->imat); + copy_m3_m4(mat, ob->world_to_object); mul_m3_v3(mat, viewDir); normalize_v3_v3(ss->cursor_view_normal, viewDir); copy_v3_v3(ss->cursor_normal, srd.face_normal); @@ -5207,7 +5213,7 @@ static void sculpt_restore_mesh(Sculpt *sd, Object *ob) /* Restore the mesh before continuing with anchored stroke. */ if ((brush->flag & BRUSH_ANCHORED) || - ((ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ELASTIC_DEFORM)) && + (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ELASTIC_DEFORM) && BKE_brush_use_size_pressure(brush)) || (brush->flag & BRUSH_DRAG_DOT)) { @@ -5243,7 +5249,6 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags) SculptSession *ss = ob->sculpt; ARegion *region = CTX_wm_region(C); MultiresModifierData *mmd = ss->multires.modifier; - View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); if (rv3d) { @@ -5268,7 +5273,7 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags) /* Only current viewport matters, slower update for all viewports will * be done in sculpt_flush_update_done. */ - if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) { + if (!BKE_sculptsession_use_pbvh_draw(ob, rv3d)) { /* Slow update with full dependency graph update and all that comes with it. * Needed when there are modifiers or full shading in the 3D viewport. */ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -5310,16 +5315,15 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up /* After we are done drawing the stroke, check if we need to do a more * expensive depsgraph tag to update geometry. */ wmWindowManager *wm = CTX_wm_manager(C); - View3D *current_v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); + RegionView3D *current_rv3d = CTX_wm_region_view3d(C); SculptSession *ss = ob->sculpt; Mesh *mesh = ob->data; /* Always needed for linked duplicates. */ bool need_tag = (ID_REAL_USERS(&mesh->id) > 1); - if (rv3d) { - rv3d->rflag &= ~RV3D_PAINTING; + if (current_rv3d) { + current_rv3d->rflag &= ~RV3D_PAINTING; } LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { @@ -5329,16 +5333,17 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up if (sl->spacetype != SPACE_VIEW3D) { continue; } - View3D *v3d = (View3D *)sl; - if (v3d != current_v3d) { - need_tag |= !BKE_sculptsession_use_pbvh_draw(ob, v3d); - } /* Tag all 3D viewports for redraw now that we are done. Others * viewports did not get a full redraw, and anti-aliasing for the * current viewport was deactivated. */ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = region->regiondata; + if (rv3d != current_rv3d) { + need_tag |= !BKE_sculptsession_use_pbvh_draw(ob, rv3d); + } + ED_region_tag_redraw(region); } } @@ -5370,15 +5375,19 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor); } - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_bmesh_after_stroke(ss->pbvh); - } + BKE_sculpt_attributes_destroy_temporary_stroke(ob); - /* Optimization: if there is locked key and active modifiers present in */ - /* the stack, keyblock is updating at each step. otherwise we could update */ - /* keyblock only when stroke is finished. */ - if (ss->shapekey_active && !ss->deform_modifiers_active) { - sculpt_update_keyblock(ob); + if (update_flags & SCULPT_UPDATE_COORDS) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BKE_pbvh_bmesh_after_stroke(ss->pbvh); + } + + /* Optimization: if there is locked key and active modifiers present in */ + /* the stack, keyblock is updating at each step. otherwise we could update */ + /* keyblock only when stroke is finished. */ + if (ss->shapekey_active && !ss->deform_modifiers_active) { + sculpt_update_keyblock(ob); + } } if (need_tag) { @@ -5394,6 +5403,38 @@ static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mv return SCULPT_stroke_get_location(C, co_dummy, mval, false); } +static void sculpt_stroke_undo_begin(const bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + ToolSettings *tool_settings = CTX_data_tool_settings(C); + + /* Setup the correct undo system. Image painting and sculpting are mutual exclusive. + * Color attributes are part of the sculpting undo system. */ + if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && + SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + ED_image_undo_push_begin(op->type->name, PAINT_MODE_SCULPT); + } + else { + SCULPT_undo_push_begin_ex(ob, sculpt_tool_name(sd)); + } +} + +static void sculpt_stroke_undo_end(const bContext *C, Brush *brush) +{ + Object *ob = CTX_data_active_object(C); + ToolSettings *tool_settings = CTX_data_tool_settings(C); + + if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && + SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + ED_image_undo_push_end(); + } + else { + SCULPT_undo_push_end(ob); + } +} + bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) { switch (BKE_pbvh_type(ss->pbvh)) { @@ -5442,15 +5483,10 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f SculptCursorGeometryInfo sgi; SCULPT_cursor_geometry_info_update(C, &sgi, mval, false); - /* Setup the correct undo system. Image painting and sculpting are mutual exclusive. - * Color attributes are part of the sculpting undo system. */ - if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && - SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { - ED_image_undo_push_begin(op->type->name, PAINT_MODE_SCULPT); - } - else { - SCULPT_undo_push_begin_ex(ob, sculpt_tool_name(sd)); - } + sculpt_stroke_undo_begin(C, op); + + SCULPT_stroke_id_next(ob); + ss->cache->stroke_id = ss->stroke_id; return true; } @@ -5476,7 +5512,8 @@ static void sculpt_stroke_update_step(bContext *C, sculpt_restore_mesh(sd, ob); if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { - float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); + float object_space_constant_detail = 1.0f / (sd->constant_detail * + mat4_to_scale(ob->object_to_world)); BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); } else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { @@ -5579,13 +5616,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str SCULPT_cache_free(ss->cache); ss->cache = NULL; - if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && - SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { - ED_image_undo_push_end(); - } - else { - SCULPT_undo_push_end(ob); - } + sculpt_stroke_undo_end(C, brush); if (brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); @@ -5594,6 +5625,10 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str if (SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_IMAGE); } + else { + BKE_sculpt_attributes_destroy_temporary_stroke(ob); + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR); + } } else { SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); @@ -5627,11 +5662,20 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; if (SCULPT_tool_is_paint(brush->sculpt_tool) && !SCULPT_handles_colors_report(ob->sculpt, op->reports)) { return OPERATOR_CANCELLED; } + if (SCULPT_tool_is_mask(brush->sculpt_tool)) { + MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); + BKE_sculpt_mask_layers_ensure(CTX_data_depsgraph_pointer(C), CTX_data_main(C), ob, mmd); + } + if (SCULPT_tool_is_face_sets(brush->sculpt_tool)) { + Mesh *mesh = BKE_object_get_original_mesh(ob); + ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + } stroke = paint_stroke_new(C, op, @@ -5652,9 +5696,10 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_PASS_THROUGH; } - if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + retval = op->type->modal(C, op, event); + if (ELEM(retval, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { paint_stroke_free(C, op, op->customdata); - return OPERATOR_FINISHED; + return retval; } /* Add modal handler. */ WM_event_add_modal_handler(C, op); @@ -5709,7 +5754,29 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) { - return paint_stroke_modal(C, op, event, (struct PaintStroke **)&op->customdata); + bool started = op->customdata && paint_stroke_started((struct PaintStroke *)op->customdata); + + int retval = paint_stroke_modal(C, op, event, (struct PaintStroke **)&op->customdata); + + if (!started && ELEM(retval, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { + /* Did the stroke never start? If so push a blank sculpt undo + * step to prevent a global undo step (which is triggered by the + * #OPTYPE_UNDO flag in #SCULPT_OT_brush_stroke). + * + * Having blank global undo steps interleaved with sculpt steps + * corrupts the DynTopo undo stack. + * See T101430. + * + * NOTE: simply returning #OPERATOR_CANCELLED was not + * sufficient to prevent this. */ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + sculpt_stroke_undo_begin(C, op); + sculpt_stroke_undo_end(C, brush); + } + + return retval; } static void sculpt_redo_empty_ui(bContext *UNUSED(C), wmOperator *UNUSED(op)) @@ -6041,4 +6108,91 @@ void SCULPT_fake_neighbors_free(Object *ob) sculpt_pose_fake_neighbors_free(ss); } +void SCULPT_automasking_node_begin(Object *ob, + const SculptSession *UNUSED(ss), + AutomaskingCache *automasking, + AutomaskingNodeData *automask_data, + PBVHNode *node) +{ + if (!automasking) { + memset(automask_data, 0, sizeof(*automask_data)); + return; + } + + automask_data->node = node; + automask_data->have_orig_data = automasking->settings.flags & + (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL); + + if (automask_data->have_orig_data) { + SCULPT_orig_vert_data_init(&automask_data->orig_data, ob, node, SCULPT_UNDO_COORDS); + } + else { + memset(&automask_data->orig_data, 0, sizeof(automask_data->orig_data)); + } +} + +void SCULPT_automasking_node_update(SculptSession *UNUSED(ss), + AutomaskingNodeData *automask_data, + PBVHVertexIter *vd) +{ + if (automask_data->have_orig_data) { + SCULPT_orig_vert_data_update(&automask_data->orig_data, vd); + } +} + +bool SCULPT_vertex_is_occluded(SculptSession *ss, PBVHVertRef vertex, bool original) +{ + float ray_start[3], ray_end[3], ray_normal[3], face_normal[3]; + float co[3]; + + copy_v3_v3(co, SCULPT_vertex_co_get(ss, vertex)); + float mouse[2]; + + ED_view3d_project_float_v2_m4(ss->cache->vc->region, co, mouse, ss->cache->projection_mat); + + int depth = SCULPT_raycast_init(ss->cache->vc, mouse, ray_end, ray_start, ray_normal, original); + + negate_v3(ray_normal); + + copy_v3_v3(ray_start, SCULPT_vertex_co_get(ss, vertex)); + madd_v3_v3fl(ray_start, ray_normal, 0.002); + + SculptRaycastData srd = {0}; + srd.original = original; + srd.ss = ss; + srd.hit = false; + srd.ray_start = ray_start; + srd.ray_normal = ray_normal; + srd.depth = depth; + srd.face_normal = face_normal; + + isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); + BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + + return srd.hit; +} + +void SCULPT_stroke_id_next(Object *ob) +{ + /* Manually wrap in int32 space to avoid tripping up undefined behavior + * sanitizers. + */ + ob->sculpt->stroke_id = (uchar)(((int)ob->sculpt->stroke_id + 1) & 255); +} + +void SCULPT_stroke_id_ensure(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + if (!ss->attrs.automasking_stroke_id) { + SculptAttributeParams params = {0}; + ss->attrs.automasking_stroke_id = BKE_sculpt_attribute_ensure( + ob, + ATTR_DOMAIN_POINT, + CD_PROP_INT8, + SCULPT_ATTRIBUTE_NAME(automasking_stroke_id), + ¶ms); + } +} + /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index a9fe8cc4b2f..505440c9272 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -7,17 +7,22 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.hh" #include "BLI_blenlib.h" #include "BLI_hash.h" #include "BLI_index_range.hh" #include "BLI_math.h" +#include "BLI_math_vec_types.hh" +#include "BLI_set.hh" #include "BLI_task.h" +#include "BLI_vector.hh" #include "DNA_brush_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BKE_brush.h" +#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" @@ -47,7 +52,10 @@ #include <cmath> #include <cstdlib> +using blender::float3; using blender::IndexRange; +using blender::Set; +using blender::Vector; AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss) { @@ -64,10 +72,13 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, const Brush *br, const eAutomasking_flag mode) { + int automasking = sd->automasking_flags; + if (br) { - return br->automasking_flags & mode || sd->automasking_flags & mode; + automasking |= br->automasking_flags; } - return sd->automasking_flags & mode; + + return (eAutomasking_flag)automasking & mode; } bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br) @@ -87,17 +98,85 @@ bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, co if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) { return true; } + if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BRUSH_NORMAL)) { + return true; + } + if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_VIEW_NORMAL)) { + return true; + } + if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_CAVITY_ALL)) { + return true; + } + return false; } static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush) { if (brush) { - return sculpt->automasking_flags | brush->automasking_flags; + int flags = sculpt->automasking_flags | brush->automasking_flags; + + /* Check if we are using brush cavity settings. */ + if (brush->automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL) { + flags &= ~(BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_CAVITY_USE_CURVE | + BRUSH_AUTOMASKING_CAVITY_NORMAL); + flags |= brush->automasking_flags; + } + else if (sculpt->automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL) { + flags &= ~(BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_CAVITY_USE_CURVE | + BRUSH_AUTOMASKING_CAVITY_NORMAL); + flags |= sculpt->automasking_flags; + } + + return flags; } return sculpt->automasking_flags; } +bool SCULPT_automasking_needs_normal(const SculptSession * /*ss*/, + const Sculpt *sculpt, + const Brush *brush) +{ + int flags = sculpt_automasking_mode_effective_bits(sculpt, brush); + + return flags & (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL); +} + +static float sculpt_automasking_normal_calc(SculptSession *ss, + PBVHVertRef vertex, + float3 &normal, + float limit_lower, + float limit_upper, + AutomaskingNodeData *automask_data) +{ + float3 normal_v; + + if (automask_data->have_orig_data) { + normal_v = automask_data->orig_data.no; + } + else { + SCULPT_vertex_normal_get(ss, vertex, normal_v); + } + + float angle = saacos(dot_v3v3(normal, normal_v)); + + /* note that limit is pre-divided by M_PI */ + + if (angle > limit_lower && angle < limit_upper) { + float t = 1.0f - (angle - limit_lower) / (limit_upper - limit_lower); + + /* smoothstep */ + t = t * t * (3.0 - 2.0 * t); + + return t; + } + if (angle > limit_upper) { + return 0.0f; + } + + return 1.0f; +} + static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush) { @@ -105,30 +184,352 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) { return true; } - if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) { - return brush && brush->automasking_boundary_edges_propagation_steps != 1; - } - if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) { + + if (automasking_flags & + (BRUSH_AUTOMASKING_BOUNDARY_EDGES | BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS | + BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL)) { return brush && brush->automasking_boundary_edges_propagation_steps != 1; } return false; } +static float automasking_brush_normal_factor(AutomaskingCache *automasking, + SculptSession *ss, + PBVHVertRef vertex, + AutomaskingNodeData *automask_data) +{ + float falloff = automasking->settings.start_normal_falloff * M_PI; + float3 initial_normal; + + if (ss->cache) { + initial_normal = ss->cache->initial_normal; + } + else { + initial_normal = ss->filter_cache->initial_normal; + } + + return sculpt_automasking_normal_calc(ss, + vertex, + initial_normal, + automasking->settings.start_normal_limit - falloff * 0.5f, + automasking->settings.start_normal_limit + falloff * 0.5f, + automask_data); +} + +static float automasking_view_normal_factor(AutomaskingCache *automasking, + SculptSession *ss, + PBVHVertRef vertex, + AutomaskingNodeData *automask_data) +{ + float falloff = automasking->settings.view_normal_falloff * M_PI; + + float3 view_normal; + + if (ss->cache) { + view_normal = ss->cache->view_normal; + } + else { + view_normal = ss->filter_cache->view_normal; + } + + return sculpt_automasking_normal_calc(ss, + vertex, + view_normal, + automasking->settings.view_normal_limit, + automasking->settings.view_normal_limit + falloff, + automask_data); +} + +static float automasking_view_occlusion_factor(AutomaskingCache *automasking, + SculptSession *ss, + PBVHVertRef vertex, + uchar stroke_id, + AutomaskingNodeData * /*automask_data*/) +{ + char f = *(char *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_occlusion); + + if (stroke_id != automasking->current_stroke_id) { + f = *(char *)SCULPT_vertex_attr_get( + vertex, + ss->attrs.automasking_occlusion) = SCULPT_vertex_is_occluded(ss, vertex, true) ? 2 : 1; + } + + return f == 2; +} + +/* Updates vertex stroke id. */ +static float automasking_factor_end(SculptSession *ss, + AutomaskingCache *automasking, + PBVHVertRef vertex, + float value) +{ + if (ss->attrs.automasking_stroke_id) { + *(uchar *)SCULPT_vertex_attr_get( + vertex, ss->attrs.automasking_stroke_id) = automasking->current_stroke_id; + } + + return value; +} + +static float sculpt_cavity_calc_factor(AutomaskingCache *automasking, float factor) +{ + float sign = signf(factor); + + factor = fabsf(factor) * automasking->settings.cavity_factor * 50.0f; + + factor = factor * sign * 0.5f + 0.5f; + CLAMP(factor, 0.0f, 1.0f); + + return (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_INVERTED) ? 1.0f - factor : + factor; +} + +struct CavityBlurVert { + PBVHVertRef vertex; + float dist; + int depth; + + CavityBlurVert(PBVHVertRef vertex_, float dist_, int depth_) + : vertex(vertex_), dist(dist_), depth(depth_) + { + } + + CavityBlurVert() = default; +}; + +static void sculpt_calc_blurred_cavity(SculptSession *ss, + AutomaskingCache *automasking, + int steps, + PBVHVertRef vertex) +{ + float3 sno1(0.0f); + float3 sno2(0.0f); + float3 sco1(0.0f); + float3 sco2(0.0f); + float len1_sum = 0.0f; + int sco1_len = 0, sco2_len = 0; + + /* Steps starts at 1, but API and user interface + * are zero-based. + */ + steps++; + + Vector<CavityBlurVert, 64> queue; + Set<int64_t, 64> visit; + + int start = 0, end = 0; + + queue.resize(64); + + CavityBlurVert initial(vertex, 0.0f, 0); + + visit.add_new(vertex.i); + queue[0] = initial; + end = 1; + + const float *co1 = SCULPT_vertex_co_get(ss, vertex); + + while (start != end) { + CavityBlurVert &blurvert = queue[start]; + PBVHVertRef v = blurvert.vertex; + start = (start + 1) % queue.size(); + + float3 no; + + const float *co = SCULPT_vertex_co_get(ss, v); + SCULPT_vertex_normal_get(ss, v, no); + + float centdist = len_v3v3(co, co1); + + sco1 += co; + sno1 += no; + len1_sum += centdist; + sco1_len++; + + if (blurvert.depth < steps) { + sco2 += co; + sno2 += no; + sco2_len++; + } + + if (blurvert.depth >= steps) { + continue; + } + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v, ni) { + PBVHVertRef v2 = ni.vertex; + + if (visit.contains(v2.i)) { + continue; + } + + float dist = len_v3v3(SCULPT_vertex_co_get(ss, v2), SCULPT_vertex_co_get(ss, v)); + + visit.add_new(v2.i); + CavityBlurVert blurvert2(v2, dist, blurvert.depth + 1); + + int nextend = (end + 1) % queue.size(); + + if (nextend == start) { + int oldsize = queue.size(); + + queue.resize(queue.size() << 1); + + if (end < start) { + int n = oldsize - start; + + for (int i = 0; i < n; i++) { + queue[queue.size() - n + i] = queue[i + start]; + } + + start = queue.size() - n; + } + } + + queue[end] = blurvert2; + end = (end + 1) % queue.size(); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + + BLI_assert(sco1_len != sco2_len); + + if (!sco1_len) { + sco1 = SCULPT_vertex_co_get(ss, vertex); + } + else { + sco1 /= float(sco1_len); + len1_sum /= sco1_len; + } + + if (!sco2_len) { + sco2 = SCULPT_vertex_co_get(ss, vertex); + } + else { + sco2 /= float(sco2_len); + } + + normalize_v3(sno1); + if (dot_v3v3(sno1, sno1) == 0.0f) { + SCULPT_vertex_normal_get(ss, vertex, sno1); + } + + normalize_v3(sno2); + if (dot_v3v3(sno2, sno2) == 0.0f) { + SCULPT_vertex_normal_get(ss, vertex, sno2); + } + + float3 vec = sco1 - sco2; + float factor_sum = dot_v3v3(vec, sno2) / len1_sum; + + factor_sum = sculpt_cavity_calc_factor(automasking, factor_sum); + + *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity) = factor_sum; +} + +int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking) +{ + SculptSession *ss = ob->sculpt; + + int hash; + int totvert = SCULPT_vertex_count_get(ss); + + hash = BLI_hash_int(automasking->settings.flags); + hash = BLI_hash_int_2d(hash, totvert); + + if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) { + hash = BLI_hash_int_2d(hash, automasking->settings.cavity_blur_steps); + hash = BLI_hash_int_2d(hash, *reinterpret_cast<uint *>(&automasking->settings.cavity_factor)); + + if (automasking->settings.cavity_curve) { + CurveMap *cm = automasking->settings.cavity_curve->cm; + + for (int i = 0; i < cm->totpoint; i++) { + hash = BLI_hash_int_2d(hash, *reinterpret_cast<uint *>(&cm->curve[i].x)); + hash = BLI_hash_int_2d(hash, *reinterpret_cast<uint *>(&cm->curve[i].y)); + hash = BLI_hash_int_2d(hash, uint(cm->curve[i].flag)); + hash = BLI_hash_int_2d(hash, uint(cm->curve[i].shorty)); + } + } + } + + if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) { + hash = BLI_hash_int_2d(hash, automasking->settings.initial_face_set); + } + + if (automasking->settings.flags & BRUSH_AUTOMASKING_VIEW_NORMAL) { + hash = BLI_hash_int_2d(hash, + *reinterpret_cast<uint *>(&automasking->settings.view_normal_falloff)); + hash = BLI_hash_int_2d(hash, + *reinterpret_cast<uint *>(&automasking->settings.view_normal_limit)); + } + + if (automasking->settings.flags & BRUSH_AUTOMASKING_BRUSH_NORMAL) { + hash = BLI_hash_int_2d(hash, + *reinterpret_cast<uint *>(&automasking->settings.start_normal_falloff)); + hash = BLI_hash_int_2d(hash, + *reinterpret_cast<uint *>(&automasking->settings.start_normal_limit)); + } + + return hash; +} + +static float sculpt_automasking_cavity_factor(AutomaskingCache *automasking, + SculptSession *ss, + PBVHVertRef vertex) +{ + uchar stroke_id = *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_stroke_id); + + if (stroke_id != automasking->current_stroke_id) { + sculpt_calc_blurred_cavity(ss, automasking, automasking->settings.cavity_blur_steps, vertex); + } + + float factor = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity); + bool inverted = automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_INVERTED; + + if ((automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) && + (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_USE_CURVE)) { + factor = inverted ? 1.0f - factor : factor; + factor = BKE_curvemapping_evaluateF(automasking->settings.cavity_curve, 0, factor); + factor = inverted ? 1.0f - factor : factor; + } + + return factor; +} + float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, - PBVHVertRef vert) + PBVHVertRef vert, + AutomaskingNodeData *automask_data) { - if (!automasking) { + if (!automasking || vert.i == PBVH_REF_NONE) { return 1.0f; } - int index = BKE_pbvh_vertex_to_index(ss->pbvh, vert); - /* If the cache is initialized with valid info, use the cache. This is used when the * automasking information can't be computed in real time per vertex and needs to be * initialized for the whole mesh when the stroke starts. */ - if (automasking->factor) { - return automasking->factor[index]; + if (ss->attrs.automasking_factor) { + float factor = *(float *)SCULPT_vertex_attr_get(vert, ss->attrs.automasking_factor); + + if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) { + factor *= sculpt_automasking_cavity_factor(automasking, ss, vert); + } + + return factor; + } + + uchar stroke_id = ss->attrs.automasking_stroke_id ? + *(uchar *)SCULPT_vertex_attr_get(vert, ss->attrs.automasking_stroke_id) : + -1; + + bool do_occlusion = (automasking->settings.flags & + (BRUSH_AUTOMASKING_VIEW_OCCLUSION | BRUSH_AUTOMASKING_VIEW_NORMAL)) == + (BRUSH_AUTOMASKING_VIEW_OCCLUSION | BRUSH_AUTOMASKING_VIEW_NORMAL); + if (do_occlusion && + automasking_view_occlusion_factor(automasking, ss, vert, stroke_id, automask_data)) { + return automasking_factor_end(ss, automasking, vert, 0.0f); } if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) { @@ -149,7 +550,23 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, } } - return 1.0f; + float mask = 1.0f; + + if ((ss->cache || ss->filter_cache) && + (automasking->settings.flags & BRUSH_AUTOMASKING_BRUSH_NORMAL)) { + mask *= automasking_brush_normal_factor(automasking, ss, vert, automask_data); + } + + if ((ss->cache || ss->filter_cache) && + (automasking->settings.flags & BRUSH_AUTOMASKING_VIEW_NORMAL)) { + mask *= automasking_view_normal_factor(automasking, ss, vert, automask_data); + } + + if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) { + mask *= sculpt_automasking_cavity_factor(automasking, ss, vert); + } + + return automasking_factor_end(ss, automasking, vert, mask); } void SCULPT_automasking_cache_free(AutomaskingCache *automasking) @@ -158,7 +575,6 @@ void SCULPT_automasking_cache_free(AutomaskingCache *automasking) return; } - MEM_SAFE_FREE(automasking->factor); MEM_SAFE_FREE(automasking); } @@ -176,43 +592,39 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br) } struct AutomaskFloodFillData { - float *automask_factor; float radius; bool use_radius; float location[3]; char symm; }; -static bool automask_floodfill_cb(SculptSession *ss, - PBVHVertRef from_v, - PBVHVertRef to_v, - bool UNUSED(is_duplicate), - void *userdata) +static bool automask_floodfill_cb( + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool /*is_duplicate*/, void *userdata) { AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata; - int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); - int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); - data->automask_factor[to_v_i] = 1.0f; - data->automask_factor[from_v_i] = 1.0f; + *(float *)SCULPT_vertex_attr_get(to_v, ss->attrs.automasking_factor) = 1.0f; + *(float *)SCULPT_vertex_attr_get(from_v, ss->attrs.automasking_factor) = 1.0f; return (!data->use_radius || SCULPT_is_vertex_inside_brush_radius_symm( SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm)); } -static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor) +static void SCULPT_topology_automasking_init(Sculpt *sd, Object *ob) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { - BLI_assert_msg(0, "Topology masking: pmap missing"); - return nullptr; + BLI_assert_unreachable(); + return; } const int totvert = SCULPT_vertex_count_get(ss); for (int i : IndexRange(totvert)) { - automask_factor[i] = 0.0f; + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f; } /* Flood fill automask to connected vertices. Limited to vertices inside @@ -222,9 +634,8 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au const float radius = ss->cache ? ss->cache->radius : FLT_MAX; SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius); - AutomaskFloodFillData fdata = {nullptr}; + AutomaskFloodFillData fdata = {0}; - fdata.automask_factor = automask_factor; fdata.radius = radius; fdata.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush); fdata.symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -232,22 +643,20 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss)); SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata); SCULPT_floodfill_free(&flood); - - return automask_factor; } -static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor) +static void sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); if (!SCULPT_is_automasking_enabled(sd, ss, brush)) { - return nullptr; + return; } if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { BLI_assert_msg(0, "Face Sets automasking: pmap missing"); - return nullptr; + return; } int tot_vert = SCULPT_vertex_count_get(ss); @@ -256,25 +665,22 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { - automask_factor[i] *= 0.0f; + *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = 0.0f; } } - - return automask_factor; } #define EDGE_DISTANCE_INF -1 -float *SCULPT_boundary_automasking_init(Object *ob, - eBoundaryAutomaskMode mode, - int propagation_steps, - float *automask_factor) +static void SCULPT_boundary_automasking_init(Object *ob, + eBoundaryAutomaskMode mode, + int propagation_steps) { SculptSession *ss = ob->sculpt; if (!ss->pmap) { BLI_assert_msg(0, "Boundary Edges masking: pmap missing"); - return nullptr; + return; } const int totvert = SCULPT_vertex_count_get(ss); @@ -316,16 +722,19 @@ float *SCULPT_boundary_automasking_init(Object *ob, } for (int i : IndexRange(totvert)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (edge_distance[i] == EDGE_DISTANCE_INF) { continue; } - const float p = 1.0f - ((float)edge_distance[i] / (float)propagation_steps); + const float p = 1.0f - (float(edge_distance[i]) / float(propagation_steps)); const float edge_boundary_automask = pow2f(p); - automask_factor[i] *= (1.0f - edge_boundary_automask); + + *(float *)SCULPT_vertex_attr_get( + vertex, ss->attrs.automasking_factor) *= (1.0f - edge_boundary_automask); } MEM_SAFE_FREE(edge_distance); - return automask_factor; } static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking, @@ -335,6 +744,66 @@ static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automaski { automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush); automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss); + + automasking->settings.view_normal_limit = sd->automasking_view_normal_limit; + automasking->settings.view_normal_falloff = sd->automasking_view_normal_falloff; + automasking->settings.start_normal_limit = sd->automasking_start_normal_limit; + automasking->settings.start_normal_falloff = sd->automasking_start_normal_falloff; + + if (brush && (brush->automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) { + automasking->settings.cavity_curve = brush->automasking_cavity_curve; + automasking->settings.cavity_factor = brush->automasking_cavity_factor; + automasking->settings.cavity_blur_steps = brush->automasking_cavity_blur_steps; + } + else { + automasking->settings.cavity_curve = sd->automasking_cavity_curve; + automasking->settings.cavity_factor = sd->automasking_cavity_factor; + automasking->settings.cavity_blur_steps = sd->automasking_cavity_blur_steps; + } +} + +static void sculpt_normal_occlusion_automasking_fill(AutomaskingCache *automasking, + Object *ob, + eAutomasking_flag mode) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + + /* No need to build original data since this is only called at the beginning of strokes.*/ + AutomaskingNodeData nodedata; + nodedata.have_orig_data = false; + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + float f = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor); + + if (int(mode) & BRUSH_AUTOMASKING_BRUSH_NORMAL) { + f *= automasking_brush_normal_factor(automasking, ss, vertex, &nodedata); + } + if (int(mode) & BRUSH_AUTOMASKING_VIEW_NORMAL) { + if (int(mode) & BRUSH_AUTOMASKING_VIEW_OCCLUSION) { + f *= automasking_view_occlusion_factor(automasking, ss, vertex, -1, &nodedata); + } + + f *= automasking_view_normal_factor(automasking, ss, vertex, &nodedata); + } + + if (ss->attrs.automasking_stroke_id) { + *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_stroke_id) = ss->stroke_id; + } + + *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = f; + } +} + +bool SCULPT_tool_can_reuse_automask(int sculpt_tool) +{ + return ELEM(sculpt_tool, + SCULPT_TOOL_PAINT, + SCULPT_TOOL_SMEAR, + SCULPT_TOOL_MASK, + SCULPT_TOOL_DRAW_FACE_SETS); } AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob) @@ -351,38 +820,134 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush); SCULPT_boundary_info_ensure(ob); + automasking->current_stroke_id = ss->stroke_id; + + bool use_stroke_id = false; + int mode = sculpt_automasking_mode_effective_bits(sd, brush); + + if ((mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) && (mode & BRUSH_AUTOMASKING_VIEW_NORMAL)) { + use_stroke_id = true; + + if (!ss->attrs.automasking_occlusion) { + SculptAttributeParams params = {0}; + ss->attrs.automasking_occlusion = BKE_sculpt_attribute_ensure( + ob, + ATTR_DOMAIN_POINT, + CD_PROP_INT8, + SCULPT_ATTRIBUTE_NAME(automasking_occlusion), + ¶ms); + } + } + + if (mode & BRUSH_AUTOMASKING_CAVITY_ALL) { + use_stroke_id = true; + + if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CAVITY_USE_CURVE)) { + BKE_curvemapping_init(brush->automasking_cavity_curve); + BKE_curvemapping_init(sd->automasking_cavity_curve); + } + + if (!ss->attrs.automasking_cavity) { + SculptAttributeParams params = {0}; + ss->attrs.automasking_cavity = BKE_sculpt_attribute_ensure( + ob, + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + SCULPT_ATTRIBUTE_NAME(automasking_cavity), + ¶ms); + } + } + + if (use_stroke_id) { + SCULPT_stroke_id_ensure(ob); + + bool have_occlusion = (mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) && + (mode & BRUSH_AUTOMASKING_VIEW_NORMAL); + + if (brush && SCULPT_tool_can_reuse_automask(brush->sculpt_tool) && !have_occlusion) { + int hash = SCULPT_automasking_settings_hash(ob, automasking); + + if (hash == ss->last_automasking_settings_hash) { + automasking->current_stroke_id = ss->last_automask_stroke_id; + automasking->can_reuse_mask = true; + } + } + + if (!automasking->can_reuse_mask) { + ss->last_automask_stroke_id = ss->stroke_id; + } + } + if (!SCULPT_automasking_needs_factors_cache(sd, brush)) { return automasking; } - automasking->factor = (float *)MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor"); + SculptAttributeParams params = {0}; + params.stroke_only = true; + + ss->attrs.automasking_factor = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(automasking_factor), ¶ms); + + float initial_value; + + /* Topology, boundary and boundary face sets build up the mask + * from zero which other modes can subtract from. If none of them are + * enabled initialize to 1. + */ + if (!(mode & (BRUSH_AUTOMASKING_BOUNDARY_EDGES | BRUSH_AUTOMASKING_TOPOLOGY | + BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS))) { + initial_value = 1.0f; + } + else { + initial_value = 0.0f; + } + for (int i : IndexRange(totvert)) { - automasking->factor[i] = 1.0f; + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = initial_value; } const int boundary_propagation_steps = brush ? brush->automasking_boundary_edges_propagation_steps : 1; + /* Additive modes. */ if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) { SCULPT_vertex_random_access_ensure(ss); - SCULPT_topology_automasking_init(sd, ob, automasking->factor); + SCULPT_topology_automasking_init(sd, ob); } if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) { SCULPT_vertex_random_access_ensure(ss); - sculpt_face_sets_automasking_init(sd, ob, automasking->factor); + sculpt_face_sets_automasking_init(sd, ob); } if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) { SCULPT_vertex_random_access_ensure(ss); - SCULPT_boundary_automasking_init( - ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor); + SCULPT_boundary_automasking_init(ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps); } if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) { SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_automasking_init( - ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor); + ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps); + } + + /* Subtractive modes. */ + int normal_bits = sculpt_automasking_mode_effective_bits(sd, brush) & + (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL | + BRUSH_AUTOMASKING_VIEW_OCCLUSION); + + if (normal_bits) { + sculpt_normal_occlusion_automasking_fill(automasking, ob, (eAutomasking_flag)normal_bits); } return automasking; } + +bool SCULPT_automasking_needs_original(const Sculpt *sd, const Brush *brush) +{ + + return sculpt_automasking_mode_effective_bits(sd, brush) & + (BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_BRUSH_NORMAL | + BRUSH_AUTOMASKING_VIEW_NORMAL); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index 005892b88a0..355f260ae11 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -7,13 +7,11 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_edgehash.h" #include "BLI_math.h" #include "BLI_task.h" #include "DNA_brush_types.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -21,20 +19,13 @@ #include "BKE_ccg.h" #include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_scene.h" #include "paint_intern.h" #include "sculpt_intern.h" #include "GPU_immediate.h" -#include "GPU_immediate_util.h" -#include "GPU_matrix.h" #include "GPU_state.h" #include "bmesh.h" @@ -681,12 +672,16 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, angle_factor = floorf(angle_factor * 10) / 10.0f; } const float angle = angle_factor * M_PI; + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { @@ -694,7 +689,8 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); float t_orig_co[3]; float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]); @@ -729,12 +725,16 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { @@ -742,7 +742,8 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -773,6 +774,9 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); @@ -781,6 +785,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { @@ -788,7 +793,8 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -819,12 +825,16 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { @@ -832,7 +842,8 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -862,6 +873,9 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); float angle_factor = disp / ss->cache->radius; @@ -876,6 +890,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { @@ -883,7 +898,8 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); float t_orig_co[3]; float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position); diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.c b/source/blender/editors/sculpt_paint/sculpt_brush_types.c index 245cbe0f54e..666fa884e03 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.c +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.c @@ -8,26 +8,15 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" -#include "BLI_dial_2d.h" #include "BLI_ghash.h" #include "BLI_gsqueue.h" -#include "BLI_hash.h" #include "BLI_math.h" -#include "BLI_math_color.h" -#include "BLI_math_color_blend.h" #include "BLI_task.h" #include "BLI_utildefines.h" -#include "BLT_translation.h" - -#include "PIL_time.h" - #include "DNA_brush_types.h" #include "DNA_customdata_types.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -35,53 +24,16 @@ #include "BKE_ccg.h" #include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_image.h" #include "BKE_kelvinlet.h" -#include "BKE_key.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" -#include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" -#include "BKE_mesh_mirror.h" -#include "BKE_modifier.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_particle.h" #include "BKE_pbvh.h" -#include "BKE_pointcache.h" -#include "BKE_report.h" -#include "BKE_scene.h" -#include "BKE_screen.h" -#include "BKE_subdiv_ccg.h" -#include "BKE_subsurf.h" - -#include "DEG_depsgraph.h" - -#include "IMB_colormanagement.h" - -#include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" -#include "WM_types.h" -#include "ED_object.h" -#include "ED_screen.h" -#include "ED_sculpt.h" #include "ED_view3d.h" #include "paint_intern.h" #include "sculpt_intern.h" -#include "RNA_access.h" -#include "RNA_define.h" - -#include "UI_interface.h" -#include "UI_resources.h" - #include "bmesh.h" -#include "bmesh_tools.h" #include <math.h> #include <stdlib.h> @@ -179,7 +131,7 @@ static void calc_sculpt_plane( } /* For area normal. */ - if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) && + if (!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) && (brush->flag & BRUSH_ORIGINAL_NORMAL)) { copy_v3_v3(r_area_no, ss->cache->sculpt_normal); } @@ -188,7 +140,7 @@ static void calc_sculpt_plane( } /* For flatten center. */ - if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) && + if (!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) && (brush->flag & BRUSH_ORIGINAL_PLANE)) { copy_v3_v3(r_area_co, ss->cache->last_center); } @@ -302,10 +254,17 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + /* Offset vertex. */ const float fade = SCULPT_brush_strength_factor(ss, brush, @@ -315,7 +274,8 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -387,6 +347,10 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -405,6 +369,8 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -413,7 +379,8 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -485,6 +452,10 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -503,6 +474,8 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -511,7 +484,8 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -601,6 +575,10 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, /* Tilted plane (front part of the brush). */ plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -621,6 +599,8 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, interp_v3_v3v3(intr, intr, intr_tilt, tilt_mix); sub_v3_v3v3(val, intr_tilt, vd.co); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -629,7 +609,8 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -764,6 +745,10 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -776,6 +761,8 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, sub_v3_v3v3(val, intr, vd.co); if (SCULPT_plane_trim(ss->cache, brush, val)) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -784,7 +771,8 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -922,6 +910,10 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -933,6 +925,8 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, sub_v3_v3v3(val, intr, vd.co); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -941,7 +935,8 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -1041,6 +1036,10 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) { continue; @@ -1058,6 +1057,9 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, if (!SCULPT_plane_trim(ss->cache, brush, val)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + /* The normal from the vertices is ignored, it causes glitch with planes, see: T44390. */ const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, @@ -1067,7 +1069,8 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -1202,6 +1205,10 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, KelvinletParams params; BKE_kelvinlet_init_params(¶ms, ss->cache->radius, bstrength, 1.0f, 0.4f); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!do_elastic && !sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -1212,6 +1219,8 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, fade = 1.0f; } else { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1220,7 +1229,8 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); } mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -1267,7 +1277,9 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, if (vd.mask) { mul_v3_fl(disp, 1.0f - *vd.mask); } - mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); + mul_v3_fl( + disp, + SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex, &automask_data)); copy_v3_v3(proxy[vd.i], disp); } @@ -1339,12 +1351,18 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, orig_data.co, @@ -1353,7 +1371,8 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -1412,12 +1431,19 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float vec[3], rot[3][3]; const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, @@ -1427,7 +1453,8 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); sub_v3_v3v3(vec, orig_data.co, ss->cache->location); axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade); @@ -1472,7 +1499,8 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, Sculpt *sd = data->sd; const Brush *brush = data->brush; - const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT; + const bool use_persistent_base = !ss->bm && ss->attrs.persistent_co && + brush->flag & BRUSH_PERSISTENT; PBVHVertexIter vd; SculptOrigVertData orig_data; @@ -1484,12 +1512,18 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1498,12 +1532,13 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); const int vi = vd.index; float *disp_factor; if (use_persistent_base) { - disp_factor = &ss->persistent_base[vi].disp; + disp_factor = (float *)SCULPT_vertex_attr_get(vd.vertex, ss->attrs.persistent_disp); } else { disp_factor = &ss->cache->layer_displacement_factor[vi]; @@ -1599,10 +1634,16 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1611,7 +1652,8 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float val[3]; if (vd.fno) { @@ -1667,10 +1709,16 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1679,7 +1727,8 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -1745,11 +1794,17 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } /* Offset vertex. */ + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1758,7 +1813,8 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float val1[3]; float val2[3]; @@ -1862,10 +1918,16 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, copy_v3_v3(x_object_space, stroke_xz[0]); copy_v3_v3(z_object_space, stroke_xz[1]); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1874,7 +1936,8 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float disp_center[3]; float x_disp[3]; float z_disp[3]; @@ -1976,12 +2039,18 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE; + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, orig_data.co, @@ -1990,7 +2059,8 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); if (grab_silhouette) { float silhouette_test_dir[3]; @@ -2054,6 +2124,9 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, const float bstrength = ss->cache->bstrength; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -2079,6 +2152,8 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float final_disp[3]; switch (brush->elastic_deform_type) { case BRUSH_ELASTIC_DEFORM_GRAB: @@ -2109,7 +2184,9 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, mul_v3_fl(final_disp, 1.0f - *vd.mask); } - mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); + mul_v3_fl( + final_disp, + SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex, &automask_data)); copy_v3_v3(proxy[vd.i], final_disp); @@ -2173,12 +2250,18 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } /* Offset vertex. */ + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, brush, orig_data.co, @@ -2187,7 +2270,8 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -2257,11 +2341,17 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, brush, orig_data.co, @@ -2270,7 +2360,8 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float current_disp[3]; float current_disp_norm[3]; float final_disp[3] = {0.0f, 0.0f, 0.0f}; @@ -2414,11 +2505,17 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, brush, orig_data.co, @@ -2427,7 +2524,8 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co); if (vd.mvert) { @@ -2490,11 +2588,17 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -2503,7 +2607,8 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float limit_co[3]; float disp[3]; @@ -2556,11 +2661,17 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -2569,7 +2680,8 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float current_disp[3]; float current_disp_norm[3]; @@ -2717,16 +2829,29 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } - const float fade = - bstrength * - SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.vertex, thread_id) * - ss->cache->pressure; + SCULPT_automasking_node_update(ss, &automask_data, &vd); + + const float fade = bstrength * + SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + *vd.mask, + vd.vertex, + thread_id, + &automask_data) * + ss->cache->pressure; float avg[3], val[3]; @@ -2796,13 +2921,26 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } - const float fade = SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.vertex, thread_id); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + 0.0f, + vd.vertex, + thread_id, + &automask_data); if (bstrength > 0.0f) { (*vd.mask) += fade * bstrength * (1.0f - *vd.mask); diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 691dfa21851..cf7e1d027f7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -7,22 +7,15 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" -#include "BLI_dial_2d.h" #include "BLI_edgehash.h" #include "BLI_gsqueue.h" -#include "BLI_hash.h" #include "BLI_math.h" #include "BLI_task.h" #include "BLI_utildefines.h" -#include "BLT_translation.h" - #include "DNA_brush_types.h" #include "DNA_customdata_types.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -32,41 +25,16 @@ #include "BKE_collision.h" #include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_image.h" -#include "BKE_kelvinlet.h" -#include "BKE_key.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" -#include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" -#include "BKE_mesh_mirror.h" #include "BKE_modifier.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_particle.h" #include "BKE_pbvh.h" -#include "BKE_pointcache.h" -#include "BKE_report.h" -#include "BKE_scene.h" -#include "BKE_screen.h" -#include "BKE_subdiv_ccg.h" -#include "BKE_subsurf.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" -#include "ED_object.h" -#include "ED_screen.h" -#include "ED_sculpt.h" -#include "ED_view3d.h" -#include "paint_intern.h" #include "sculpt_intern.h" #include "RNA_access.h" @@ -78,10 +46,8 @@ #include "GPU_state.h" #include "UI_interface.h" -#include "UI_resources.h" #include "bmesh.h" -#include "bmesh_tools.h" #include <math.h> #include <stdlib.h> @@ -504,7 +470,13 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, madd_v3_v3fl(gravity, ss->cache->gravity_direction, -data->sd->gravity_factor); } + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float force[3]; float sim_location[3]; cloth_brush_simulation_location_get(ss, brush, sim_location); @@ -544,7 +516,8 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float brush_disp[3]; @@ -617,10 +590,11 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, static ListBase *cloth_brush_collider_cache_create(Object *object, Depsgraph *depsgraph) { ListBase *cache = NULL; - DEG_OBJECT_ITER_BEGIN (depsgraph, - ob, - DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE | - DEG_ITER_OBJECT_FLAG_DUPLI) { + DEGObjectIterSettings deg_iter_settings = {0}; + deg_iter_settings.depsgraph = depsgraph; + deg_iter_settings.flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE | + DEG_ITER_OBJECT_FLAG_DUPLI; + DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) { if (STREQ(object->id.name, ob->id.name)) { continue; } @@ -693,15 +667,15 @@ static void cloth_brush_solve_collision(Object *object, BVHTreeRayHit hit; float obmat_inv[4][4]; - invert_m4_m4(obmat_inv, object->obmat); + invert_m4_m4(obmat_inv, object->object_to_world); for (collider_cache = cloth_sim->collider_list->first; collider_cache; collider_cache = collider_cache->next) { float ray_start[3], ray_normal[3]; float pos_world_space[3], prev_pos_world_space[3]; - mul_v3_m4v3(pos_world_space, object->obmat, cloth_sim->pos[i]); - mul_v3_m4v3(prev_pos_world_space, object->obmat, cloth_sim->last_iteration_pos[i]); + mul_v3_m4v3(pos_world_space, object->object_to_world, cloth_sim->pos[i]); + mul_v3_m4v3(prev_pos_world_space, object->object_to_world, cloth_sim->last_iteration_pos[i]); sub_v3_v3v3(ray_normal, pos_world_space, prev_pos_world_space); copy_v3_v3(ray_start, prev_pos_world_space); hit.index = -1; @@ -764,8 +738,13 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( } AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float sim_location[3]; cloth_brush_simulation_location_get(ss, brush, sim_location); const float sim_factor = @@ -787,7 +766,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( mul_v3_fl(pos_diff, (1.0f - cloth_sim->damping) * sim_factor); const float mask_v = (1.0f - (vd.mask ? *vd.mask : 0.0f)) * - SCULPT_automasking_factor_get(automasking, ss, vd.vertex); + SCULPT_automasking_factor_get(automasking, ss, vd.vertex, &automask_data); madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v); madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v); @@ -820,6 +799,9 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, { AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss); + AutomaskingNodeData automask_data = {0}; + + automask_data.have_orig_data = true; for (int constraint_it = 0; constraint_it < CLOTH_SIMULATION_ITERATIONS; constraint_it++) { for (int i = 0; i < cloth_sim->tot_length_constraints; i++) { @@ -858,10 +840,17 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, PBVHVertRef vertex1 = BKE_pbvh_index_to_vertex(ss->pbvh, v1); PBVHVertRef vertex2 = BKE_pbvh_index_to_vertex(ss->pbvh, v2); + automask_data.orig_data.co = cloth_sim->init_pos[v1]; + automask_data.orig_data.no = cloth_sim->init_no[v1]; const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, vertex1)) * - SCULPT_automasking_factor_get(automasking, ss, vertex1); + SCULPT_automasking_factor_get( + automasking, ss, vertex1, &automask_data); + + automask_data.orig_data.co = cloth_sim->init_pos[v2]; + automask_data.orig_data.no = cloth_sim->init_no[v2]; const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, vertex2)) * - SCULPT_automasking_factor_get(automasking, ss, vertex2); + SCULPT_automasking_factor_get( + automasking, ss, vertex2, &automask_data); float sim_location[3]; cloth_brush_simulation_location_get(ss, brush, sim_location); @@ -1064,6 +1053,7 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(Object *ob, cloth_sim->last_iteration_pos = MEM_calloc_arrayN( totverts, sizeof(float[3]), "cloth sim last iteration pos"); cloth_sim->init_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim init pos"); + cloth_sim->init_no = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim init normals"); cloth_sim->length_constraint_tweak = MEM_calloc_arrayN( totverts, sizeof(float), "cloth sim length tweak"); @@ -1139,6 +1129,7 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, vertex)); copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, vertex)); + SCULPT_vertex_normal_get(ss, vertex, cloth_sim->init_no[i]); copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, vertex)); if (has_deformation_pos) { copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, vertex)); @@ -1252,6 +1243,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim) MEM_SAFE_FREE(cloth_sim->deformation_pos); MEM_SAFE_FREE(cloth_sim->softbody_pos); MEM_SAFE_FREE(cloth_sim->init_pos); + MEM_SAFE_FREE(cloth_sim->init_no); MEM_SAFE_FREE(cloth_sim->deformation_strength); MEM_SAFE_FREE(cloth_sim->node_state); BLI_ghash_free(cloth_sim->node_state_index, NULL, NULL); @@ -1427,17 +1419,23 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, float sculpt_gravity[3] = {0.0f}; if (sd->gravity_object) { - copy_v3_v3(sculpt_gravity, sd->gravity_object->obmat[2]); + copy_v3_v3(sculpt_gravity, sd->gravity_object->object_to_world[2]); } else { sculpt_gravity[2] = -1.0f; } mul_v3_fl(sculpt_gravity, sd->gravity_factor * data->filter_strength); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, node); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float fade = vd.mask ? *vd.mask : 0.0f; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); + fade *= SCULPT_automasking_factor_get( + ss->filter_cache->automasking, ss, vd.vertex, &automask_data); fade = 1.0f - fade; float force[3] = {0.0f, 0.0f, 0.0f}; float disp[3], temp[3], transform[3][3]; @@ -1579,8 +1577,11 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent /* Needs mask data to be available as it is used when solving the constraints. */ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + SCULPT_stroke_id_next(ob); + SCULPT_undo_push_begin(ob, op); - SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); + SCULPT_filter_cache_init( + C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius")); ss->filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob); @@ -1643,14 +1644,14 @@ void SCULPT_OT_cloth_filter(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* RNA. */ + SCULPT_mesh_filter_properties(ot); + RNA_def_enum(ot->srna, "type", prop_cloth_filter_type, CLOTH_FILTER_GRAVITY, "Filter Type", "Operation that is going to be applied to the mesh"); - RNA_def_float( - ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); RNA_def_enum_flag(ot->srna, "force_axis", prop_cloth_filter_force_axis_items, diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index 8f87cd1b6ed..0e46fd50f3b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -30,7 +30,6 @@ #include "WM_types.h" #include "ED_screen.h" -#include "ED_sculpt.h" #include "ED_space_api.h" #include "ED_view3d.h" #include "sculpt_intern.h" @@ -103,7 +102,8 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) size = max_fff(dim[0], dim[1], dim[2]); /* Update topology size. */ - float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); + float object_space_constant_detail = 1.0f / + (sd->constant_detail * mat4_to_scale(ob->object_to_world)); BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); SCULPT_undo_push_begin(ob, op); @@ -224,7 +224,7 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2 if (srd.hit && srd.edge_length > 0.0f) { /* Convert edge length to world space detail resolution. */ - sd->constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob->obmat)); + sd->constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob->object_to_world)); } } @@ -474,8 +474,8 @@ static void dyntopo_detail_size_parallel_lines_draw(uint pos3d, bool flip, const float angle) { - float object_space_constant_detail = 1.0f / - (cd->detail_size * mat4_to_scale(cd->active_object->obmat)); + float object_space_constant_detail = 1.0f / (cd->detail_size * + mat4_to_scale(cd->active_object->object_to_world)); /* The constant detail represents the maximum edge length allowed before subdividing it. If the * triangle grid preview is created with this value it will represent an ideal mesh density where @@ -593,7 +593,8 @@ static void dyntopo_detail_size_sample_from_surface(Object *ob, if (num_neighbors > 0) { const float avg_edge_len = len_accum / num_neighbors; /* Use 0.7 as the average of min and max dyntopo edge length. */ - const float detail_size = 0.7f / (avg_edge_len * mat4_to_scale(cd->active_object->obmat)); + const float detail_size = 0.7f / + (avg_edge_len * mat4_to_scale(cd->active_object->object_to_world)); cd->detail_size = clamp_f(detail_size, 1.0f, 500.0f); } } @@ -717,7 +718,7 @@ static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wm float cursor_trans[4][4], cursor_rot[4][4]; const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; float quat[4]; - copy_m4_m4(cursor_trans, active_object->obmat); + copy_m4_m4(cursor_trans, active_object->object_to_world); translate_m4( cursor_trans, ss->cursor_location[0], ss->cursor_location[1], ss->cursor_location[2]); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 46674c5d239..388f4111555 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -7,9 +7,6 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" -#include "BLI_hash.h" -#include "BLI_math.h" #include "BLI_task.h" #include "BLT_translation.h" @@ -18,12 +15,10 @@ #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" -#include "BKE_brush.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -31,26 +26,15 @@ #include "BKE_pbvh.h" #include "BKE_pointcache.h" #include "BKE_scene.h" -#include "BKE_screen.h" #include "DEG_depsgraph.h" #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" -#include "ED_object.h" -#include "ED_screen.h" -#include "ED_sculpt.h" #include "ED_undo.h" -#include "ED_view3d.h" -#include "paint_intern.h" #include "sculpt_intern.h" -#include "RNA_access.h" -#include "RNA_define.h" - #include "UI_interface.h" #include "UI_resources.h" @@ -88,45 +72,6 @@ void SCULPT_pbvh_clear(Object *ob) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } -void SCULPT_dyntopo_node_layers_add(SculptSession *ss) -{ - int cd_node_layer_index; - - char node_vertex_id[] = "_dyntopo_vnode_id"; - char node_face_id[] = "_dyntopo_fnode_id"; - - cd_node_layer_index = CustomData_get_named_layer_index( - &ss->bm->vdata, CD_PROP_INT32, node_vertex_id); - - if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT32, node_vertex_id); - cd_node_layer_index = CustomData_get_named_layer_index( - &ss->bm->vdata, CD_PROP_INT32, node_vertex_id); - } - - ss->cd_vert_node_offset = CustomData_get_n_offset( - &ss->bm->vdata, - CD_PROP_INT32, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT32)); - - ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; - - cd_node_layer_index = CustomData_get_named_layer_index( - &ss->bm->pdata, CD_PROP_INT32, node_face_id); - if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, node_face_id); - cd_node_layer_index = CustomData_get_named_layer_index( - &ss->bm->pdata, CD_PROP_INT32, node_face_id); - } - - ss->cd_face_node_offset = CustomData_get_n_offset( - &ss->bm->pdata, - CD_PROP_INT32, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT32)); - - ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; -} - void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { SculptSession *ss = ob->sculpt; @@ -156,8 +101,9 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene .active_shapekey = ob->shapenr, })); SCULPT_dynamic_topology_triangulate(ss->bm); + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); - SCULPT_dyntopo_node_layers_add(ss); + /* Make sure the data for existing faces are initialized. */ if (me->totpoly != ss->bm->totface) { BM_mesh_normals_update(ss->bm); @@ -185,6 +131,14 @@ static void SCULPT_dynamic_topology_disable_ex( SculptSession *ss = ob->sculpt; Mesh *me = ob->data; + if (ss->attrs.dyntopo_node_id_vertex) { + BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_vertex); + } + + if (ss->attrs.dyntopo_node_id_face) { + BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_face); + } + SCULPT_pbvh_clear(ob); if (unode) { @@ -215,7 +169,7 @@ static void SCULPT_dynamic_topology_disable_ex( BKE_sculptsession_bm_to_me(ob, true); /* Reset Face Sets as they are no longer valid. */ - CustomData_free_layers(&me->pdata, CD_SCULPT_FACE_SETS, me->totpoly); + CustomData_free_layer_named(&me->pdata, ".sculpt_face_set", me->totpoly); me->face_sets_color_default = 1; /* Sync the visibility to vertices manually as the pmap is still not initialized. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 414a855ab2f..3133bb2007e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -7,13 +7,10 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_linklist_stack.h" #include "BLI_math.h" #include "BLI_task.h" -#include "BLT_translation.h" - #include "DNA_brush_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -27,9 +24,6 @@ #include "BKE_image.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_report.h" @@ -39,17 +33,13 @@ #include "DEG_depsgraph.h" #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" #include "RNA_access.h" #include "RNA_define.h" -#include "ED_object.h" #include "ED_screen.h" #include "ED_sculpt.h" -#include "ED_view3d.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -177,15 +167,16 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, if (expand_cache->texture_distortion_strength == 0.0f) { return expand_cache->vert_falloff[v_i]; } - - if (!expand_cache->brush->mtex.tex) { + const Brush *brush = expand_cache->brush; + const MTex *mtex = BKE_brush_mask_texture_get(brush, OB_MODE_SCULPT); + if (!mtex->tex) { return expand_cache->vert_falloff[v_i]; } float rgba[4]; const float *vertex_co = SCULPT_vertex_co_get(ss, v); const float avg = BKE_brush_sample_tex_3d( - expand_cache->scene, expand_cache->brush, vertex_co, rgba, 0, ss->tex_pool); + expand_cache->scene, brush, mtex, vertex_co, rgba, 0, ss->tex_pool); const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength * expand_cache->max_vert_falloff; @@ -617,7 +608,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const P for (int i = 0; i < boundary->verts_num; i++) { BLI_gsqueue_push(queue, &boundary->verts[i]); - BLI_BITMAP_ENABLE(visited_verts, boundary->verts_i[i]); + BLI_BITMAP_ENABLE(visited_verts, BKE_pbvh_vertex_to_index(ss->pbvh, boundary->verts[i])); } SCULPT_boundary_data_free(boundary); } @@ -2102,6 +2093,8 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SCULPT_stroke_id_next(ob); + /* Create and configure the Expand Cache. */ ss->expand_cache = MEM_callocN(sizeof(ExpandCache), "expand cache"); sculpt_expand_cache_initial_config_set(C, op, ss->expand_cache); @@ -2116,6 +2109,11 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even depsgraph = CTX_data_ensure_evaluated_depsgraph(C); } + if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { + MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); + } + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, needs_colors); /* Do nothing when the mesh has 0 vertices. */ @@ -2130,11 +2128,6 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); } - if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { - MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); - BKE_sculpt_mask_layers_ensure(ob, mmd); - } - /* Face Set operations are not supported in dyntopo. */ if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS && BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { @@ -2244,7 +2237,7 @@ void sculpt_expand_modal_keymap(wmKeyConfig *keyconf) static const char *name = "Sculpt Expand Modal"; wmKeyMap *keymap = WM_modalkeymap_find(keyconf, name); - /* This function is called for each spacetype, only needs to add map once. */ + /* This function is called for each space-type, only needs to add map once. */ if (keymap && keymap->modal_items) { return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 8aa645c6af5..40835172be9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -5,12 +5,20 @@ * \ingroup edsculpt */ +#include <cmath> +#include <cstdlib> +#include <queue> + #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" +#include "BLI_bit_vector.hh" +#include "BLI_function_ref.hh" #include "BLI_hash.h" #include "BLI_math.h" +#include "BLI_math_vector.hh" +#include "BLI_span.hh" #include "BLI_task.h" +#include "BLI_task.hh" #include "DNA_brush_types.h" #include "DNA_customdata_types.h" @@ -19,7 +27,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "BKE_brush.h" +#include "BKE_attribute.hh" #include "BKE_ccg.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -27,25 +35,17 @@ #include "BKE_mesh.h" #include "BKE_mesh_fair.h" #include "BKE_mesh_mapping.h" -#include "BKE_multires.h" -#include "BKE_node.h" #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_scene.h" #include "DEG_depsgraph.h" #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" -#include "ED_object.h" -#include "ED_screen.h" #include "ED_sculpt.h" -#include "ED_view3d.h" -#include "paint_intern.h" + #include "sculpt_intern.h" #include "RNA_access.h" @@ -53,21 +53,19 @@ #include "bmesh.h" -#include <math.h> -#include <stdlib.h> - /* Utils. */ int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh) { - const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = static_cast<const int *>( + CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, ".sculpt_face_set")); if (!face_sets) { return SCULPT_FACE_SET_NONE; } int next_face_set_id = 0; for (int i = 0; i < mesh->totpoly; i++) { - next_face_set_id = max_ii(next_face_set_id, abs(face_sets[i])); + next_face_set_id = max_ii(next_face_set_id, face_sets[i]); } next_face_set_id++; @@ -76,7 +74,8 @@ int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh) void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, const int new_id) { - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + int *face_sets = static_cast<int *>( + CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, ".sculpt_face_set")); if (!face_sets) { return; } @@ -109,7 +108,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; + SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = data->ob->sculpt; const Brush *brush = data->brush; const float bstrength = ss->cache->bstrength; @@ -122,8 +121,13 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { MeshElemMap *vert_map = &ss->pmap[vd.index]; for (int j = 0; j < ss->pmap[vd.index].count; j++) { @@ -135,6 +139,10 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, if (!sculpt_brush_test_sq_fn(&test, poly_center)) { continue; } + const bool face_hidden = ss->hide_poly && ss->hide_poly[vert_map->indices[j]]; + if (face_hidden) { + continue; + } const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -143,10 +151,11 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); - if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) { - ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set); + if (fade > 0.05f) { + ss->face_sets[vert_map->indices[j]] = ss->cache->paint_face_set; } } } @@ -162,7 +171,8 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); if (fade > 0.05f) { SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set); @@ -176,7 +186,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; + SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = data->ob->sculpt; const Brush *brush = data->brush; float bstrength = ss->cache->bstrength; @@ -189,13 +199,18 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0); /* This operations needs a strength tweak as the relax deformation is too weak by default. */ - if (relax_face_sets) { - bstrength *= 2.0f; + if (relax_face_sets && data->iteration < 2) { + bstrength *= 1.5f; } const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -211,7 +226,8 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co); if (vd.mvert) { @@ -226,21 +242,27 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + if (ss->pbvh) { + Mesh *mesh = BKE_mesh_from_object(ob); + BKE_pbvh_face_sets_color_set( + ss->pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default); + } + BKE_curvemapping_init(brush->curve); /* Threaded loop over nodes. */ - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; + SculptThreadedTaskData data{}; + data.sd = sd; + data.ob = ob; + data.brush = brush; + data.nodes = nodes; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); if (ss->cache->alt_smooth) { SCULPT_boundary_info_ensure(ob); for (int i = 0; i < 4; i++) { + data.iteration = i; BLI_task_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings); } } @@ -251,12 +273,12 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in /* Face Sets Operators */ -typedef enum eSculptFaceGroupsCreateModes { +enum eSculptFaceGroupsCreateModes { SCULPT_FACE_SET_MASKED = 0, SCULPT_FACE_SET_VISIBLE = 1, SCULPT_FACE_SET_ALL = 2, SCULPT_FACE_SET_SELECTION = 3, -} eSculptFaceGroupsCreateModes; +}; static EnumPropertyItem prop_sculpt_face_set_create_types[] = { { @@ -287,11 +309,12 @@ static EnumPropertyItem prop_sculpt_face_set_create_types[] = { "Face Set from Edit Mode Selection", "Create an Face Set corresponding to the Edit Mode face selection", }, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) { + using namespace blender; Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); @@ -303,7 +326,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - Mesh *mesh = ob->data; + Mesh *mesh = static_cast<Mesh *>(ob->data); ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false); @@ -314,7 +337,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); if (!nodes) { return OPERATOR_CANCELLED; @@ -375,28 +398,16 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } if (mode == SCULPT_FACE_SET_SELECTION) { - BMesh *bm; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - - BM_mesh_bm_from_me(bm, - mesh, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - .calc_vert_normal = true, - })); - - BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - ss->face_sets[BM_elem_index_get(f)] = next_face_set; + const bke::AttributeAccessor attributes = mesh->attributes(); + const VArraySpan<bool> select_poly = attributes.lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); + threading::parallel_for(IndexRange(mesh->totvert), 4096, [&](const IndexRange range) { + for (const int i : range) { + if (select_poly[i]) { + ss->face_sets[i] = next_face_set; + } } - } - BM_mesh_free(bm); + }); } for (int i = 0; i < totnode; i++) { @@ -429,7 +440,7 @@ void SCULPT_OT_face_sets_create(wmOperatorType *ot) ot->srna, "mode", prop_sculpt_face_set_create_types, SCULPT_FACE_SET_MASKED, "Mode", ""); } -typedef enum eSculptFaceSetsInitMode { +enum eSculptFaceSetsInitMode { SCULPT_FACE_SETS_FROM_LOOSE_PARTS = 0, SCULPT_FACE_SETS_FROM_MATERIALS = 1, SCULPT_FACE_SETS_FROM_NORMALS = 2, @@ -439,7 +450,7 @@ typedef enum eSculptFaceSetsInitMode { SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT = 6, SCULPT_FACE_SETS_FROM_FACE_MAPS = 7, SCULPT_FACE_SETS_FROM_FACE_SET_BOUNDARIES = 8, -} eSculptFaceSetsInitMode; +}; static EnumPropertyItem prop_sculpt_face_sets_init_types[] = { { @@ -506,203 +517,120 @@ static EnumPropertyItem prop_sculpt_face_sets_init_types[] = { "Create a Face Set per isolated Face Set", }, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; -typedef bool (*face_sets_flood_fill_test)( - BMesh *bm, BMFace *from_f, BMEdge *from_e, BMFace *to_f, const float threshold); - -static bool sculpt_face_sets_init_loose_parts_test(BMesh *UNUSED(bm), - BMFace *UNUSED(from_f), - BMEdge *UNUSED(from_e), - BMFace *UNUSED(to_f), - const float UNUSED(threshold)) -{ - return true; -} - -static bool sculpt_face_sets_init_normals_test( - BMesh *UNUSED(bm), BMFace *from_f, BMEdge *UNUSED(from_e), BMFace *to_f, const float threshold) -{ - return fabsf(dot_v3v3(from_f->no, to_f->no)) > threshold; -} - -static bool sculpt_face_sets_init_uv_seams_test(BMesh *UNUSED(bm), - BMFace *UNUSED(from_f), - BMEdge *from_e, - BMFace *UNUSED(to_f), - const float UNUSED(threshold)) -{ - return !BM_elem_flag_test(from_e, BM_ELEM_SEAM); -} - -static bool sculpt_face_sets_init_crease_test( - BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold) -{ - return BM_elem_float_data_get(&bm->edata, from_e, CD_CREASE) < threshold; -} - -static bool sculpt_face_sets_init_bevel_weight_test( - BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold) -{ - return BM_elem_float_data_get(&bm->edata, from_e, CD_BWEIGHT) < threshold; -} - -static bool sculpt_face_sets_init_sharp_edges_test(BMesh *UNUSED(bm), - BMFace *UNUSED(from_f), - BMEdge *from_e, - BMFace *UNUSED(to_f), - const float UNUSED(threshold)) -{ - return BM_elem_flag_test(from_e, BM_ELEM_SMOOTH); -} +using FaceSetsFloodFillFn = blender::FunctionRef<bool(int from_face, int edge, int to_face)>; -static bool sculpt_face_sets_init_face_set_boundary_test( - BMesh *bm, BMFace *from_f, BMEdge *UNUSED(from_e), BMFace *to_f, const float UNUSED(threshold)) -{ - const int cd_face_sets_offset = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS); - return BM_ELEM_CD_GET_INT(from_f, cd_face_sets_offset) == - BM_ELEM_CD_GET_INT(to_f, cd_face_sets_offset); -} - -static void sculpt_face_sets_init_flood_fill(Object *ob, - face_sets_flood_fill_test test, - const float threshold) +static void sculpt_face_sets_init_flood_fill(Object *ob, const FaceSetsFloodFillFn &test_fn) { + using namespace blender; SculptSession *ss = ob->sculpt; - Mesh *mesh = ob->data; - BMesh *bm; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); + Mesh *mesh = static_cast<Mesh *>(ob->data); - BM_mesh_bm_from_me(bm, - mesh, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - .calc_vert_normal = true, - })); - - BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces"); - const int totfaces = mesh->totpoly; + BitVector<> visited_faces(mesh->totpoly, false); int *face_sets = ss->face_sets; - BM_mesh_elem_table_init(bm, BM_FACE); - BM_mesh_elem_table_ensure(bm, BM_FACE); + const Span<MEdge> edges = mesh->edges(); + const Span<MPoly> polys = mesh->polys(); + const Span<MLoop> loops = mesh->loops(); + + if (!ss->epmap) { + BKE_mesh_edge_poly_map_create(&ss->epmap, + &ss->epmap_mem, + edges.data(), + edges.size(), + polys.data(), + polys.size(), + loops.data(), + loops.size()); + } int next_face_set = 1; - for (int i = 0; i < totfaces; i++) { - if (BLI_BITMAP_TEST(visited_faces, i)) { + for (const int i : polys.index_range()) { + if (visited_faces[i]) { continue; } - GSQueue *queue; - queue = BLI_gsqueue_new(sizeof(int)); + std::queue<int> queue; face_sets[i] = next_face_set; - BLI_BITMAP_ENABLE(visited_faces, i); - BLI_gsqueue_push(queue, &i); - - while (!BLI_gsqueue_is_empty(queue)) { - int from_f; - BLI_gsqueue_pop(queue, &from_f); - - BMFace *f, *f_neighbor; - BMEdge *ed; - BMIter iter_a, iter_b; - - f = BM_face_at_index(bm, from_f); - - BM_ITER_ELEM (ed, &iter_a, f, BM_EDGES_OF_FACE) { - BM_ITER_ELEM (f_neighbor, &iter_b, ed, BM_FACES_OF_EDGE) { - if (f_neighbor == f) { + visited_faces[i].set(true); + queue.push(i); + + while (!queue.empty()) { + const int poly_i = queue.front(); + const MPoly &poly = polys[poly_i]; + queue.pop(); + + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + const int edge_i = loop.e; + const Span<int> neighbor_polys(ss->epmap[edge_i].indices, ss->epmap[edge_i].count); + for (const int neighbor_i : neighbor_polys) { + if (neighbor_i == poly_i) { continue; } - int neighbor_face_index = BM_elem_index_get(f_neighbor); - if (BLI_BITMAP_TEST(visited_faces, neighbor_face_index)) { + if (visited_faces[neighbor_i]) { continue; } - if (!test(bm, f, ed, f_neighbor, threshold)) { + if (!test_fn(poly_i, edge_i, neighbor_i)) { continue; } - face_sets[neighbor_face_index] = next_face_set; - BLI_BITMAP_ENABLE(visited_faces, neighbor_face_index); - BLI_gsqueue_push(queue, &neighbor_face_index); + face_sets[neighbor_i] = next_face_set; + visited_faces[neighbor_i].set(true); + queue.push(neighbor_i); } } } next_face_set += 1; - - BLI_gsqueue_free(queue); } - - MEM_SAFE_FREE(visited_faces); - - BM_mesh_free(bm); } static void sculpt_face_sets_init_loop(Object *ob, const int mode) { - Mesh *mesh = ob->data; + using namespace blender; + Mesh *mesh = static_cast<Mesh *>(ob->data); SculptSession *ss = ob->sculpt; - BMesh *bm; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - - BM_mesh_bm_from_me(bm, - mesh, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - .calc_vert_normal = true, - })); - BMIter iter; - BMFace *f; - - const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP); - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) { - ss->face_sets[BM_elem_index_get(f)] = (int)(f->mat_nr + 1); + if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) { + const bke::AttributeAccessor attributes = mesh->attributes(); + const VArraySpan<int> material_indices = attributes.lookup_or_default<int>( + "material_index", ATTR_DOMAIN_FACE, 0); + for (const int i : IndexRange(mesh->totpoly)) { + ss->face_sets[i] = material_indices[i] + 1; } - else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) { - if (cd_fmaps_offset != -1) { - ss->face_sets[BM_elem_index_get(f)] = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2; - } - else { - ss->face_sets[BM_elem_index_get(f)] = 1; - } + } + else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) { + const int *face_maps = static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_FACEMAP)); + for (const int i : IndexRange(mesh->totpoly)) { + ss->face_sets[i] = face_maps ? face_maps[i] : 1; } } - BM_mesh_free(bm); } static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) { + using namespace blender; Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); const int mode = RNA_enum_get(op->ptr, "mode"); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + /* Dyntopo not supported. */ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { return OPERATOR_CANCELLED; } - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); - PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); if (!nodes) { return OPERATOR_CANCELLED; @@ -713,44 +641,85 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) const float threshold = RNA_float_get(op->ptr, "threshold"); - Mesh *mesh = ob->data; + Mesh *mesh = static_cast<Mesh *>(ob->data); ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + const bke::AttributeAccessor attributes = mesh->attributes(); switch (mode) { - case SCULPT_FACE_SETS_FROM_LOOSE_PARTS: - sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_loose_parts_test, threshold); + case SCULPT_FACE_SETS_FROM_LOOSE_PARTS: { + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + sculpt_face_sets_init_flood_fill( + ob, [&](const int from_face, const int /*edge*/, const int to_face) { + return hide_poly[from_face] == hide_poly[to_face]; + }); break; - case SCULPT_FACE_SETS_FROM_MATERIALS: + } + case SCULPT_FACE_SETS_FROM_MATERIALS: { sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_MATERIALS); break; - case SCULPT_FACE_SETS_FROM_NORMALS: - sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_normals_test, threshold); + } + case SCULPT_FACE_SETS_FROM_NORMALS: { + const Span<float3> poly_normals( + reinterpret_cast<const float3 *>(BKE_mesh_poly_normals_ensure(mesh)), mesh->totpoly); + sculpt_face_sets_init_flood_fill( + ob, [&](const int from_face, const int /*edge*/, const int to_face) -> bool { + return std::abs(math::dot(poly_normals[from_face], poly_normals[to_face])) > threshold; + }); break; - case SCULPT_FACE_SETS_FROM_UV_SEAMS: - sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_uv_seams_test, threshold); + } + case SCULPT_FACE_SETS_FROM_UV_SEAMS: { + const Span<MEdge> edges = mesh->edges(); + sculpt_face_sets_init_flood_fill( + ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool { + return (edges[edge].flag & ME_SEAM) == 0; + }); break; - case SCULPT_FACE_SETS_FROM_CREASES: - sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_crease_test, threshold); + } + case SCULPT_FACE_SETS_FROM_CREASES: { + const float *creases = static_cast<const float *>( + CustomData_get_layer(&mesh->edata, CD_CREASE)); + sculpt_face_sets_init_flood_fill( + ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool { + return creases[edge] < threshold; + }); break; - case SCULPT_FACE_SETS_FROM_SHARP_EDGES: - sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_sharp_edges_test, threshold); + } + case SCULPT_FACE_SETS_FROM_SHARP_EDGES: { + const Span<MEdge> edges = mesh->edges(); + sculpt_face_sets_init_flood_fill( + ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool { + return (edges[edge].flag & ME_SHARP) == 0; + }); break; - case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT: - sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_bevel_weight_test, threshold); + } + case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT: { + const float *bevel_weights = static_cast<const float *>( + CustomData_get_layer(&mesh->edata, CD_BWEIGHT)); + sculpt_face_sets_init_flood_fill( + ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool { + return bevel_weights ? bevel_weights[edge] / 255.0f < threshold : true; + }); break; - case SCULPT_FACE_SETS_FROM_FACE_SET_BOUNDARIES: + } + case SCULPT_FACE_SETS_FROM_FACE_SET_BOUNDARIES: { + Array<int> face_sets_copy(Span<int>(ss->face_sets, mesh->totpoly)); sculpt_face_sets_init_flood_fill( - ob, sculpt_face_sets_init_face_set_boundary_test, threshold); + ob, [&](const int from_face, const int /*edge*/, const int to_face) -> bool { + return face_sets_copy[from_face] == face_sets_copy[to_face]; + }); break; - case SCULPT_FACE_SETS_FROM_FACE_MAPS: + } + case SCULPT_FACE_SETS_FROM_FACE_MAPS: { sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_FACE_MAPS); break; + } } SCULPT_undo_push_end(ob); /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ - SCULPT_visibility_sync_all_face_sets_to_verts(ob); + SCULPT_visibility_sync_all_from_faces(ob); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); @@ -761,7 +730,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); if (BKE_pbvh_type(pbvh) == PBVH_FACES) { - BKE_mesh_flush_hidden_from_verts(ob->data); + BKE_mesh_flush_hidden_from_verts(mesh); } SCULPT_tag_update_overlays(C); @@ -796,13 +765,12 @@ void SCULPT_OT_face_sets_init(wmOperatorType *ot) 1.0f); } -typedef enum eSculptFaceGroupVisibilityModes { +enum eSculptFaceGroupVisibilityModes { SCULPT_FACE_SET_VISIBILITY_TOGGLE = 0, SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE = 1, SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE = 2, SCULPT_FACE_SET_VISIBILITY_INVERT = 3, - SCULPT_FACE_SET_VISIBILITY_SHOW_ALL = 4, -} eSculptFaceGroupVisibilityModes; +}; static EnumPropertyItem prop_sculpt_face_sets_change_visibility_types[] = { { @@ -833,14 +801,7 @@ static EnumPropertyItem prop_sculpt_face_sets_change_visibility_types[] = { "Invert Face Set Visibility", "Invert Face Set Visibility", }, - { - SCULPT_FACE_SET_VISIBILITY_SHOW_ALL, - "SHOW_ALL", - 0, - "Show All Face Sets", - "Show All Face Sets", - }, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) @@ -849,102 +810,123 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - /* Dyntopo not supported. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } + Mesh *mesh = BKE_object_get_original_mesh(ob); - if (!pbvh_has_face_sets(ss->pbvh)) { + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + + /* Not supported for dyntopo. */ + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { return OPERATOR_CANCELLED; } - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); - - const int tot_vert = SCULPT_vertex_count_get(ss); const int mode = RNA_enum_get(op->ptr, "mode"); - const int active_face_set = SCULPT_active_face_set_get(ss); - - SCULPT_undo_push_begin(ob, op); + const int tot_vert = SCULPT_vertex_count_get(ss); PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); if (totnode == 0) { MEM_SAFE_FREE(nodes); return OPERATOR_CANCELLED; } - SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); - - if (mode == SCULPT_FACE_SET_VISIBILITY_TOGGLE) { - bool hidden_vertex = false; + const int active_face_set = SCULPT_active_face_set_get(ss); - /* This can fail with regular meshes with non-manifold geometry as the visibility state can't - * be synced from face sets to non-manifold vertices. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - for (int i = 0; i < tot_vert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + SCULPT_undo_push_begin(ob, op); + for (int i = 0; i < totnode; i++) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_HIDDEN); + } - if (!SCULPT_vertex_visible_get(ss, vertex)) { - hidden_vertex = true; - break; + switch (mode) { + case SCULPT_FACE_SET_VISIBILITY_TOGGLE: { + bool hidden_vertex = false; + + /* This can fail with regular meshes with non-manifold geometry as the visibility state can't + * be synced from face sets to non-manifold vertices. */ + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + for (int i = 0; i < tot_vert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_visible_get(ss, vertex)) { + hidden_vertex = true; + break; + } } } - } - for (int i = 0; i < ss->totfaces; i++) { - if (ss->face_sets[i] <= 0) { - hidden_vertex = true; - break; + if (ss->hide_poly) { + for (int i = 0; i < ss->totfaces; i++) { + if (ss->hide_poly[i]) { + hidden_vertex = true; + break; + } + } } - } - if (hidden_vertex) { - SCULPT_face_sets_visibility_all_set(ss, true); - } - else { - SCULPT_face_sets_visibility_all_set(ss, false); - SCULPT_face_set_visibility_set(ss, active_face_set, true); - } - } + ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); - if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ALL) { - SCULPT_face_sets_visibility_all_set(ss, true); - } + if (hidden_vertex) { + SCULPT_face_visibility_all_set(ss, true); + } + else { + if (ss->face_sets) { + SCULPT_face_visibility_all_set(ss, false); + SCULPT_face_set_visibility_set(ss, active_face_set, true); + } + else { + SCULPT_face_visibility_all_set(ss, true); + } + } + break; + } + case SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE: + ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); - if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE) { - SCULPT_face_sets_visibility_all_set(ss, false); - SCULPT_face_set_visibility_set(ss, active_face_set, true); - } + if (ss->face_sets) { + SCULPT_face_visibility_all_set(ss, false); + SCULPT_face_set_visibility_set(ss, active_face_set, true); + } + else { + SCULPT_face_set_visibility_set(ss, active_face_set, true); + } + break; + case SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE: + ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); - if (mode == SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE) { - SCULPT_face_set_visibility_set(ss, active_face_set, false); - } + if (ss->face_sets) { + SCULPT_face_set_visibility_set(ss, active_face_set, false); + } + else { + SCULPT_face_visibility_all_set(ss, false); + } - if (mode == SCULPT_FACE_SET_VISIBILITY_INVERT) { - SCULPT_face_sets_visibility_invert(ss); + break; + case SCULPT_FACE_SET_VISIBILITY_INVERT: + ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); + SCULPT_face_visibility_all_invert(ss); + break; } /* For modes that use the cursor active vertex, update the rotation origin for viewport - * navigation. */ + * navigation. + */ if (ELEM(mode, SCULPT_FACE_SET_VISIBILITY_TOGGLE, SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE)) { UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; float location[3]; copy_v3_v3(location, SCULPT_active_vertex_co_get(ss)); - mul_m4_v3(ob->obmat, location); + mul_m4_v3(ob->object_to_world, location); copy_v3_v3(ups->average_stroke_accum, location); ups->average_stroke_counter = 1; ups->last_stroke_valid = true; } /* Sync face sets visibility and vertex visibility. */ - SCULPT_visibility_sync_all_face_sets_to_verts(ob); + SCULPT_visibility_sync_all_from_faces(ob); SCULPT_undo_push_end(ob); - for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); } @@ -968,7 +950,7 @@ static int sculpt_face_sets_change_visibility_invoke(bContext *C, /* Update the active vertex and Face Set using the cursor position to avoid relying on the paint * cursor updates. */ SculptCursorGeometryInfo sgi; - const float mval_fl[2] = {UNPACK2(event->mval)}; + const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; SCULPT_vertex_random_access_ensure(ss); SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); @@ -997,7 +979,7 @@ void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot) ""); } -static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSED(op)) +static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator * /*op*/) { Object *ob = CTX_data_active_object(C); @@ -1008,14 +990,14 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSE return OPERATOR_CANCELLED; } - if (!pbvh_has_face_sets(ss->pbvh)) { + if (!ss->face_sets) { return OPERATOR_CANCELLED; } PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - Mesh *mesh = ob->data; + Mesh *mesh = static_cast<Mesh *>(ob->data); mesh->face_sets_color_seed += 1; if (ss->face_sets) { @@ -1026,7 +1008,7 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSE } BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default); - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_redraw(nodes[i]); } @@ -1052,13 +1034,13 @@ void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -typedef enum eSculptFaceSetEditMode { +enum eSculptFaceSetEditMode { SCULPT_FACE_SET_EDIT_GROW = 0, SCULPT_FACE_SET_EDIT_SHRINK = 1, SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY = 2, SCULPT_FACE_SET_EDIT_FAIR_POSITIONS = 3, SCULPT_FACE_SET_EDIT_FAIR_TANGENCY = 4, -} eSculptFaceSetEditMode; +}; static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { { @@ -1098,7 +1080,7 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { "Creates a smooth as possible geometry patch from the Face Set minimizing changes in " "vertex tangents", }, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static void sculpt_face_set_grow(Object *ob, @@ -1164,22 +1146,25 @@ static void sculpt_face_set_shrink(Object *ob, } } -static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool check_visible_only) +static bool check_single_face_set(SculptSession *ss, + const int *face_sets, + const bool check_visible_only) { - if (face_sets == NULL) { + if (face_sets == nullptr) { return true; } int first_face_set = SCULPT_FACE_SET_NONE; if (check_visible_only) { for (int f = 0; f < ss->totfaces; f++) { - if (face_sets[f] > 0) { - first_face_set = face_sets[f]; - break; + if (ss->hide_poly && ss->hide_poly[f]) { + continue; } + first_face_set = face_sets[f]; + break; } } else { - first_face_set = abs(face_sets[0]); + first_face_set = face_sets[0]; } if (first_face_set == SCULPT_FACE_SET_NONE) { @@ -1187,8 +1172,10 @@ static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool } for (int f = 0; f < ss->totfaces; f++) { - const int face_set_id = check_visible_only ? face_sets[f] : abs(face_sets[f]); - if (face_set_id != first_face_set) { + if (check_visible_only && ss->hide_poly && ss->hide_poly[f]) { + continue; + } + if (face_sets[f] != first_face_set) { return false; } } @@ -1201,19 +1188,16 @@ static void sculpt_face_set_delete_geometry(Object *ob, const bool modify_hidden) { - Mesh *mesh = ob->data; + Mesh *mesh = static_cast<Mesh *>(ob->data); const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - BMesh *bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - - BM_mesh_bm_from_me(bm, - mesh, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - .calc_vert_normal = true, - })); + BMeshCreateParams create_params{}; + create_params.use_toolflags = true; + BMesh *bm = BM_mesh_create(&allocsize, &create_params); + + BMeshFromMeshParams convert_params{}; + convert_params.calc_vert_normal = true; + convert_params.calc_face_normal = true; + BM_mesh_bm_from_me(bm, mesh, &convert_params); BM_mesh_elem_table_init(bm, BM_FACE); BM_mesh_elem_table_ensure(bm, BM_FACE); @@ -1222,32 +1206,31 @@ static void sculpt_face_set_delete_geometry(Object *ob, BMFace *f; BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { const int face_index = BM_elem_index_get(f); - const int face_set_id = modify_hidden ? abs(ss->face_sets[face_index]) : - ss->face_sets[face_index]; - BM_elem_flag_set(f, BM_ELEM_TAG, face_set_id == active_face_set_id); + if (!modify_hidden && ss->hide_poly && ss->hide_poly[face_index]) { + continue; + } + BM_elem_flag_set(f, BM_ELEM_TAG, ss->face_sets[face_index] == active_face_set_id); } BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); - BM_mesh_bm_to_me(NULL, - bm, - ob->data, - (&(struct BMeshToMeshParams){ - .calc_object_remap = false, - })); + BMeshToMeshParams bmesh_to_mesh_params{}; + bmesh_to_mesh_params.calc_object_remap = false; + BM_mesh_bm_to_me(nullptr, bm, mesh, &bmesh_to_mesh_params); BM_mesh_free(bm); } static void sculpt_face_set_edit_fair_face_set(Object *ob, const int active_face_set_id, - const int fair_order) + const eMeshFairingDepth fair_order) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - Mesh *mesh = ob->data; - bool *fair_verts = MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices"); + Mesh *mesh = static_cast<Mesh *>(ob->data); + bool *fair_verts = static_cast<bool *>( + MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices")); SCULPT_boundary_info_ensure(ob); @@ -1273,13 +1256,13 @@ static void sculpt_face_set_apply_edit(Object *ob, switch (mode) { case SCULPT_FACE_SET_EDIT_GROW: { - int *prev_face_sets = MEM_dupallocN(ss->face_sets); + int *prev_face_sets = static_cast<int *>(MEM_dupallocN(ss->face_sets)); sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); MEM_SAFE_FREE(prev_face_sets); break; } case SCULPT_FACE_SET_EDIT_SHRINK: { - int *prev_face_sets = MEM_dupallocN(ss->face_sets); + int *prev_face_sets = static_cast<int *>(MEM_dupallocN(ss->face_sets)); sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); MEM_SAFE_FREE(prev_face_sets); break; @@ -1339,21 +1322,23 @@ static void sculpt_face_set_edit_modify_geometry(bContext *C, const bool modify_hidden, wmOperator *op) { + Mesh *mesh = static_cast<Mesh *>(ob->data); ED_sculpt_undo_geometry_begin(ob, op); sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden); ED_sculpt_undo_geometry_end(ob); - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh); } static void face_set_edit_do_post_visibility_updates(Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; PBVH *pbvh = ss->pbvh; + Mesh *mesh = static_cast<Mesh *>(ob->data); /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ - SCULPT_visibility_sync_all_face_sets_to_verts(ob); + SCULPT_visibility_sync_all_from_faces(ob); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); @@ -1362,7 +1347,7 @@ static void face_set_edit_do_post_visibility_updates(Object *ob, PBVHNode **node BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); if (BKE_pbvh_type(pbvh) == PBVH_FACES) { - BKE_mesh_flush_hidden_from_verts(ob->data); + BKE_mesh_flush_hidden_from_verts(mesh); } } @@ -1375,7 +1360,7 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob, PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); if (!nodes) { return; @@ -1399,7 +1384,7 @@ static void sculpt_face_set_edit_modify_coordinates(bContext *C, PBVH *pbvh = ss->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); SCULPT_undo_push_begin(ob, op); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update(nodes[i]); @@ -1422,19 +1407,21 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - const int mode = RNA_enum_get(op->ptr, "mode"); + const eSculptFaceSetEditMode mode = static_cast<eSculptFaceSetEditMode>( + RNA_enum_get(op->ptr, "mode")); const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden"); if (!sculpt_face_set_edit_is_operation_valid(ss, mode, modify_hidden)) { return OPERATOR_CANCELLED; } + ss->face_sets = BKE_sculpt_face_sets_ensure(BKE_mesh_from_object(ob)); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); /* Update the current active Face Set and Vertex as the operator can be used directly from the * tool without brush cursor. */ SculptCursorGeometryInfo sgi; - const float mval_fl[2] = {UNPACK2(event->mval)}; + const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) { /* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */ return OPERATOR_CANCELLED; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 161fc563950..89cb67a875f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -7,47 +7,29 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" -#include "BLI_hash.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" #include "BLI_task.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "BKE_brush.h" -#include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_report.h" -#include "BKE_scene.h" #include "IMB_colormanagement.h" #include "DEG_depsgraph.h" #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" -#include "ED_object.h" #include "ED_paint.h" -#include "ED_screen.h" -#include "ED_sculpt.h" -#include "paint_intern.h" #include "sculpt_intern.h" #include "RNA_access.h" #include "RNA_define.h" -#include "UI_interface.h" - #include "bmesh.h" #include <math.h> @@ -95,16 +77,23 @@ static void color_filter_task_cb(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[n]); + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float orig_color[3], final_color[4], hsv_color[3]; int hue; float brightness, contrast, gain, delta, offset; float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); + fade *= SCULPT_automasking_factor_get( + ss->filter_cache->automasking, ss, vd.vertex, &automask_data); if (fade == 0.0f) { continue; } @@ -334,6 +323,9 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL); if (use_automasking) { + /* Increment stroke id for automasking system. */ + SCULPT_stroke_id_next(ob); + /* Update the active face set manually as the paint cursor is not enabled when using the Mesh * Filter Tool. */ float mval_fl[2] = {UNPACK2(event->mval)}; @@ -358,7 +350,8 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COLOR); + SCULPT_filter_cache_init( + C, ob, sd, SCULPT_UNDO_COLOR, event->mval, RNA_float_get(op->ptr, "area_normal_radius")); FilterCache *filter_cache = ss->filter_cache; filter_cache->active_face_set = SCULPT_FACE_SET_NONE; filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob); @@ -383,9 +376,9 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* rna */ + SCULPT_mesh_filter_properties(ot); + RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter Type", ""); - RNA_def_float( - ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); PropertyRNA *prop = RNA_def_float_color( ot->srna, "fill_color", 3, NULL, 0.0f, FLT_MAX, "Fill Color", "", 0.0f, 1.0f); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index bb27e4f1e9e..8e199a72858 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -7,20 +7,13 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" -#include "BLI_hash.h" #include "BLI_math.h" #include "BLI_task.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" -#include "BKE_brush.h" #include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_scene.h" @@ -28,21 +21,13 @@ #include "DEG_depsgraph.h" #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" -#include "ED_object.h" -#include "ED_screen.h" -#include "ED_sculpt.h" -#include "paint_intern.h" #include "sculpt_intern.h" #include "RNA_access.h" #include "RNA_define.h" -#include "UI_interface.h" - #include "bmesh.h" #include <math.h> @@ -182,7 +167,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) int filter_type = RNA_enum_get(op->ptr, "filter_type"); MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - BKE_sculpt_mask_layers_ensure(ob, mmd); + BKE_sculpt_mask_layers_ensure(CTX_data_depsgraph_pointer(C), CTX_data_main(C), ob, mmd); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); @@ -303,173 +288,3 @@ void SCULPT_OT_mask_filter(struct wmOperatorType *ot) "Auto Iteration Count", "Use a automatic number of iterations based on the number of vertices of the sculpt"); } - -static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd) -{ - int total = 0; - float avg[3]; - zero_v3(avg); - - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) { - float normalized[3]; - sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.vertex), vd->co); - normalize_v3(normalized); - add_v3_v3(avg, normalized); - total++; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - if (total > 0) { - mul_v3_fl(avg, 1.0f / total); - float dot = dot_v3v3(avg, vd->no ? vd->no : vd->fno); - float angle = max_ff(saacosf(dot), 0.0f); - return angle; - } - return 0.0f; -} - -typedef struct DirtyMaskRangeData { - float min, max; -} DirtyMaskRangeData; - -static void dirty_mask_compute_range_task_cb(void *__restrict userdata, - const int i, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - PBVHNode *node = data->nodes[i]; - DirtyMaskRangeData *range = tls->userdata_chunk; - PBVHVertexIter vd; - - BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - float dirty_mask = neighbor_dirty_mask(ss, &vd); - range->min = min_ff(dirty_mask, range->min); - range->max = max_ff(dirty_mask, range->max); - } - BKE_pbvh_vertex_iter_end; -} - -static void dirty_mask_compute_range_reduce(const void *__restrict UNUSED(userdata), - void *__restrict chunk_join, - void *__restrict chunk) -{ - DirtyMaskRangeData *join = chunk_join; - DirtyMaskRangeData *range = chunk; - join->min = min_ff(range->min, join->min); - join->max = max_ff(range->max, join->max); -} - -static void dirty_mask_apply_task_cb(void *__restrict userdata, - const int i, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - PBVHNode *node = data->nodes[i]; - PBVHVertexIter vd; - - const bool dirty_only = data->dirty_mask_dirty_only; - const float min = data->dirty_mask_min; - const float max = data->dirty_mask_max; - - float range = max - min; - if (range < 0.0001f) { - range = 0.0f; - } - else { - range = 1.0f / range; - } - - BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - float dirty_mask = neighbor_dirty_mask(ss, &vd); - float mask = *vd.mask + (1.0f - ((dirty_mask - min) * range)); - if (dirty_only) { - mask = fminf(mask, 0.5f) * 2.0f; - } - *vd.mask = CLAMPIS(mask, 0.0f, 1.0f); - } - BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_mark_update_mask(node); -} - -static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) -{ - ARegion *region = CTX_wm_region(C); - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - PBVH *pbvh = ob->sculpt->pbvh; - PBVHNode **nodes; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int totnode; - - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); - - SCULPT_vertex_random_access_ensure(ss); - - if (!ob->sculpt->pmap) { - return OPERATOR_CANCELLED; - } - - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); - SCULPT_undo_push_begin(ob, op); - - for (int i = 0; i < totnode; i++) { - SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); - } - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .nodes = nodes, - .dirty_mask_dirty_only = RNA_boolean_get(op->ptr, "dirty_only"), - }; - DirtyMaskRangeData range = { - .min = FLT_MAX, - .max = -FLT_MAX, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - - settings.func_reduce = dirty_mask_compute_range_reduce; - settings.userdata_chunk = ⦥ - settings.userdata_chunk_size = sizeof(DirtyMaskRangeData); - - BLI_task_parallel_range(0, totnode, &data, dirty_mask_compute_range_task_cb, &settings); - data.dirty_mask_min = range.min; - data.dirty_mask_max = range.max; - BLI_task_parallel_range(0, totnode, &data, dirty_mask_apply_task_cb, &settings); - - MEM_SAFE_FREE(nodes); - - BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); - - SCULPT_undo_push_end(ob); - - ED_region_tag_redraw(region); - - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - return OPERATOR_FINISHED; -} - -void SCULPT_OT_dirty_mask(struct wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Dirty Mask"; - ot->idname = "SCULPT_OT_dirty_mask"; - ot->description = "Generates a mask based on the geometry cavity and pointiness"; - - /* API callbacks. */ - ot->exec = sculpt_dirty_mask_exec; - ot->poll = SCULPT_mode_poll; - - ot->flag = OPTYPE_REGISTER; - - /* RNA. */ - RNA_def_boolean( - ot->srna, "dirty_only", false, "Dirty Only", "Don't calculate cleans for convex areas"); -} diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index e576cfda3af..c158cf33f6d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -7,34 +7,24 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_hash.h" #include "BLI_math.h" #include "BLI_task.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BKE_brush.h" #include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_scene.h" #include "DEG_depsgraph.h" #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" -#include "ED_object.h" -#include "ED_screen.h" -#include "ED_sculpt.h" #include "ED_view3d.h" + #include "paint_intern.h" #include "sculpt_intern.h" @@ -101,7 +91,12 @@ static void filter_cache_init_task_cb(void *__restrict userdata, SCULPT_undo_push_node(data->ob, node, data->filter_undo_type); } -void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int undo_type) +void SCULPT_filter_cache_init(bContext *C, + Object *ob, + Sculpt *sd, + const int undo_type, + const int mval[2], + float area_normal_radius) { SculptSession *ss = ob->sculpt; PBVH *pbvh = ob->sculpt->pbvh; @@ -151,14 +146,87 @@ void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int und 0, ss->filter_cache->totnode, &data, filter_cache_init_task_cb, &settings); /* Setup orientation matrices. */ - copy_m4_m4(ss->filter_cache->obmat, ob->obmat); - invert_m4_m4(ss->filter_cache->obmat_inv, ob->obmat); + copy_m4_m4(ss->filter_cache->obmat, ob->object_to_world); + invert_m4_m4(ss->filter_cache->obmat_inv, ob->object_to_world); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; ED_view3d_viewcontext_init(C, &vc, depsgraph); copy_m4_m4(ss->filter_cache->viewmat, vc.rv3d->viewmat); copy_m4_m4(ss->filter_cache->viewmat_inv, vc.rv3d->viewinv); + + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + + float co[3]; + float mval_fl[2] = {(float)mval[0], (float)mval[1]}; + + if (SCULPT_stroke_get_location(C, co, mval_fl, false)) { + PBVHNode **nodes; + int totnode; + + /* Get radius from brush. */ + Brush *brush = BKE_paint_brush(&sd->paint); + float radius; + + if (brush) { + if (BKE_brush_use_locked_size(scene, brush)) { + radius = paint_calc_object_space_radius( + &vc, co, (float)BKE_brush_size_get(scene, brush) * area_normal_radius); + } + else { + radius = BKE_brush_unprojected_radius_get(scene, brush) * area_normal_radius; + } + } + else { + radius = paint_calc_object_space_radius(&vc, co, (float)ups->size * area_normal_radius); + } + + SculptSearchSphereData search_data2 = { + .original = true, + .center = co, + .radius_squared = radius * radius, + .ignore_fully_ineffective = true, + }; + + BKE_pbvh_search_gather(pbvh, SCULPT_search_sphere_cb, &search_data2, &nodes, &totnode); + + if (SCULPT_pbvh_calc_area_normal( + brush, ob, nodes, totnode, true, ss->filter_cache->initial_normal)) { + copy_v3_v3(ss->last_normal, ss->filter_cache->initial_normal); + } + else { + copy_v3_v3(ss->filter_cache->initial_normal, ss->last_normal); + } + + MEM_SAFE_FREE(nodes); + + /* Update last stroke location */ + + mul_m4_v3(ob->object_to_world, co); + + add_v3_v3(ups->average_stroke_accum, co); + ups->average_stroke_counter++; + ups->last_stroke_valid = true; + } + else { + /* Use last normal. */ + copy_v3_v3(ss->filter_cache->initial_normal, ss->last_normal); + } + + /* Update view normal */ + float projection_mat[4][4]; + float mat[3][3]; + float viewDir[3] = {0.0f, 0.0f, 1.0f}; + + ED_view3d_ob_project_mat_get(vc.rv3d, ob, projection_mat); + + invert_m4_m4(ob->world_to_object, ob->object_to_world); + copy_m3_m4(mat, vc.rv3d->viewinv); + mul_m3_v3(mat, viewDir); + copy_m3_m4(mat, ob->world_to_object); + mul_m3_v3(mat, viewDir); + normalize_v3_v3(ss->filter_cache->view_normal, viewDir); } void SCULPT_filter_cache_free(SculptSession *ss) @@ -288,15 +356,20 @@ static void mesh_filter_task_cb(void *__restrict userdata, /* This produces better results as the relax operation is no completely focused on the * boundaries. */ const bool relax_face_sets = !(ss->filter_cache->iteration_count % 3 == 0); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin(data->ob, ss, ss->filter_cache->automasking, &automask_data, node); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float orig_co[3], val[3], avg[3], disp[3], disp2[3], transform[3][3], final_pos[3]; float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); + fade *= SCULPT_automasking_factor_get( + ss->filter_cache->automasking, ss, vd.vertex, &automask_data); if (fade == 0.0f && filter_type != MESH_FILTER_SURFACE_SMOOTH) { /* Surface Smooth can't skip the loop for this vertex as it needs to calculate its @@ -580,11 +653,18 @@ static void mesh_filter_surface_smooth_displace_task_cb( PBVHNode *node = data->nodes[i]; PBVHVertexIter vd; + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[i]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); + fade *= SCULPT_automasking_factor_get( + ss->filter_cache->automasking, ss, vd.vertex, &automask_data); if (fade == 0.0f) { continue; } @@ -681,6 +761,9 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent } if (use_automasking) { + /* Increment stroke id for automasking system. */ + SCULPT_stroke_id_next(ob); + /* Update the active face set manually as the paint cursor is not enabled when using the Mesh * Filter Tool. */ float mval_fl[2] = {UNPACK2(event->mval)}; @@ -696,7 +779,8 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_undo_push_begin(ob, op); - SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); + SCULPT_filter_cache_init( + C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius")); FilterCache *filter_cache = ss->filter_cache; filter_cache->active_face_set = SCULPT_FACE_SET_NONE; @@ -743,6 +827,22 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_RUNNING_MODAL; } +void SCULPT_mesh_filter_properties(struct wmOperatorType *ot) +{ + RNA_def_float( + ot->srna, + "area_normal_radius", + 0.25, + 0.001, + 5.0, + "Normal Radius", + "Radius used for calculating area normal on initial click,\nin percentage of brush radius", + 0.01, + 1.0); + RNA_def_float( + ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); +} + void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) { /* Identifiers. */ @@ -758,14 +858,14 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* RNA. */ + SCULPT_mesh_filter_properties(ot); + RNA_def_enum(ot->srna, "type", prop_mesh_filter_types, MESH_FILTER_INFLATE, "Filter Type", "Operation that is going to be applied to the mesh"); - RNA_def_float( - ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); RNA_def_enum_flag(ot->srna, "deform_axis", prop_mesh_filter_deform_axis_items, diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.c b/source/blender/editors/sculpt_paint/sculpt_geodesic.c index c0856ab21d2..5d74853be8c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.c +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.c @@ -7,52 +7,26 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_linklist_stack.h" #include "BLI_math.h" #include "BLI_task.h" -#include "BLT_translation.h" - #include "DNA_brush_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" -#include "BKE_brush.h" #include "BKE_ccg.h" -#include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_image.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" -#include "BKE_multires.h" -#include "BKE_node.h" #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_scene.h" -#include "BKE_subdiv_ccg.h" - -#include "DEG_depsgraph.h" - -#include "WM_api.h" -#include "WM_toolsystem.h" -#include "WM_types.h" -#include "RNA_access.h" -#include "RNA_define.h" - -#include "ED_object.h" -#include "ED_screen.h" -#include "ED_sculpt.h" -#include "ED_view3d.h" #include "paint_intern.h" #include "sculpt_intern.h" -#include "IMB_colormanagement.h" -#include "IMB_imbuf.h" - #include "bmesh.h" #include <math.h> @@ -170,8 +144,6 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, } } - const bool *hide_poly = BKE_pbvh_get_poly_hide(ss->pbvh); - /* Add edges adjacent to an initial vertex to the queue. */ for (int i = 0; i < totedge; i++) { const int v1 = edges[i].v1; @@ -201,7 +173,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, if (ss->epmap[e].count != 0) { for (int poly_map_index = 0; poly_map_index < ss->epmap[e].count; poly_map_index++) { const int poly = ss->epmap[e].indices[poly_map_index]; - if (hide_poly && hide_poly[poly]) { + if (ss->hide_poly && ss->hide_poly[poly]) { continue; } const MPoly *mpoly = &polys[poly]; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 7a72e5cc84b..bf47b64d176 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -27,6 +27,7 @@ extern "C" { #endif struct AutomaskingCache; +struct AutomaskingNodeData; struct Image; struct ImageUser; struct KeyBlock; @@ -34,6 +35,9 @@ struct Object; struct SculptUndoNode; struct bContext; struct PaintModeSettings; +struct wmKeyConfig; +struct wmOperator; +struct wmOperatorType; /* Updates */ @@ -311,10 +315,6 @@ typedef struct SculptThreadedTaskData { float *cloth_sim_initial_location; float cloth_sim_radius; - float dirty_mask_min; - float dirty_mask_max; - bool dirty_mask_dirty_only; - /* Mask By Color Tool */ float mask_by_color_threshold; @@ -332,7 +332,7 @@ typedef struct SculptThreadedTaskData { int mask_init_seed; ThreadMutex mutex; - + int iteration; } SculptThreadedTaskData; /*************** Brush testing declarations ****************/ @@ -398,13 +398,20 @@ typedef struct AutomaskingSettings { /* Flags from eAutomasking_flag. */ int flags; int initial_face_set; + + float cavity_factor; + int cavity_blur_steps; + struct CurveMapping *cavity_curve; + + float start_normal_limit, start_normal_falloff; + float view_normal_limit, view_normal_falloff; } AutomaskingSettings; typedef struct AutomaskingCache { AutomaskingSettings settings; - /* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and - * initialized in #SCULPT_automasking_cache_init when needed. */ - float *factor; + + bool can_reuse_mask; + uchar current_stroke_id; } AutomaskingCache; typedef struct FilterCache { @@ -464,6 +471,8 @@ typedef struct FilterCache { /* Auto-masking. */ AutomaskingCache *automasking; + float initial_normal[3]; + float view_normal[3]; /* Pre-smoothed colors used by sharpening. Colors are HSL. */ float (*pre_smoothed_color)[4]; @@ -636,6 +645,7 @@ typedef struct StrokeCache { rcti previous_r; /* previous redraw rectangle */ rcti current_r; /* current redraw rectangle */ + int stroke_id; } StrokeCache; /* -------------------------------------------------------------------- */ @@ -913,6 +923,8 @@ float SCULPT_vertex_mask_get(struct SculptSession *ss, PBVHVertRef vertex); void SCULPT_vertex_color_get(const SculptSession *ss, PBVHVertRef vertex, float r_color[4]); void SCULPT_vertex_color_set(SculptSession *ss, PBVHVertRef vertex, const float color[4]); +bool SCULPT_vertex_is_occluded(SculptSession *ss, PBVHVertRef vertex, bool original); + /** Returns true if a color attribute exists in the current sculpt session. */ bool SCULPT_has_colors(const SculptSession *ss); @@ -1004,9 +1016,13 @@ void SCULPT_connected_components_ensure(Object *ob); void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible); bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex); +bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef vertex); +bool SCULPT_vertex_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex); + +void SCULPT_face_visibility_all_invert(SculptSession *ss); +void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible); -void SCULPT_visibility_sync_all_face_sets_to_verts(struct Object *ob); -void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss); +void SCULPT_visibility_sync_all_from_faces(struct Object *ob); /** \} */ @@ -1024,11 +1040,6 @@ bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex); int SCULPT_face_set_next_available_get(SculptSession *ss); void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible); -bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, PBVHVertRef vertex); -bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, PBVHVertRef vertex); - -void SCULPT_face_sets_visibility_invert(SculptSession *ss); -void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible); /** \} */ @@ -1195,7 +1206,8 @@ float SCULPT_brush_strength_factor(struct SculptSession *ss, const float fno[3], float mask, const PBVHVertRef vertex, - int thread_id); + int thread_id, + struct AutomaskingNodeData *automask_data); /** * Tilts a normal by the x and y tilt values using the view axis. @@ -1274,7 +1286,6 @@ void sculpt_dynamic_topology_disable_with_undo(struct Main *bmain, bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush); void SCULPT_dynamic_topology_triangulate(struct BMesh *bm); -void SCULPT_dyntopo_node_layers_add(struct SculptSession *ss); enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob); @@ -1284,9 +1295,30 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob); /** \name Auto-masking. * \{ */ +typedef struct AutomaskingNodeData { + PBVHNode *node; + SculptOrigVertData orig_data; + bool have_orig_data; +} AutomaskingNodeData; + +/** Call before PBVH vertex iteration. + * \param automask_data: pointer to an uninitialized AutomaskingNodeData struct. + */ +void SCULPT_automasking_node_begin(struct Object *ob, + const SculptSession *ss, + struct AutomaskingCache *automasking, + AutomaskingNodeData *automask_data, + PBVHNode *node); + +/* Call before SCULPT_automasking_factor_get and SCULPT_brush_strength_factor. */ +void SCULPT_automasking_node_update(SculptSession *ss, + AutomaskingNodeData *automask_data, + PBVHVertexIter *vd); + float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking, SculptSession *ss, - PBVHVertRef vertex); + PBVHVertRef vertex, + AutomaskingNodeData *automask_data); /* Returns the automasking cache depending on the active tool. Used for code that can run both for * brushes and filter. */ @@ -1302,6 +1334,12 @@ float *SCULPT_boundary_automasking_init(Object *ob, eBoundaryAutomaskMode mode, int propagation_steps, float *automask_factor); +bool SCULPT_automasking_needs_normal(const SculptSession *ss, + const Sculpt *sculpt, + const Brush *brush); +bool SCULPT_automasking_needs_original(const struct Sculpt *sd, const struct Brush *brush); +int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking); + /** \} */ /* -------------------------------------------------------------------- */ @@ -1315,7 +1353,7 @@ float *SCULPT_boundary_automasking_init(Object *ob, * fallback to euclidean distances to one of the initial vertices in the set. */ float *SCULPT_geodesic_distances_create(struct Object *ob, - struct GSet *initial_vertices, + struct GSet *initial_verts, float limit_radius); float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd, struct Object *ob, @@ -1328,8 +1366,14 @@ float *SCULPT_geodesic_from_vertex(Object *ob, PBVHVertRef vertex, float limit_r /** \name Filter API * \{ */ -void SCULPT_filter_cache_init(struct bContext *C, Object *ob, Sculpt *sd, int undo_type); +void SCULPT_filter_cache_init(struct bContext *C, + Object *ob, + Sculpt *sd, + int undo_type, + const int mval[2], + float area_normal_radius); void SCULPT_filter_cache_free(SculptSession *ss); +void SCULPT_mesh_filter_properties(struct wmOperatorType *ot); void SCULPT_mask_filter_smooth_apply( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, int smooth_iterations); @@ -1592,7 +1636,6 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot); /* Mask filter and Dirty Mask. */ void SCULPT_OT_mask_filter(struct wmOperatorType *ot); -void SCULPT_OT_dirty_mask(struct wmOperatorType *ot); /* Mask and Face Sets Expand. */ @@ -1847,6 +1890,15 @@ BLI_INLINE bool SCULPT_tool_is_face_sets(int tool) return ELEM(tool, SCULPT_TOOL_DRAW_FACE_SETS); } +void SCULPT_stroke_id_ensure(struct Object *ob); +void SCULPT_stroke_id_next(struct Object *ob); +bool SCULPT_tool_can_reuse_automask(int sculpt_tool); + #ifdef __cplusplus } #endif + +/* Make SCULPT_ alias to a few blenkernel sculpt methods. */ + +#define SCULPT_vertex_attr_get BKE_sculpt_vertex_attr_get +#define SCULPT_face_attr_get BKE_sculpt_face_attr_get diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index ec246cd3788..1b8cc5347ac 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -7,44 +7,30 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_task.h" #include "BLT_translation.h" #include "DNA_brush_types.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" -#include "BKE_brush.h" #include "BKE_ccg.h" -#include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_scene.h" #include "DEG_depsgraph.h" #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" #include "RNA_access.h" #include "RNA_define.h" -#include "ED_object.h" #include "ED_screen.h" -#include "ED_sculpt.h" -#include "ED_view3d.h" -#include "paint_intern.h" #include "sculpt_intern.h" #include "bmesh.h" @@ -346,6 +332,11 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent SculptCursorGeometryInfo sgi; const float mval_fl[2] = {UNPACK2(event->mval)}; + MultiresModifierData *mmd = BKE_sculpt_multires_active(CTX_data_scene(C), ob); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + SCULPT_vertex_random_access_ensure(ss); op->customdata = MEM_mallocN(sizeof(float[2]), "initial mouse position"); @@ -353,8 +344,6 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); - int vertex_count = SCULPT_vertex_count_get(ss); ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache"); diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c index b9b889ab2ce..99a7bb8a926 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_init.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c @@ -7,27 +7,20 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_hash.h" #include "BLI_math.h" #include "BLI_task.h" -#include "BLT_translation.h" - #include "PIL_time.h" #include "DNA_brush_types.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" -#include "BKE_brush.h" #include "BKE_ccg.h" #include "BKE_context.h" -#include "BKE_mesh.h" #include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" @@ -39,8 +32,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "ED_sculpt.h" -#include "paint_intern.h" #include "sculpt_intern.h" #include "bmesh.h" @@ -120,6 +111,9 @@ static int sculpt_mask_init_exec(bContext *C, wmOperator *op) const int mode = RNA_enum_get(op->ptr, "mode"); + MultiresModifierData *mmd = BKE_sculpt_multires_active(CTX_data_scene(C), ob); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); PBVH *pbvh = ob->sculpt->pbvh; diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c index 1e8731e54c0..fd8f5b8945c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -5,36 +5,22 @@ * \ingroup edsculpt */ -#include "MEM_guardedalloc.h" - -#include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_task.h" #include "DNA_brush_types.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" -#include "BKE_brush.h" #include "BKE_ccg.h" -#include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_scene.h" -#include "paint_intern.h" #include "sculpt_intern.h" #include "GPU_immediate.h" -#include "GPU_immediate_util.h" #include "GPU_matrix.h" -#include "GPU_state.h" #include "bmesh.h" @@ -69,6 +55,10 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, test_radius *= brush->normal_radius_factor; test.radius_squared = test_radius * test_radius; + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -78,6 +68,9 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, float normal[3]; copy_v3_v3(normal, vd.no ? vd.no : vd.fno); mul_v3_m4v3(local_co, mat, vd.co); + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + /* Use the brush falloff to weight the sampled normals. */ const float fade = SCULPT_brush_strength_factor(ss, brush, @@ -87,7 +80,8 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); /* Sample the normal and area of the +X and -X axis individually. */ if (local_co[0] > 0.0f) { @@ -144,6 +138,10 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -184,6 +182,9 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, if (!SCULPT_plane_trim(ss->cache, brush, val)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + /* Deform the local space along the Y axis to avoid artifacts on curved strokes. */ /* This produces a not round brush tip. */ local_co[1] *= 2.0f; @@ -195,7 +196,8 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 055e02a5703..8affb0e9d53 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -8,33 +8,20 @@ #include "MEM_guardedalloc.h" -#include "BLI_array.h" -#include "BLI_blenlib.h" -#include "BLI_dial_2d.h" #include "BLI_ghash.h" #include "BLI_gsqueue.h" -#include "BLI_hash.h" -#include "BLI_link_utils.h" -#include "BLI_linklist.h" -#include "BLI_linklist_stack.h" -#include "BLI_listbase.h" #include "BLI_math.h" -#include "BLI_math_color_blend.h" -#include "BLI_memarena.h" -#include "BLI_rand.h" #include "BLI_task.h" #include "BLI_utildefines.h" -#include "atomic_ops.h" #include "BLT_translation.h" -#include "PIL_time.h" - #include "DNA_brush_types.h" #include "DNA_customdata_types.h" #include "DNA_listBase.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -42,44 +29,23 @@ #include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_ccg.h" -#include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_image.h" -#include "BKE_kelvinlet.h" -#include "BKE_key.h" #include "BKE_layer.h" -#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_mesh.h" -#include "BKE_mesh_fair.h" -#include "BKE_mesh_mapping.h" #include "BKE_mesh_mirror.h" #include "BKE_modifier.h" #include "BKE_multires.h" -#include "BKE_node.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_particle.h" #include "BKE_pbvh.h" -#include "BKE_pointcache.h" #include "BKE_report.h" #include "BKE_scene.h" -#include "BKE_screen.h" -#include "BKE_subdiv_ccg.h" -#include "BKE_subsurf.h" #include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" #include "IMB_colormanagement.h" -#include "GPU_batch.h" -#include "GPU_batch_presets.h" -#include "GPU_immediate.h" -#include "GPU_immediate_util.h" -#include "GPU_matrix.h" -#include "GPU_state.h" - #include "WM_api.h" #include "WM_message.h" #include "WM_toolsystem.h" @@ -89,9 +55,6 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_sculpt.h" -#include "ED_space_api.h" -#include "ED_transform_snap_object_context.h" -#include "ED_view3d.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -103,7 +66,6 @@ #include "UI_resources.h" #include "bmesh.h" -#include "bmesh_tools.h" #include <math.h> #include <stdlib.h> @@ -117,24 +79,33 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op)) Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - if (!ss) { + /* Do not allow in DynTopo just yet. */ + if (!ss || (ss && ss->bm)) { return OPERATOR_FINISHED; } SCULPT_vertex_random_access_ensure(ss); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); - MEM_SAFE_FREE(ss->persistent_base); + SculptAttributeParams params = {0}; + params.permanent = true; + + ss->attrs.persistent_co = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co), ¶ms); + ss->attrs.persistent_no = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_no), ¶ms); + ss->attrs.persistent_disp = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(persistent_disp), ¶ms); const int totvert = SCULPT_vertex_count_get(ss); - ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert, - "layer persistent base"); for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, vertex)); - SCULPT_vertex_normal_get(ss, vertex, ss->persistent_base[i].no); - ss->persistent_base[i].disp = 0.0f; + copy_v3_v3((float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co), + SCULPT_vertex_co_get(ss, vertex)); + SCULPT_vertex_normal_get( + ss, vertex, (float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no)); + (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_disp)) = 0.0f; } return OPERATOR_FINISHED; @@ -300,7 +271,11 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); ob->sculpt->mode_type = OB_MODE_SCULPT; - BKE_sculpt_ensure_orig_mesh_data(ob); + /* Trigger evaluation of modifier stack to ensure + * multires modifier sets .runtime.ccg in + * the evaluated mesh. + */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); BKE_scene_graph_evaluated_ensure(depsgraph, bmain); @@ -348,7 +323,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain, BKE_report( reports, RPT_WARNING, "Object has non-uniform scale, sculpting may be unpredictable"); } - else if (is_negative_m4(ob->obmat)) { + else if (is_negative_m4(ob->object_to_world)) { BKE_report(reports, RPT_WARNING, "Object has negative scale, sculpting may be unpredictable"); } @@ -421,6 +396,7 @@ void ED_object_sculptmode_enter(struct bContext *C, Depsgraph *depsgraph, Report Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports); } @@ -473,6 +449,7 @@ void ED_object_sculptmode_exit(bContext *C, Depsgraph *depsgraph) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); ED_object_sculptmode_exit_ex(bmain, depsgraph, scene, ob); } @@ -485,6 +462,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ToolSettings *ts = scene->toolsettings; ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); const int mode_flag = OB_MODE_SCULPT; const bool is_mode_set = (ob->mode & mode_flag) != 0; @@ -986,6 +964,368 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot) 1.0f); } +typedef enum { + AUTOMASK_BAKE_MIX, + AUTOMASK_BAKE_MULTIPLY, + AUTOMASK_BAKE_DIVIDE, + AUTOMASK_BAKE_ADD, + AUTOMASK_BAKE_SUBTRACT, +} CavityBakeMixMode; + +typedef struct AutomaskBakeTaskData { + SculptSession *ss; + AutomaskingCache *automasking; + PBVHNode **nodes; + CavityBakeMixMode mode; + float factor; + Object *ob; +} AutomaskBakeTaskData; + +static void sculpt_bake_cavity_exec_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + AutomaskBakeTaskData *tdata = userdata; + SculptSession *ss = tdata->ss; + PBVHNode *node = tdata->nodes[n]; + PBVHVertexIter vd; + const CavityBakeMixMode mode = tdata->mode; + const float factor = tdata->factor; + + SCULPT_undo_push_node(tdata->ob, node, SCULPT_UNDO_MASK); + + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin(tdata->ob, ss, tdata->automasking, &automask_data, node); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + + float automask = SCULPT_automasking_factor_get( + tdata->automasking, ss, vd.vertex, &automask_data); + float mask; + + switch (mode) { + case AUTOMASK_BAKE_MIX: + mask = automask; + break; + case AUTOMASK_BAKE_MULTIPLY: + mask = *vd.mask * automask; + break; + break; + case AUTOMASK_BAKE_DIVIDE: + mask = automask > 0.00001f ? *vd.mask / automask : 0.0f; + break; + break; + case AUTOMASK_BAKE_ADD: + mask = *vd.mask + automask; + break; + case AUTOMASK_BAKE_SUBTRACT: + mask = *vd.mask - automask; + break; + } + + mask = *vd.mask + (mask - *vd.mask) * factor; + CLAMP(mask, 0.0f, 1.0f); + + *vd.mask = mask; + } + BKE_pbvh_vertex_iter_end; + + BKE_pbvh_node_mark_update_mask(node); +} + +static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + MultiresModifierData *mmd = BKE_sculpt_multires_active(CTX_data_scene(C), ob); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + SCULPT_vertex_random_access_ensure(ss); + + SCULPT_undo_push_begin(ob, op); + + CavityBakeMixMode mode = RNA_enum_get(op->ptr, "mix_mode"); + float factor = RNA_float_get(op->ptr, "mix_factor"); + + PBVHNode **nodes; + int totnode; + + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + + AutomaskBakeTaskData tdata; + + /* Set up automasking settings. + */ + Sculpt sd2 = *sd; + + /* Override cavity mask settings if use_automask_settings is false. */ + if (!RNA_boolean_get(op->ptr, "use_automask_settings")) { + if (RNA_boolean_get(op->ptr, "invert")) { + sd2.automasking_flags = BRUSH_AUTOMASKING_CAVITY_INVERTED; + } + else { + sd2.automasking_flags = BRUSH_AUTOMASKING_CAVITY_NORMAL; + } + + if (RNA_boolean_get(op->ptr, "use_curve")) { + sd2.automasking_flags |= BRUSH_AUTOMASKING_CAVITY_USE_CURVE; + } + + sd2.automasking_cavity_blur_steps = RNA_int_get(op->ptr, "blur_steps"); + sd2.automasking_cavity_factor = RNA_float_get(op->ptr, "factor"); + + sd2.automasking_cavity_curve = sd->automasking_cavity_curve_op; + } + else { + sd2.automasking_flags &= BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_CAVITY_USE_CURVE; + + /* Ensure cavity mask is actually enabled. */ + if (!(sd2.automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) { + sd2.automasking_flags |= BRUSH_AUTOMASKING_CAVITY_NORMAL; + } + } + + /* Create copy of brush with cleared automasking settings. */ + Brush brush2 = *brush; + brush2.automasking_flags = 0; + brush2.automasking_boundary_edges_propagation_steps = 1; + brush2.automasking_cavity_curve = sd2.automasking_cavity_curve; + + SCULPT_stroke_id_next(ob); + + tdata.ob = ob; + tdata.mode = mode; + tdata.factor = factor; + tdata.ss = ss; + tdata.nodes = nodes; + tdata.automasking = SCULPT_automasking_cache_init(&sd2, &brush2, ob); + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &tdata, sculpt_bake_cavity_exec_task_cb, &settings); + + MEM_SAFE_FREE(nodes); + SCULPT_automasking_cache_free(tdata.automasking); + + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); + SCULPT_undo_push_end(ob); + + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + SCULPT_tag_update_overlays(C); + + return OPERATOR_FINISHED; +} + +static void cavity_bake_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + Scene *scene = CTX_data_scene(C); + Sculpt *sd = scene->toolsettings ? scene->toolsettings->sculpt : NULL; + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + bool use_curve = false; + + if (!sd || !RNA_boolean_get(op->ptr, "use_automask_settings")) { + uiItemR(layout, op->ptr, "mix_mode", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "mix_factor", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_automask_settings", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "factor", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "blur_steps", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "invert", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_curve", 0, NULL, ICON_NONE); + + use_curve = RNA_boolean_get(op->ptr, "use_curve"); + } + else { + PointerRNA sculpt_ptr; + + RNA_pointer_create(&scene->id, &RNA_Sculpt, sd, &sculpt_ptr); + uiItemR(layout, op->ptr, "mix_mode", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "mix_factor", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_automask_settings", 0, NULL, ICON_NONE); + + use_curve = false; + } + + if (use_curve) { + PointerRNA sculpt_ptr; + + const char *curve_prop; + + if (RNA_boolean_get(op->ptr, "use_automask_settings")) { + curve_prop = "automasking_cavity_curve"; + } + else { + curve_prop = "automasking_cavity_curve_op"; + } + + if (scene->toolsettings && scene->toolsettings->sculpt) { + RNA_pointer_create(&scene->id, &RNA_Sculpt, scene->toolsettings->sculpt, &sculpt_ptr); + uiTemplateCurveMapping(layout, &sculpt_ptr, curve_prop, 'v', false, false, false, false); + } + } +} + +static void SCULPT_OT_mask_from_cavity(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Mask From Cavity"; + ot->idname = "SCULPT_OT_mask_from_cavity"; + ot->description = "Creates a mask based on the curvature of the surface"; + ot->ui = cavity_bake_ui; + + static EnumPropertyItem mix_modes[] = { + {AUTOMASK_BAKE_MIX, "MIX", ICON_NONE, "Mix", ""}, + {AUTOMASK_BAKE_MULTIPLY, "MULTIPLY", ICON_NONE, "Multiply", ""}, + {AUTOMASK_BAKE_DIVIDE, "DIVIDE", ICON_NONE, "Divide", ""}, + {AUTOMASK_BAKE_ADD, "ADD", ICON_NONE, "Add", ""}, + {AUTOMASK_BAKE_SUBTRACT, "SUBTRACT", ICON_NONE, "Subtract", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* api callbacks */ + ot->exec = sculpt_bake_cavity_exec; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "mix_mode", mix_modes, AUTOMASK_BAKE_MIX, "Mode", "Mix mode"); + RNA_def_float(ot->srna, "mix_factor", 1.0f, 0.0f, 5.0f, "Mix Factor", "", 0.0f, 1.0f); + + RNA_def_boolean(ot->srna, + "use_automask_settings", + false, + "Use Automask Settings", + "Use default settings from Options panel in sculpt mode"); + + RNA_def_float(ot->srna, + "factor", + 0.5f, + 0.0f, + 5.0f, + "Cavity Factor", + "The contrast of the cavity mask", + 0.0f, + 1.0f); + RNA_def_int(ot->srna, + "blur_steps", + 2, + 0, + 25, + "Cavity Blur", + "The number of times the cavity mask is blurred", + 0, + 25); + RNA_def_boolean(ot->srna, "use_curve", false, "Use Curve", ""); + + RNA_def_boolean(ot->srna, "invert", false, "Cavity (Inverted)", ""); +} + +static int sculpt_reveal_all_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + + Mesh *mesh = BKE_object_get_original_mesh(ob); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + + if (!ss->pbvh) { + return OPERATOR_CANCELLED; + } + + PBVHNode **nodes; + int totnode; + bool with_bmesh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH; + + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + + if (!totnode) { + return OPERATOR_CANCELLED; + } + + /* Propagate face hide state to verts for undo. */ + SCULPT_visibility_sync_all_from_faces(ob); + + SCULPT_undo_push_begin(ob, op); + + for (int i = 0; i < totnode; i++) { + BKE_pbvh_node_mark_update_visibility(nodes[i]); + + if (!with_bmesh) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_HIDDEN); + } + } + + if (!with_bmesh) { + /* As an optimization, free the hide attribute when making all geometry visible. This allows + * reduced memory usage without manually clearing it later, and allows sculpt operations to + * avoid checking element's hide status. */ + CustomData_free_layer_named(&mesh->pdata, ".hide_poly", mesh->totpoly); + ss->hide_poly = NULL; + } + else { + SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_HIDDEN); + + BMIter iter; + BMFace *f; + BMVert *v; + const int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + BM_log_vert_before_modified(ss->bm_log, v, cd_mask); + } + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + BM_log_face_modified(ss->bm_log, f); + } + + SCULPT_face_visibility_all_set(ss, true); + } + + SCULPT_visibility_sync_all_from_faces(ob); + + /* NOTE: #SCULPT_visibility_sync_all_from_faces may have deleted + * `pbvh->hide_vert` if hide_poly did not exist, which is why + * we call #BKE_pbvh_update_hide_attributes_from_mesh here instead of + * after #CustomData_free_layer_named above. */ + if (!with_bmesh) { + BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh); + } + + BKE_pbvh_update_visibility(ss->pbvh); + + SCULPT_undo_push_end(ob); + MEM_SAFE_FREE(nodes); + + SCULPT_tag_update_overlays(C); + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + ED_region_tag_redraw(CTX_wm_region(C)); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_reveal_all(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Reveal All"; + ot->idname = "SCULPT_OT_reveal_all"; + ot->description = "Unhide all geometry"; + + /* Api callbacks. */ + ot->exec = sculpt_reveal_all_exec; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + void ED_operatortypes_sculpt(void) { WM_operatortype_append(SCULPT_OT_brush_stroke); @@ -999,7 +1339,6 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_set_detail_size); WM_operatortype_append(SCULPT_OT_mesh_filter); WM_operatortype_append(SCULPT_OT_mask_filter); - WM_operatortype_append(SCULPT_OT_dirty_mask); WM_operatortype_append(SCULPT_OT_mask_expand); WM_operatortype_append(SCULPT_OT_set_pivot_position); WM_operatortype_append(SCULPT_OT_face_sets_create); @@ -1021,4 +1360,6 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_mask_init); WM_operatortype_append(SCULPT_OT_expand); + WM_operatortype_append(SCULPT_OT_mask_from_cavity); + WM_operatortype_append(SCULPT_OT_reveal_all); } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index c494c71f1eb..ee716d1107a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -7,46 +7,24 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_hash.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" #include "BLI_task.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BKE_brush.h" #include "BKE_colorband.h" #include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_scene.h" - -#include "DEG_depsgraph.h" #include "IMB_colormanagement.h" -#include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" -#include "WM_types.h" - -#include "ED_object.h" -#include "ED_screen.h" -#include "ED_sculpt.h" -#include "paint_intern.h" #include "sculpt_intern.h" -#include "RNA_access.h" -#include "RNA_define.h" - -#include "UI_interface.h" - #include "IMB_imbuf.h" #include "bmesh.h" @@ -70,10 +48,17 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -82,7 +67,8 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float smooth_color[4]; SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex); @@ -125,6 +111,10 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + if (brush->flag & BRUSH_USE_GRADIENT) { switch (brush->gradient_stroke_mode) { case BRUSH_GRADIENT_PRESSURE: @@ -161,6 +151,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -169,7 +161,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); /* Density. */ float noise = 1.0f; @@ -195,8 +188,11 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, paint_color, paint_color, wet_mix_color, ss->cache->paint_brush.wet_mix); blend_color_mix_float(color_buffer->color[vd.i], color_buffer->color[vd.i], paint_color); - /* Final mix over the original color using brush alpha. */ - mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha); + /* Final mix over the original color using brush alpha. We apply auto-making again + * at this point to avoid washing out non-binary masking modes like cavity masking. */ + float automasking = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); + mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha * automasking); float col[4]; SCULPT_vertex_color_get(ss, vd.vertex, col); @@ -402,10 +398,17 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, sub_v3_v3v3(brush_delta, ss->cache->location, ss->cache->last_location); } + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -414,7 +417,8 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float current_disp[3]; float current_disp_norm[3]; diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index 8a3a3fe7adc..d3b3100458d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -2,13 +2,9 @@ * Copyright 2022 Blender Foundation. All rights reserved. */ #include "DNA_image_types.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_node_types.h" #include "DNA_object_types.h" #include "ED_paint.h" -#include "ED_uvedit.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" @@ -19,14 +15,11 @@ #include "BKE_brush.h" #include "BKE_image_wrappers.hh" -#include "BKE_material.h" #include "BKE_pbvh.h" #include "BKE_pbvh_pixels.hh" #include "bmesh.h" -#include "NOD_shader.h" - #include "sculpt_intern.h" namespace blender::ed::sculpt_paint::paint::image { @@ -153,7 +146,10 @@ template<typename ImageBuffer> class PaintingKernel { init_brush_test(); } - bool paint(const Triangles &triangles, const PackedPixelRow &pixel_row, ImBuf *image_buffer) + bool paint(const Triangles &triangles, + const PackedPixelRow &pixel_row, + ImBuf *image_buffer, + AutomaskingNodeData *automask_data) { image_accessor.set_image_position(image_buffer, pixel_row.start_image_coordinate); const TrianglePaintInput triangle = triangles.get_paint_input(pixel_row.triangle_index); @@ -171,6 +167,7 @@ template<typename ImageBuffer> class PaintingKernel { const float3 normal(0.0f, 0.0f, 0.0f); const float3 face_normal(0.0f, 0.0f, 0.0f); const float mask = 0.0f; + const float falloff_strength = SCULPT_brush_strength_factor( ss, brush, @@ -180,7 +177,8 @@ template<typename ImageBuffer> class PaintingKernel { face_normal, mask, BKE_pbvh_make_vref(PBVH_REF_NONE), - thread_id); + thread_id, + automask_data); float4 paint_color = brush_color * falloff_strength * brush_strength; float4 buffer_color; blend_color_mix_float(buffer_color, color, paint_color); @@ -321,6 +319,9 @@ static void do_paint_pixels(void *__restrict userdata, PaintingKernel<ImageBufferFloat4> kernel_float4(ss, brush, thread_id, mvert); PaintingKernel<ImageBufferByte4> kernel_byte4(ss, brush, thread_id, mvert); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + ImageUser image_user = *data->image_data.image_user; bool pixels_updated = false; for (UDIMTilePixels &tile_data : node_data.tiles) { @@ -347,10 +348,12 @@ static void do_paint_pixels(void *__restrict userdata, } bool pixels_painted = false; if (image_buffer->rect_float != nullptr) { - pixels_painted = kernel_float4.paint(node_data.triangles, pixel_row, image_buffer); + pixels_painted = kernel_float4.paint( + node_data.triangles, pixel_row, image_buffer, &automask_data); } else { - pixels_painted = kernel_byte4.paint(node_data.triangles, pixel_row, image_buffer); + pixels_painted = kernel_byte4.paint( + node_data.triangles, pixel_row, image_buffer, &automask_data); } if (pixels_painted) { @@ -421,7 +424,7 @@ static void push_undo(const NodeData &node_data, static void do_push_undo_tile(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict /*tls*/) { TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata); PBVHNode *node = data->nodes[n]; @@ -450,7 +453,7 @@ static void do_push_undo_tile(void *__restrict userdata, static void do_mark_dirty_regions(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict /*tls*/) { TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata); PBVHNode *node = data->nodes[n]; diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index d1418c8dc35..5f671c1f0e1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -7,12 +7,10 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_task.h" #include "DNA_brush_types.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -20,13 +18,8 @@ #include "BKE_ccg.h" #include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_scene.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -157,9 +150,13 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_automasking_node_update(ss, &automask_data, &vd); float total_disp[3]; zero_v3(total_disp); @@ -182,7 +179,8 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, /* Apply the vertex mask to the displacement. */ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); mul_v3_fl(disp, mask * automask); /* Accumulate the displacement. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 2ef3c28ba0c..09b1c69da45 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -7,40 +7,18 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" -#include "BLI_hash.h" #include "BLI_math.h" #include "BLI_task.h" #include "DNA_brush_types.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "BKE_brush.h" #include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_scene.h" -#include "DEG_depsgraph.h" - -#include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" -#include "WM_types.h" - -#include "ED_object.h" -#include "ED_screen.h" -#include "ED_sculpt.h" -#include "paint_intern.h" #include "sculpt_intern.h" -#include "RNA_access.h" -#include "RNA_define.h" - #include "bmesh.h" #include <math.h> @@ -217,11 +195,17 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -230,7 +214,8 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float disp[3]; madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade); @@ -300,11 +285,17 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor( ss, brush, @@ -314,7 +305,8 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), vd.vertex, - thread_id); + thread_id, + &automask_data); if (smooth_mask) { float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask; val *= fade * bstrength; @@ -470,12 +462,18 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( const int thread_id = BLI_task_parallel_thread_id(tls); SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -484,7 +482,8 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float disp[3]; SCULPT_surface_smooth_laplacian_step( @@ -512,11 +511,17 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -525,7 +530,8 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); SCULPT_surface_smooth_displace_step( ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, beta, fade); } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index dfaa0bd4daa..0463e8adbaf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -7,31 +7,22 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_task.h" -#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_kelvinlet.h" -#include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" -#include "BKE_scene.h" #include "DEG_depsgraph.h" #include "WM_api.h" -#include "WM_message.h" -#include "WM_toolsystem.h" #include "WM_types.h" -#include "ED_object.h" #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_view3d.h" @@ -46,7 +37,10 @@ #include <math.h> #include <stdlib.h> -void ED_sculpt_init_transform(struct bContext *C, Object *ob, const char *undo_name) +void ED_sculpt_init_transform(struct bContext *C, + Object *ob, + const int mval[2], + const char *undo_name) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; SculptSession *ss = ob->sculpt; @@ -66,7 +60,8 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob, const char *undo_n ss->pivot_rot[3] = 1.0f; SCULPT_vertex_random_access_ensure(ss); - SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); + + SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS, mval, 5.0); if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) { ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index af94cad88f3..eb92c865f18 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -56,7 +56,6 @@ #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" #include "BKE_mesh_runtime.h" #include "BKE_multires.h" #include "BKE_object.h" @@ -82,6 +81,9 @@ #include "bmesh.h" #include "sculpt_intern.h" +/* Uncomment to print the undo stack in the console on push/undo/redo. */ +//#define SCULPT_UNDO_DEBUG + /* Implementation of undo system for objects in sculpt mode. * * Each undo step in sculpt mode consists of list of nodes, each node contains: @@ -148,11 +150,130 @@ typedef struct SculptUndoStep { SculptAttrRef active_color_end; bContext *C; + +#ifdef SCULPT_UNDO_DEBUG + int id; +#endif } SculptUndoStep; static UndoSculpt *sculpt_undo_get_nodes(void); static bool sculpt_attribute_ref_equals(SculptAttrRef *a, SculptAttrRef *b); static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr); +static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p); + +#ifdef SCULPT_UNDO_DEBUG +# ifdef _ +# undef _ +# endif +# define _(type) \ + case type: \ + return #type; +static char *undo_type_to_str(int type) +{ + switch (type) { + _(SCULPT_UNDO_DYNTOPO_BEGIN) + _(SCULPT_UNDO_DYNTOPO_END) + _(SCULPT_UNDO_COORDS) + _(SCULPT_UNDO_GEOMETRY) + _(SCULPT_UNDO_DYNTOPO_SYMMETRIZE) + _(SCULPT_UNDO_FACE_SETS) + _(SCULPT_UNDO_HIDDEN) + _(SCULPT_UNDO_MASK) + _(SCULPT_UNDO_COLOR) + default: + return "unknown node type"; + } +} +# undef _ + +static int nodeidgen = 1; + +static void print_sculpt_node(Object *ob, SculptUndoNode *node) +{ + printf(" %s:%s {applied=%d}\n", undo_type_to_str(node->type), node->idname, node->applied); + + if (node->bm_entry) { + BM_log_print_entry(ob->sculpt ? ob->sculpt->bm : NULL, node->bm_entry); + } +} + +static void print_sculpt_undo_step(Object *ob, UndoStep *us, UndoStep *active, int i) +{ + SculptUndoNode *node; + + if (us->type != BKE_UNDOSYS_TYPE_SCULPT) { + printf("%d %s (non-sculpt): '%s', type:%s, use_memfile_step:%s\n", + i, + us == active ? "->" : " ", + us->name, + us->type->name, + us->use_memfile_step ? "true" : "false"); + return; + } + + int id = -1; + + SculptUndoStep *su = (SculptUndoStep *)us; + if (!su->id) { + su->id = nodeidgen++; + } + + id = su->id; + + printf("id=%d %s %d %s (use_memfile_step=%s)\n", + id, + us == active ? "->" : " ", + i, + us->name, + us->use_memfile_step ? "true" : "false"); + + if (us->type == BKE_UNDOSYS_TYPE_SCULPT) { + UndoSculpt *usculpt = sculpt_undosys_step_get_nodes(us); + + for (node = usculpt->nodes.first; node; node = node->next) { + print_sculpt_node(ob, node); + } + } +} +void sculpt_undo_print_nodes(Object *ob, void *active) +{ + + printf("=================== Sculpt undo steps ==============\n"); + + UndoStack *ustack = ED_undo_stack_get(); + UndoStep *us = ustack->steps.first; + if (active == NULL) { + active = ustack->step_active; + } + + if (!us) { + return; + } + + printf("\n"); + if (ustack->step_init) { + printf("===Undo initialization stepB===\n"); + print_sculpt_undo_step(ob, ustack->step_init, active, -1); + printf("===============\n"); + } + + int i = 0, act_i = -1; + for (; us; us = us->next, i++) { + if (active == us) { + act_i = i; + } + + print_sculpt_undo_step(ob, us, active, i); + } + + if (ustack->step_active) { + printf("\n\n==Active step:==\n"); + print_sculpt_undo_step(ob, ustack->step_active, active, act_i); + } +} +#else +# define sculpt_undo_print_nodes(ob, active) while (0) +#endif static void update_cb(PBVHNode *node, void *rebuild) { @@ -253,7 +374,9 @@ static bool sculpt_undo_restore_deformed( static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, SculptUndoNode *unode) { + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; @@ -365,7 +488,9 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool *modified_vertices) { + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; @@ -395,7 +520,9 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode, bool *modified_vertices) { + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); SculptSession *ss = ob->sculpt; @@ -427,7 +554,9 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode, bool * static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode, bool *modified_vertices) { + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; @@ -473,11 +602,18 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode, bool *m static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode) { + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); Mesh *me = BKE_object_get_original_mesh(ob); - int *face_sets = CustomData_add_layer( - &me->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, NULL, me->totpoly); + + int *face_sets = CustomData_get_layer_named(&me->pdata, CD_PROP_INT32, ".sculpt_face_set"); + if (!face_sets) { + face_sets = CustomData_add_layer_named( + &me->pdata, CD_PROP_INT32, CD_CONSTRUCT, NULL, me->totpoly, ".sculpt_face_set"); + } + for (int i = 0; i < me->totpoly; i++) { SWAP(int, face_sets[i], unode->face_sets[i]); } @@ -537,7 +673,7 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode) .use_toolflags = false, })); BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); - SCULPT_dyntopo_node_layers_add(ss); + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; /* Restore the BMLog using saved entries. */ @@ -721,7 +857,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; @@ -729,8 +866,13 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase bool update = false, rebuild = false, update_mask = false, update_visibility = false; bool need_mask = false; bool need_refine_subdiv = false; + bool clear_automask_cache = false; for (unode = lb->first; unode; unode = unode->next) { + if (!ELEM(unode->type, SCULPT_UNDO_COLOR, SCULPT_UNDO_MASK)) { + clear_automask_cache = true; + } + /* Restore pivot. */ copy_v3_v3(ss->pivot_pos, unode->pivot_pos); copy_v3_v3(ss->pivot_rot, unode->pivot_rot); @@ -744,6 +886,10 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } } + if (clear_automask_cache) { + ss->last_automasking_settings_hash = 0; + } + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); if (lb->first) { @@ -756,7 +902,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask, false); - SCULPT_visibility_sync_all_face_sets_to_verts(ob); + SCULPT_visibility_sync_all_from_faces(ob); BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); @@ -765,7 +911,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); - if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) { + if (!BKE_sculptsession_use_pbvh_draw(ob, rv3d)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } @@ -912,7 +1058,6 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } if (update_visibility) { - SCULPT_visibility_sync_all_vertex_to_face_sets(ss); BKE_pbvh_update_visibility(ss->pbvh); } @@ -925,7 +1070,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } } - tag_update |= ID_REAL_USERS(ob->data) > 1 || !BKE_sculptsession_use_pbvh_draw(ob, v3d) || + tag_update |= ID_REAL_USERS(ob->data) > 1 || !BKE_sculptsession_use_pbvh_draw(ob, rv3d) || ss->shapekey_active || ss->deform_modifiers_active; if (tag_update) { @@ -1354,7 +1499,7 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ unode->face_sets = MEM_callocN(me->totpoly * sizeof(int), "sculpt face sets"); - const int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer_named(&me->pdata, CD_PROP_INT32, ".sculpt_face_set"); if (face_sets) { for (int i = 0; i < me->totpoly; i++) { unode->face_sets[i] = face_sets[i]; @@ -1645,6 +1790,7 @@ void SCULPT_undo_push_end_ex(struct Object *ob, const bool use_nested_undo) ustack, BKE_UNDOSYS_TYPE_SCULPT); sculpt_save_active_attribute(ob, &us->active_color_end); + sculpt_undo_print_nodes(ob, NULL); } /* -------------------------------------------------------------------- */ @@ -1744,6 +1890,8 @@ static void sculpt_undosys_step_decode_undo_impl(struct bContext *C, sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); us->step.is_applied = false; + + sculpt_undo_print_nodes(CTX_data_active_object(C), NULL); } static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, @@ -1754,6 +1902,8 @@ static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); us->step.is_applied = true; + + sculpt_undo_print_nodes(CTX_data_active_object(C), NULL); } static void sculpt_undosys_step_decode_undo(struct bContext *C, @@ -1823,6 +1973,7 @@ static void sculpt_undosys_step_decode( { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); if (ob && (ob->type == OB_MESH)) { if (ob->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT)) { |